在笔者之前的文章《驱动开发:内核特征码搜索函数封装》
中我们封装实现了特征码定位功能,本章将继续使用该功能,本次我们需要枚举内核LoadImage
映像回调,在Win64环境下我们可以设置一个LoadImage
映像加载通告回调,当有新驱动或者DLL被加载时,回调函数就会被调用从而执行我们自己的回调例程,映像回调也存储在数组里,枚举时从数组中读取值之后,需要进行位运算解密得到地址。
我们来看一款闭源ARK工具是如何实现的:
如上所述,如果我们需要拿到回调数组那么首先要得到该数组,数组的符号名是PspLoadImageNotifyRoutine
我们可以在PsSetLoadImageNotifyRoutineEx
中找到。
第一步使用WinDBG输入uf PsSetLoadImageNotifyRoutineEx
首先定位到,能够找到PsSetLoadImageNotifyRoutineEx
这里的两个位置都可以被引用,当然了这个函数可以直接通过PsSetLoadImageNotifyRoutineEx
函数动态拿到此处不需要我们动态定位。
我们通过获取到PsSetLoadImageNotifyRoutineEx
函数的内存首地址,然后向下匹配特征码搜索找到488d0d88e8dbff
并取出PspLoadImageNotifyRoutine
内存地址,该内存地址就是LoadImage
映像模块的基址。
如果使用代码去定位这段空间,则你可以这样写,这样即可得到具体特征地址。
// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com
#include <ntddk.h>
#include <windef.h>
// 指定内存区域的特征码扫描
PVOID SearchMemory(PVOID pStartAddress, PVOID pEndAddress, PUCHAR pMemoryData, ULONG ulMemoryDataSize)
{
PVOID pAddress = NULL;
PUCHAR i = NULL;
ULONG m = 0;
// 扫描内存
for (i = (PUCHAR)pStartAddress; i < (PUCHAR)pEndAddress; i++)
{
// 判断特征码
for (m = 0; m < ulMemoryDataSize; m++)
{
if (*(PUCHAR)(i + m) != pMemoryData[m])
{
break;
}
}
// 判断是否找到符合特征码的地址
if (m >= ulMemoryDataSize)
{
// 找到特征码位置, 获取紧接着特征码的下一地址
pAddress = (PVOID)(i + ulMemoryDataSize);
break;
}
}
return pAddress;
}
// 根据特征码获取 PspLoadImageNotifyRoutine 数组地址
PVOID SearchPspLoadImageNotifyRoutine(PUCHAR pSpecialData, ULONG ulSpecialDataSize)
{
UNICODE_STRING ustrFuncName;
PVOID pAddress = NULL;
LONG lOffset = 0;
PVOID pPsSetLoadImageNotifyRoutine = NULL;
PVOID pPspLoadImageNotifyRoutine = NULL;
// 先获取 PsSetLoadImageNotifyRoutineEx 函数地址
RtlInitUnicodeString(&ustrFuncName, L"PsSetLoadImageNotifyRoutineEx");
pPsSetLoadImageNotifyRoutine = MmGetSystemRoutineAddress(&ustrFuncName);
if (NULL == pPsSetLoadImageNotifyRoutine)
{
return pPspLoadImageNotifyRoutine;
}
// 查找 PspLoadImageNotifyRoutine 函数地址
pAddress = SearchMemory(pPsSetLoadImageNotifyRoutine, (PVOID)((PUCHAR)pPsSetLoadImageNotifyRoutine + 0xFF), pSpecialData, ulSpecialDataSize);
if (NULL == pAddress)
{
return pPspLoadImageNotifyRoutine;
}
// 先获取偏移, 再计算地址
lOffset = *(PLONG)pAddress;
pPspLoadImageNotifyRoutine = (PVOID)((PUCHAR)pAddress + sizeof(LONG) + lOffset);
return pPspLoadImageNotifyRoutine;
}
VOID UnDriver(PDRIVER_OBJECT Driver)
{
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
PVOID pPspLoadImageNotifyRoutineAddress = NULL;
RTL_OSVERSIONINFOW osInfo = { 0 };
UCHAR pSpecialData[50] = { 0 };
ULONG ulSpecialDataSize = 0;
// 获取系统版本信息, 判断系统版本
RtlGetVersion(&osInfo);
if (10 == osInfo.dwMajorVersion)
{
// 48 8d 0d 88 e8 db ff
// 查找指令 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff804`44313ce0)]
/*
nt!PsSetLoadImageNotifyRoutineEx+0x41:
fffff801`80748a81 488d0dd8d3dbff lea rcx,[nt!PspLoadImageNotifyRoutine (fffff801`80505e60)]
fffff801`80748a88 4533c0 xor r8d,r8d
fffff801`80748a8b 488d0cd9 lea rcx,[rcx+rbx*8]
fffff801`80748a8f 488bd7 mov rdx,rdi
fffff801`80748a92 e80584a3ff call nt!ExCompareExchangeCallBack (fffff801`