进程回调可以监视进程的创建和退出,这个在前面的章节已经讲过了。 某些游戏保护的
驱动喜欢用这个函数来监视有没有黑名单中的程序运行,如果运行则阻止运行或者把游戏退
出。而线程回调则通常用来监控远程线程的建立,如果发现有远程线程注入到了游戏进程里,
则马上把游戏退出。
我们注册的进程回调,会存储在一个名为 PspCreateProcessNotifyRoutine 的数组里。
PspCreateProcessNotifyRoutine 可以理解成一个 PVOID 数组, 它记录了系统里所有进程回调
的地址。这个数组最大长度是 64*sizeof(PVOID)。 所以枚举进程回调的思路如下:找到这个
数组的地址, 然后解密数组的数据, 得到所有回调的地址(这个数组记录的数据并不是回调
的 地 址 , 而 是 经 过 加 密 地 址 , 需 要 解 密 才 行 )。 枚 举 线 程 回 调 同 理 , 要 找 到
PspCreateThreadNotifyRoutine 的地址(这个数组最大长度也是 64*sizeof(PVOID)),然后解密
数据,并把解密后的地址打印出来。
至于怎么处理这些回调就简单了。可以使用标准函数(PsSetCreateProcessNotifyRoutine、
PsRemoveCreateThreadNotifyRoutine) 将其摘掉, 也可以直接在回调函数首地址写入 RET 把
回调函数废掉。 WIN64AST 就提供了两种办法处理,以对付某些奸诈的游戏保护。
首先要获得 PspCreateProcessNotifyRoutine 的地址。 PspCreateProcessNotifyRoutine 在
PspSetCreateProcessNotifyRoutine 函数里出现了。 而 PspSetCreateProcessNotifyRoutine 则在
PsSetCreateProcessNotifyRoutine 中被调用(注意前一个是 PspXXX, 后一个是 PsXXX)。 找到
PspSetCreateProcessNotifyRoutine 之后,再匹配特征码
#include <ntddk.h>
void CreateThreadNotify1
(
IN HANDLE ProcessId,
IN HANDLE ThreadId,
IN BOOLEAN Create
)
{
DbgPrint("CreateThreadNotify1\n");
}
void CreateThreadNotify2
(
IN HANDLE ProcessId,
IN HANDLE ThreadId,
IN BOOLEAN Create
)
{
DbgPrint("CreateThreadNotify2\n");
}
void CreateThreadNotifyTest(BOOLEAN Remove)
{
if (!Remove)
{
PsSetCreateThreadNotifyRoutine(CreateThreadNotify1);
PsSetCreateThreadNotifyRoutine(CreateThreadNotify2);
}
else
{
PsRemoveCreateThreadNotifyRoutine(CreateThreadNotify1);
PsRemoveCreateThreadNotifyRoutine(CreateThreadNotify2);
}
}
ULONG64 FindPspCreateProcessNotifyRoutine()
{
LONG OffsetAddr = 0;
ULONG64 i = 0, pCheckArea = 0;
UNICODE_STRING unstrFunc;
//获得PsSetCreateProcessNotifyRoutine的地址
RtlInitUnicodeString(&unstrFunc, L"PsSetCreateProcessNotifyRoutine");
pCheckArea = (ULONG64)MmGetSystemRoutineAddress(&unstrFunc);
//获得PspSetCreateProcessNotifyRoutine的地址
memcpy(&OffsetAddr, (PUCHAR)pCheckArea + 4, 4);
pCheckArea = (pCheckArea + 3) + 5 + OffsetAddr;
DbgPrint("PspSetCreateProcessNotifyRoutine: %llx", pCheckArea);
//获得PspCreateProcessNotifyRoutine的地址
for (i = pCheckArea; i<pCheckArea + 0xff; i++)
{
if (*(PUCHAR)i == 0x4c && *(PUCHAR)(i + 1) == 0x8d && *(PUCHAR)(i + 2) == 0x35) //lea r14,xxxx
{
LONG OffsetAddr = 0;
memcpy(&OffsetAddr, (PUCHAR)(i + 3), 4);
return OffsetAddr + 7 + i;
}
}
return 0;
}
//
VOID EnumCreateProcessNotify()
{
int i = 0;
BOOLEAN b;
ULONG64 NotifyAddr = 0, MagicPtr = 0;
ULONG64 PspCreateProcessNotifyRoutine = FindPspCreateProcessNotifyRoutine();
DbgPrint("PspCreateProcessNotifyRoutine: %llx", PspCreateProcessNotifyRoutine);
if (!PspCreateProcessNotifyRoutine)
return;
for (i = 0; i<64; i++)
{
MagicPtr = PspCreateProcessNotifyRoutine + i * 8;
NotifyAddr = *(PULONG64)(MagicPtr);
if (MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr != 0)
{
NotifyAddr = *(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
DbgPrint("[CreateProcess]%llx", NotifyAddr);
}
}
}
ULONG64 FindPspCreateThreadNotifyRoutine()
{
ULONG64 i = 0, pCheckArea = 0;
UNICODE_STRING unstrFunc;
RtlInitUnicodeString(&unstrFunc, L"PsSetCreateThreadNotifyRoutine");
pCheckArea = (ULONG64)MmGetSystemRoutineAddress(&unstrFunc);
DbgPrint("PsSetCreateThreadNotifyRoutine: %llx", pCheckArea);
for (i = pCheckArea; i<pCheckArea + 0xff; i++)
{
if (*(PUCHAR)i == 0x48 && *(PUCHAR)(i + 1) == 0x8d && *(PUCHAR)(i + 2) == 0x0d) //lea rcx,xxxx
{
LONG OffsetAddr = 0;
memcpy(&OffsetAddr, (PUCHAR)(i + 3), 4);
return OffsetAddr + 7 + i;
}
}
return 0;
}
VOID EnumCreateThreadNotify()
{
int i = 0;
BOOLEAN b;
ULONG64 NotifyAddr = 0, MagicPtr = 0;
ULONG64 PspCreateThreadNotifyRoutine = FindPspCreateThreadNotifyRoutine();
DbgPrint("PspCreateThreadNotifyRoutine: %llx", PspCreateThreadNotifyRoutine);
if (!PspCreateThreadNotifyRoutine)
return;
for (i = 0; i<64; i++)
{
MagicPtr = PspCreateThreadNotifyRoutine + i * 8;
NotifyAddr = *(PULONG64)(MagicPtr);
if (MmIsAddressValid((PVOID)NotifyAddr) && NotifyAddr != 0)
{
NotifyAddr = *(PULONG64)(NotifyAddr & 0xfffffffffffffff8);
DbgPrint("[CreateThread]%llx", NotifyAddr);
}
}
}