滴水线上班驱动03的课后作业,感觉挺有意思的,就尝试做了一下。这个习题是要求使用特征码查找PspTerminateProcess函数,然后调用此函数来结束进程。
主要难点在于特征码的寻找以及PspTerminateProcess参数的确定。
PspTerminateProcess参数的确定
我使用的是系统64位的win10。由于事先我不知道PspTerminateProcess的参数有哪些,因此结合windbg的符号和ida对ntoskrnl.exe进行了分析。首先使用kd调试,在NtTerminateProcess下断(这个函数调用了PspTerminateProcess),然后随便结束一个进程。
然后运行到call指令处,发现PspTerminateProcess接收4个参数,后两个参数推测为状态码,前两个参数看起来像是内核对象。

于是使用!object命令查看,
kd> !object ffffd70c0c920340
Object: ffffd70c0c920340 Type: (ffffd70c07abdd20) Process
ObjectHeader: ffffd70c0c920310 (new version)
HandleCount: 11 PointerCount: 327001
kd> !object ffffd70c0bcaa080
Object: ffffd70c0bcaa080 Type: (ffffd70c07abd380) Thread
ObjectHeader: ffffd70c0bcaa050 (new version)
HandleCount: 2 PointerCount: 65538
并使用dt _eprocess和dt _ethread进一步分析,发现这两个参数分别是要结束进程的eprocess结构和属于调用NtTerminateProcess的进程的某个线程的ethread结构,从而确定了调用的参数。
特征码
对于特征码,我使用了两个自定义的结构来表示
typedef struct PATTERN {
PVOID pBuffer;
int len;
}PATTERN;
typedef struct SIGNATURE {
int sigLen;
PATTERN pattern[15];
int offset[14];
} SIGNATURE;
并且定义了一套函数来查找特征码
SIGNATURE initSignature(PATTERN* p, int *offset, int sigLen) {
if (sigLen > 15)
return (SIGNATURE) {.sigLen=0};
SIGNATURE sigRet = { .sigLen = sigLen };
for (int i = 0; i < sigLen; i++) {
if (p[i].pBuffer == NULL || p[i].len < 0) { // 不允许出现指向NULL的buffer或者长度小于0
sigRet.sigLen = 0;
return sigRet;
}
sigRet.pattern[i] = p[i];
if (i < sigLen - 1)
sigRet.offset[i] = offset[i];
}
return sigRet;
}
BOOLEAN myMemcmp(PVOID buf1, PVOID buf2, size_t maxCmpLen) {
for (size_t i = 0; i < maxCmpLen; i++)
if (((char*)buf1)[i] != ((char*)buf2)[i])
return FALSE;
return TRUE;
}
PVOID findProcBySignature(PVOID startAddress, int maxSearchLen, SIGNATURE s) {
if (maxSearchLen <= 0)
return NULL;
PVOID ret = NULL;
for (int curSearchInd = 0; curSearchInd < maxSearchLen; curSearchInd++) {
int patternSearchInd = curSearchInd;
for (int i = 0; i < s.sigLen && patternSearchInd < maxSearchLen; i++) {
// 与特征码不匹配的情况:如果当前的pattern长度已经超过了最大长度(也就是特征码本身比搜索长度还长,这种情况认为不匹配),或者pattern[i]
// 与内存对比不匹配,或者pattern[i].pBuffer为NULL
if (patternSearchInd + s.pattern[i].len > maxSearchLen || s.pattern[i].pBuffer == NULL ||
!myMemcmp(s.pattern[i].pBuffer, (char*)startAddress + patternSearchInd, s.pattern[i].len))
break;
// 更新curSearchInd
if (i != s.sigLen - 1)
patternSearchInd += s.pattern[i].len + s.offset[i];
else // 如果能走到i == s.sigLen-1且memcmp通过,则说明特征码查找成功
ret = (char*)startAddress + curSearchInd;
}
if (ret != NULL)
break;
}
if (ret != NULL)
DbgPrint("Found process at %p\n", ret);
return ret;
}
然后我使用ida查看了PspTerminateProcess函数的代码,并确定了其特征码,编写了寻找PspTerminateProcess的函数
PVOID findPspTerminateProcessBySignature(void) {
char patterns[] = {
0x48, 0x89, 0x5c, 0x24, 0x08, // 15
0x48, 0x83, 0xec, 0x20, 0x41, // 11
0x0f, 0x0d, 0x89, 0x64, 0x04, // 55
0xe8, 0xfb, 0xf2, 0xc4, 0xff, // 5
0xe8, 0x1d, 0xf2, 0xc4, 0xff, // 28
0xe8, 0x40, 0xef, 0xb6, 0xff};
int offset[] = { 15, 11, 55, 5, 28 };
PATTERN p[6];
for (int i = 0; i < 6; i++) {
p[i].len = 5;
p[i].pBuffer = patterns+i*5;
}
SIGNATURE s = initSignature(p, offset, 6);
PVOID ntBase = FindNt();
return findProcBySignature((CHAR*)ntBase, 0xffffff, s);
}
找到特征码之后,根据函数参数,编写了一个调用PspTerminateProcess的函数
VOID terminateProcessBypass(ULONG64 pid) {
EPROC* currentProcess = PsGetCurrentProcess();
EPROC* nextProcess = (EPROC*)((CHAR*)(currentProcess->ActiveProcessLinks.Flink)-0x448);
PVOID currentThread = (PETHREAD)((CHAR*)(currentProcess->ThreadListHead.Flink) - 0x4e8);
VOID (*pspTerminateProc)(PEPROCESS, PETHREAD, int, int) = findPspTerminateProcessBySignature();
while (nextProcess->UniqueProcessId != pid && nextProcess != currentProcess && nextProcess != NULL) {
DbgPrint("current processId - %lld\n", nextProcess->UniqueProcessId);
nextProcess = (EPROC*)((CHAR*)(nextProcess->ActiveProcessLinks.Flink)-0x448);
}
if (nextProcess == currentProcess || nextProcess == NULL) {
DbgPrint("Pid not found\n");
return;
}
pspTerminateProc(nextProcess, currentThread, 0, 0);
}
完整的驱动代码见附件main.c
[培训]科锐逆向工程师培训第53期2025年7月8日开班!
最后于 2025-6-5 13:46
被nstlgst134编辑
,原因: 排版问题