我们继续,首先实现特征码字符串的解析与扫描实现此处UtilLySharkSearchPattern函数就是LyShark封装过的 , 这里依次介绍一下参数传递的含义 。
- pattern 用于传入一段字符串特征值(以\x开头)
- len 代表输入特征码长度(除去\x后的长度)
- base 代表扫描内存的基地址
- size 代表需要向下扫描的长度
- ppFound 代表扫描到首地址以后返回的内存地址
// 署名// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"PVOID GetIoInitializeTimerAddress(){ PVOID VariableAddress = 0; UNICODE_STRING uioiTime = { 0 }; RtlInitUnicodeString(&uioiTime, L"IoInitializeTimer"); VariableAddress = (PVOID)MmGetSystemRoutineAddress(&uioiTime); if (VariableAddress != 0) {return VariableAddress; } return 0;}// 对指定内存执行特征码扫描NTSTATUS UtilLySharkSearchPattern(IN PUCHAR pattern, IN ULONG_PTR len, IN const VOID* base, IN ULONG_PTR size, OUT PVOID* ppFound){ // 计算匹配长度 // LyShark.com 特征码扫描 NT_ASSERT(ppFound != 0 && pattern != 0 && base != 0); if (ppFound == 0 || pattern == 0 || base == 0) {return STATUS_INVALID_PARAMETER; } __try {for (ULONG_PTR i = 0; i < size - len; i++){BOOLEAN found = TRUE;for (ULONG_PTR j = 0; j < len; j++){if (pattern[j] != ((PUCHAR)base)[i + j]){found = FALSE;break;}}if (found != FALSE){*ppFound = (PUCHAR)base + i;DbgPrint("[LyShark.com] 特征码匹配地址: %p \n", (PUCHAR)base + i);return STATUS_SUCCESS;}} } __except (EXCEPTION_EXECUTE_HANDLER) {return STATUS_UNHANDLED_EXCEPTION; } return STATUS_NOT_FOUND;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint(("Uninstall Driver Is OK \n"));}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint(("hello lyshark.com \n")); // 返回匹配长度5 CHAR pattern[] = "\x48\x89\x6c\x24\x10"; PVOID *find_address = NULL; int pattern_size = sizeof(pattern) - 1; DbgPrint("匹配长度: %d \n", pattern_size); // 得到基地址 PVOID address = GetIoInitializeTimerAddress(); // 扫描特征 NTSTATUS nt = UtilLySharkSearchPattern((PUCHAR)pattern, pattern_size, address, 128, &find_address); DbgPrint("[LyShark 返回地址 => ] 0x%p \n", (ULONG64)find_address); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}运行驱动程序完成特征定位,并对比定位效果 。
【驱动开发:内核特征码扫描PE代码段】

文章插图
如上述所示定位函数我们已经封装好了,相信你也能感受到这种方式要比使用数组更方便,为了能定位到内核PE结构我们需要使用RtlImageNtHeader来解析,这个内核函数专门用来得到内核程序的PE头部结构的,在下方案例中首先我们使用封装过的LySharkToolsUtilKernelBase函数拿到内核基址,如果你不懂函数实现细节请阅读《驱动开发:内核取ntoskrnl模块基地址》这篇文章,拿到基址以后可以直接使用RtlImageNtHeader对其PE头部进行解析 , 如下所示 。
// 署名// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark.h"// 定义全局变量static PVOID g_KernelBase = 0;static ULONG g_KernelSize = 0;// 得到KernelBase基地址// lyshark.comPVOID LySharkToolsUtilKernelBase(OUT PULONG pSize){ NTSTATUS status = STATUS_SUCCESS; ULONG bytes = 0; PRTL_PROCESS_MODULES pMods = 0; PVOID checkPtr = 0; UNICODE_STRING routineName; if (g_KernelBase != 0) {if (pSize){*pSize = g_KernelSize;}return g_KernelBase; } RtlInitUnicodeString(&routineName, L"NtOpenFile"); checkPtr = MmGetSystemRoutineAddress(&routineName); if (checkPtr == 0)return 0; __try {status = ZwQuerySystemInformation(SystemModuleInformation, 0, bytes, &bytes);if (bytes == 0){return 0;}pMods = (PRTL_PROCESS_MODULES)ExAllocatePoolWithTag(NonPagedPoolNx, bytes, L"LyShark");RtlZeroMemory(pMods, bytes);status = ZwQuerySystemInformation(SystemModuleInformation, pMods, bytes, &bytes);if (NT_SUCCESS(status)){PRTL_PROCESS_MODULE_INFORMATION pMod = pMods->Modules;for (ULONG i = 0; i < pMods->NumberOfModules; i++){if (checkPtr >= pMod[i].ImageBase && checkPtr < (PVOID)((PUCHAR)pMod[i].ImageBase + pMod[i].ImageSize)){g_KernelBase = pMod[i].ImageBase;g_KernelSize = pMod[i].ImageSize;if (pSize){*pSize = g_KernelSize;}break;}}} } __except (EXCEPTION_EXECUTE_HANDLER) {return 0; } if (pMods) {ExFreePoolWithTag(pMods, L"LyShark"); } DbgPrint("KernelBase = > %p \n", g_KernelBase); return g_KernelBase;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint(("Uninstall Driver Is OK \n"));}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint(("hello lyshark.com \n")); // 获取内核第一个模块的基地址 PVOID base = LySharkToolsUtilKernelBase(0); if (!base)return STATUS_NOT_FOUND; // 得到NT头部PE32+结构 // lyshark.com PIMAGE_NT_HEADERS64 pHdr = RtlImageNtHeader(base); if (!pHdr)return STATUS_INVALID_IMAGE_FORMAT; // 首先寻找代码段 PIMAGE_SECTION_HEADER pFirstSection = (PIMAGE_SECTION_HEADER)(pHdr + 1); for (PIMAGE_SECTION_HEADER pSection = pFirstSection; pSection < pFirstSection + pHdr->FileHeader.NumberOfSections; pSection++) {ANSI_STRING LySharkSection, LySharkName;RtlInitAnsiString(&LySharkSection, ".text");RtlInitAnsiString(&LySharkName, (PCCHAR)pSection->Name);DbgPrint("[LyShark.PE] 名字: %Z | 地址: %p | 长度: %d \n", LySharkName, (PUCHAR)base + pSection->VirtualAddress, pSection->Misc.VirtualSize); } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}
推荐阅读
- 驱动开发:内核枚举Minifilter微过滤驱动
- Vue3 JS 与 SCSS 变量相互使用
- 24 Node.js躬行记——低代码
- 驱动开发:内核枚举PspCidTable句柄表
- 驱动开发:内核枚举DpcTimer定时器
- envoy开发调试环境搭建
- 如何更新电脑的显卡驱动(笔记本显卡驱动要不要更新)
- 你Win7系统如何将显卡驱动更新到最新版本
- 云原生时代的DevOps平台设计之道
- Taurus.MVC 微服务框架 入门开发教程:项目部署:7、微服务节点的监控与告警。