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

C 如何在Windows 7 X64 SP1(X64模式)下执行直接系统调用?,c,windows,assembly,x86-64,system-calls,C,Windows,Assembly,X86 64,System Calls,我试图模拟syscall指令如何在Windows7x64(SP1)上工作,因此我用MinGW64编写了一个64位GCC示例。我知道,对于Windows,所有系统调用入口点都在ntdll.dll或ntdll32.dll中(在本例中,我们只关心ntdll.dll) 这是C编写的源代码的原始部分,然后我用gas重写了它 asm volatile ( "leaq %4, %%r9\n\t" "leaq %3, %%r8\n\t" "movq %2, %%rdx\n\t

我试图模拟syscall指令如何在Windows7x64(SP1)上工作,因此我用MinGW64编写了一个64位GCC示例。我知道,对于Windows,所有系统调用入口点都在ntdll.dll或ntdll32.dll中(在本例中,我们只关心ntdll.dll)

这是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,仅saw(重写为GAS AT&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”,程序失败。现在我有几个困惑的问题:

  • 在这里,存储在用户模式堆栈中的参数是如何进入内核模式的?因为我看到NtDll中没有任何操作!NtCreateFile

  • 如何将正确的返回值分配回%%rax?这一部分在disassmebler中也是错误的

  • 如何使我的代码在执行直接系统调用时能够快速工作

  • 非常感谢你的帮助


    好的,这里显示工作代码

    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"
    );
    

    模拟调用/ret并不是一件很痛苦的事情。这里我使用了Linus在其Linux 0.11中使用过的一种变通方法。

    关于堆栈的深度,我认为您是错的。 许多参数都是通过堆栈传递的。如果库调用介于两者之间,则系统调用会准确地预测它们所在的位置

    如果您跳过库调用,自己进行系统调用(您只应该为实验而不是生产性的东西做这件事!),那么堆栈上缺少一项

    因此,要么将虚拟值推送到堆栈,要么调整偏移

    具体而言,在原始代码中发生了以下情况:

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

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

    您实际上想做什么?您是否正在尝试解决如何在asm中调用NtCreateFile?或者您正在尝试实现NtCreateFile?第二个,实现NtCreateFile进行实验在这种情况下,放弃尝试在asm中编写调用。那真是把事情弄糊涂了。用C编写调用,用asm编写实现。你为什么要这么做。我只是想写一篇文章来介绍这个系统调用,所以最好给出一个简单可行的例子,让读者很容易理解它是如何工作的。这是我的目标。确切的机制不取决于确切的系统吗?也就是说,AMD使用的是SysCenter,而不是SYSCALL?非常感谢您的帮助,我现在更正了错误。裸函数在X86/X64中不支持,因此我不得不删除编译器manullay生成的序言/尾声序列,测试表明您的观点完全正确。regardsA✓ 可以用来表示答案已经解决了问题。
    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"
    );
    
    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"
    );