【问题标题】:how to perform direct syscall under Windows 7 X64 SP1 (x64 mode)?如何在 Windows 7 X64 SP1(x64 模式)下执行直接系统调用?
【发布时间】:2013-08-23 09:32:31
【问题描述】:

我试图模拟 syscall 指令在 Windows 7 X64 (SP1) 上的工作方式,因此我使用 MinGW64 编写了一个 64 位 GCC 示例。据我所知,对于 Windows,所有系统调用入口点都在 ntdll.dll 或 ntdll32.dll 中(在这种情况下,我们只关心 ntdll.dll)。

Status = NtCreateFile(&FileHandle,                      // returned file handle
                      (GENERIC_WRITE | SYNCHRONIZE),    // desired access
                      &ObjectAttributes,                // ptr to object attributes
                      &Iosb,                            // ptr to I/O status block
                      0,                                // allocation size
                      FILE_ATTRIBUTE_NORMAL,            // file attributes
                      0,                                // share access
                      FILE_SUPERSEDE,                   // create disposition
                      FILE_SYNCHRONOUS_IO_NONALERT,     // create options
                      NULL,                             // ptr to extended attributes
                      0);                               // length of ea buffer

这是C写的原始部分源代码,然后我用gas重写了它

asm volatile
(
    "leaq   %4, %%r9\n\t"
    "leaq   %3, %%r8\n\t"
    "movq   %2, %%rdx\n\t"
    "leaq   %1, %%rcx\n\t"
    "movq   %11,0x50(%%rsp)\n\t"
    "movq   %10,0x48(%%rsp)\n\t"
    "movq   %9, 0x40(%%rsp)\n\t"
    "movq   %8, 0x38(%%rsp)\n\t"
    "movq   %7, 0x30(%%rsp)\n\t"
    "movq   %6, 0x28(%%rsp)\n\t"
    "movq   %5, 0x20(%%rsp)\n\t"
    "movq   %%r9, 0x18(%%rsp)\n\t"
    "movq   %%r8, 0x10(%%rsp)\n\t"
    "movq   %%rdx, 0x8(%%rsp)\n\t"
    "movq   %%rcx, (%%rsp)\n\t"
    "movq   __imp_NtCreateFile(%%rip), %%rax\n\t"
    "call   *%%rax\n\t"
    : "=a"(Status)
    : "m"(FileHandle), "g"(GENERIC_WRITE | SYNCHRONIZE),"m"(ObjectAttributes),"m"(Iosb),"g"(0),"g"(FILE_ATTRIBUTE_NORMAL),"g"(0),"g"(FILE_SUPERSEDE),"g"(FILE_SYNCHRONOUS_IO_NONALERT),"g"(NULL),"g"(0)
    : "%rcx", "%rdx", "%r8", "%r9", "%r10","%r11"
);

到目前为止,程序按预期工作:它创建了一个文本文件并在文件中写入了一些内容。

我用windbg反汇编ntdll!NtCreateFile,只看到(改写为GAS AT&T格式)

    "movq   $0x52, %%rax\n\t"
    "movq   %%rcx, %%r10\n\t"
    "syscall\n\t"
    "ret\n\t"

我在我的程序中添加了这部分代码

asm volatile
(
    "leaq   %4, %%r9\n\t"
    "leaq   %3, %%r8\n\t"
    "movq   %2, %%rdx\n\t"
    "leaq   %1, %%rcx\n\t"
    "movq   %11,0x50(%%rsp)\n\t"
    "movq   %10,0x48(%%rsp)\n\t"
    "movq   %9, 0x40(%%rsp)\n\t"
    "movq   %8, 0x38(%%rsp)\n\t"
    "movq   %7, 0x30(%%rsp)\n\t"
    "movq   %6, 0x28(%%rsp)\n\t"
    "movq   %5, 0x20(%%rsp)\n\t"
    "movq   %%r9, 0x18(%%rsp)\n\t"
    "movq   %%r8, 0x10(%%rsp)\n\t"
    "movq   %%rdx, 0x8(%%rsp)\n\t"
    "movq   %%rcx, (%%rsp)\n\t"
    "movq   $0x52, %%rax\n\t"
    "movq   %%rcx, %%r10\n\t"
    "syscall\n\t"
    : "=a"(Status)
    : "m"(FileHandle), "g"(GENERIC_WRITE | SYNCHRONIZE),"m"(ObjectAttributes),"m"(Iosb),"g"(0),"g"(FILE_ATTRIBUTE_NORMAL),"g"(0),"g"(FILE_SUPERSEDE),"g"(FILE_SYNCHRONOUS_IO_NONALERT),"g"(NULL),"g"(0)
    : "%rcx", "%rdx", "%r8", "%r9", "%r10","%r11"
);

