-
-
[分享]X86的SSDTHook
-
2020-6-3 21:50
5383
-
思路: 自己再创建一份 SSDT表, 并且将线程中的服务表地址指向我们的SSDT表
实现如下:
SSDT.h 头文件代码
#pragma once
#ifndef SSDT
#define SSDT
#include <Ntifs.h>
#include <windef.h>
// 系统服务表结构体
typedef struct _SystemServiceTable
{
PULONG FunctionsAddrTable;
ULONG CallCount;
ULONG FunctionsLimit;
PUCHAR FunctionsArgsAddrTable;
}SystemServiceTable, *PSystemServiceTable;
// SSDT表结构体
typedef struct _SSDT_TABLE
{
SystemServiceTable serviceTableNt;
SystemServiceTable serviceTableWin32k;
SystemServiceTable Null3;
SystemServiceTable Null4;
}SSDT_TABLE, *PSSDT_TABLE;
extern PSSDT_TABLE KeServiceDescriptorTable; // 获取 XP导出的内核服务表
PSSDT_TABLE CopyServiceDescriptorTable; // 我们额外拷贝的一份SSDT表
// hook线程句柄
HANDLE hookThread;
// 用于控制 Hook 线程回调
BOOLEAN IsFlags;
#endif
0环驱动代码
#include "SSDT.h"
PCHAR PsGetProcessImageFileName(PEPROCESS Process);
// 获取影子表地址
ULONG GetWin32KTable()
{
ULONG KeServiceDescriptorTableShadow = *(PULONG)&KeServiceDescriptorTable - 0x40;
return KeServiceDescriptorTableShadow;
}
// 1. 获取指定进程 EPROCESS
// 2. 获取指定进程的线程链表
// 3. 获取线程SSDT地址
// 4. Copy SSDT表
// 5. 将进程下的所有线程指向我们 SSDT
// 6. 判断是否为 UI 线程,因为 UI 线程切换 PsConvertToGuiThread 函数内部 0x3A处 会做一个比较
// 判断当前线程的 ServiceTable 是否等于 KeServiceDescriptorTable , 所以我们需要修改
// 遍历返回目标进程的 EPROCESS
ULONG EnumGetTargetProcess(PCHAR ProcessName)
{
// KPCR.PrcbData.CurrentThread.ApcState.Process 存储的是当前进程的 EPROCESS 地址
ULONG CurrentEprocess = 0;
__asm
{
mov eax, dword ptr fs:[0x124]; // 获取 KPCR.PrcbData.CurrentThread
mov ecx, dword ptr[eax + 0x44]; // 获取 CurrentThread.ApcState.Proces
mov CurrentEprocess, ecx;
}
// _EPROCESS.ActiveProcessLinks 存储的是当前系统活动进程的链表
PLIST_ENTRY ProcessEntry = (PLIST_ENTRY)(CurrentEprocess + 0x88); // ActiveProcessLinks
PLIST_ENTRY CurrentProcessEntry = ProcessEntry;
while (CurrentProcessEntry->Flink != ProcessEntry)
{
// 循环进程链表获取目标进程
// 获取当前进程 EPROCESS 的首地址
ULONG NextEprocess = (ULONG)CurrentProcessEntry - 0x88;
// 获取当前进程的名称 _EPROCESS.ImageFileName
PCHAR NextProcessName = (PCHAR)(NextEprocess + 0x174);
if (_strnicmp(NextProcessName, ProcessName, strlen(ProcessName)) == 0)
{
// 如果为指定进程,返回 EPROCESS 的首地址
return NextEprocess;
}
// 否则继续循环
CurrentProcessEntry = CurrentProcessEntry->Flink;
}
// 到达这里表示没有找到,指定进程
return 0;
}
// 获取指定进程的活动线程链表
PLIST_ENTRY GetThreadEntry(ULONG Eprocess)
{
// _EPROCESS.ThreadListHead 是当前进程的活动线程链表
return (PLIST_ENTRY)(*(PULONG)(Eprocess + 0x190));
}
// 修改 PsConvertToGuiThread
VOID ModifyPsConvertToGuiThread()
{
// PsConvertToGuiThread 为未导出函数
ULONG PsConvertToGuiThread = (ULONG)0x805c3720;
// 805c3764 740a je nt!PsConvertToGuiThread + 0x50 (805c3770)
// 变为 805c3764 EB0a jmp nt!PsConvertToGuiThread + 0x50 (805c3770)
*(PBYTE)(PsConvertToGuiThread + 0x44) = (BYTE)0xEB;
}
// 将其进程的线程指向我们的 SSDT
NTSTATUS HookALLThreadServiceTable(PLIST_ENTRY ThreadEntry)
{
//KdBreakPoint();
NTSTATUS status = STATUS_SUCCESS;
DbgPrint("HookALLThreadServiceTable进入\n");
// 获取进程的线程链表
PLIST_ENTRY CurrentProcessThreadEntry = ThreadEntry;
// 遍历线程链表
PLIST_ENTRY NextProcessThreadEntry = CurrentProcessThreadEntry;
while (NextProcessThreadEntry->Flink != CurrentProcessThreadEntry)
{
// Hook 当前进程中的所有线程
// 获取当前 ETHREAD 首地址
ULONG NextEThread = (ULONG)NextProcessThreadEntry - 0x22c;
if (NextEThread < 0x80000000) goto NextListEntry;
if (*(PULONG)(NextEThread + 0xE0) == (ULONG)CopyServiceDescriptorTable) goto NextListEntry;
// 修改 _KTHREAD.ServiceTable 为我们的 SSDT
*(PULONG)(NextEThread + 0xE0) = (ULONG)CopyServiceDescriptorTable;
NextListEntry:
NextProcessThreadEntry = NextProcessThreadEntry->Flink;
}
DbgPrint("HookALLThreadServiceTable退出\n");
return status;
}
// Copy 一份 SSDT 表
NTSTATUS InitCopySSDT()
{
NTSTATUS status = STATUS_SUCCESS;
// 开辟 SSDT 表空间
CopyServiceDescriptorTable = ExAllocatePool(NonPagedPool, sizeof(SSDT_TABLE));
if (CopyServiceDescriptorTable == NULL)
{
DbgPrint("ExAllocatePoolOne Error\n");
return STATUS_UNSUCCESSFUL;
}
memset(CopyServiceDescriptorTable, 0, sizeof(SSDT_TABLE));
// 开辟一段空间用来存储 函数地址表
CopyServiceDescriptorTable->serviceTableNt.FunctionsAddrTable =
ExAllocatePool(NonPagedPool, KeServiceDescriptorTable->serviceTableNt.FunctionsLimit * 4);
if (CopyServiceDescriptorTable->serviceTableNt.FunctionsAddrTable == NULL)
{
ExFreePool(CopyServiceDescriptorTable);
DbgPrint("ExAllocatePoolTwo Error\n");
return STATUS_UNSUCCESSFUL;
}
memset(CopyServiceDescriptorTable->serviceTableNt.FunctionsAddrTable, 0, KeServiceDescriptorTable->serviceTableNt.FunctionsLimit * 4);
// 复制函数地址表内容
RtlMoveMemory(CopyServiceDescriptorTable->serviceTableNt.FunctionsAddrTable, KeServiceDescriptorTable->serviceTableNt.FunctionsAddrTable,
KeServiceDescriptorTable->serviceTableNt.FunctionsLimit * 4);
// 复制函数个数、函数参数表地址
CopyServiceDescriptorTable->serviceTableNt.FunctionsLimit =
KeServiceDescriptorTable->serviceTableNt.FunctionsLimit;
CopyServiceDescriptorTable->serviceTableNt.FunctionsArgsAddrTable =
KeServiceDescriptorTable->serviceTableNt.FunctionsArgsAddrTable;
// 复制影子表
// 开辟一段空间用来存储 函数地址表
PSSDT_TABLE KeServiceDescriptorTableShadow = (PSSDT_TABLE)(GetWin32KTable()); // 获取Win32K服务表地址
// 复制函数地址表内容
CopyServiceDescriptorTable->serviceTableWin32k.FunctionsAddrTable =
KeServiceDescriptorTableShadow->serviceTableWin32k.FunctionsAddrTable;
// 复制函数个数、函数参数表地址
CopyServiceDescriptorTable->serviceTableWin32k.FunctionsLimit =
KeServiceDescriptorTableShadow->serviceTableWin32k.FunctionsLimit;
CopyServiceDescriptorTable->serviceTableWin32k.FunctionsArgsAddrTable =
KeServiceDescriptorTableShadow->serviceTableWin32k.FunctionsArgsAddrTable;
return status;
}
//卸载驱动的时候,也修复 HOOK 的线程
NTSTATUS FixALLThreadServiceTable(PLIST_ENTRY ThreadEntry)
{
NTSTATUS status = STATUS_SUCCESS;
DbgPrint("FixALLThreadServiceTable进入\n");
// 获取进程的线程链表
PLIST_ENTRY CurrentProcessThreadEntry = ThreadEntry;
// 遍历线程链表
PLIST_ENTRY NextProcessThreadEntry = CurrentProcessThreadEntry;
PSSDT_TABLE KeServiceDescriptorTableShadow = (PSSDT_TABLE)(GetWin32KTable());
while (NextProcessThreadEntry->Flink != CurrentProcessThreadEntry)
{
// Hook 当前进程中的所有线程
// 获取当前 ETHREAD 首地址
ULONG NextEThread = (ULONG)NextProcessThreadEntry - 0x22c;
// 修改 _KTHREAD.ServiceTable 一律替换为 SSSDT
*(PULONG)(NextEThread + 0xE0) = (ULONG)KeServiceDescriptorTableShadow;
NextProcessThreadEntry = NextProcessThreadEntry->Flink;
}
DbgPrint("FixALLThreadServiceTable退出\n");
return status;
}
//后续工作, 考虑到线程会有创建和销毁,我们需要不停的修改线程 ServiceTable
VOID ThreadHookRoutine(_In_ PVOID StartContext)
{
IsFlags = TRUE; //激活标志
LARGE_INTEGER timeOut = RtlConvertLongToLargeInteger(-30 * 1000 * 1000);
while (IsFlags)
{
DbgPrint("循环Hook中\n");
KeDelayExecutionThread(KernelMode, FALSE, &timeOut);
HookALLThreadServiceTable((PLIST_ENTRY)StartContext);
}
DbgPrint("循环Hook结束!!!\n");
// 一旦停止Hook,标志开始复原系统环境
// 复原指定进程的线程
FixALLThreadServiceTable((PLIST_ENTRY)StartContext);
ZwClose(hookThread);
}
VOID Unload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("卸载驱动\n");
//IsFlags = FALSE; // 禁用标志, 开始复原
//// 等待一段时间
//LARGE_INTEGER timeOut = RtlConvertLongToLargeInteger(-30 * 1000 * 1000);
//KeDelayExecutionThread(KernelMode, FALSE, &timeOut);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Regedit)
{
DbgPrint("加载驱动\n");
DriverObject->DriverUnload = Unload;
NTSTATUS status = STATUS_SUCCESS;
//KdBreakPoint();
// 寻找目标进程 EPROCESS
ULONG Process = EnumGetTargetProcess("Dbgview.exe");
if (!Process)
{
return STATUS_UNSUCCESSFUL;
}
// 拷贝一份 SSDT 表
InitCopySSDT();
// 获取进程线程活动链表
PLIST_ENTRY ThreadEntry = GetThreadEntry(Process);
ModifyPsConvertToGuiThread();
// Hook 进程的线程
HookALLThreadServiceTable(ThreadEntry);
// 启动一个线程循环HOOK _KTHREAD.ServiceTable
PsCreateSystemThread(&hookThread, THREAD_ALL_ACCESS, NULL, NULL, NULL, ThreadHookRoutine, (PVOID)ThreadEntry);
DbgPrint("%p\n", CopyServiceDescriptorTable);
return status;
}
结果如下,等待了10分钟没有报错,以及HOOK 内核函数并不会走原来SSDT,判断结果成功
[培训]《安卓高级研修班(网课)》月薪三万计划,掌
握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法