我试图模拟系统调用指令如何在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”返回,程序失败。现在我有几个疑惑的问题:
这里存储在用户模式堆栈中的参数如何进入内核模式?因为我在NtDll中看不到任何内容!NtCreateFile。
如何将正确的返回值分配回%% rax?这部分内容也是不诚实的。
如何在执行直接系统调用时使我的代码工作正常?
非常感谢你的帮助。
好的,这里显示工作代码
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中使用过的解决方法。
答案 0 :(得分:3)
我认为你对堆栈的深度是错误的。 许多参数都是通过堆栈传递的。如果库调用介于两者之间,系统调用会将它们确切地置于它们所在的位置。
如果您跳过库调用并自己进行系统调用(您只应该进行实验,而不是生产性的东西!),堆栈中缺少一个项目。
因此要么将虚拟值推入堆栈,要么调整偏移量。
详细说明,原始代码中会发生以下情况:
movq %%rcx, (%%rsp)
)。call
到__imp_NtCreateFile
。 这会将返回地址放入堆栈并执行%tip
到库函数的传输。如果您自己进行系统调用,则必须放入另一个项目以补偿此移动内核的堆栈视图的返回地址。