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;
}