win10 x64中inlineHook SSDT里面的函数
程序员文章站
2022-05-28 11:08:03
...
inlineHook的原理:
为了方便好理解,一些变量名和函数名在这里使用中文命名,有些编译器不支持中文命名,在这里要注意(我的是VS2019)
hook.h:
#pragma once
#include<ntifs.h>
#include<ntddk.h>
#pragma intrinsic(__readmsr)
//SSDT表
typedef struct _SYSTEM_SERVICE_TABLE {
PLONG ServiceTableBase;
PVOID ServiceCounterTableBase;
ULONGLONG NumberOfServices;
PVOID ParamTableBase;
} SYSTEM_SERVICE_TABLE, * PSYSTEM_SERVICE_TABLE;
/*全局变量*/
extern PSYSTEM_SERVICE_TABLE SSDT地址;
//函数声明
UINT64 获取SSDT地址();
UINT64 获取函数地址(ULONG dwIndex);
KIRQL 关闭写保护();
VOID 开启写保护(KIRQL irql);
VOID 开始HOOK(UINT64 HOOK函数地址,UINT64 代理函数地址,USHORT 改写的长度, PVOID* 原函数);
VOID 恢复HOOK(UINT64 HOOK函数地址, USHORT 改写的长度, PVOID 原函数);
hook.c
#include"hook.h"
PSYSTEM_SERVICE_TABLE SSDT地址 = 0;
UINT64 获取SSDT地址()
{
PUCHAR msr = (PUCHAR)__readmsr(0xC0000082);
PUCHAR startaddr = 0, Endaddr = 0;
PUCHAR i = NULL;
UCHAR b1, b2, b3;
ULONG temp = 0;
ULONGLONG addr = 0;
KdPrint(("msr c0000082的值:%x\n", msr));
if (*(msr + 0x9) == 0x00)
{
startaddr = msr;
Endaddr = startaddr + 0x500;
}
else if (*(msr + 0x9) == 0x70)
{
PUCHAR pKiSystemCall64Shadow = msr;
PUCHAR EndSearchAddress = pKiSystemCall64Shadow + 0x500;
PUCHAR i = NULL;
INT Temp = 0;
for (i = pKiSystemCall64Shadow; i < EndSearchAddress; i++)
{
if (MmIsAddressValid(i) && MmIsAddressValid(i + 5))
{
if (*i == 0xe9 && *(i + 5) == 0xc3)
{
memcpy(&Temp, i + 1, 4);
startaddr = Temp + (i + 5);
KdPrint(("KiSystemServiceUser的地址:%x\n", startaddr));
Endaddr = startaddr + 0x500;
}
}
}
}
for (i = startaddr; i < Endaddr; i++)
{
b1 = *i;
b2 = *(i + 1);
b3 = *(i + 2);
if (b1 == 0x4c && b2 == 0x8d && b3 == 0x15)
{
memcpy(&temp, i + 3, 4);
addr = (ULONGLONG)temp + (ULONGLONG)i + 7;
KdPrint(("SSDT地址:%x\n", addr));
return addr;
}
}
return 0;
}
UINT64 获取函数地址(ULONG dwIndex)
{
PULONG lpBase = SSDT地址->ServiceTableBase;
ULONG dwCount = (ULONG)SSDT地址->NumberOfServices;
UINT64 lpAddr = 0;
ULONG dwOffset = lpBase[dwIndex];
if (dwOffset & 0x80000000)
dwOffset = (dwOffset >> 4) | 0xF0000000;
else
dwOffset >>= 4;
lpAddr = (UINT64)((PUCHAR)lpBase + (LONG)dwOffset);
return lpAddr;
}
KIRQL 关闭写保护()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
ULONG_PTR cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
_disable();
__writecr0(cr0);
return irql;
}
VOID 开启写保护(KIRQL irql)
{
ULONG_PTR cr0 = __readcr0();
cr0 |= 0x10000;
__writecr0(cr0);
_enable();
KeLowerIrql(irql);
}
/*
要改写的指令长度网上一些方法是用LDE这个反编译引擎计算,但有时候这并不靠谱,比如我在HOOK NtOpenProcess的时候
发现需要改写的字节数为20,但LDE只算出了16,导致跳回原函数的地址与实际我们需要的地址差4个字节
这里的 原函数 是用来储存原API函数被修改掉的函数的指令及,后面需要调用它跳回原API函数
*/
VOID 开始HOOK(UINT64 HOOK函数地址, UINT64 代理函数地址,USHORT 改写的长度,PVOID *原函数)
{
UCHAR 跳到代理函数[] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
UCHAR 跳回原函数[]= "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
memcpy(跳到代理函数 + 6, &代理函数地址, 8);
/*
下面的数值14是跳转指令的总长度 假设该指令地址为0x410000
0x410000 jmp qword ptr [0x410006]
0x410006 xxxxxxxx
其中0x410006中储存代理函数的地址
*/
UINT64 跳回原函数的地址 = HOOK函数地址 + 改写的长度;
memcpy(跳回原函数 + 6, &跳回原函数的地址, 8);
*原函数 = ExAllocatePool(NonPagedPool, 改写的长度+ 14);
RtlFillMemory(*原函数, 改写的长度 + 14, 0x90);
KIRQL irql = 关闭写保护();
memcpy(*原函数, (PVOID)HOOK函数地址, 改写的长度);
memcpy((PCHAR)(*原函数)+ 改写的长度, 跳回原函数, 14);
RtlFillMemory((void*)HOOK函数地址, 改写的长度, 0x90);
memcpy((PVOID)HOOK函数地址, &跳到代理函数, 14);
开启写保护(irql);
}
VOID 恢复HOOK(UINT64 HOOK函数地址,USHORT 改写的长度,PVOID 原函数)
{
KIRQL irql = 关闭写保护();
memcpy((PVOID)HOOK函数地址, 原函数, 改写的长度);
开启写保护(irql);
ExFreePool(原函数);
}
调用例子:inlinehook NtOpenProcess实现防止“NewTzmTool.exe”的进程被OpenProcess
inlinehook NtReadVirtualMemory,有函数调用ReadProcessMemory时给出提示
main.c
#include"hook.h"
PVOID S_OpenProcess;
PVOID S_ReadVritualMemory;
//要HOOK的函数原型声明,win x64系统的API函数调用约定为__fastcall
typedef NTSTATUS(__fastcall* pMyOpenProcess)(OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL);
typedef NTSTATUS(__fastcall* pMyReadVirtualMemory)(IN HANDLE ProcessHandle, IN PVOID BaseAddress, OUT PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength OPTIONAL);
//用到的内核函数,该函数使用前需要声明
PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTSTATUS MyOpenProcess(OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId OPTIONAL)
{
PEPROCESS process = 0;
if (STATUS_SUCCESS == PsLookupProcessByProcessId(ClientId->UniqueProcess, &process))
{
if (strcmp(PsGetProcessImageFileName(process), "NewTzmTool.exe") == 0)
{
KdPrint(("受保护进程:%s", PsGetProcessImageFileName(process)));
return STATUS_PNP_INVALID_ID;
}
}
return ((pMyOpenProcess)S_OpenProcess)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);;
}
NTSTATUS MyReadVirtualMemory(IN HANDLE ProcessHandle, IN PVOID BaseAddress, OUT PVOID Buffer, IN ULONG BufferLength, OUT PULONG ReturnLength OPTIONAL)
{
KdPrint(("读内存\n"));
return ((pMyReadVirtualMemory)S_ReadVritualMemory)(ProcessHandle,BaseAddress,Buffer,BufferLength,ReturnLength);
}
void DriverUnLoad(PDRIVER_OBJECT driver)
{
KdPrint(("驱动卸载\n"));
恢复HOOK(获取函数地址(63),17, S_ReadVritualMemory);
恢复HOOK(获取函数地址(38),20, S_OpenProcess);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING path)
{
KdPrint(("驱动加载\n"));
driver->DriverUnload = DriverUnLoad;
SSDT地址 = (PSYSTEM_SERVICE_TABLE)获取SSDT地址();
if (SSDT地址 == NULL)
{
KdPrint(("SSDT地址获取失败!\n"));
return STATUS_SUCCESS;
}
开始HOOK(获取函数地址(63), (UINT64)&MyReadVirtualMemory, 17,&S_ReadVritualMemory);
开始HOOK(获取函数地址(38), (UINT64)&MyOpenProcess,20,&S_OpenProcess);
return STATUS_SUCCESS;
}
效果
下一篇: 爬取有道翻译自制小软件