驱动开发:内核特征码扫描PE代码段( 二 )

我们继续,首先实现特征码字符串的解析与扫描实现此处UtilLySharkSearchPattern函数就是LyShark封装过的,这里依次介绍一下参数传递的含义 。

  • pattern 用于传入一段字符串特征值(以\x开头)
  • len 代表输入特征码长度(除去\x后的长度)
  • base 代表扫描内存的基地址
  • size 代表需要向下扫描的长度
  • 【驱动开发:内核特征码扫描PE代码段】ppFound 代表扫描到首地址以后返回的内存地址
这段代码该如何使用,如下我们以定位IoInitializeTimer为例,演示UtilLySharkSearchPattern如何定位特征的,如下代码pattern变量中就是我们需要定位的特征值,pattern_size则是需要定位的特征码长度,在address地址位置向下扫描128字节,找到则返回到find_address变量内 。
// 署名// 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头部进行解析,如下所示 。

经验总结扩展阅读