x64下读取SSDT表,并且获取SSDT表函数.
64位下读取SSDT表并且获取SSDT函数
一丶读取SSDT表 (KeServiceDescriptorTable)
1.1 原理
在64位系统下我们可以通过读取msr 寄存器来获取内核函数入口.
msr在开启内核隔离模式下获取的是 KiSystemCall64Shadow
而在未开启内核模式下则是获取的 KiSystemCall64
1.2 手动获取SSDT表
windbg链接双机调试. 输入命令 rdmsr 0xC0000082 即可看到内核函数入口.
反汇编此函数的地址往下找即可看到获取SSDT表位置代码.
这里我以IDA举例子
如果你能反汇编内核文件,并且为其下载好符号.则在函数列表中直接搜索 KiSystemCall64 即可.
如下:
观看其位置反汇编代码:
.text:00000001401D2980 KiSystemServiceStart: ; DATA XREF: KiServiceInternal+5A↑o 重要点1 .text:00000001401D2980 .text:00000001401D2980 48 89 A3 90 00 00 00 mov [rbx+90h], rsp .text:00000001401D2987 8B F8 mov edi, eax .text:00000001401D2989 C1 EF 07 shr edi, 7 .text:00000001401D298C 83 E7 20 and edi, 20h .text:00000001401D298F 25 FF 0F 00 00 and eax, 0FFFh .text:00000001401D2994 .text:00000001401D2994 KiSystemServiceRepeat: .text:00000001401D2994 4C 8D 15 E5 9E 3B 00 lea r10, KeServiceDescriptorTable_0 重要点2 .text:00000001401D299B 4C 8D 1D DE 20 3A 00 lea r11, KeServiceDescriptorTableShadow .text:00000001401D29A2 F7 43 78 80 00 00 00 test dword ptr [rbx+78h], 80h .text:00000001401D29A9 74 13 jz short loc_1401D29BE .text:00000001401D29AB F7 43 78 00 00 20 00 test dword ptr [rbx+78h], 200000h .text:00000001401D29B2 74 07 jz short loc_1401D29BB .text:00000001401D29B4 4C 8D 1D 05 21 3A 00 lea r11, KeServiceDescriptorTableFilter .text:00000001401D29BB .text:00000001401D29BB loc_1401D29BB: .text:00000001401D29BB 4D 8B D3 mov r10, r11 .text:00000001401D29BE .text:00000001401D29BE loc_1401D29BE: .text:00000001401D29BE 41 3B 44 3A 10 cmp eax, [r10+rdi+10h] .text:00000001401D29C3 0F 83 2C 05 00 00 jnb loc_1401D2EF5 .text:00000001401D29C9 4D 8B 14 3A mov r10, [r10+rdi] .text:00000001401D29CD 4D 63 1C 82 movsxd r11, dword ptr [r10+rax*4] .text:00000001401D29D1 49 8B C3 mov rax, r11 .text:00000001401D29D4 49 C1 FB 04 sar r11, 4 重要点3 .text:00000001401D29D8 4D 03 D3 add r10, r11 .text:00000001401D29DB 83 FF 20 cmp edi, 20h ; ' ' .text:00000001401D29DE 75 50 jnz short loc_1401D2A30 .text:00000001401D29E0 4C 8B 9B F0 00 00 00 mov r11, [rbx+0F0h]
上述汇编描述了三个重要点
1.2.1 重点1 了解引用流程以及其它方式寻找SSDT表的方式
KiServiceInternal 与 KiSystemServiceStart
这里要了解下SSDT表起始获取是 KiSystemServiceStart 而 KiServiceInternal 则会引用 KiSystemServiceStart
那么为什么讲一下这里. 因为在内核中我们可以通过任意一个内核函数来找到 KiServiceInternal 然后通过 KiServiceInternal 来找到 KiSystemServiceStart 然后通过 KiSystemServiceStart 来定位SSDT表或者SSDTShadow表
例子:
1.2.2 重要点2 获取SSDT表以及Shadow表位置
重要点2位置的两行代码则是获取SSDT表与Shadow表. 表示为如下:
.text:00000001401D2994 KiSystemServiceRepeat: .text:00000001401D2994 4C 8D 15 E5 9E 3B 00 lea r10, KeServiceDescriptorTable_0 .text:00000001401D299B 4C 8D 1D DE 20 3A 00 lea r11, KeServiceDescriptorTableShadow
特征码分别为
0x4c 0x8d 0x15 ---> Get SSDT
0x4c 0x8d 0x1d ---> Get SSDTShadow
1.2.3 重要点3 SSDT表的加密获取以及使用
这里是重点在32位下的SSDT表你可以任意HOOK 而到了64位下你则不能 "HOOK" 了
因为你的函数定义不在同一个4GB空间中.所以不能直接跳转使用.而为什么这样.
就是重要点三所在的汇编所体现的.
.text:00000001401D29BE 41 3B 44 3A 10 cmp eax, [r10+rdi+10h] .text:00000001401D29C3 0F 83 2C 05 00 00 jnb loc_1401D2EF5 .text:00000001401D29C9 4D 8B 14 3A mov r10, [r10+rdi] .text:00000001401D29CD 4D 63 1C 82 movsxd r11, dword ptr [r10+rax*4] offset = SSDT[sizeof(int) * index] .text:00000001401D29D1 49 8B C3 mov rax, r11 .text:00000001401D29D4 49 C1 FB 04 sar r11, 4 offset = offset >> 4 .text:00000001401D29D8 4D 03 D3 add r10, r11 pfn = ssdt.base + offset = 实际的函数地址 .text:00000001401D29DB 83 FF 20 cmp edi, 20h ; ' ' .text:00000001401D29DE 75 50 jnz short loc_1401D2A30 .text:00000001401D29E0 4C 8B 9B F0 00 00 00 mov r11, [rbx+0F0h]
这里有一个右移的操作.观看汇编反汇编为高级代码则如下:
offset = SSDT[index * 4] ; offset = offset >> 4 ; 亦或者等价于 offset = offset / 16 pfnAddr = ssdt.base + offset;
在内存中如下:
1: kd> dq 0xfffff8067758c880 查看SSDT表 fffff806`7758c880 fffff806`77424cc0 00000000`00000000 fffff806`7758c890 00000000`000001d0 fffff806`77425404 fffff806`7758c8a0 00000000`00000000 00000000`00000000 fffff806`7758c8b0 00000000`00000000 00000000`00000000 fffff806`7758c8c0 fffff806`771cc8c0 fffff806`771ccc00 fffff806`7758c8d0 fffff806`771d1580 fffff806`771d18c0 fffff806`7758c8e0 fffff806`771d1c00 fffff806`771d2640 fffff806`7758c8f0 fffff806`771d2180 00000000`00000000 1: kd> dd fffff806`77424cc0 查看SSDT表数组中的内容 fffff806`77424cc0 fced6104 fcf76a00 02b81c02 04749800 fffff806`77424cd0 01ce2700 fd9fe900 01c03705 01c38c06 fffff806`77424ce0 0220d205 0288b301 028aaa00 01a96400 fffff806`77424cf0 01e26500 01c27900 028a4600 01cc7c00 fffff806`77424d00 0221e201 01bf7001 0295a500 01fde702 fffff806`77424d10 01a86600 01e0a200 01d09201 01ce8102 fffff806`77424d20 022b9002 01f4a401 01fbc601 02871e05 fffff806`77424d30 0228ee00 028bcf03 02362000 0461a300
我们可以手动计算出地址.
根据上面反汇编的代码得出
offset = *(PLONG)SSDT + uid * 4 = fced6104 注意offset不是ULONG类型. 而是LONG类型. 偏移记录的是整数 而不是无符号整数.否则你计算出的基地址就会加10000000的数据.导致计算出错
offset = offset >> 4;
pfnAddr = (PULONGLONG)(offset + ssdt.base) ==> offset + 0xfffff80677424cc0 最终计算出的地址为: pfnAddr = 0xfffff806
771122d0
查看pcHunter
核心代码:
提供了两种方式.指针或者数组寻址 都是可以可以的.数组那块我是转换为了ULONG来操作的
因为: char * ary; offset = ary + sizeof(type) * index 就是数组寻址.
可以优化为: PULONG ssdt; offset = ssdt[index];
PVOID Cssdt::GetProcById(ULONG uId) { PKSERVICE_TABLE_DESCRIPTOR pSsdt = NULL; PULONGLONG pFunctionAddr = NULL; PUCHAR ssdtbase = NULL; LONG offset = 0; pSsdt = this->GetSsdtBaseByKernelFunction((PVOID)ZwCreateFile, 0x500); if (pSsdt == NULL) { return NULL; } //calc function //pointer get addr // ssdtbase = (PUCHAR)pSsdt->Base; // offset = (LONG) * (PULONG)(ssdtbase + uId * 4); // offset = (LONG)offset >> 4; // pFunctionAddr = (PULONGLONG)(offset + ssdtbase); //array get addr ssdtbase = (PUCHAR)pSsdt->Base; offset = (LONG)((PULONG)ssdtbase)[uId]; offset = (LONG)offset >> 4; pFunctionAddr = (PULONGLONG)(offset + ssdtbase); return pFunctionAddr; }
二丶两种方式实现获取SSDT表
2.1 常规方式获取SSDT表.
暂时待写
2.2 通过API寻找方式来找寻SSDT
.h
#pragma once #ifdef __cplusplus extern "C" { #endif #include <ntifs.h> #include <ntddk.h> #include <Ntstrsafe.h> #include "ntimage.h" #ifdef __cplusplus } #endif #ifdef _AMD64_ typedef struct _KSERVICE_TABLE_DESCRIPTOR { PULONG_PTR Base; PULONG_PTR Count; PULONG_PTR Limit; PULONG_PTR Number; } KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; #else #endif class Cssdt { private: /* data */ public: Cssdt(/* args */); ~Cssdt(); public: PVOID GetProcById(ULONG uId); PKSERVICE_TABLE_DESCRIPTOR GetSsdtBase(); PKSERVICE_TABLE_DESCRIPTOR GetSsdtBaseByKernelFunction(PVOID pfnKernelFunction, ULONG findSize); };
.cpp
#include "ssdt.h" Cssdt::Cssdt(/* args */) { } Cssdt::~Cssdt() { } PVOID Cssdt::GetProcById(ULONG uId) { PKSERVICE_TABLE_DESCRIPTOR pSsdt = NULL; PULONGLONG pFunctionAddr = NULL; PUCHAR ssdtbase = NULL; LONG offset = 0; pSsdt = this->GetSsdtBaseByKernelFunction((PVOID)ZwCreateFile, 0x500); if (pSsdt == NULL) { return NULL; } //calc function //pointer get addr // ssdtbase = (PUCHAR)pSsdt->Base; // offset = (LONG) * (PULONG)(ssdtbase + uId * 4); // offset = (LONG)offset >> 4; // pFunctionAddr = (PULONGLONG)(offset + ssdtbase); //array get addr ssdtbase = (PUCHAR)pSsdt->Base; offset = (LONG)((PULONG)ssdtbase)[uId]; offset = (LONG)offset >> 4; pFunctionAddr = (PULONGLONG)(offset + ssdtbase); return pFunctionAddr; } PKSERVICE_TABLE_DESCRIPTOR Cssdt::GetSsdtBaseByKernelFunction(PVOID pfnKernelFunction, ULONG findSize) { BOOLEAN bIsFind = FALSE; PVOID pFindAddress = NULL; ULONG uSearchStartIndex = 0; PUCHAR pSearchAddress = (PUCHAR)pfnKernelFunction; PUCHAR pfnKiServiceInternal = NULL; PKSERVICE_TABLE_DESCRIPTOR pSsdtInfo = NULL; if (pfnKernelFunction == NULL) { return NULL; } if (!MmIsAddressValid(pfnKernelFunction)) { return NULL; } //查找函数中的 .text:00000001401BD9E9 E9 D2 4B 01 00 jmp KiServiceInternal for (uSearchStartIndex = 0; uSearchStartIndex < findSize; uSearchStartIndex++) { if (MmIsAddressValid(&pSearchAddress[uSearchStartIndex])) { if (pSearchAddress[uSearchStartIndex] == 0xE9) { //取出它记录的偏移地址. 公式: DstProc = offset + len(opcode) + CurrendRip if (MmIsAddressValid((PULONG)&pSearchAddress[uSearchStartIndex + 1])) { ULONG offset = *(PULONG)&pSearchAddress[uSearchStartIndex + 1]; PUCHAR pCurRip = &pSearchAddress[uSearchStartIndex]; pfnKiServiceInternal = pCurRip + offset + 5; break; } } } } if (pfnKiServiceInternal == NULL) { return NULL; } for (uSearchStartIndex = 0; uSearchStartIndex < findSize; uSearchStartIndex++) { if (MmIsAddressValid((PULONGLONG)&pfnKiServiceInternal[uSearchStartIndex])) { if ( pfnKiServiceInternal[uSearchStartIndex] == 0x4C && pfnKiServiceInternal[uSearchStartIndex + 1] == 0x8D && pfnKiServiceInternal[uSearchStartIndex + 2] == 0x15) { ULONG offset = *(PULONG)&pfnKiServiceInternal[uSearchStartIndex + 3]; PUCHAR pCurRip = &pfnKiServiceInternal[uSearchStartIndex]; pSsdtInfo = (PKSERVICE_TABLE_DESCRIPTOR)(pCurRip + offset + 7); break; } } } //Shadow 同上 return pSsdtInfo; } PKSERVICE_TABLE_DESCRIPTOR Cssdt::GetSsdtBase() { return NULL; }
坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912(收费)群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。
详情请点击链接查看置顶博客 https://www.cnblogs.com/iBinary/p/7572603.html
本文来自博客园,作者:iBinary,未经允许禁止转载 转载前可联系本人.对于爬虫人员来说如果发现保留起诉权力.https://www.cnblogs.com/iBinary/p/15757208.html
欢迎大家关注我的微信公众号.不定期的更新文章.更新技术. 关注公众号后请大家养成 不白嫖的习惯.欢迎大家赞赏. 也希望在看完公众号文章之后 不忘 点击 收藏 转发 以及点击在看功能.

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
2019-01-02 Linux基础知识第八讲,系统相关操作命令
2019-01-02 Linux基础知识第七讲,用户权限以及用户操作命令