驱动开发:摘除InlineHook内核钩子

来源:博客园时间:2023-06-24 13:25:41


【资料图】

在笔者上一篇文章《驱动开发:内核层InlineHook挂钩函数》中介绍了通过替换函数头部代码的方式实现Hook挂钩,对于ARK工具来说实现扫描与摘除InlineHook钩子也是最基本的功能,此类功能的实现一般可在应用层进行,而驱动层只需要保留一个读写字节的函数即可,将复杂的流程放在应用层实现是一个非常明智的选择,与《驱动开发:内核实现进程反汇编》中所使用的读写驱动基本一致,本篇文章中的驱动只保留两个功能,控制信号IOCTL_GET_CUR_CODE用于读取函数的前16个字节的内存,信号IOCTL_SET_ORI_CODE则用于设置前16个字节的内存。

之所以是前16个字节是因为一般的内联Hook只需要使用两条指令就可实现劫持,如下是通用ARK工具扫描到的被挂钩函数的样子。

首先将内核驱动程序代码放到如下,内核驱动程序没有任何特别的,仅仅只是一个通用驱动模板,在其基础上使用CR3读写,如果不理解CR3读写的原理您可以去看《驱动开发:内核CR3切换读写内存》这一篇中的详细介绍。

// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include #include #include #defineDEVICE_NAMEL"\\Device\\WinDDK"#define LINK_NAMEL"\\DosDevices\\WinDDK"#define LINK_GLOBAL_NAMEL"\\DosDevices\\Global\\WinDDK"// 控制信号 IOCTL_GET_CUR_CODE 用于读 | IOCTL_SET_ORI_CODE 用于写#define IOCTL_GET_CUR_CODECTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)#define IOCTL_SET_ORI_CODECTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)// 引用__readcr0等函数必须增加#pragma intrinsic(_disable)#pragma intrinsic(_enable)// 定义读写结构体typedef struct{PVOID Address;ULONG64 Length;UCHAR data[256];} KF_DATA, *PKF_DATA;KIRQL g_irql;// 关闭写保护void WPOFFx64(){ULONG64 cr0;g_irql = KeRaiseIrqlToDpcLevel();cr0 = __readcr0();cr0 &= 0xfffffffffffeffff;__writecr0(cr0);_disable();}// 开启写保护void WPONx64(){ULONG64 cr0;cr0 = __readcr0();cr0 |= 0x10000;_enable();__writecr0(cr0);KeLowerIrql(g_irql);}// 设备创建时触发NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp){pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;DbgPrint("[LyShark] 设备已创建 \n");IoCompleteRequest(pIrp, IO_NO_INCREMENT);return STATUS_SUCCESS;}// 设备关闭时触发NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp){pIrp->IoStatus.Status = STATUS_SUCCESS;pIrp->IoStatus.Information = 0;DbgPrint("[LyShark] 设备已关闭 \n");IoCompleteRequest(pIrp, IO_NO_INCREMENT);return STATUS_SUCCESS;}// 主派遣函数NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp){NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;PIO_STACK_LOCATION pIrpStack;ULONG uIoControlCode;PVOID pIoBuffer;ULONG uInSize;ULONG uOutSize;// 获取当前设备栈pIrpStack = IoGetCurrentIrpStackLocation(pIrp);uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;// 获取缓冲区pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;// 获取缓冲区长度uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;// 输出缓冲区长度uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;switch (uIoControlCode){// 读内存case IOCTL_GET_CUR_CODE:{KF_DATA dat = { 0 };// 将缓冲区格式化为KF_DATA结构体RtlCopyMemory(&dat, pIoBuffer, 16);WPOFFx64();// 将数据写回到缓冲区RtlCopyMemory(pIoBuffer, dat.Address, dat.Length);WPONx64();status = STATUS_SUCCESS;break;}// 写内存case IOCTL_SET_ORI_CODE:{KF_DATA dat = { 0 };// 将缓冲区格式化为KF_DATA结构体RtlCopyMemory(&dat, pIoBuffer, sizeof(KF_DATA));WPOFFx64();// 将数据写回到缓冲区RtlCopyMemory(dat.Address, dat.data, dat.Length);WPONx64();status = STATUS_SUCCESS;break;}}if (status == STATUS_SUCCESS)pIrp->IoStatus.Information = uOutSize;elsepIrp->IoStatus.Information = 0;pIrp->IoStatus.Status = status;IoCompleteRequest(pIrp, IO_NO_INCREMENT);return status;}// 驱动卸载VOID DriverUnload(PDRIVER_OBJECT pDriverObj){UNICODE_STRING strLink;// 删除符号链接卸载设备RtlInitUnicodeString(&strLink, LINK_NAME);IoDeleteSymbolicLink(&strLink);IoDeleteDevice(pDriverObj->DeviceObject);}// 驱动程序入口NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString){NTSTATUS status = STATUS_SUCCESS;UNICODE_STRING ustrLinkName;UNICODE_STRING ustrDevName;PDEVICE_OBJECT pDevObj;// 初始化派遣函数pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;DbgPrint("hello lysahrk.com \n");// 初始化设备名RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);// 创建设备status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);if (!NT_SUCCESS(status)){return status;}// 创建符号链接RtlInitUnicodeString(&ustrLinkName, LINK_NAME);status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);if (!NT_SUCCESS(status)){IoDeleteDevice(pDevObj);return status;}pDriverObj->DriverUnload = DriverUnload;return STATUS_SUCCESS;}

