前面我们在TerminateProcess流程追踪、PspTerminateThreadByPointer的实现这篇文章中讲到,TerminateProcess其实是通过遍历进程的线程链表,然后调PspTerminateThreadByPointer这个函数来结束进程的。
但是我们前面也说到可以hook NtTerminateProcess来实现进程防杀,如果我们直接使用PspTerminateThreadByPointer就能绕过这个api了。
但是我们发现PspTerminateThreadByPointer这个函数是未导出的,但是PsTerminateSystemThread这个函数是导出的,而这个函数里面又有调用PspTerminateThreadByPointer。所以我们可以通过导出函数和特征码搜索来得到PspTerminateThreadByPointer的地址。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | ULONG getPspTerminateThreadByPointerAddress() { UNICODE_STRING FunName; UCHAR FeatureCode[2]={0x50,0xe8};//特征码push eax call XXXX ULONG FeatureAddress = 0; //找到的特征码的首地址 ULONG FunAddress = 0; ULONG RetAddress = 0;//最终返回 ULONG FunAdd = 0; RtlInitUnicodeString(&FunName,L"PsTerminateSystemThread"); FunAdd =(ULONG) MmGetSystemRoutineAddress(&FunName); DbgPrint("PsTerminateSystemThread -- %08X",FunAdd); //首先获取PsTerminateSystemThread这个函数地址.其实这个就是直接调用的PspTerminateThreadByPointer //这个函数本身非常短小,所以通过搜索特征.定位call 然后将call的地址拿出来并进行运算即可 FeatureAddress = SearchFeatureCodeForAddress(FunAdd,FeatureCode,2,0x27); //PsTerminateSystemThread函数需要搜索的长度0x27 DbgPrint("FeatureAddress -- %08X",FeatureAddress); RtlCopyMemory(&FunAddress,(PULONG)(FeatureAddress + 2),4); //目标地址=下条指令的地址+机器码E8后面所跟的32位数 //注意,机器码E8后面所跟的32位数 是 little-endian 格式的,低8位在前,高8位在后 RetAddress = FunAddress + ( FeatureAddress + 2 + 4 ); return RetAddress; } |
搜索特征码的函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | ULONG SearchFeatureCodeForAddress(ULONG StartAddress,PUCHAR FeatureCode,int FeatureCodeSize,int Search_MaxLength) { PUCHAR Start_scan = (PUCHAR)StartAddress; BOOLEAN IsTrue = FALSE; ULONG Real_address = 0; int i,fi; for (i = 0; i < Search_MaxLength; i++) { if (Start_scan[i] == FeatureCode[0]) { for (fi = 0; fi < FeatureCodeSize; fi++) { if (Start_scan[i + fi] != FeatureCode[fi]) { //任何一个字节不同,都标记失败,跳出 IsTrue = FALSE; break; }else { if (fi == FeatureCodeSize-1) { IsTrue = TRUE; //指定长度的字节都相同.认为找到了 } } } if (IsTrue == TRUE) { Real_address = (ULONG)&Start_scan[i]; break; } } } return Real_address; } |
虽然线程都被结束了,那么进程结构又是怎么被销毁的呢?
进程线程都被销毁了就没有代码执行能力,从进程链中删除了也看不到了。
实际销毁对象结构本身是在句柄和引用计数都清0了完成,如果继续持有进程对象引用或句柄,进程对象不会销毁,但也只是留一个空壳 没有实际能力。
本文链接:http://www.alonemonkey.com/killprocess-by-killallthread.html