如何在Windows 7 X64 SP1(x64模式)下执行直接系统调用?

时间:2013-08-21 06:21:15

标签: c windows assembly

我试图模拟系统调用指令如何在Windows 7 X64(SP1)上运行,因此我使用MinGW64编写64位GCC示例。据我所知,对于Windows,所有syscall入口点都在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?这部分内容也是不诚实的。

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

  4. 非常感谢你的帮助。


    好的,这里显示工作代码

    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中使用过的解决方法。

1 个答案:

答案 0 :(得分:3)

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

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

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

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

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

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