其他
通过PsSetLoadImageNotifyRoutine学习模块监控与反模块监控
本文为看雪论坛优秀文章
看雪论坛作者ID:1900
一
模块监控
1、实现原理
NTSTATUS
PsSetLoadImageNotifyRoutine(
IN PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine
);
typedef
VOID
(*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 PsSetLoadImageNotifyRoutine
PAGE:005709B3 PsSetLoadImageNotifyRoutine proc near ; CODE XREF: sub_580241+215↓p
PAGE:005709B3
PAGE:005709B3 NotifyRoutine = dword ptr 8
PAGE:005709B3
PAGE:005709B3 mov edi, edi
PAGE:005709B5 push ebp
PAGE:005709B6 mov ebp, esp
PAGE:005709B8 push ebx
PAGE:005709B9 push esi
PAGE:005709BA push edi
PAGE:005709BB xor edi, edi ; edi清0
PAGE:005709BD push edi ; 将0入栈
PAGE:005709BE push [ebp+NotifyRoutine] ; 将函数地址入栈
PAGE:005709C1 call AllocateAssign ; 分配12字节的内存,低4位和高4位清0,中间4位存放函数地址
PAGE:005709C6 mov ebx, eax ; 申请到的内存地址赋给ebx
PAGE:005709C8 cmp ebx, edi ; 判断是否为0,也就是是否分配失败
PAGE:005709CA jz short loc_5709F1 ; 如果内存分配失败则退出
PAGE:005709CC mov esi, offset LoadImageFuncArray ; 将一个数组地址赋给esi
PAGE:005709D1
PAGE:005709D1 loc_5709D1: ; CODE XREF: PsSetLoadImageNotifyRoutine+36↓j
PAGE:005709D1 push 0 ; 将0入栈
PAGE:005709D3 mov ecx, ebx ; 将分配的地址赋给ecx
PAGE:005709D5 mov eax, esi ; 将数组地址赋给eax
PAGE:005709D7 call SetArray
PAGE:005709DC test al, al
PAGE:005709DE jnz short loc_5709FD ; 判断返回值是否为0,不为0则执行成功,跳转
PAGE:005709E0 add edi, 4 ; edi加4
PAGE: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 ; Buffer
PAGE:005709EC call FreeAllocate ; 否则函数执行失败,释放申请到的内存
PAGE:005709F1
PAGE:005709F1 loc_5709F1: ; CODE XREF: PsSetLoadImageNotifyRoutine+17↑j
PAGE:005709F1 mov eax, STATUS_INSUFFICIENT_RESOURCES ; 赋值失败的返回值
PAGE:005709F6
PAGE:005709F6 loc_5709F6: ; CODE XREF: PsSetLoadImageNotifyRoutine+6B↓j
PAGE:005709F6 pop edi
PAGE:005709F7 pop esi
PAGE:005709F8 pop ebx
PAGE:005709F9 pop ebp
PAGE:005709FA retn 4
PAGE:005709FD ; ---------------------------------------------------------------------------
PAGE:005709FD
PAGE:005709FD loc_5709FD: ; CODE XREF: PsSetLoadImageNotifyRoutine+2B↑j
PAGE:005709FD xor ecx, ecx
PAGE:005709FF mov eax, offset unk_542BA0
PAGE:00570A04 inc ecx
PAGE:00570A05 lock xadd [eax], ecx
PAGE:00570A09 mov eax, dword_542B78
PAGE:00570A0E test al, 1
PAGE:00570A10 jnz short loc_570A1C
PAGE:00570A12 mov eax, offset dword_542B78
PAGE:00570A17 lock bts dword ptr [eax], 0
PAGE:00570A1C
PAGE:00570A1C loc_570A1C: ; CODE XREF: PsSetLoadImageNotifyRoutine+5D↑j
PAGE:00570A1C xor eax, eax
PAGE:00570A1E jmp short loc_5709F6
PAGE:00570A1E PsSetLoadImageNotifyRoutine endp
2、反模块监控
#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
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!