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

守听  金牌会员 | 2023-6-24 17:07:50 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 873|帖子 873|积分 2619

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

首先将内核驱动程序代码放到如下,内核驱动程序没有任何特别的,仅仅只是一个通用驱动模板,在其基础上使用CR3读写,如果不理解CR3读写的原理您可以去看《驱动开发:内核CR3切换读写内存》这一篇中的详细介绍。
  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #include <ntifs.h>
  6. #include <intrin.h>
  7. #include <windef.h>
  8. #define        DEVICE_NAME                        L"\\Device\\WinDDK"
  9. #define LINK_NAME                        L"\\DosDevices\\WinDDK"
  10. #define LINK_GLOBAL_NAME        L"\\DosDevices\\Global\\WinDDK"
  11. // 控制信号 IOCTL_GET_CUR_CODE 用于读 | IOCTL_SET_ORI_CODE 用于写
  12. #define IOCTL_GET_CUR_CODE        CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
  13. #define IOCTL_SET_ORI_CODE        CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
  14. // 引用__readcr0等函数必须增加
  15. #pragma intrinsic(_disable)
  16. #pragma intrinsic(_enable)
  17. // 定义读写结构体
  18. typedef struct
  19. {
  20.         PVOID Address;
  21.         ULONG64 Length;
  22.         UCHAR data[256];
  23. } KF_DATA, *PKF_DATA;
  24. KIRQL g_irql;
  25. // 关闭写保护
  26. void WPOFFx64()
  27. {
  28.         ULONG64 cr0;
  29.         g_irql = KeRaiseIrqlToDpcLevel();
  30.         cr0 = __readcr0();
  31.         cr0 &= 0xfffffffffffeffff;
  32.         __writecr0(cr0);
  33.         _disable();
  34. }
  35. // 开启写保护
  36. void WPONx64()
  37. {
  38.         ULONG64 cr0;
  39.         cr0 = __readcr0();
  40.         cr0 |= 0x10000;
  41.         _enable();
  42.         __writecr0(cr0);
  43.         KeLowerIrql(g_irql);
  44. }
  45. // 设备创建时触发
  46. NTSTATUS DispatchCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  47. {
  48.         pIrp->IoStatus.Status = STATUS_SUCCESS;
  49.         pIrp->IoStatus.Information = 0;
  50.         DbgPrint("[LyShark] 设备已创建 \n");
  51.         IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  52.         return STATUS_SUCCESS;
  53. }
  54. // 设备关闭时触发
  55. NTSTATUS DispatchClose(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  56. {
  57.         pIrp->IoStatus.Status = STATUS_SUCCESS;
  58.         pIrp->IoStatus.Information = 0;
  59.         DbgPrint("[LyShark] 设备已关闭 \n");
  60.         IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  61.         return STATUS_SUCCESS;
  62. }
  63. // 主派遣函数
  64. NTSTATUS DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  65. {
  66.         NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
  67.         PIO_STACK_LOCATION pIrpStack;
  68.         ULONG uIoControlCode;
  69.         PVOID pIoBuffer;
  70.         ULONG uInSize;
  71.         ULONG uOutSize;
  72.         // 获取当前设备栈
  73.         pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
  74.         uIoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
  75.         // 获取缓冲区
  76.         pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
  77.         // 获取缓冲区长度
  78.         uInSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
  79.         // 输出缓冲区长度
  80.         uOutSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
  81.         switch (uIoControlCode)
  82.         {
  83.                 // 读内存
  84.         case IOCTL_GET_CUR_CODE:
  85.         {
  86.                 KF_DATA dat = { 0 };
  87.                 // 将缓冲区格式化为KF_DATA结构体
  88.                 RtlCopyMemory(&dat, pIoBuffer, 16);
  89.                 WPOFFx64();
  90.                 // 将数据写回到缓冲区
  91.                 RtlCopyMemory(pIoBuffer, dat.Address, dat.Length);
  92.                 WPONx64();
  93.                 status = STATUS_SUCCESS;
  94.                 break;
  95.         }
  96.         // 写内存
  97.         case IOCTL_SET_ORI_CODE:
  98.         {
  99.                 KF_DATA dat = { 0 };
  100.                 // 将缓冲区格式化为KF_DATA结构体
  101.                 RtlCopyMemory(&dat, pIoBuffer, sizeof(KF_DATA));
  102.                 WPOFFx64();
  103.                 // 将数据写回到缓冲区
  104.                 RtlCopyMemory(dat.Address, dat.data, dat.Length);
  105.                 WPONx64();
  106.                 status = STATUS_SUCCESS;
  107.                 break;
  108.         }
  109.         }
  110.         if (status == STATUS_SUCCESS)
  111.                 pIrp->IoStatus.Information = uOutSize;
  112.         else
  113.                 pIrp->IoStatus.Information = 0;
  114.         pIrp->IoStatus.Status = status;
  115.         IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  116.         return status;
  117. }
  118. // 驱动卸载
  119. VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
  120. {
  121.         UNICODE_STRING strLink;
  122.         // 删除符号链接卸载设备
  123.         RtlInitUnicodeString(&strLink, LINK_NAME);
  124.         IoDeleteSymbolicLink(&strLink);
  125.         IoDeleteDevice(pDriverObj->DeviceObject);
  126. }
  127. // 驱动程序入口
  128. NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString)
  129. {
  130.         NTSTATUS status = STATUS_SUCCESS;
  131.         UNICODE_STRING ustrLinkName;
  132.         UNICODE_STRING ustrDevName;
  133.         PDEVICE_OBJECT pDevObj;
  134.         // 初始化派遣函数
  135.         pDriverObj->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
  136.         pDriverObj->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
  137.         pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
  138.         DbgPrint("hello lysahrk.com \n");
  139.         // 初始化设备名
  140.         RtlInitUnicodeString(&ustrDevName, DEVICE_NAME);
  141.         // 创建设备
  142.         status = IoCreateDevice(pDriverObj, 0, &ustrDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
  143.         if (!NT_SUCCESS(status))
  144.         {
  145.                 return status;
  146.         }
  147.         // 创建符号链接
  148.         RtlInitUnicodeString(&ustrLinkName, LINK_NAME);
  149.         status = IoCreateSymbolicLink(&ustrLinkName, &ustrDevName);
  150.         if (!NT_SUCCESS(status))
  151.         {
  152.                 IoDeleteDevice(pDevObj);
  153.                 return status;
  154.         }
  155.         pDriverObj->DriverUnload = DriverUnload;
  156.         return STATUS_SUCCESS;
  157. }
复制代码
接着来分析下应用层做了什么,首先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)hKernel
  • address2 = KernelBase - (ULONG64)hKernel + (ULONG64)GetProcAddress(hKernel, "NtWriteFile")
