2

I tried the following payload for APC injection in Windows.
It gets executed successfully(pops up a MessageBox) but the process crashes after the execution of payload.
What could be the possible reason(s) for this?

Payload:

push   ebp                     ;save ebp
mov    ebp,esp                 ;start new frame
sub    esp,0x8                 ;make space for strings
mov    BYTE PTR [ebp-0x4],0x48 ;store strings
mov    BYTE PTR [ebp-0x3],0x0
mov    BYTE PTR [ebp-0x8],0x46
mov    BYTE PTR [ebp-0x7],0x0
push   eax                     ;save registers to restore the values later
                               ;(I think that I need not save all registers
                               ;but only the ones I use,
                               ;but since the process kept crashing after the payload execution
                               ;I thought let's not take a chance! :P
push   ebx
push   ecx
push   edx
push   edi
push   esi
push   0x0                     ;push arguments for MessageBox()
lea    eax,[ebp-0x4]
push   eax
lea    eax,[ebp-0x8]
push   eax
push   0x0
call   DWORD PTR [ebp+0x8]     ;call MessageBox()
pop    esi                     ;restore registers
pop    edi
pop    edx
pop    ecx
pop    ebx
pop    eax
xor    eax, eax
mov    esp,ebp
pop    ebp                     ;restore ebp
ret                            ;return

Injection code:

QueueUserAPC((PAPCFUNC)p, hThread, messageBoxAddr); //p: address of payload(written to victim process)

Error Message(From IDA Pro, WinDbg):

The instruction at 0x771C63BD referenced memory at 0x75C2442D. The memory could not be read -> 75C2442D (exc.code c0000005, tid 2948)

It basically says that edi in mov ecx, [edi+2CCh] (coming after the payload execution) has an invalid address.

Disassembled code near crash:

ntdll:771C63BD ; ---------------------------------------------------------------------------
ntdll:771C63BD mov     ecx, [edi+2CCh]    ;<--- It CRASHES here
ntdll:771C63C3 mov     large fs:0, ecx
ntdll:771C63CA push    1
ntdll:771C63CC push    edi
ntdll:771C63CD call    near ptr ntdll_ZwContinue
ntdll:771C63D2 mov     esi, eax
ntdll:771C63D4
ntdll:771C63D4 loc_771C63D4:                           ; CODE XREF: ntdll:ntdll_KiUserApcDispatcher+42j
ntdll:771C63D4 push    esi
ntdll:771C63D5 call    near ptr ntdll_RtlRaiseStatus
ntdll:771C63DA jmp     short loc_771C63D4

Call stack:

enter image description here

9
  • 3
    Please provide more information as backtrace, instructions around the crash etc
    – sudhackar
    Oct 15, 2018 at 10:48
  • @sudhackar, I have added the disassembled code Oct 15, 2018 at 11:03
  • Excute your code step by step at the assembly level in a debugger and try to catch the bug...
    – perror
    Oct 15, 2018 at 13:41
  • 1
    Could it be you need to clear the stack before returning? (I can see you use [ebp + 8], but ret with no argument. It depends on the calling convention though). It doesn't explain the crush if it's right after the return but it might be it if some processing was done before the crash
    – user22970
    Oct 22, 2018 at 1:03
  • 1
    Cool :D. What made me think about that was raymond chen's article: blogs.msdn.microsoft.com/oldnewthing/20040115-00/?p=41043
    – user22970
    Oct 23, 2018 at 0:20

1 Answer 1

3

Here's the rest of the function where the crash happens:

_KiUserApcDispatcher@16 proc near
 lea     eax, [esp+2DCh]
 mov     ecx, large fs:0
 mov     edx, offset _KiUserApcExceptionHandler@16 ; KiUserApcExceptionHandler(x,x,x,x)
 mov     [eax], ecx
 mov     [eax+4], edx
 mov     large fs:0, eax
 pop     eax
 lea     edi, [esp+0Ch]
 call    eax

As you can see, edi is intialized from esp, so the crash likely happens because of wrong esp after the call. Now, let's check the API headers:

WINBASEAPI
DWORD
WINAPI
QueueUserAPC(
    _In_ PAPCFUNC pfnAPC,
    _In_ HANDLE hThread,
    _In_ ULONG_PTR dwData
    );

and PACPFUNC is:

typedef
VOID
(NTAPI *PAPCFUNC)(
    _In_ ULONG_PTR Parameter
    );

Where NTAPI is:

#define NTAPI __stdcall

__stdcall functions are responsible for cleaning up the stack from their incoming arguments, and since our function accepts one argument of type ULONG_PTR (a pointer, so 4 bytes), it must clean up the stack at return , i.e. use retn 4 instead of just retn which is enough for __cdecl functions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.