这个例子就是从内核模式中启动一个用户进程,网上讲解的有很多了,不过源代码编译后不能正常启动用户进程,例子自带的ntifs.h中EPROCESS,_KPROCESS,ETHREAD,_KTHREAD结构中成员偏移位置有偏差。
查看上述结构方法很简单,用WinDbg的dt命令,如 dt EPROCESS
我的系统是windows sp2,其他系统需做相应调整
修改部分源代码见下: void RunProcess(LPSTR lpProcess)
{
//全部定义为ULONG类型
ULONG pTargetProcess; //self explanatory
ULONG pTargetThread; //thread that can be either alerable or non-alertable
ULONG pNotAlertableThread; //non-alertable thread
ULONG pSystemProcess; //May not necessarily be the 'System' process
ULONG pTempThread;
ULONG pNextEntry, pListHead, pThNextEntry,pThListHead;
if(strlen(lpProcess)>300) return; //name not longer than 300 characters
//获得系统进程
pSystemProcess =(ULONG)PsGetCurrentProcess(); //make sure you are running at IRQL PASSIVE_LEVEL
while(pNextEntry != pListHead) //start looping through the available processes
{ //得到EPROCESS的首地址
pSystemProcess =pNextEntry-0x88;
//进程名偏移
//+0x174 ImageFileName:[16] UChar
DbgPrint("ProcessName %s \n",(char*)pSystemProcess+0x174);
//Is this explorer.exe?
if(_strnicmp((char*)pSystemProcess+0x174,"explorer.exe",12)==0)
{ //得到进程的EPROCESS结构的地址
pTargetProcess = pSystemProcess; //Yes,we have found it!
DbgPrint("yes,we have found explorer.exe!");
pTargetThread = pNotAlertableThread = 0;
//获取线程列表头
//+0x050 ThreadListHead : _LIST_ENTRY
//也就是_KPROCESS(PCB)中ThreadListHead的偏移地址
pThListHead = pSystemProcess+0x50;
//得到ETHREAD结构中_KTHREAD(Tcb)的+0x1b0 ThreadListEntry : _LIST_ENTRY地址
pThNextEntry=*(ULONG *)pThListHead;
//Now we loop through it's threads, seeking an alertable thread
while(pThNextEntry != pThListHead)
{ //所属ETHREAD的首地址
pTempThread =pThNextEntry-0x1b0;
DbgPrint("ethread address is:0x%x\n",(ULONG *)pTempThread);
//线程ID
//ETHREAD+0x1ec Cid : _CLIENT_ID为进程ID
//再向下+4为线程ID
DbgPrint("thread Id is %d\n",*(ULONG *)(pTempThread+0x1f0));
//是否警告状态
//+0x164 Alertable : UChar
DbgPrint("Alertable is:%x",*(char *)(pTempThread+0x164));
//tcb=*(PKTHREAD)pTempThread;
if(*(char *)(pTempThread+0x164)) //Tcb is the KTHREAD of this ETHREAD and stands for 'Thread Control Block'
{
//Good, an alertable thread was found.
//得到explorer.exe中符合条件的线程PKTHREAD(ETHREAD)结构的地址
pTargetThread =pTempThread;
DbgPrint("KernelExec -> 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;
}
//下一个线程块
pThNextEntry = *(ULONG *)pThNextEntry; //check next thread
}
break;
}
//下一个进程块
pNextEntry = *(ULONG *)pNextEntry; //get next process
}
}
if(!pTargetProcess)
{
DbgPrint("KernelExec -> 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("KernelExec -> Targeted thread: 0x%p",pTargetThread);
//We have one thread (alertable or n/a), now install the APC
InstallUserModeApc(lpProcess,pTargetThread,pTargetProcess);
}
else
DbgPrint("KernelExec -> No thread found!"); //Explorer exe with NO threads (???)*/
}
另外还需要修改WinExec函数的地址
顺便说一下遍历所有线程的方法,这里用的是第一种(详见JIURL玩玩Win2k进程线程篇)
1.一个链表是以 EPROCESS 结构的 KPROCESS Pcb 中的 ThreadListHead 为链表的链表头。链上的每一项是一个线程的 KTHREAD ETHREAD 结构的 Tcb 中的 ThreadListEntry 。
2.另一个链表是以 EPROCESS 结构中的 ThreadListHead 为链表的链表头。链上的每一项是一个线程的 ETHREAD 结构中的 ThreadListEntry 。
通过这两个链表中的任何一个,都可以找到一个进程的所有线程的 ETHREAD 结构,当然找到 ETHREAD 结构,就可以找到 ETHREAD 结构中的 KTHREAD。