通过PsSetLoadImageNotifyRoutine学习模块监控与反模块监控
本文为看雪论坛优秀文章
看雪论坛作者ID:1900
一
模块监控
1、实现原理
NTSTATUS PsSetLoadImageNotifyRoutine( IN PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine);typedefVOID(*PLOAD_IMAGE_NOTIFY_ROUTINE)( __in PUNICODE_STRING FullImageName, __in HANDLE ProcessId, // pid into which image is being mapped __in PIMAGE_INFO ImageInfo );typedef struct _IMAGE_INFO { union { ULONG Properties; struct { ULONG ImageAddressingMode : 8; // Code addressing mode ULONG SystemModeImage : 1; // System mode image ULONG ImageMappedToAllPids : 1; // Image mapped into all processes ULONG ExtendedInfoPresent : 1; // IMAGE_INFO_EX available ULONG Reserved : 21; }; }; PVOID ImageBase; ULONG ImageSelector; SIZE_T ImageSize; ULONG ImageSectionNumber;} IMAGE_INFO, *PIMAGE_INFO;NTSTATUS PsRemoveLoadImageNotifyRoutine( IN PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine );#include <ntifs.h>#include <ntimage.h> #define DRIVER_NAME L"TestDriver.sys" //要拦截的驱动名#define DLL_NAME L"InjectDll.dll" //要拦截的DLL名 typedef struct _DLL_INFO{ HANDLE ProcessId; PVOID pImageBase;}DLL_INFO, *PDLL_INFO; //Dll的信息,用来作为线程的参数传递 VOID DriverUnload(IN PDRIVER_OBJECT driverObject);VOID LoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo); //模块监控回调函数BOOLEAN DenyLoadDriver(PVOID pLoadImageBase); //对驱动的加载进行拦截BOOLEAN DenyLoadDll(HANDLE ProcessId, PVOID pImageBase); //对DLL的加载进行拦截NTSTATUS MmUnmapViewOfSection(PEPROCESS Process, PVOID BaseAddr); //未导出函数声明VOID ThreadProc(PVOID StartContext); //运行的线程函数 NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath){ NTSTATUS status = STATUS_SUCCESS; DbgPrint("驱动加载完成\r\n"); status = PsSetLoadImageNotifyRoutine(LoadImageNotifyRoutine); if (!NT_SUCCESS(status)) { DbgPrint("模块监控设置失败 0x%X\r\n", status); } else { DbgPrint("模块监控设置成功\r\n"); }exit: driverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS;} VOID LoadImageNotifyRoutine(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo){ PDLL_INFO pDllInfo = NULL; HANDLE hThread = NULL; /* DbgPrint("===========================================================================\r\n"); DbgPrint("检测到新加载的模块,模块信息如下:\r\n"); DbgPrint("加载该模块的进程ID:%d 模块完整名:%wZ 模块基址:0x%X 模块大小:0x%X", ProcessId, FullImageName, ImageInfo->ImageBase, ImageInfo->ImageSize); DbgPrint("===========================================================================\r\n"); */ // 是否是exe或者dll文件 if (ProcessId) { if (wcsstr(FullImageName->Buffer, DLL_NAME) != NULL) { pDllInfo = (PDLL_INFO)ExAllocatePool(NonPagedPool, sizeof(DLL_INFO)); if (!pDllInfo) { DbgPrint("ExAllocatePool Error"); } else { pDllInfo->ProcessId = ProcessId; pDllInfo->pImageBase = ImageInfo->ImageBase; PsCreateSystemThread(&hThread, 0, NULL, NtCurrentProcess(), NULL, ThreadProc, pDllInfo); if (hThread) ZwClose(hThread); } } } else { //加载的是驱动,判断是否是要拦截的驱动 if (wcsstr(FullImageName->Buffer, DRIVER_NAME) != NULL ) { if (DenyLoadDriver(ImageInfo->ImageBase)) { DbgPrint("成功拦截驱动%wZ的加载\r\n", FullImageName); } } }} VOID ThreadProc(PVOID StartContext){ PDLL_INFO pDllInfo = (PDLL_INFO)StartContext; LARGE_INTEGER liTime = { 0 }; //延时5秒 liTime.QuadPart = -50 * 1000 * 1000; KeDelayExecutionThread(KernelMode, FALSE, &liTime); //卸载DLL if (DenyLoadDll(pDllInfo->ProcessId, pDllInfo->pImageBase)) { DbgPrint("Dll卸载完成\r\n"); } if (pDllInfo) ExFreePool(pDllInfo);} BOOLEAN DenyLoadDll(HANDLE ProcessId, PVOID pImageBase){ BOOLEAN bRet = TRUE; NTSTATUS status = STATUS_SUCCESS; PEPROCESS pEprocess = NULL; //保存加载DLL的进程的EPROCESS //根据进程PID获取EPROCESS status = PsLookupProcessByProcessId(ProcessId, &pEprocess); if (!NT_SUCCESS(status)) { DbgPrint("PsLookupProcessByProcessId Error 0x%X", status); bRet = FALSE; goto exit; } //卸载模块 status = MmUnmapViewOfSection(pEprocess, pImageBase); if (!NT_SUCCESS(status)) { DbgPrint("MmUnmapViewOfSection Error 0x%X\r\n", status); bRet = FALSE; goto exit; }exit: return bRet;} BOOLEAN DenyLoadDriver(PVOID pLoadImageBase){ BOOLEAN bRet = TRUE; NTSTATUS status = STATUS_SUCCESS; PVOID pVoid = NULL; PIMAGE_DOS_HEADER pDosHead = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; PVOID pDriverEntry = NULL; PMDL pMdl = NULL; // 要写入的ShellCode,硬编码的意思是 // mov eax, 0xC0000022 // ret UCHAR szShellCode[6] = { 0xB8, 0x22, 0x00, 0x00, 0xC0, 0xC3}; ULONG uShellCodeLength = 6; pDosHead = (PIMAGE_DOS_HEADER)pLoadImageBase; pNtHeader = (PIMAGE_NT_HEADERS)((ULONG)pLoadImageBase + pDosHead->e_lfanew); pDriverEntry = (PVOID)((ULONG)pDosHead + pNtHeader->OptionalHeader.AddressOfEntryPoint); //获取驱动入口点位置 //创建MDL并为内存属性添加可写属性 pMdl = MmCreateMdl(NULL, pDriverEntry, uShellCodeLength); if (pMdl == NULL) { bRet = FALSE; goto exit; } //建立内存页的MDL描述 MmBuildMdlForNonPagedPool(pMdl); //改变MDL的标记为可写 pMdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA; //映射MDL空间 pVoid = MmMapLockedPages(pMdl, KernelMode); //将shellcode拷到目标地址 RtlCopyMemory(pVoid, szShellCode, uShellCodeLength); //释放MDL MmUnmapLockedPages(pVoid, pMdl); IoFreeMdl(pMdl); pMdl = NULL;exit: return bRet;} VOID DriverUnload(IN PDRIVER_OBJECT driverObject){ NTSTATUS status = STATUS_SUCCESS; status = PsRemoveLoadImageNotifyRoutine(LoadImageNotifyRoutine); if (!NT_SUCCESS(status)) { DbgPrint("模块监控删除失败 0x%X\r\n", status); } else { DbgPrint("模块监控删除成功\r\n"); } DbgPrint("驱动卸载完成\r\n");}2、实验结果
二
反模块监控
1、逆向分析PsSetLoadImageNotifyRoutine
PAGE:005709B3 ; NTSTATUS __stdcall PsSetLoadImageNotifyRoutine(PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine)PAGE:005709B3 public PsSetLoadImageNotifyRoutinePAGE:005709B3 PsSetLoadImageNotifyRoutine proc near ; CODE XREF: sub_580241+215↓pPAGE:005709B3PAGE:005709B3 NotifyRoutine = dword ptr 8PAGE:005709B3PAGE:005709B3 mov edi, ediPAGE:005709B5 push ebpPAGE:005709B6 mov ebp, espPAGE:005709B8 push ebxPAGE:005709B9 push esiPAGE:005709BA push ediPAGE:005709BB xor edi, edi ; edi清0PAGE:005709BD push edi ; 将0入栈PAGE:005709BE push [ebp+NotifyRoutine] ; 将函数地址入栈PAGE:005709C1 call AllocateAssign ; 分配12字节的内存,低4位和高4位清0,中间4位存放函数地址PAGE:005709C6 mov ebx, eax ; 申请到的内存地址赋给ebxPAGE:005709C8 cmp ebx, edi ; 判断是否为0,也就是是否分配失败PAGE:005709CA jz short loc_5709F1 ; 如果内存分配失败则退出PAGE:005709CC mov esi, offset LoadImageFuncArray ; 将一个数组地址赋给esiPAGE:005709D1PAGE:005709D1 loc_5709D1: ; CODE XREF: PsSetLoadImageNotifyRoutine+36↓jPAGE:005709D1 push 0 ; 将0入栈PAGE:005709D3 mov ecx, ebx ; 将分配的地址赋给ecxPAGE:005709D5 mov eax, esi ; 将数组地址赋给eaxPAGE:005709D7 call SetArrayPAGE:005709DC test al, alPAGE:005709DE jnz short loc_5709FD ; 判断返回值是否为0,不为0则执行成功,跳转PAGE:005709E0 add edi, 4 ; edi加4PAGE:005709E3 add esi, 4 ; esi,也就是数组地址+4,所以这是在取数组的下一个元素PAGE:005709E6 cmp edi, 20h ; 判断edi是否小于0x20,小于则跳转过去执行函数PAGE:005709E9 jb short loc_5709D1 ; 根据前面的清0操作知道这里是为了保证循环调用8次,所以可以判断这个数组中最多保存7个地址PAGE:005709EB push ebx ; BufferPAGE:005709EC call FreeAllocate ; 否则函数执行失败,释放申请到的内存PAGE:005709F1PAGE:005709F1 loc_5709F1: ; CODE XREF: PsSetLoadImageNotifyRoutine+17↑jPAGE:005709F1 mov eax, STATUS_INSUFFICIENT_RESOURCES ; 赋值失败的返回值PAGE:005709F6PAGE:005709F6 loc_5709F6: ; CODE XREF: PsSetLoadImageNotifyRoutine+6B↓jPAGE:005709F6 pop ediPAGE:005709F7 pop esiPAGE:005709F8 pop ebxPAGE:005709F9 pop ebpPAGE:005709FA retn 4PAGE:005709FD ; ---------------------------------------------------------------------------PAGE:005709FDPAGE:005709FD loc_5709FD: ; CODE XREF: PsSetLoadImageNotifyRoutine+2B↑jPAGE:005709FD xor ecx, ecxPAGE:005709FF mov eax, offset unk_542BA0PAGE:00570A04 inc ecxPAGE:00570A05 lock xadd [eax], ecxPAGE:00570A09 mov eax, dword_542B78PAGE:00570A0E test al, 1PAGE:00570A10 jnz short loc_570A1CPAGE:00570A12 mov eax, offset dword_542B78PAGE:00570A17 lock bts dword ptr [eax], 0PAGE:00570A1CPAGE:00570A1C loc_570A1C: ; CODE XREF: PsSetLoadImageNotifyRoutine+5D↑jPAGE:00570A1C xor eax, eaxPAGE:00570A1E jmp short loc_5709F6PAGE:00570A1E PsSetLoadImageNotifyRoutine endp2、反模块监控
#include <ntifs.h>#include <ntimage.h> VOID DriverUnload(IN PDRIVER_OBJECT driverObject);PULONG GetImageArray(); NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath){ NTSTATUS status = STATUS_SUCCESS; PULONG pImageArray = NULL; PULONG pFuncAddr = NULL; ULONG i = 0; pImageArray = GetImageArray(); if (pImageArray) { for (i = 0; i < 8; i++) { if (pImageArray[i] & ~0x7) { pFuncAddr = (PULONG)(pImageArray[i] & ~0x7 + 4); status = PsRemoveLoadImageNotifyRoutine((PLOAD_IMAGE_NOTIFY_ROUTINE)*pFuncAddr); if (NT_SUCCESS(status)) { DbgPrint("模块监控删除成功 模块地址:0x%X\r\n", *pFuncAddr); } } } } exit: driverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS;} VOID DriverUnload(IN PDRIVER_OBJECT driverObject){ DbgPrint("驱动卸载完成\r\n");} PULONG GetImageArray(){ PULONG pImageArray = NULL; //要获取的函数地址的函数名 UNICODE_STRING uStrFuncName = RTL_CONSTANT_STRING(L"PsSetLoadImageNotifyRoutine"); PUCHAR pPsSetCreateThreadNotifyRoutine = NULL; //获取函数地址 pPsSetCreateThreadNotifyRoutine = (PUCHAR)MmGetSystemRoutineAddress(&uStrFuncName); while (*pPsSetCreateThreadNotifyRoutine != 0xC2) { if (*pPsSetCreateThreadNotifyRoutine == 0x74 && *(pPsSetCreateThreadNotifyRoutine + 1) == 0x25 && *(pPsSetCreateThreadNotifyRoutine + 2) == 0xBE) { pImageArray = (PULONG)*(PULONG)(pPsSetCreateThreadNotifyRoutine + 3); break; } pPsSetCreateThreadNotifyRoutine++; } return pImageArray;}3、运行截图
看雪ID:1900
https://bbs.pediy.com/user-home-835440.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!