接着来分析下应用层做了什么,首先GetKernelBase64函数的作用,该函数内部通过GetProcAddress()函数动态寻找到ZwQuerySystemInformation()函数的内存地址(此函数未被到处所以只能动态找到),找到后调用ZwQuerySystemInformation()直接拿到系统中的所有模块信息,通过pSystemModuleInformation->Module[0].Base得到系统中第一个模块的基地址,此模块就是ntoskrnl.exe,该模块也是系统运行后的第一个启动的,此时我们即可拿到KernelBase也就是系统内存中的基地址。

此时通过LoadLibraryExA()函数动态加载,此时加载的是磁盘中的被Hook函数的所属模块,获得映射地址后将此地址装入hKernel变量内,此时我们拥有了内存中的KernelBase以及磁盘中加载的hKernel,接着调用RepairRelocationTable()让两者的重定位表保持一致。

此时当用户调用GetSystemRoutineAddress()则执行如下流程,想要获取当前内存地址,则需要使用当前内存中的KernelBase模块基址加上通过GetProcAddress()动态获取到的磁盘基址中的函数地址减去磁盘中的基地址,将内存中的KernelBase加上磁盘中的相对偏移就得到了当前内存中加载函数的实际地址。

address1 = KernelBase + (ULONG64)GetProcAddress(hKernel, "NtWriteFile") - (ULONG64)hKerneladdress2 = KernelBase - (ULONG64)hKernel + (ULONG64)GetProcAddress(hKernel, "NtWriteFile")

调用GetOriginalMachineCode()则用于获取相对偏移地址,该地址的获取方式如下,用户传入一个Address当前地址,该地址减去KernelBase内存中的基址,然后再加上hKernel磁盘加载的基址来获取到相对偏移。

OffsetAddress = Address - KernelBase + hKernel

有了这两条信息那么功能也就实现了,通过GetOriginalMachineCode()得到指定内存地址处原始机器码,通过GetCurrentMachineCode()得到当前内存机器码,两者通过memcmp()函数比对即可知道是否被挂钩了,如果被挂钩则可以通过CR3切换将原始机器码覆盖到特定位置替换即可,这段程序的完整代码如下;

