Hook SwapContext函数

在开始的时候我们要注意 SwapContext函数 是个高并发的函数

话不多说开始我们的Hook(注意是WindowsXp下的Hook)

Object.h 文件代码

#pragma once
 
#ifndef LGOBJECT
#define LGOBJECT
#include <Ntifs.h>
 
// 定义操作系统版本
#define WINXP 51
#define WIN7  61
#define WIN8  62
#define WIN10 100
 
// 定义分页模式
#define NormalPageMode 0x1
#define NotNormalPageMode 0x0
 
// 定义ShellCode汇编指令长度
#define ASMCODELEN 0x8
 
// 定义 DLL SectionAlignment 对齐长度
#define DLLAlignment 0x1000000
 
// 定义特征码数组长度
#define TRAITCODElLEN 0x4
 
// 用于接收函数地址
ULONG AcceptFunction;
 
// 获取操作系统版本
ULONG GetWindowsVersion();
// 获取操作系统分页模式
ULONG GetWindowsPageMode();
 
// 生成跳转的ShellCode
//++++++++++++++++++++++++++++
// ShellCode: 存储代码的容器
// TargetAddr: 要写入 ShellCode 的地址
// JmpAddr: 跳转的目标地址
//++++++++++++++++++++++++++++
// 返回 ShellCode 实际长度
ULONG CreateJmpShellCode(CHAR * ShellCode, ULONG TargetAddr, ULONG JmpAddr);
 
// 返回具体需要多少汇编指令长度
ULONG GetAsmCodeLen(ULONG TargetAddr, ULONG * RetnAddr);
 
// 快速交换内存
VOID  _fastcall FastSwapMemory(ULONG * TargetAddr, ULONG * SoulAddr);
 
// 快速修复代码
VOID _fastcall ReStore(ULONG * TargetAddr);
 
// Hook 的主函数
//++++++++++++++++++++++++++++
// hookAddr: 要Hook的地址
// hookFunctions: 你自己的Hook函数地址
// OriFun: 伪原函数地址
//++++++++++++++++++++++++++++
BOOLEAN InlineHook(ULONG hookAddr, ULONG hookFunctions, ULONG * OriFun);
 
// 修改Cr0寄存器, 去除写保护(内存保护机制)
KIRQL RemovWP();
// 复原Cr0寄存器
KIRQL UndoWP();
 
// 遍历内存搜索指定函数
/*
opCodeArray: 特征码数组
IsStable: TRUE(映射搜索), FALSE(直接当前内核搜索)
*/
ULONG TraitCodeByErgodicMemory(ULONG * opCodeArray, BOOLEAN IsStable);
 
// 将指定模块映射到内存中
// DllFileName: 要映射的DLL文件路径名称
// phFile: 返回DLL文件的句柄
// phSection: 返回节的句柄
// pBaseAddress: DLL映射在此进程中的起始地址
NTSTATUS DllFileMap(UNICODE_STRING * pDllFileName, HANDLE * phFile,
	HANDLE * phSection, PVOID * pBaseAddress, SIZE_T * pviewSize);
#endif

Driver.c 文件代码

