3

我试图模拟 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 中使用过的解决方法。

4

1 回答 1

3

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

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

因此,要么将一个虚拟值推入堆栈,要么调整偏移量。

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

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

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

于 2013-08-21T06:27:24.360 回答