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

标签 c windows assembly x86-64 system-calls

我试图模拟 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"
);

现在 Status 总是返回值“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 中使用过的解决方法。

最佳答案

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

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

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

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

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

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

关于c - 如何在 Windows 7 X64 SP1(x64 模式)下执行直接系统调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18350185/

相关文章:

c++ - 跟踪反汇编代码中的调用堆栈

c - 'private' 是 C 关键字吗?

c - 用C存储结构元素

c - 在数组中存储整数和字符

c++ - 如何将功能齐全的 Web 浏览器嵌入到我的 C++ 应用程序中

winapi - 如何让这两行在 cmd 控制台中的不同行上打印

c - 嘈杂的 ADC dsPIC

windows - 是否有任何可用的脚本语言与 expect 脚本的 linux 完全相似

c++ - 在安装了 VS08 的旧 XP 中使用最新的 dll 函数

c++ - 通过 DLL 在 x64 上进行内联汇编