初识SSDT、读取内核API地址
SSDT 表的初步学习
1 理论知识
大概思路和理论知识
首先明白应用层到 内核层 会通过一张SSDT 表、、我们要根据SSDT结构和索引号来获取函数的当前地址
如果检测一下这个函数有木有被HOOK我们要在获取这个函数的起源地址 对比一下即可知道、、
1 读取SSDT表函数当前地址
了解SSDT结构
系统服务描述符表 在ntoskrnl.exe导出KeServiceDescriptorTable这个表
typedef struct _ServiceDescriptorTable {
PVOID ServiceTableBase; //System Service Dispatch Table 的基地址
PVOID ServiceCounterTable;
//包含着SSDT 中每个服务被调用次数的计数器。这个计数器一般由sysenter 更新。
unsigned int NumberOfServices;//由ServiceTableBase 描述的服务的数目。
PVOID ParamTableBase; //包含每个系统服务参数字节数表的基地址-系统服务参数表
}*PServiceDescriptorTable;
//由SSDT索引号获取当前函数地址
extern PServiceDescriptorTable KeServiceDescriptorTable;
用KeServiceDescriptorTable这张表 可以获取SSDT的基地址 再使用索引号 即可得到函数的地址
这里是NTOpenProcess 索引号是7A(122) ,所以
NTOpenProcess 的地址是保存在KeServiceDescriptorTable+ 7A*4的位置
使用汇编得到这个地址 这么写
首先加一句
extern long KeServiceDescriptorTable;// 这里使用long不太准确
ULONGSSDT_NtOpenProcess_Cur_Addr; //定义一个全局变量 打印出这个值
__asm
{
int 3//为了调试方便 下一个int 3断点
push ebx
push eax
mov ebx,KeServiceDescriptorTable//直接使用这个表
mov ebx,[ebx]
mov eax,0x7A
shl eax,2 // imul eax,eax,4//左移两位
add ebx,eax// 地址[KeServiceDescriptorTable]+0x7A*4
mov ebx,[ebx]//取上边地址的内容
mov SSDT_NtOpenProcess_Cur_Addr,ebx//赋予全局变量
pop eax
pop ebx
} 使用指针这样写、
先定义一个
typedef struct _ServiceDescriptorTable {
PVOID ServiceTableBase; //System Service Dispatch Table 的基地址
PVOID ServiceCounterTable;
//包含着SSDT 中每个服务被调用次数的计数器。这个计数器一般由sysenter 更新。
unsigned int NumberOfServices;//由ServiceTableBase 描述的服务的数目。
PVOID ParamTableBase; //包含每个系统服务参数字节数表的基地址-系统服务参数表
}*PServiceDescriptorTable;
然后
extern PServiceDescriptorTable KeServiceDescriptorTable; //这个是准确的、
LONG *SSDT_Adr;
LONG SSDT_NtOpenProcess_Cur_Addr; //最终结果
LONG t_addr;//基地址
t_addr=(LONG)KeServiceDescriptorTable->ServiceTableBase;
SSDT_Adr=(PLONG)(t_addr+0x7A*4);//得到存放NTOpenProcess的地址的地址、、
SSDT_NtOpenProcess_Cur_Addr = *SSDT_Adr;//得到NTOpenProcess的地址
KdPrint(("SSDT_NtOpenProcess_Cur_Addr =%x\n",SSDT_NtOpenProcess_Cur_Addr));
可以双机调试一下、使用Kernel Detective 软件对照分析、、
2 读取SSDT函数表的真正地址、、即原始地址、
上边获取的是SSDT当前地址 不一定为函数的原始地址(该函数若有HOOK行为、)
这个有固定的函数、、
PVOID MmGetSystemRoutineAddress
(
__inPUNICODE_STRING SystemRoutineName
//参数是 保存要得到的函数名字的 缓冲区、、
);
实现
ULONG GetSSDT_Old_ADDR()
{
UNICODE_STRING Old_NtOpenProcess;
ULONG Old_Addr;
RtlInitUnicodeString(&Old_NtOpenProcess,L"NtOpenProcess");//初始化字符串
Old_Addr=(ULONG)MmGetSystemRoutineAddress(&Old_NtOpenProcess)NtOpenProcess地址
KdPrint(("取得原函数NtOpenProcess的值为%x",Old_Addr));
return Old_Addr;
}