Starting a Process from KernelMode

本文介绍了一种从内核模式启动Win32进程的方法,通过利用APC(异步过程调用)机制,将指定的应用程序注入到Explorer进程的地址空间中并执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Introduction

After many unsuccessful attempts trying to find a way to start a working Win32 process from KernelMode, I finally stumbled upon a promising piece of code that was both original and innovative (Note: the idea belongs to Valerino).

Unfortunately that code didn't seem to work properly on my machine, and it always ended up crashing it, or just a process (in the most fortunate cases). So I've decided to reimplement Mighty Valerino's idea in my own way (although the structure of the code is basically the same).

So let's get started. First, you must know of...

APCs

APC, standing for Asynchronous Procedure Call, represents a kernel procedure that is queued to a particular thread for execution. In other words, it's a code that is forcibly executed in a thread's context (This method is mostly employed by the I/O Manager). That is the SIMPLEST explanation I can give you, and that's all you need to know for now. There are three types of APCs:

  1. Kernel APCs - They can be queued to any kernel thread and they will execute only if the specified thread isn't already executing a Kernel APC.
  2. Special Kernel APCs - Basically the same as above. They run at IRQL APC_LEVEL and cannot be blocked except by running at a raised IRQL. They can always preempt normal Kernel APCs.
  3. User APCs - These are APCs that can be queued to UserMode threads, but there's a catch: the thread must have previously called a wait service like WaitForSingleObject with the Alertable field set to TRUE. The APC will be invoked next time the thread returns from KernelMode. This is the kind of APC that we will be dealing with from now on.

Enough talking. Let's get to the fun part.

Running That Process

A brief description of the idea behind starting a Win32 process is the following:

  1. We loop through the list of running processes until we find Explorer.exe. Why explorer.exe? Because it's a desktop-interactive service (I tried popping up a message box from WinLogon.exe and I could only hear the sound of it). It also has many waiting threads (both alertable and non-alertable), so it works best with this code.
  2. Once we've found Explorer.exe, we iterate through its threads searching for an alertable thread. If no such thread is found, we simply save a pointer to a non-alertable one and set its ApcState.UserApcPending to TRUE, thus making it alertable (Notice: in this case it usually takes a few seconds for the thread to return to KernelMode).
  3. Now we have the PEPROCESS of Explorer.exe and one of its PETHREADs. Next we queue our APC object (which will contain the code to be executed in UserMode) and when it completes, we just release the memory that we have previously allocated for it. That's all.

The Implementation

The main procedure is RunProcess(LPSTR lpProcess) where lpProcess must be the FULL PATH to the application that is to be run (c:/RawWrite.exe in our example):

Collapse Copy Code
void RunProcess(LPSTR lpProcess)
{
  PEPROCESS pTargetProcess = NULL;//self explanatory
  PKTHREAD pTargetThread = NULL;  //thread that can be either
                                  //alertable or not
  PKTHREAD pNotAlertableThread = NULL;//non-alertable thread
  PEPROCESS pSystemProcess = NULL; //May not necessarily be the
                                   //'System' process

  PETHREAD pTempThread = NULL;
  PLIST_ENTRY  pNextEntry, pListHead, pThNextEntry;

//...
}

We start off by retrieving a pointer to the 'System' process:

Collapse Copy Code
  pSystemProcess = PsGetCurrentProcess();
  //make sure you are running at IRQL PASSIVE_LEVEL

pSystemProcess->ActiveProcessLinks is a LIST_ENTRY field that contains links(pointers) to other processes (PEPROCESS) running on the machine. Let's search for Explorer.exe and save a pointer to it and to one of its threads. (Note: you can queue an APC to ANY process, even CSRSS or SVCHOST, but the system will probably crash.) Once we've got a pointer to Explorer.exe and one of its threads (I will not explain how to do that here), it's time to queue our APC to that thread:

Collapse Copy Code
if(!pTargetThread)
{
  //No alertable thread was found, so let's hope
  //we've at least got a non-alertable one
  pTargetThread = pNotAlertableThread;
}

if(pTargetThread)
{
  DbgPrint("KernelExec -> Targeted thread: 0x%p",
           pTargetThread);
  //We have a thread, now install the APC
  InstallUserModeApc(lpProcess,
                     pTargetThread,
                     pTargetProcess);
}

InstallUserModeApc has the following prototype...

Collapse Copy Code
NTSTATUS
InstallUserModeApc(
                   IN LPSTR lpProcess,
                   IN PKTHREAD pTargetThread,
                   IN PEPROCESS pTargetProcess);

... where pTargetProcess points to the PEPROCESS of Explorer.exe and pTargetThread is the PKTHREAD which the APC will be queued to. Let's now allocate some memory for that APC and for an MDL (Memory Descriptor List) to map our UserMode code:

Collapse Copy Code
PRKAPC pApc = NULL;
PMDL pMdl = NULL;
ULONG dwSize = 0; //Size of code to be executed in Explorer's address space

pApc = ExAllocatePool (NonPagedPool,sizeof (KAPC));

dwSize = (unsigned char*)ApcCreateProcessEnd-
         (unsigned char*)ApcCreateProcess;
pMdl = IoAllocateMdl (ApcCreateProcess, dwSize, FALSE,FALSE,NULL);

//Probe the pages for Write access and make them memory resident
MmProbeAndLockPages (pMdl,KernelMode,IoWriteAccess);