调用GetOriginalMachineCode()则用于获取相对偏移地址,该地址的获取方式如下,用户传入一个Address当前地址,该地址减去KernelBase内存中的基址,然后再加上hKernel磁盘加载的基址来获取到相对偏移。

  • OffsetAddress = Address - KernelBase + hKernel
有了这两条信息那么功能也就实现了,通过GetOriginalMachineCode()得到指定内存地址处原始机器码,通过GetCurrentMachineCode()得到当前内存机器码,两者通过memcmp()函数比对即可知道是否被挂钩了,如果被挂钩则可以通过CR3切换将原始机器码覆盖到特定位置替换即可,这段程序的完整代码如下;
[code]// 署名权// 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;HMODULE        hKernel = 0;ULONG64        KernelBase = 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_HEADER                pDosHeader;        PIMAGE_NT_HEADERS64                pNtHeader;        PIMAGE_BASE_RELOCATION        pRelocTable;        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                {                        // 得到重定位号                        ULONG        numofReloc = (pRelocTable->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;                        SHORT        minioffset = 0;                                                // 得到重定位数据                        PUSHORT pRelocData = (PUSHORT)((ULONG64)pRelocTable + sizeof(IMAGE_BASE_RELOCATION));                        // 循环或直接判断*pRelocData是否为0也可以作为结束标记                        for (i = 0; i> 12) == IMAGE_REL_BASED_DIR64)                                {                                        // 计算需要进行重定位的地址                                        // 重定位数据的低12位再加上本重定位块头的RVA即真正需要重定位的数据的RVA                                        minioffset = (*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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

守听

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表