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

app*_*der 6 c++ windows assembly

我有一个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 );
}
Run Code Online (Sandbox Code Playgroud)

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

以下是函数ZwCreateThreadEx的实际调用门(位于ntdll.dll中)

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
Run Code Online (Sandbox Code Playgroud)

现在这里是Windows 8.1上的完全相同的NT函数(ZwCreateThreadEx)

mov eax, 0xB0 //the call index
call dword ptr fs:[0xC0] 
retn 0x2C //2c/4 = 11 parameters
Run Code Online (Sandbox Code Playgroud)

我一直在尝试各种各样的东西,以便在Windows 8.1上工作,但无济于事.我无法解释问题是什么或出了什么问题,我所知道的是我在Windows 7上正确地做到了.

从W8.1功能的外观来看,我试图提出这个单一的功能(不起作用):

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;
    };
}
Run Code Online (Sandbox Code Playgroud)

任何帮助将非常感激.

Ros*_*dge 2

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

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

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

以下是在新的 callGate 函数中执行 JMP 指令后堆栈的样子:

  • 返回地址(callGate 的调用者)
  • 论据

您的新 callGate 函数还存在其他问题。它将值保存在全局变量中,这意味着您的函数不是线程安全的。两个线程不能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
Run Code Online (Sandbox Code Playgroud)

此实现仅限于MAXARGS参数(如果任何 Windows 系统调用采用超过 16 个参数,请更改值)。它使用宏生成 CALL/RET 代码块表,以避免在调用过程中在某处存储参数数量。我有一个支持任意数量参数的版本,但它更复杂并且速度慢一些。此实现未经测试,我没有 Windows 8.1。