// 署名权// right to sign one"s name on a piece of work// PowerBy: LyShark// Email: me@lyshark.com#include #include #pragma comment(lib,"user32.lib")#pragma comment(lib,"Advapi32.lib")#ifndef NT_SUCCESS#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)#endif#define BYTE_ARRAY_LENGTH 16#define SystemModuleInformation 11#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)typedef long(__stdcall *ZWQUERYSYSTEMINFORMATION)(IN ULONG SystemInformationClass,IN PVOID SystemInformation,IN ULONG SystemInformationLength,IN PULONG ReturnLength OPTIONAL);typedef struct{ULONG Unknow1;ULONG Unknow2;ULONG Unknow3;ULONG Unknow4;PVOID Base;ULONG Size;ULONG Flags;USHORT Index;USHORT NameLength;USHORT LoadCount;USHORT ModuleNameOffset;char ImageName[256];} SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY;typedef struct{ULONG Count;SYSTEM_MODULE_INFORMATION_ENTRY Module[1];} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;typedef struct{PVOID Address;ULONG64 Length;UCHAR data[256];} KF_DATA, *PKF_DATA;HANDLE hDriver = 0;HMODULEhKernel = 0;ULONG64KernelBase = 0;CHAR NtosFullName[260] = { 0 };// 生成控制信号DWORD CTL_CODE_GEN(DWORD lngFunction){return (FILE_DEVICE_UNKNOWN * 65536) | (FILE_ANY_ACCESS * 16384) | (lngFunction * 4) | METHOD_BUFFERED;}// 发送控制信号的函数BOOL IoControl(HANDLE hDrvHandle, DWORD dwIoControlCode, PVOID lpInBuffer, DWORD nInBufferSize, PVOID lpOutBuffer, DWORD nOutBufferSize){DWORD lDrvRetSize;return DeviceIoControl(hDrvHandle, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, &lDrvRetSize, 0);}// 动态获取ntdll.dll模块的基地址ULONG64 GetKernelBase64(PCHAR NtosName){ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation;PSYSTEM_MODULE_INFORMATION pSystemModuleInformation;ULONG NeedSize, BufferSize = 0x5000;PVOID pBuffer = NULL;NTSTATUS Result;// 该函数只能通过动态方式得到地址ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwQuerySystemInformation");do{pBuffer = malloc(BufferSize);if (pBuffer == NULL) return 0;// 查询系统中的所有模块信息Result = ZwQuerySystemInformation(SystemModuleInformation, pBuffer, BufferSize, &NeedSize);if (Result == STATUS_INFO_LENGTH_MISMATCH){free(pBuffer);BufferSize *= 2;}else if (!NT_SUCCESS(Result)){free(pBuffer);return 0;}} while (Result == STATUS_INFO_LENGTH_MISMATCH);// 取模块信息结构pSystemModuleInformation = (PSYSTEM_MODULE_INFORMATION)pBuffer;// 得到模块基地址ULONG64 ret = (ULONG64)(pSystemModuleInformation->Module[0].Base);// 拷贝模块名if (NtosName != NULL){strcpy(NtosName, pSystemModuleInformation->Module[0].ImageName + pSystemModuleInformation->Module[0].ModuleNameOffset);}free(pBuffer);return ret;}// 判断并修复重定位表BOOL RepairRelocationTable(ULONG64 HandleInFile, ULONG64 BaseInKernel){PIMAGE_DOS_HEADERpDosHeader;PIMAGE_NT_HEADERS64pNtHeader;PIMAGE_BASE_RELOCATIONpRelocTable;ULONG i, dwOldProtect;// 得到DOS头并判断是否符合DOS规范pDosHeader = (PIMAGE_DOS_HEADER)HandleInFile;if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE){return FALSE;}// 得到Nt头pNtHeader = (PIMAGE_NT_HEADERS64)((ULONG64)HandleInFile + pDosHeader->e_lfanew);// 是否存在重定位表if (pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size){// 获取到重定位表基地址pRelocTable = (PIMAGE_BASE_RELOCATION)((ULONG64)HandleInFile + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);do{// 得到重定位号ULONGnumofReloc = (pRelocTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;SHORTminioffset = 0;// 得到重定位数据PUSHORT pRelocData = (PUSHORT)((ULONG64)pRelocTable + sizeof(IMAGE_BASE_RELOCATION));// 循环或直接判断*pRelocData是否为0也可以作为结束标记for (i = 0; i> 12) == IMAGE_REL_BASED_DIR64){// 计算需要进行重定位的地址// 重定位数据的低12位再加上本重定位块头的RVA即真正需要重定位的数据的RVAminioffset = (*pRelocData) & 0xFFF; // 小偏移// 模块基址+重定位基址+每个数据表示的小偏移量RelocAddress = (PULONG64)(HandleInFile + pRelocTable->VirtualAddress + minioffset);// 直接在RING3修改: 原始数据+基址-IMAGE_OPTINAL_HEADER中的基址VirtualProtect((PVOID)RelocAddress, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);// 因为是R3直接LOAD的所以要修改一下内存权限*RelocAddress = *RelocAddress + BaseInKernel - pNtHeader->OptionalHeader.ImageBase;VirtualProtect((PVOID)RelocAddress, 4, dwOldProtect, NULL);}// 下一个重定位数据pRelocData++;}// 下一个重定位块pRelocTable = (PIMAGE_BASE_RELOCATION)((ULONG64)pRelocTable + pRelocTable->SizeOfBlock);} while (pRelocTable->VirtualAddress);return TRUE;}return FALSE;}// 初始化BOOL InitEngine(BOOL IsClear){if (IsClear == TRUE){// 动态获取ntdll.dll模块的基地址KernelBase = GetKernelBase64(NtosFullName);printf("模块基址: %llx | 模块名: %s \n", KernelBase, NtosFullName);if (!KernelBase){return FALSE;}// 动态加载模块到内存,并获取到模块句柄hKernel = LoadLibraryExA(NtosFullName, 0, DONT_RESOLVE_DLL_REFERENCES);if (!hKernel){return FALSE;}// 判断并修复重定位表if (!RepairRelocationTable((ULONG64)hKernel, KernelBase)){return FALSE;}return TRUE;}else{FreeLibrary(hKernel);return TRUE;}}// 获取原始函数机器码VOID GetOriginalMachineCode(ULONG64 Address, PUCHAR ba, SIZE_T Length){ULONG64 OffsetAddress = Address - KernelBase + (ULONG64)hKernel;RtlCopyMemory(ba, (PVOID)OffsetAddress, Length);}// 获取传入函数的内存地址ULONG64 GetSystemRoutineAddress(PCHAR FuncName){return KernelBase + (ULONG64)GetProcAddress(hKernel, FuncName) - (ULONG64)hKernel;}// 获取当前函数机器码VOID GetCurrentMachineCode(ULONG64 Address, PUCHAR ba, SIZE_T Length){ULONG64 dat[2] = { 0 };dat[0] = Address;dat[1] = Length;IoControl(hDriver, CTL_CODE_GEN(0x800), dat, 16, ba, Length);}// 清除特定位置的机器码VOID ClearInlineHook(ULONG64 Address, PUCHAR ba, SIZE_T Length){KF_DATA dat = { 0 };dat.Address = (PVOID)Address;dat.Length = Length;// 直接调用写出控制码RtlCopyMemory(dat.data, ba, Length);IoControl(hDriver, CTL_CODE_GEN(0x801), &dat, sizeof(KF_DATA), 0, 0);}// 打印数据VOID PrintBytes(PCHAR DescriptionString, PUCHAR ba, UINT Length){printf("%s", DescriptionString);for (UINT i = 0; i

