我试图模拟 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',程序失败。现在我有几个困惑的问题:
存储在用户模式堆栈中的参数如何在这里传递到内核模式?因为我看到在 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"
);
模拟 call/ret 并不是很痛苦。在这里,我使用了 Linus 在他的 Linux 0.11 中使用过的解决方法。