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;
}