现在状态总是返回值“0xc000000d”,程序失败。现在我有几个困惑的问题:

  1. 存储在用户模式堆栈中的参数如何在这里传递到内核模式?因为我看到在 NtDll!NtCreateFile 中什么都没做。

  2. 如何将正确的返回值分配回 %%rax?这部分在 disassmebler 中也丢失了。

  3. 在执行直接系统调用时如何使我的代码按预期工作?

非常感谢您的大力帮助。


好的,这里显示工作代码

asm volatile
(
    "leaq   %4, %%r9\n\t"
    "leaq   %3, %%r8\n\t"
    "movq   %2, %%rdx\n\t"
    "leaq   %1, %%rcx\n\t"
    "movq   %11,0x50(%%rsp)\n\t"
    "movq   %10,0x48(%%rsp)\n\t"
    "movq   %9, 0x40(%%rsp)\n\t"
    "movq   %8, 0x38(%%rsp)\n\t"
    "movq   %7, 0x30(%%rsp)\n\t"
    "movq   %6, 0x28(%%rsp)\n\t"
    "movq   %5, 0x20(%%rsp)\n\t"
    "push $_end \n\t"
    "movq  %%rcx,%%r10\n\t"
    "movq  $0x52,%%rax\n\t"
    "syscall\n\t"
    "ret\n\t"
    "_end:\n\t"
    : "=a"(Status)
    : "m"(FileHandle), "g"(GENERIC_WRITE | SYNCHRONIZE),"m"(ObjectAttributes),"m"(Iosb),"g"(0),"g"(FILE_ATTRIBUTE_NORMAL),"g"(0),"g"(FILE_SUPERSEDE),"g"(FILE_SYNCHRONOUS_IO_NONALERT),"g"(NULL),"g"(0)
    : "%rcx", "%rdx", "%r8", "%r9", "%r10","%r11"
);

模拟 call/ret 并不是很痛苦。在这里,我使用了 Linus 在他的 Linux 0.11 中使用过的解决方法。

【问题讨论】:

  • 你到底想做什么?你想弄清楚如何在 asm 中调用 NtCreateFile 吗?还是您尝试实现 NtCreateFile?
  • 第二个,实现NtCreateFile进行实验
  • 在这种情况下,放弃尝试在 asm 中编写调用。这使事情变得混乱。用 C 写调用,用 asm 写实现。你为什么还要这样做。这似乎毫无意义,我预计它会失败。
  • 我只是想写一篇文章介绍系统调用,所以最好给出一个简单可行的例子,让读者容易理解它是如何工作的。这是我的目标。
  • 确切的机制不是依赖于确切的系统吗? IE。 AMD 使用 SYSENTER,而不是 SYSCALL?

标签: c windows assembly x86-64 system-calls


【解决方案1】:

我认为您对堆栈深度的看法是错误的。 许多参数是通过堆栈传递的。如果库调用介于两者之间,则系统调用期望它们准确地位于它们的位置。

如果您跳过库调用并自己执行系统调用(您应该这样做只是为了试验,而不是为了生产性的东西!),堆栈中缺少一项。

所以要么将一个虚拟值压入堆栈,要么调整偏移量。

详细来说,原代码中发生了以下情况:

  • 您将参数放入堆栈(最多 movq %%rcx, (%%rsp))。
  • 您执行call__imp_NtCreateFile这会将返回地址放入堆栈,并将%tip 传输到库函数。
  • 然后,库函数基本上执行系统调用。
  • 然后内核期望数据距栈顶一项,因为上述调用增加了一项。

如果您自己进行系统调用,则必须放入另一个项目以补偿此返回地址,该地址会移动内核的堆栈视图。

【讨论】:

  • 非常感谢您的帮助,我现在更正了错误。在 X86/X64 中不支持裸函数,所以我不得不删除编译器手动生成的序言/尾声序列,测试表明您的观点完全正确。问候
  • 可以用✓表示答案已经解决了问题。
猜你喜欢
  • 2013-11-14
  • 2012-05-17
  • 1970-01-01
  • 2013-09-24
  • 2011-02-25
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多