Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to create a windows 8 syscall callgate function

I have a windows 7 callgate function that I use to call NT functions directly:

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

I am trying to do the same thing for windows 8.1. I have updated all of the function call indexes; however I noticed the actual callgate function is quite different on windows 8.1:

Here is what the actual call gate looks like (located in ntdll.dll) for the function 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

Now here is the EXACT same NT function (ZwCreateThreadEx) on windows 8.1

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

I have been trying all kinds of stuff to get this working on windows 8.1 but have had no avail. I cannot explain what the issue is or what is going wrong, all I know is I am doing it correctly on windows 7.

From the looks of the W8.1 function, I have attempted to come up with this single function (Does not work):

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

Any help would be appreciated greatly.

like image 695
applecider Avatar asked Apr 24 '15 09:04

applecider


1 Answers

Your new callGate function doesn't set up the stack frame you want, the return address at the top of the stack is return address of callGate not the instruction after the call.

This is what the stack looks like after the CALL instruction is executed in your example ZwCreateThreadEx from Windows 8.1:

  • return address (retn 0x2c instruction)
  • return address (caller of ZwCreateThreadEx)
  • arguments (11 DWORDs)

Here's what the stack looks like after the JMP instruction is executed in your new callGate function:

  • return address (caller of callGate)
  • arguments

There are other problems with your new callGate function. It saves values in global variables which means you function isn't thread safe. Two threads can't call callBack at the same time without trashing these saved values. It uses inline assembly which both makes your code more complicated that it needs to be and make its dependent on undocumented behaviour: how the compiler will set up the stack for the function.

Here's how I write your Windows 8.1 version of callGate in MASM:

_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

This implementation is limited to MAXARGS arguments (change the value if any Windows system call takes more than 16 arguments). It uses macros generate a table of CALL/RET code blocks to avoid having to store the number of arguments somewhere across the call. I have a version that supports any number of arguments but it's more complicated and a fair bit slower. This implementation is untested, I don't have Windows 8.1.

like image 74
Ross Ridge Avatar answered Nov 07 '22 04:11

Ross Ridge