Our APC is now valid and pMdl is memory resident and maps our UserMode code (ApcCreateProcess() that is. We'll get to that later). So what now? Should we deliver our APC to the thread and watch our Win32 process run? No no no.. not so fast!

How is Explorer.exe's thread supposed to invoke our APC routine if it doesn't have access to the Kernel memory? It cannot do that! Fine then, let's map our APC code to UserMode memory:

Collapse Copy Code
KAPC_STATE ApcState;

//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);

To continue, first I must show you how ApcCreateProcess (the code mapped to UserMode memory, into Explorer's address space) works:

Collapse Copy Code
__declspec(naked)
void ApcCreateProcess(
  PVOID NormalContext,
  PVOID  SystemArgument1,
  PVOID SystemArgument2)
{
  __asm
  {
    mov eax,0x7C86114D
    push 1
    nop
    push 0xabcd
    call eax
    jmp end
    nop
    nop
    //...about 400 nop's here
end:
    nop
    ret 0x0c
  }
}

void ApcCreateProcessEnd(){}
//Used only to calculate the size of the code above

We move the address of WinExec into eax (0x7C86114D is its address, on WinXP SP2), we push 1 on the stack (SW_SHOWNORMAL) and then we push... 0xabcd before calling WinExec. Why 0xabcd you might ask? Well, push 0xabcd is the first parameter of WinExec and it points to the path of the application to be executed. But that means 0xabcd cannot possibly point to the path all the time!

Why don't you simply push lpProcess from RunProcess(LPSTR lpProcess) then? Answer - Because WinExec will NOT be able to access it and it will throw an 'Access Violation' at you! You cannot access Kernel memory from UserMode, remember? Instead, right after we map our code to UserMode memory, we copy the path to the location right after the first nop instruction (that's why there are so many nops in there) and we modify 0xabcd to point to it. Now here's the code:

Collapse Copy Code
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

pMappedAddress =
         MmMapLockedPagesSpecifyCache(pMdl,
                                      UserMode,
                                      MmCached,
                                      NULL,FALSE,
                                      NormalPagePriority);
dwMappedAddress = (ULONG)pMappedAddress;

//zero everything out except our assembler code
memset ((unsigned char*)pMappedAddress + 0x14, 0, 300);

//copy the path to the executable
memcpy ((unsigned char*)pMappedAddress + 0x14,
        lpProcess,
        strlen (lpProcess));

data_addr = (ULONG*)((char*)pMappedAddress+0x9);//address pushed on the stack
                                                //(originally 0xabcd)...
*data_addr = dwMappedAddress+0x14; //gets changed to point to our exe's path

//all done, detach now
KeUnstackDetachProcess (&ApcState);

What's left now is to initialize the APC and queue it to the thread. I will not explain how KeInitializeApc and KeInsertQueueApc work as Tim Deveaux has already done that here.

Collapse Copy Code
  //Initialize the APC...
  KeInitializeApc(pApc,
                  pTargetThread,
                  OriginalApcEnvironment,
                  &ApcKernelRoutine, //this will fire after
                                     //the APC has returned
                  NULL,
                  pMappedAddress,
                  UserMode,
                  NULL);

  //...and queue it
  KeInsertQueueApc(pApc,0,NULL,0);

  //is this a non-alertable thread?
  if(!pTargetThread->ApcState.UserApcPending)
  {
    //if yes then alert it
    pTargetThread->ApcState.UserApcPending = TRUE;
  }

  return STATUS_SUCCESS;
}

Compiling the Code

This is simple - do a cd sys_path, where sys_path is the path to the driver project, then run build -ceZ. Or just press the F7 key in Microsoft Visual Studio 6.

Now copy KernelExec.sys to your C:/ directory, run Dbgview to see the output of the driver. Then double-click on Start_KE_Driver.exe to install and start the driver. Et voila! RawWrite.exe's window should be on your screen right now!

P.S.: Make sure you first put an application called RawWrite.exe in your c:/ directory, because that is what the driver attempts to run.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

 

From:http://www.codeproject.com/KB/system/KernelExec.aspx

Line 17196: [ 124.790242] init: init: Control message: Could not ctl.interface_start for 'aidl/android.hardware.nfc.INfc/default' from pid: 417 (/system/bin/servicemanager): File /vendor/bin/hw/android.hardware.nfc2-service.nxp(labeled "u:object_r:vendor_file:s0") has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly? https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials. Note: this error shows up even in permissive mode in order to make auditing denials possible. Line 17196: [ 124.790242] init: init: Control message: Could not ctl.interface_start for 'aidl/android.hardware.nfc.INfc/default' from pid: 417 (/system/bin/servicemanager): File /vendor/bin/hw/android.hardware.nfc2-service.nxp(labeled "u:object_r:vendor_file:s0") has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly? https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials. Note: this error shows up even in permissive mode in order to make auditing denials possible. Line 17196: [ 124.790242] init: init: Control message: Could not ctl.interface_start for 'aidl/android.hardware.nfc.INfc/default' from pid: 417 (/system/bin/servicemanager): File /vendor/bin/hw/android.hardware.nfc2-service.nxp(labeled "u:object_r:vendor_file:s0") has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly? https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials. Note: this error shows up even in permissive mode in order to make auditing denials possible. Line 18001: [ 148.994352] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting). Line 18001: [ 148.994352] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting). Line 18001: [ 148.994352] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting). Line 18081: [ 154.022668] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting). Line 18081: [ 154.022668] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting). Line 18081: [ 154.022668] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting). Line 18196: [ 159.036990] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting). Line 18196: [ 159.036990] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting). Line 18196: [ 159.036990] servicemanager: servicemanager: Caller(pid=4330,uid=1027,sid=u:r:nfc:s0) Since 'android.hardware.nfc.INfc/default' could not be found trying to start it as a lazy AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or still starting).
07-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值