驱动开发笔记3—SSDT表详解

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

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

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]

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值