x64下读取SSDT表,并且获取SSDT表函数.

x64下读取SSDT表,并且获取SSDT表函数.

目录64位下读取SSDT表并且获取SSDT函数一丶读取SSDT表 (KeServiceDescriptorTable)1.1 原理1.2 手动获取SSDT表1.2.1 重点1 了解引用流程以及其它方式寻找SSDT表的方式1.2.2 重要点2 获取SSDT表以及Shadow表位置1.2.3 重要点3 SSDT表的加密获取以及使用二丶两种方式实现获取SSDT表2.1 常规方式获取SSDT表.2.2 通过API寻找方式来找寻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 = 0xfffff806771122d0

查看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

#include

#include

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

}

相关文章

设计总监岗位职责要求 彩票365软件是什么样的

设计总监岗位职责要求

📅 07-04 👁️ 8986
我的世界怎么做流沙 我的世界流沙制作方法 365会提款不成功吗

我的世界怎么做流沙 我的世界流沙制作方法

📅 07-03 👁️ 1279
傲视竞争对手 OCZ饥饿鲨两款SSD评测 彩票365软件是什么样的

傲视竞争对手 OCZ饥饿鲨两款SSD评测

📅 07-03 👁️ 9103