【问题标题】:Trying to create a windows 8 syscall callgate function试图创建一个 Windows 8 系统调用 callgate 函数
【发布时间】:2015-04-24 09:45:42
【问题描述】:

我有一个 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 做同样的事情。我已经更新了所有的函数调用索引;但是我注意到实际的 callgate 函数在 windows 8.1 上是完全不同的:

这是函数 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

现在这里是 Windows 8.1 上完全相同的 NT 函数 (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 功能的外观来看,我试图提出这个单一的功能(不起作用):

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;
    };
}

任何帮助将不胜感激。

【问题讨论】:

  • 为什么-哦-为什么要在不编写驱动程序的情况下这样做?

标签: c++ windows assembly


【解决方案1】:

您的新 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

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

【讨论】:

  • 我知道有人说不要离开 cmets 只是为了表示感谢,但我特别想回来感谢您,因为您的代码向我打开了 retn 表的想法,因此允许我写这个:pastebin.com/PBHTd5E9
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-09
  • 2022-01-02
  • 2021-01-08
  • 2021-04-20
  • 1970-01-01
相关资源
最近更新 更多