GetLastError

GetLastError() in the Win32 API retrieves the error code for the last operation, aiding in diagnosing and handling errors in Windows programming.

Sometimes when we are calling Win32 APIs from shellcode we need to understand why the function has not carried out the tasks we expected. The Win32 APIs have a really useful function called GetLastError. We can make this one of the first functions we resolve and use it later in subsequent assembly code.

Resolving the function is the same as any other:

call_getprocaddress_getlasterror:
    mov [rbp-0x28], rbx             ; [RBP-0x28] = Kernel32 base address
    mov rcx, [rbp-0x28]             ; RCX = hModule = Kernel32 base address
    mov rax, 0x726f7272             ;
    push rax                        ;
    mov rax, 0x457473614c746547     ;
    push rax                        ;
    mov rdx, rsp                    ; RDX = lpProcName = GetLastError  
    sub rsp, 0x2c                   ; Allocate stack space for the function call 
                                    ; (+ alignment)
    call [rbp-0x18]                 ; CALL GetProcAddress
    add rsp, 0x2c                   ; Clean up allocated space
    add rsp, 0x10                   ; Clean up GetLastError on stack
    mov [rbp-0x8], rax              ; [RBP-0x8] = *GetLastError

Now, if we are not seeing the results we expect from Win32 APIs, we can call GetLastError and the error code will be in rax after the call:

sub rsp, 0x2c                   ; Allocate stack space for the function call (+         
                                ; alignment)
call [rbp-0x8]                  ; Call GetLastError
int3                            ; Break to examine error in rax

Once your shellcode is working as intended you can remove the GetLastError code.

Example

The following example uses a the WinHttp APIs to download some malicious shellcode, with the intention of injectiing it in to memory (this could be a basic stager):

The flow on the left shows the order in which the calls should be made. The flow on the right shows what happens if we forget to call WinHttpReceiveResponse.

WinHttpQueryDataAvailable will return 0 instead of the number of bytes in the shellcode. This is because it has failed. If we find this during debugging our mistake might not be that obvious.

During the different calls the HINTERNET handle is used to track the request. If we use the GetLastError call imediately after our mistake we can get an insight in to why it failed.

The 12019 error is the ERROR_INTERNET_INCORRECT_HANDLE_STATE, which should help us diagnose the problem.

Last updated