首先编译驱动程序WinDDK.sys并通过KmdManager将驱动程序拉起来,运行客户端lyshark.exe程序会输出当前FunctionList列表中,指定的4个函数的挂钩情况。

参考文献

WIN64内核编程基础 胡文亮

关键词:

相关阅读

推荐阅读

银鹏控股公司:业务交流促提升互学互鉴共进步|世

3月2日,银鹏控股公司董事长刘懿婷带队前往中粮集团期货公司进行业务学习和交流。中粮期货公司从业多年、具有丰富实战经验的风险管理专家现场更多

2023-03-07 17:52:41

我国首个万吨级钠离子电池材料项目在山西综改区开

山西晚报讯(记者温丽芳通讯员张晓茹)日前,总投资约11亿元的山西华钠铜能(碳能)科技有限责任公司万吨级钠离子电池正(负)极材料项目在山更多

2023-03-07 11:51:14

建行阳江市分行:践行金融惠民理念-全球关注

2月24日,随着客户办妥抵押登记手续并成功放款,标志着阳江市建行“带押过户”业务取得历史性突破。根据中国人民银行发布《关于鼓励推广二手房更多

2023-03-07 11:53:21

焦点简讯:证监会同意三超新材向特定对象发行股票

中证网讯(记者昝秀丽)证监会网站3月6日消息,证监会发布关于同意南京三超新材料股份有限公司向特定对象发行股票注册的批复。【来源:中国证更多

2023-03-07 09:57:39

美股航空股6日全线走低

中证网讯(记者赵中昊)当地时间周一(3月6日),美股航空股全线走低。据wind数据,截至收盘,波音跌1 49%,美国航空跌1 47%,达美航空跌1%,更多

2023-03-07 09:43:44

世界热资讯!晋钢控股集团荣登2022山西省品牌十强

近日,“品牌强国(龙城)论坛”在太原举行,发布“2022中国上市公司品牌500强”以及“2022山西省品牌100强”。晋钢控股集团以218 9亿元的品牌更多

2023-03-06 17:38:52

手绘报告:2023预期目标

【来源:中国政府网】声明:转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请作者持权属证明发至邮箱newmedia@xx更多

2023-03-06 16:46:31

惠州首笔数字人民币缴纳税费业务成功落地

3月1日,TCL科技集团财务有限公司拿到了全市第一张使用数字人民币账户缴纳税款和非税收入的完税凭证,标志着惠州市首笔数字人民币缴纳税费业务更多

2023-03-06 11:50:50
    首套房公积金利率下调原来贷款也下调吗首套房公积金利率下调原来...
    政府回购商品房意味着什么政府收购商品房的首要目的是稳定市场。...
    把自己房子卖了再买算首套房吗买过一套房卖了再买算首套房。简单...
    买房交首付注意事项有哪些1、核实开发商五证。在交首付时,需要先...
    房屋契税征收比例契税税率的缴纳一般分为以下几种情况:1、面积小...
    房山区住建委近日发布《房山区2022年第二批毕业大学生对接保障性...

关于我们 Copyright   2015-2022 当代财经网  www.ddcjw.cn 版权所有  备案号:京ICP备2021034106号-19  联系邮箱:55 16 53 8 @qq.com