在上一章《驱动开发:内核LDE64引擎计算汇编长度》
中,LyShark
教大家如何通过LDE64
引擎实现计算反汇编指令长度 , 本章将在此基础之上实现内联函数挂钩,内核中的InlineHook
函数挂钩其实与应用层一致 , 都是使用劫持执行流
并跳转到我们自己的函数上来做处理 , 唯一的不同的是内核Hook
只针对内核API
函数,但由于其身处在最底层
所以一旦被挂钩其整个应用层都将会受到影响,这就直接决定了在内核层挂钩的效果是应用层无法比拟的,对于安全从业者来说学会使用内核挂钩也是很重要 。
【驱动开发:内核层InlineHook挂钩函数】挂钩的原理可以总结为,通过MmGetSystemRoutineAddress
得到原函数地址,然后保存该函数的前15
个字节的指令,将自己的MyPsLookupProcessByProcessId
代理函数地址写出到原始函数上 , 此时如果有API被调用则默认会转向到我们自己的函数上面执行,恢复原理则是将提前保存好的前15个原始字节写回则恢复原函数的调用 。
原理很简单,基本上InlineHook
类的代码都是一个样子,如下是一段完整的挂钩PsLookupProcessByProcessId
的驱动程序,当程序被加载时则默认会保护lyshark.exe
进程,使其无法被用户使用任务管理器结束掉 。
// 署名权// right to sign one's name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include "lyshark_lde64.h"#include <ntifs.h>#include <windef.h>#include <intrin.h>#pragmaintrinsic(_disable)#pragmaintrinsic(_enable)// --------------------------------------------------------------// 汇编计算方法// --------------------------------------------------------------// 计算地址处指令有多少字节// address = 地址// bits 32位驱动传入0 64传入64typedef INT(*LDE_DISASM)(PVOID address, INT bits);LDE_DISASM lde_disasm;// 初始化引擎VOID lde_init(){ lde_disasm = ExAllocatePool(NonPagedPool, 12800); memcpy(lde_disasm, szShellCode, 12800);}// 得到完整指令长度,避免截断ULONG GetFullPatchSize(PUCHAR Address){ ULONG LenCount = 0, Len = 0; // 至少需要14字节 while (LenCount <= 14) {Len = lde_disasm(Address, 64);Address = Address + Len;LenCount = LenCount + Len; } return LenCount;}// --------------------------------------------------------------// Hook函数封装// --------------------------------------------------------------// 定义指针方便调用typedef NTSTATUS(__fastcall *PSLOOKUPPROCESSBYPROCESSID)(HANDLE ProcessId, PEPROCESS *Process);ULONG64 protect_eprocess = 0;// 需要保护进程的eprocessULONG patch_size = 0;// 被修改了几个字节PUCHAR head_n_byte = NULL;// 前几个字节数组PVOID original_address = NULL;// 原函数地址KIRQL WPOFFx64(){ KIRQL irql = KeRaiseIrqlToDpcLevel(); UINT64 cr0 = __readcr0(); cr0 &= 0xfffffffffffeffff; __writecr0(cr0); _disable(); return irql;}VOID WPONx64(KIRQL irql){ UINT64 cr0 = __readcr0(); cr0 |= 0x10000; _enable(); __writecr0(cr0); KeLowerIrql(irql);}// 动态获取内存地址PVOID GetProcessAddress(PCWSTR FunctionName){ UNICODE_STRING UniCodeFunctionName; RtlInitUnicodeString(&UniCodeFunctionName, FunctionName); return MmGetSystemRoutineAddress(&UniCodeFunctionName);}/* InlineHookAPI 挂钩地址 参数1:待HOOK函数地址 参数2:代理函数地址 参数3:接收原始函数地址的指针 参数4:接收补丁长度的指针 返回:原来头N字节的数据*/PVOID KernelHook(IN PVOID ApiAddress, IN PVOID Proxy_ApiAddress, OUT PVOID *Original_ApiAddress, OUT ULONG *PatchSize){ KIRQL irql; UINT64 tmpv; PVOID head_n_byte, ori_func; // 保存跳转指令 JMP QWORD PTR [本条指令结束后的地址] UCHAR jmp_code[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; // 保存原始指令 UCHAR jmp_code_orifunc[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; // 获取函数地址处指令长度 *PatchSize = GetFullPatchSize((PUCHAR)ApiAddress); // 分配空间 head_n_byte = ExAllocatePoolWithTag(NonPagedPool, *PatchSize, "LyShark"); irql = WPOFFx64(); // 跳转地址拷贝到原函数上 RtlCopyMemory(head_n_byte, ApiAddress, *PatchSize); WPONx64(irql); // 构建跳转 // 1.原始机器码+跳转机器码 ori_func = ExAllocatePoolWithTag(NonPagedPool, *PatchSize + 14, "LyShark"); RtlFillMemory(ori_func, *PatchSize + 14, 0x90); // 2.跳转到没被打补丁的那个字节 tmpv = (ULONG64)ApiAddress + *PatchSize; RtlCopyMemory(jmp_code_orifunc + 6, &tmpv, 8); RtlCopyMemory((PUCHAR)ori_func, head_n_byte, *PatchSize); RtlCopyMemory((PUCHAR)ori_func + *PatchSize, jmp_code_orifunc, 14); *Original_ApiAddress = ori_func; // 3.得到代理地址 tmpv = (UINT64)Proxy_ApiAddress; RtlCopyMemory(jmp_code + 6, &tmpv, 8); //4.打补丁 irql = WPOFFx64(); RtlFillMemory(ApiAddress, *PatchSize, 0x90); RtlCopyMemory(ApiAddress, jmp_code, 14); WPONx64(irql); return head_n_byte;}/* InlineHookAPI 恢复挂钩地址 参数1:被HOOK函数地址 参数2:原始数据 参数3:补丁长度*/VOID KernelUnHook(IN PVOID ApiAddress, IN PVOID OriCode, IN ULONG PatchSize){ KIRQL irql; irql = WPOFFx64(); RtlCopyMemory(ApiAddress, OriCode, PatchSize); WPONx64(irql);}// 实现我们自己的代理函数NTSTATUS MyPsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process){ NTSTATUS st; st = ((PSLOOKUPPROCESSBYPROCESSID)original_address)(ProcessId, Process); if (NT_SUCCESS(st)) {// 判断是否是需要保护的进程if (*Process == (PEPROCESS)protect_eprocess){*Process = 0;DbgPrint("[lyshark] 拦截结束进程 \n");st = STATUS_ACCESS_DENIED;} } return st;}VOID UnDriver(PDRIVER_OBJECT driver){ DbgPrint("驱动已卸载 \n"); // 恢复Hook KernelUnHook(GetProcessAddress(L"PsLookupProcessByProcessId"), head_n_byte, patch_size);}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath){ DbgPrint("hello lyshark.com \n"); // 初始化反汇编引擎 lde_init(); // 设置需要保护进程EProcess /* lyshark.com: kd> !process 0 0 lyshark.exePROCESS ffff9a0a44ec4080SessionId: 1Cid: 05b8Peb: 0034d000ParentCid: 13f0DirBase: 12a7d2002ObjectTable: ffffd60bc036f080HandleCount: 159.Image: lyshark.exe */ protect_eprocess = 0xffff9a0a44ec4080; // Hook挂钩函数 head_n_byte = KernelHook(GetProcessAddress(L"PsLookupProcessByProcessId"), (PVOID)MyPsLookupProcessByProcessId, &original_address, &patch_size); DbgPrint("[lyshark] 挂钩保护完成 --> 修改字节: %d | 原函数地址: 0x%p \n", patch_size, original_address); for (size_t i = 0; i < patch_size; i++) {DbgPrint("[byte] = %x", head_n_byte[i]); } Driver->DriverUnload = UnDriver; return STATUS_SUCCESS;}
推荐阅读
- 20 基于SqlSugar的开发框架循序渐进介绍-- 在基于UniApp+Vue的移动端实现多条件查询的处理
- 苹果iOS App上架流程,非iOS开发人员上架教程
- 京东云开发者|ElasticSearch降本增效常见的方法
- 二 沁恒CH32V003: Ubuntu20.04 MRS和Makefile开发环境配置
- 三十九 Java开发学习----SpringBoot整合mybatis
- 更新显卡驱动的方法(更新显卡驱动需要卸载旧版本吗)
- 电脑用驱动精灵怎么更新显卡驱动(怎么用驱动精灵将显卡驱动升级)
- 如何正确更新NVIDIA显卡驱动(win7显卡驱动更新)
- 沁恒CH32V003F4P6 开发板上手报告和Win10环境配置
- visual studio插件开发-Menu