首页
社区
课程
招聘
[分享]X86的SSDTHook
2020-6-3 21:50 5383

[分享]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虚拟机自动化脱壳的方法

收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 5
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_qimwvxnv 2020-7-11 13:41
2
0
没有x64的 ssdt hook吗
游客
登录 | 注册 方可回帖
返回