试图创build一个Windows 8的系统调用callgate函数

我有一个Windows 7的callgate函数,我用它来直接调用NT函数:

//Windows 7 syscall __declspec(naked) NTSTATUS __fastcall wow64 ( DWORD ecxId, char *edxArgs ) { __asm { mov eax, ecx; mov ecx, m_param; call DWORD ptr fs:[0xc0]; add esp, 0x4; retn; }; } NTSTATUS callGate ( DWORD id, ... ) { va_list valist; va_start(valist,id); return wow64(id,valist); } //Example NTClose function NTSTATUS closeHandle ( void *object ) { m_param = 0; return callGate ( 0xc, object ); } 

我正在尝试为Windows 8.1做同样的事情。 我已经更新了所有的函数调用索引; 但是我注意到在Windows 8.1上实际的callgate函数是完全不同的:

这里是实际的调用门看起来像(位于ntdll.dll)函数ZwCreateThreadEx

 mov eax, 0xA5 //the call index xor ecx, ecx //(m_param) lea edx, dword ptr ss:[esp + 0x4] //this causes an sp-analysis failure in IDA call dword ptr fs:[0xC0] add esp, 0x4 retn 0x2C 

现在这里是在Windows 8.1上相同的NTfunction(ZwCreateThreadEx)

 mov eax, 0xB0 //the call index call dword ptr fs:[0xC0] retn 0x2C //2c/4 = 11 parameters 

我一直在尝试各种各样的东西,让这个工作在Windows 8.1,但没有成功。 我无法解释是什么问题或什么是错误的,我所知道的是我在Windows 7上正确地做到这一点。

从W8.1的function看,我试图想出这个单一的function(不起作用):

 DWORD dwebp,dwret,dwparams; //for saving stuff NTSTATUS __cdecl callGate ( DWORD id, DWORD numparams, ... ) { _asm { pop dwebp; //save ebp off stack pop dwret; //save return address pop eax; //save id pop dwparams; //save param count push dwret; //push return addy back onto stack cuz thats how windows has it JMP DWORD ptr fs:[0xc0]; //call with correct stackframe (i think) mov ecx, numparams; //store num params imul ecx, 4; //multiply numparams by sizeof(int) add esp, ecx; //add to esp ret; }; } 

任何帮助将不胜感激。

你的新的callGate函数没有设置你想要的堆栈帧,堆栈顶部的返回地址是callGate的返回地址,而不是调用后的指令。

在您的示例ZwCreateThreadEx从Windows 8.1中执行CALL指令后,这就是堆栈的样子:

  • 返回地址( retn 0x2c指令)
  • 返回地址(ZwCreateThreadEx的调用者)
  • 参数(11个DWORD)

在新的callGate函数中执行JMP指令后,堆栈看起来像这样:

  • 返回地址(callGate的来电者)
  • 参数

您的新callGate函数还有其他问题。 它将值保存在全局变量中,这意味着你的函数不是线程安全的。 两个线程不能同时调用callBack ,而不会callBack这些保存的值。 它使用内联汇编,它使得你的代码更加复杂,并且依赖于未记录的行为:编译器如何为函数设置堆栈。

以下是我在MASM中编写Windows 8.1版callGate的方法:

 _text SEGMENT MAXARGS = 16 do_call MACRO argcount @@call&argcount: call DWORD PTR fs:[0C0h] ret argcount * 4 ENDM call_table_entry MACRO argcount DD OFFSET @@call&argcount ENDM _callGate PROC pop edx ; return address pop eax ; id pop ecx ; numparams push edx ; return address cmp ecx, MAXARGS jg @@fail jmp [@@call_table + ecx * 4] @@args = 0 REPT MAXARGS + 1 do_call %@@args @@args = @@args + 1 ENDM @@fail: ; add better error handling int 3 jmp @@fail @@call_table: @@args = 0 REPT MAXARGS + 1 call_table_entry %@@args @@args = @@args + 1 ENDM _callGate ENDP _TEXT ENDS END 

此实现仅限于MAXARGS参数(如果任何Windows系统调用的参数超过16个,则更改该值)。 它使用宏生成一个CALL / RET代码块的表格,以避免在整个调用中存储参数的数量。 我有一个支持任意数量的参数的版本,但是这个版本比较复杂,而且稍慢一些。 这个实现是未经测试的,我没有Windows 8.1。