#include "Object.h"
// Hook SwapContext函数
// 注意该函数的高并发
// 定义函数原形
typedef void(*pSwapContext)();
pSwapContext SwapContext = NULL;
ULONG CurrentThread = 0x0;
ULONG NextThread = 0x0;
void __declspec(naked) HookSwapContextFunction()
{
        __asm
        {
               mov dword ptr[CurrentThread], edi;
               mov dword ptr[NextThread], esi;
        }
        __asm
        {
               pushad;
               pushfd;
        }
        //KdBreakPoint();
        DbgPrint("当前线程为: %X\t\t下一个线程为: %X\n", CurrentThread, NextThread);
        __asm
        {
               popfd;
               popad;
               mov eax, dword ptr[SwapContext];
               jmp eax; // 跳回原来函数位置
        }
}
VOID Unload(PDRIVER_OBJECT DriverObject)
{
        KdPrint(("驱动卸载\n"));
        //KdBreakPoint();
        if (AcceptFunction) { ; }
               //ReStore((ULONG *)(AcceptFunction));
               //ReStore((ULONG *)(AcceptFunction + 0x20));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regedit)
{
        KdPrint(("驱动加载\n"));
        NTSTATUS status = STATUS_SUCCESS;
        DriverObject->DriverUnload = Unload;
        AcceptFunction = 0x0;
        //KdBreakPoint();
        //ULONG opCodeArray[TRAITCODElLEN] = { 0xc626c90a ,0x9c022d46 ,0xbb830b8b  ,0x00000994 };
        ULONG opCodeArray[TRAITCODElLEN] = { 0xc626c90a ,0x9c022d46 ,0x05408b8d  ,0xdde80000 };
        
        AcceptFunction = TraitCodeByErgodicMemory(opCodeArray, FALSE);
        if (AcceptFunction)
        {
               SwapContext = (pSwapContext)AcceptFunction;
               // 注意其是一次性Hook 8个字节
               //InlineHook((ULONG)SwapContext, (ULONG)HookSwapContextFunction,  &(ULONG)SwapContext);
               InlineHook((ULONG)SwapContext + 0x20, (ULONG)HookSwapContextFunction,  &(ULONG)SwapContext);
        }
        
        return status;
}

HookFunctions.c 文件代码

#include "Object.h"
#include "LDE64x64.h"
// 主函数
//++++++++++++++++++++++++++++
// hookAddr: 要Hook的地址
// hookFunctions: 你自己的Hook函数地址
// OriFun: 伪原函数地址
//++++++++++++++++++++++++++++
BOOLEAN InlineHook(ULONG hookAddr, ULONG hookFunctions, ULONG * OriFun)
{
        ULONG SystemVersion = GetWindowsVersion();
        switch (SystemVersion)
        {
        case WINXP:
               // 如果是 XP 系统
        {
               // 判断分页模式
               if (GetWindowsPageMode() == NormalPageMode)
               {
                       // 为 2-9-9-12 分页
                       CHAR MyJmpOverShellCode[ASMCODELEN] = { 0x90 , 0x90 , 0x90 , 0x90 ,  0x90 , 0x90 , 0x90 , 0x90 };
                       CHAR MyJmpBackShellCode[ASMCODELEN] = { 0x90 , 0x90 , 0x90 , 0x90 ,  0x90 , 0x90 , 0x90 , 0x90 };
                       ULONG TargetAddr = hookAddr;
                       ULONG JmpAddr = hookFunctions;
                       // 判断需要多少汇编指令长度
                       ULONG RetAddr = 0x0; // 返回原来代码的地址
                       ULONG AsmLen = ASMCODELEN;//GetAsmCodeLen(TargetAddr, &RetAddr);
                       RetAddr = hookAddr + ASMCODELEN;
                       // 获取JmpOverCode
                       CreateJmpShellCode(MyJmpOverShellCode, TargetAddr, JmpAddr);
                       // 构建返回的通道
                       PVOID JmpBackShellCode = ExAllocatePool(NonPagedPool, AsmLen +  ASMCODELEN);
                       RtlZeroMemory(JmpBackShellCode, AsmLen + ASMCODELEN);
                       // 获取JmpBackCode
                       ULONG BackAddr = (ULONG)JmpBackShellCode + AsmLen;
                       CreateJmpShellCode(MyJmpBackShellCode, BackAddr, RetAddr);
                       // 读取Hook地址指定长度数据
                       RtlCopyMemory(JmpBackShellCode, (PVOID)TargetAddr, AsmLen);
                       // 添加返回通道
                       RtlCopyMemory((PCHAR)JmpBackShellCode + AsmLen,  (PVOID)MyJmpBackShellCode, ASMCODELEN);
                       // 填写要原函数的地址
                       *OriFun = (ULONG)JmpBackShellCode;
                       // InlineHook
                       RemovWP();
                       //KdBreakPoint();
                       // Hook TargetAddr
                       FastSwapMemory((PULONG)TargetAddr, (PULONG)MyJmpOverShellCode);
                       UndoWP();
               }
        }
        break;
        }
        return TRUE;
}
// 获取操作系统版本
ULONG GetWindowsVersion()
{
        RTL_OSVERSIONINFOW lpVersionInformation = { sizeof(RTL_OSVERSIONINFOW) };
        if (NT_SUCCESS(RtlGetVersion(&lpVersionInformation)))
        {
               ULONG dwMajorVersion = lpVersionInformation.dwMajorVersion;
               ULONG dwMinorVersion = lpVersionInformation.dwMinorVersion;
               if (dwMajorVersion == 5 && dwMinorVersion == 1)
               {
                       return WINXP;
               }
               else if (dwMajorVersion == 6 && dwMinorVersion == 1)
               {
                       return WIN7;
               }
               else if (dwMajorVersion == 6 && dwMinorVersion == 2)
               {
                       return WIN8;
               }
               else if (dwMajorVersion == 10 && dwMinorVersion == 0)
               {
                       return WIN10;
               }
        }
        return 0;
}
// 获取操作系统分页模式
ULONG GetWindowsPageMode()
{
        ULONG PageMode = 0x1; // 默认为 2-9-9-12 分页
        __asm
        {
               _emit 0x0F;     // mov  eax, cr4;
               _emit 0x20;
               _emit 0xE0;
               test eax, 0x20; // 判断为什么分页模式
               jnz  End;
               // 为 10-10-12 分页
               mov  dword ptr[PageMode], 0x0;
        End:
               ;
        }
        return PageMode;
}
// 生成跳转的ShellCode
//++++++++++++++++++++++++++++
// ShellCode: 存储代码的容器
// TargetAddr: 要写入 ShellCode 的地址
// JmpAddr: 跳转的目标地址
//++++++++++++++++++++++++++++
// 返回 ShellCode 实际长度
ULONG CreateJmpShellCode(CHAR * ShellCode, ULONG TargetAddr, ULONG JmpAddr)
{
        ULONG CodeLen = 0;
        ULONG ShellCodeLen = 0;
        if (GetWindowsVersion() == WINXP)
        {
               // 如果为 XP
               CHAR GetJmpCode[0x5] = { 0xE9, 0x0, 0x0, 0x0, 0x0 };
               *(PVOID *)(&GetJmpCode[1]) = (PVOID)(JmpAddr - (TargetAddr + 5));
               CodeLen = sizeof(GetJmpCode);
               RtlMoveMemory(ShellCode, GetJmpCode, 0x5);
        }
        return CodeLen;
}
// 快速交换内存
ULONG DataLow = 0x0, DataHigh = 0x0;
VOID _declspec(naked) _fastcall FastSwapMemory(ULONG * TargetAddr, ULONG * SoulAddr)
{
        __asm
        {
               pushad;
               pushfd;
               
               mov esi, ecx; // 保存原来地址
               mov edi, edx;
               mov edx, 0x0;
               mov eax, 0x0;
               // 读取ShellCode
               lock CMPXCHG8B qword ptr[edi];
               mov DataLow, eax;
               mov DataHigh, edx;
               // 读取目标内存
               lock CMPXCHG8B qword ptr[esi];
               mov ebx, dword ptr[DataLow];
               mov ecx, dword ptr[DataHigh];
               // HOOK目标内存
               lock CMPXCHG8B qword ptr[esi];
 
               popfd;
               popad;
               retn;
        }
}
// 修复代码
VOID _declspec(naked) _fastcall ReStore(ULONG * TargetAddr)
{
        //__asm
        //{
        //      lock XCHG EAX, dword ptr[DataLow];
        //      lock XCHG EDX, dword ptr[DataHigh];
        //      lock XCHG EAX, dword ptr[ecx];
        //      lock XCHG EDX, dword ptr[ecx + 0x4];
        //      retn;
        //}
}
KIRQL irQl;
// 修改Cr0寄存器, 去除写保护(内存保护机制)
KIRQL RemovWP()
{
        DbgPrint("RemovWP\n");
        // (PASSIVE_LEVEL)提升 IRQL 等级为DISPATCH_LEVEL,并返回旧的 IRQL
        // 需要一个高的IRQL才能修改
        irQl = KeRaiseIrqlToDpcLevel();
        ULONG_PTR cr0 = __readcr0(); // 内联函数:读取Cr0寄存器的值, 相当于: mov eax,    cr0;
        // 将第16位(WP位)清0,消除写保护
        cr0 &= ~0x10000; // ~ 按位取反
        _disable(); // 清除中断标记, 相当于 cli 指令,修改 IF标志位
        __writecr0(cr0); // 将cr0变量数据重新写入Cr0寄存器中,相当于: mov cr0, eax
        DbgPrint("退出RemovWP\n");
        return irQl;
}
// 复原Cr0寄存器
KIRQL UndoWP()
{
        DbgPrint("UndoWP\n");
        ULONG_PTR cr0 = __readcr0();
        cr0 |= 0x10000; // WP复原为1
        _disable(); // 清除中断标记, 相当于 cli 指令,清空 IF标志位
        __writecr0(cr0); // 将cr0变量数据重新写入Cr0寄存器中,相当于: mov cr0, eax
        // 恢复IRQL等级
        KeLowerIrql(irQl);
        DbgPrint("退出UndoWP\n");
        return irQl;
}
// 遍历内存搜索指定函数
/*
opCodeArray: 特征码数组
IsStable: TRUE(映射搜索), FALSE(直接当前内核搜索)
*/
ULONG TraitCodeByErgodicMemory(ULONG * opCodeArray, BOOLEAN IsStable)
{
        // 准备一份特征码
        ULONG OpCode1 = opCodeArray[0];
        ULONG OpCode2 = opCodeArray[1];
        ULONG OpCode3 = opCodeArray[2];
        ULONG OpCode4 = opCodeArray[3];
        // 映射内核模块到进程中
        HANDLE hFile = NULL;
        HANDLE hSection = NULL;
        PVOID DllBase = NULL;
        SIZE_T viewSize = 0;
        if (IsStable)
        {
               // 特征码搜索映射文件
               UNICODE_STRING ModuleName =  RTL_CONSTANT_STRING(L"\\??\\C:\\WINDOWS\\system32\\ntoskrnl.exe");
               NTSTATUS status = DllFileMap(&ModuleName, &hFile, &hSection, &DllBase,  &viewSize);
               if (!NT_SUCCESS(status) || (DllBase == NULL))
                       return 0x0;
        }
        else
        {
               // 特征码搜索当前内核
               KeSetSystemAffinityThread(1);
               __asm {
                       push eax;
                       push ebx;
                       mov eax, fs:[0x34]; // 得到KdVersionBlock的地址
                       add eax, 18h;       // 得到指向PsLoadedModuleList的地址
                       mov eax, [eax];     // 得到PsLodedModuleList的地址
                       mov eax, [eax];     // 取出PsLoadedModuleList里面的内容, 即KLDR_DATA_TABLE_ENTRY结构
                       mov ebx, [eax + 18h]; // 取出DllBase, 即ntoskrnl.exe的基地址
                       mov DllBase, ebx;
                       mov ebx, [eax + 20h]; // 取出模块的大小
                       mov viewSize, ebx;
                       pop ebx;
                       pop eax;
               }
               KeRevertToUserAffinityThread();//恢复线程运行的处理器
        }
        ULONG endDllAddr = (ULONG)DllBase + viewSize;
        for (ULONG i = (ULONG)DllBase; i < endDllAddr; i++)
        {
               try
               {
                       if (*(PULONG)i == OpCode1 && *(PULONG)(i + 4) == OpCode2
                              && *(PULONG)(i + 8) == OpCode3 && *(PULONG)(i + 0xC) ==  OpCode4)
                       {
                              // 返回函数地址偏移
                              return i;
                       }
               }
               except(1)
               {
                       continue;
               }
        }
        return 0x0;
}
// 将指定模块映射到内存中
// DllFileName: 要映射的DLL文件路径名称
// phFile: 返回DLL文件的句柄
// phSection: 返回节的句柄
// pBaseAddress: DLL映射在此进程中的起始地址
// pviewSize: 映射在内存中的大小
NTSTATUS DllFileMap(UNICODE_STRING * pDllFileName, HANDLE * phFile,
        HANDLE * phSection, PVOID * pBaseAddress, SIZE_T * pviewSize)
{
        NTSTATUS status = STATUS_SUCCESS;
        OBJECT_ATTRIBUTES objectAttributes = {sizeof(OBJECT_ATTRIBUTES)};
        IO_STATUS_BLOCK iosb;
        // 打开DLL文件,并获取文件句柄
        InitializeObjectAttributes(&objectAttributes, pDllFileName,
               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
        status = ZwOpenFile(phFile, GENERIC_READ, &objectAttributes, &iosb,
               FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT);
        if (!NT_SUCCESS(status))
        {
               KdPrint(("ZwOpenFile: %X\n", status)); return status;
        }
        // 创建一个节对象,按照 PE 结构中的 SectionAlignment 大小对齐映射文件
        status = ZwCreateSection(phSection, SECTION_MAP_READ | SECTION_MAP_WRITE,
               NULL, 0, PAGE_READWRITE, 0x1000000, *phFile);
        if (!NT_SUCCESS(status))
        {
               KdPrint(("ZwCreateSection: %X\n", status)); return status;
        }
        // 映射到当前R0进程内存中
        status = ZwMapViewOfSection(*phSection, NtCurrentProcess(), pBaseAddress,
               0, 1024, 0, pviewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
        if (!NT_SUCCESS(status))
        {
               KdPrint(("ZwMapViewOfSection: %X\n", status)); return status;
        }
        return status;
}

代码运行结果如下(成功监控所有的线程切换)

Hook 高并发的函数一定要注意多个线程执行而Hook代码需要时间替换目标代码,这段时间里会不会有其他线程来执行这段还没被替换完的代码


原创文章,转载请注明: 转载自Windows内核安全驱动编程

本文链接地址: Hook SwapContext函数

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注