SSDT表
SSDT的全称是"System Services Descriptor Table",即系统服务描述表,在内核中的实际名称是KeServiceDescriptorTable,这个表由ntoskrnl.exe导出(在x64里不导出)。
SSDT用于处理应用层通过Kernel32.dll下发的各个API操作请求。ntdll.dll中的API是一个简单的包装函数,当Kernel32.dll中的API通过Ntdll.dll时,会先完成对参数的检查,再调用一个中断(int 2Eh 或者 SysEnter指令),从而实现从Ring3层进入Ring0层,并将要调用的服务号(也就是SSDT数组中的索引号index值)存放到寄存器EAX中,最后根据存放在EAX中的索引值在SSDT数组中调用指定的服务(Nt系列函数)。

KeServiceDescriptorTable变量是在NTOS模块中导出的一个全局变量,声明一下就可以用
3: kd> dd KeServiceDescriptorTable
849acb00 848c143c 00000000 00000191 848c1a84
(其中,848c143c就是SSDT表的基地址,00000191是SSDT表中服务函数的个数)
extern PSYSTEM_SERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
typedef struct _SYSTEM_SERVICE_DESCRIPTOR_TABLE
{
PVOID ServiceTableBase; //SSDT表的基地址
PVOID ServiceCounterTableBase;
ULONG_PTR NumberOfServices; //SSDT表中服务函数的个数
PVOID ParameterTableBase;
}SYSTEM_SERVICE_DESCRIPTOR_TABLE, *PSYSTEM_SERVICE_DESCRIPTOR_TABLE;
知道了SSDT表的基地址(数组的首地址)和SSDT函数的索引号(Index),就可以求出对应的服务函数的地址。
在x86平台上: FuncAddress = KeServiceDescriptorTable + 4 * Index
在x64平台上:FuncAddress = [KeServiceDescriptorTable+4*Index]>>4 + KeServiceDescriptorTable

获得SSDT表中Nt函数索引的方法:
先获得Zw函数的地址,然后加一个字节得到Nt函数的索引
注意:
ntkrnlpa.exe的物理内存是映射到Ring0层内存空间的,和我们的进程(xxx.sys)是在一片内存空间中,因此可以直接访问。
ntdll.dll的物理内存是映射在Ring3层内存空间的(映射到每一个应用程序的内存中),我们的进程(xxx.sys)若想要访问ntdll.dll的数据,需要先把它的物理内存映射到自己(xxx.sys)的内存空间,然后才能访问。
1. 直接访问ntkrnlpa.exe中的SSDT表
UNICODE_STRING v1;
RtlInitUnicodeString(&v1, L"ZwOpenProcess");
ZwOpenProcess = MmGetSystemRoutineAddress(&v1);
#define SSDT_INDEX(ZwFunctionAddress) *(PULONG)(ZwFunctionAddress + 1)
/*
3: kd> u ZwOpenProcess
nt!ZwOpenProcess:
8487ecd8 b8be000000 mov eax,0BEh
3: kd> dd 8487ecd8
8487ecd8 0000beb8 24548d00 086a9c04 0019d5e8
3: kd> dd 8487ecd9
8487ecd9 000000be 0424548d e8086a9c 000019d5
*/
//ZwFunctionAddress + 1,加一个字节,越过那个b8,就能得到索引
2. 通过访问Ntdll.dll模块的导出表
首先通过Ntdll.dll的路径获得文件句柄,然后通过文件句柄获得映射句柄,然后映射Ntdll.dll物理内存到当前进程的内存空间,得到映射内存的首地址和映射内存大小。
通过模块基地址,访问ntdll.dll模块的导出表,利用那三个数组,来获得Zw函数地址,最后加一个字节得到Nt函数索引。
NTSTATUS MyGetSSDTFunctionIndex(char* ZwFunctionName, ULONG* NtFunctionIndex)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
WCHAR FileFullPath[] = L"\\SystemRoot\\System32\\ntdll.dll";
PVOID MappedFileVA = NULL;
ULONG MappedFileSize = 0;
PIMAGE_NT_HEADERS ImageNtHeaders = NULL;
PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL;
ULONG* AddressOfFunctions = NULL;
ULONG* AddressOfNames = NULL;
USHORT* AddressOfNameOrdinals = NULL;
ULONG i = 0;
char* FunctionName = NULL;
PUCHAR FunctionAddress = NULL;
ULONG Offset = 1;
if (!ZwFunctionName)
{
return Status;
}
//将Ntdll.dll文件映射到系统空间中
if (!NT_SUCCESS(MyMappingPEFileInRing0Space(FileFullPath, &MappedFileVA, &MappedFileSize)))
{
return Status;
}
__try
{
//通过Dos头获得Nt头
ImageNtHeaders = RtlImageNtHeader(MappedFileVA);
if (ImageNtHeaders && ImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
{
ImageExportDirectory = (IMAGE_EXPORT_DIRECTORY*)((ULONG_PTR)MappedFileVA +
ImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]

本文详细介绍了系统服务描述表(SSDT)在Windows操作系统中的作用,它是内核处理API调用的关键结构。SSDT包含了一系列Nt函数,如NtAcceptConnectPort、NtAccessCheck等,通过服务函数的索引可以在Ring3层进入Ring0层进行系统调用。文章还展示了如何获取SSDT表的基地址和服务函数的索引,涉及了在x86和x64平台上的不同计算方式,并提供了访问ntkrnlpa.exe和ntdll.dll中SSDT函数索引的示例代码。

2769

被折叠的 条评论
为什么被折叠?



