动态获取SSTD,并打印函数名、调用编号、函数地址

c++

Posted by YiMiTuMi on May 13, 2021

动态获取KeServiceDescriptorTable(SSDT)

在windows系统中,32位系统是导出了SSTD的,我们可以通过全局的KeServiceDescriptorTable去获取:

extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;    //只有在32位才能导出

在64位系统中,并没有导出函数地址所以我们需要去动态获取一下:

KiSystemServiceRepeat函数在_strnicmp函数到KdDebuggerNotPresent之间了。我们通过特征码去搜索。

//动态定位KeServiceDescriptorTable的方法  
ULONGLONG GetKeServiceDescriptorTable64()
{
	UNICODE_STRING strStrnicmp;
	RtlInitUnicodeString(&strStrnicmp, L"_strnicmp");
	ULONGLONG ulongStrnicmp = (ULONGLONG)MmGetSystemRoutineAddress(&strStrnicmp);
	//KdPrint(("_strnicmp %x\n", ulongStrnicmp));

	char KiSystemServiceStart_pattern[] = "\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x25\xFF\x0F\x00\x00";   //特征码  
	ULONGLONG CodeScanStart = ulongStrnicmp;
	ULONGLONG CodeScanEnd = (ULONGLONG)&KdDebuggerNotPresent;
	UNICODE_STRING Symbol;
	ULONGLONG i, tbl_address, b;
	for (i = 0; i < CodeScanEnd - CodeScanStart; i++)
	{
		if (!memcmp((char*)(ULONGLONG)CodeScanStart + i, (char*)KiSystemServiceStart_pattern, 13))
		{
			for (b = 0; b < 50; b++)
			{
				tbl_address = ((ULONGLONG)CodeScanStart + i + b);
				if (*(USHORT*)((ULONGLONG)tbl_address) == (USHORT)0x8d4c)
					return ((LONGLONG)tbl_address + 7) + *(LONG*)(tbl_address + 3);
			}
		}
	}
	return 0;
}

但是注意: _strnicmp 函数的地址需要用MmGetSystemRoutineAddress去获取如:

UNICODE_STRING strStrnicmp;
RtlInitUnicodeString(&strStrnicmp, L"_strnicmp");
ULONGLONG ulongStrnicmp = (ULONGLONG)MmGetSystemRoutineAddress(&strStrnicmp);
KdPrint(("_strnicmp %x\n", ulongStrnicmp));

而不能用取地址直接获取,这两个获取出来的函数地址不相同:

ULONGLONG CodeScanStart = (ULONGLONG)&_strnicmp;

第一个获取是函数被映射到内存中,从导出表动态获取的地址。

第二个获取的是函数在文件中保存的地址。

根据编号获取函数在SSDT中的地址

根据系统不同获取函数地址的方法也不同:

//根据编号找到SSDT表中函数的地址  
ULONGLONG GetFuncAddr(ULONG id)
{
	LONG dwtmp = 0;
	ULONGLONG addr = 0;
	PULONG stb = NULL;
	stb = GetSSDTBaseAddress();

	DbgPrint("ssdt = %x\n", stb);

#ifdef _WIN64
	dwtmp = stb[id];
	dwtmp = dwtmp >> 4;
	addr = (LONGLONG)dwtmp + (ULONGLONG)stb;
#else
	addr = stb[id];     //32位
#endif

	DbgPrint("SSDT TABLE BASEADDRESS:%11x", addr);
	return addr;
}

32位可以直接获取,而64位则要右移四位后加上基址。

打印函数名、调用编号、函数地址程序

#include <ntddk.h>
#include <WinDef.h>
#include <aux_klib.h>

#pragma comment(lib,"aux_klib.lib")  

typedef struct _KSYSTEM_SERVICE_TABLE
{
	PULONG ServiceTableBase;         //函数地址表基地址
	PULONG ServicrCounterTableBase;  //SSDT函数被调用的次数
	ULONG  NumberOfService;          //函数的个数
	PULONG ParamTableBase;           //函数参数表基地址

} KSYSTEM_SERVICE_TABLE, * PKSYSTEM_SERVICE_TABLE;


typedef struct _KSERVICE_TABLE_DESCRIPTOR
{

	KSYSTEM_SERVICE_TABLE ntoskrnl;   //Ntoskrl.exe 的函数
	KSYSTEM_SERVICE_TABLE win32k;     //Win32K.sys 的函数
	KSYSTEM_SERVICE_TABLE notUsed1;
	KSYSTEM_SERVICE_TABLE notUsed2;

} KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;


#ifdef _WIN64

#else
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;    //只有在32位才能导出
#endif


//MiTerminateProcess的函数定义
typedef NTSTATUS(__stdcall* PMiTerminateProcess)(HANDLE ProcessHandle, NTSTATUS ExitStatus);

PMiTerminateProcess pMiTerminateProcess = NULL;

//动态定位KeServiceDescriptorTable的方法  
ULONGLONG GetKeServiceDescriptorTable64()
{
	UNICODE_STRING strStrnicmp;
	RtlInitUnicodeString(&strStrnicmp, L"_strnicmp");
	ULONGLONG ulongStrnicmp = (ULONGLONG)MmGetSystemRoutineAddress(&strStrnicmp);
	//KdPrint(("_strnicmp %x\n", ulongStrnicmp));

	char KiSystemServiceStart_pattern[] = "\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x25\xFF\x0F\x00\x00";   //特征码  
	ULONGLONG CodeScanStart = ulongStrnicmp;
	ULONGLONG CodeScanEnd = (ULONGLONG)&KdDebuggerNotPresent;

	ULONGLONG i, tbl_address, b;
	for (i = 0; i < CodeScanEnd - CodeScanStart; i++)
	{
		if (!memcmp((char*)(ULONGLONG)CodeScanStart + i, (char*)KiSystemServiceStart_pattern, 13))
		{
			for (b = 0; b < 50; b++)
			{
				tbl_address = ((ULONGLONG)CodeScanStart + i + b);
				if (*(USHORT*)((ULONGLONG)tbl_address) == (USHORT)0x8d4c)
					return ((LONGLONG)tbl_address + 7) + *(LONG*)(tbl_address + 3);
			}
		}
	}
	return 0;
}

//根据KeServiceDescriptorTable找到SSDT基址  
PULONG GetSSDTBaseAddress()
{
	PULONG addr = NULL;
	PKSYSTEM_SERVICE_TABLE ssdt = NULL;

#ifdef _WIN64
	ssdt = (PKSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable64();
	addr = (PULONG)(ssdt->ServiceTableBase);
#else
	ssdt = &KeServiceDescriptorTable->ntoskrnl;    //只有在32位才能导出
	addr = (PULONG)ssdt->ServiceTableBase;    //32位中ServiceTableBase中保存的值才是地址表的基址
#endif

	return addr;
}


//根据标号找到SSDT表中函数的地址  
ULONGLONG GetFuncAddr(ULONG id)
{
	LONG dwtmp = 0;
	ULONGLONG addr = 0;
	PULONG stb = NULL;
	stb = GetSSDTBaseAddress();

#ifdef _WIN64
	dwtmp = stb[id];
	dwtmp = dwtmp >> 4;
	addr = (LONGLONG)dwtmp + (ULONGLONG)stb;
#else
	addr = stb[id];
#endif

	//DbgPrint("SSDT TABLE BASEADDRESS:%11x", addr);
	return addr;
}


//查找函数
NTSTATUS DllFileMap(UNICODE_STRING ustrDllFileName, HANDLE* phFile, HANDLE* phSection, PVOID* ppBaseAddress)
{
	NTSTATUS status = STATUS_SUCCESS;
	HANDLE hFile = NULL;
	HANDLE hSection = NULL;
	OBJECT_ATTRIBUTES objectAttributes = { 0 };
	IO_STATUS_BLOCK iosb = { 0 };
	PVOID pBaseAddress = NULL;
	SIZE_T viewSize = 0;

	// 打开 DLL 文件, 并获取文件句柄
	InitializeObjectAttributes(&objectAttributes, &ustrDllFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
	status = ZwOpenFile(&hFile, GENERIC_READ, &objectAttributes, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("ZwOpenFile Error! [error code: 0x%X]", status));
		return status;
	}

	// 创建一个节对象, 以 PE 结构中的 SectionALignment 大小对齐映射文件
	status = ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE, NULL, 0, PAGE_READWRITE, 0x1000000, hFile);
	if (!NT_SUCCESS(status))
	{
		ZwClose(hFile);
		KdPrint(("ZwCreateSection Error! [error code: 0x%X]", status));
		return status;
	}

	// 映射到内存
	status = ZwMapViewOfSection(hSection, NtCurrentProcess(), &pBaseAddress, 0, 1024, 0, &viewSize, ViewShare, MEM_TOP_DOWN, PAGE_READWRITE);
	if (!NT_SUCCESS(status))
	{
		ZwClose(hSection);
		ZwClose(hFile);
		KdPrint(("ZwMapViewOfSection Error! [error code: 0x%X]", status));
		return status;
	}

	// 返回数据
	*phFile = hFile;
	*phSection = hSection;
	*ppBaseAddress = pBaseAddress;

	return status;
}

//映射文件
ULONGLONG  GetIndexFromExportTable(PVOID pBaseAddress, PCHAR pszFunctionName)
{
	ULONGLONG  ulFunctionIndexAdd = 0; 	// Dos Header

	ULONGLONG NtTerminateProcessAdd = 0;

	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBaseAddress;  // NT Header

	PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PUCHAR)pDosHeader + pDosHeader->e_lfanew); // Export Table

	PIMAGE_EXPORT_DIRECTORY pExportTable = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)pDosHeader + pNtHeaders->OptionalHeader.DataDirectory[0].VirtualAddress);  // 有名称的导出函数个数
	ULONG ulNumberOfNames = pExportTable->NumberOfNames;

	// 导出函数名称地址表
	PULONG lpNameArray = (PULONG)((PUCHAR)pDosHeader + pExportTable->AddressOfNames);
	PCHAR lpName = NULL;

	// 开始遍历导出表
	for (ULONG i = 0; i < ulNumberOfNames; i++)
	{
		lpName = (PCHAR)((PUCHAR)pDosHeader + lpNameArray[i]);

		// 判断是否查找的函数
		if (0 == _strnicmp(pszFunctionName, lpName, strlen(pszFunctionName)))
		{
			// 获取导出函数地址
			USHORT uHint = *(USHORT*)((PUCHAR)pDosHeader + pExportTable->AddressOfNameOrdinals + 2 * i);
			ULONG ulFuncAddr = *(PULONG)((PUCHAR)pDosHeader + pExportTable->AddressOfFunctions + 4 * uHint);
			PVOID lpFuncAddr = (PVOID)((PUCHAR)pDosHeader + ulFuncAddr);

			
			// 获取 SSDT 函数 Index
#ifdef _WIN64
			ulFunctionIndexAdd = *(ULONG*)((PUCHAR)lpFuncAddr + 4);
#else
			ulFunctionIndexAdd = *(ULONG*)((PUCHAR)lpFuncAddr + 1);
#endif
			
			//DbgPrint("Function = %s, %d, %x\n", lpName, ulFunctionIndexAdd, lpFuncAddr);
			
			if (((int)ulFunctionIndexAdd > -1) && ((int)ulFunctionIndexAdd < 1000))
			{
				NtTerminateProcessAdd = GetFuncAddr(ulFunctionIndexAdd);
			}

			DbgPrint("SerialNumber = %d", ulFunctionIndexAdd);
			DbgPrint("FunctionName = %s", lpName);
			DbgPrint("FunctioneAddress = %11x\n", NtTerminateProcessAdd);

			//break;
		}
	}

	return ulFunctionIndexAdd;
}

//获取函数名对应编号
ULONGLONG GetSSDTFunctionIndexAndAdd(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName)
{
	ULONGLONG  ulFunctionIndexAdd = 0;
	NTSTATUS status = STATUS_SUCCESS;
	HANDLE hFile = NULL;
	HANDLE hSection = NULL;
	PVOID pBaseAddress = NULL;

	// 内存映射文件
	status = DllFileMap(ustrDllFileName, &hFile, &hSection, &pBaseAddress);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("DllFileMap Error!\n"));
		return ulFunctionIndexAdd;
	}

	// 根据导出表获取导出函数地址, 从而获取 SSDT 函数索引号
	ulFunctionIndexAdd = GetIndexFromExportTable(pBaseAddress, pszFunctionName);

	// 释放
	ZwUnmapViewOfSection(NtCurrentProcess(), pBaseAddress);
	ZwClose(hSection);
	ZwClose(hFile);

	return ulFunctionIndexAdd;
}

//卸载
VOID DriverUpLoad(PDRIVER_OBJECT pdriver)
{
	DbgPrint("卸载了\n");
}


NTSTATUS DriverEntry(PDRIVER_OBJECT pdriver, PUNICODE_STRING pReg) //返回一个地址、返回驱动被注册到注册表的某个地方
{
	DbgPrint("pdriver = %wZ, , %x\n", pReg, pdriver);
	pdriver->DriverUnload = DriverUpLoad;

	//获取SSDT 函数编号
	UNICODE_STRING ustrDllFileName;
	RtlInitUnicodeString(&ustrDllFileName, L"\\??\\C:\\Windows\\System32\\ntdll.dll");
	GetSSDTFunctionIndexAndAdd(ustrDllFileName, "Nt"); //只打印Nt,Nt的才是导出的


	return STATUS_SUCCESS;
}

结果

X64(一部分):

pdriver = \REGISTRY\MACHINE\SYSTEM\ControlSet001\services\GetSSDTFunctionIndex, , 397d890	
00000003	9.03247166	SerialNumber = 96, FunctionName = NtAcceptConnectPort, FunctionAddress = fffff800041542e0	
00000004	9.03679848	SerialNumber = 97, FunctionName = NtAccessCheck, FunctionAddress = fffff80003e60ad4	
00000005	9.04150772	SerialNumber = 38, FunctionName = NtAccessCheckAndAuditAlarm, FunctionAddress = fffff8000417e0a0	
00000006	9.04584599	SerialNumber = 98, FunctionName = NtAccessCheckByType, FunctionAddress = fffff80003e754d4	
00000007	9.05020046	SerialNumber = 86, FunctionName = NtAccessCheckByTypeAndAuditAlarm, FunctionAddress = fffff8000413bba4	
00000008	9.05474281	SerialNumber = 99, FunctionName = NtAccessCheckByTypeResultList, FunctionAddress = fffff80003fafe20	
00000009	9.05834675	SerialNumber = 100, FunctionName = NtAccessCheckByTypeResultListAndAuditAlarm, FunctionAddress = fffff800042bf560	
00000010	9.06329155	SerialNumber = 101, FunctionName = NtAccessCheckByTypeResultListAndAuditAlarmByHandle, FunctionAddress = fffff800042bf4a0	
00000011	9.06729412	SerialNumber = 68, FunctionName = NtAddAtom, FunctionAddress = fffff800040ed06c	
00000012	9.07230282	SerialNumber = 102, FunctionName = NtAddBootEntry, FunctionAddress = fffff800042dc030	
00000013	9.07630253	SerialNumber = 103, FunctionName = NtAddDriverEntry, FunctionAddress = fffff800042dbd90	
00000014	9.08029556	SerialNumber = 104, FunctionName = NtAdjustGroupsToken, FunctionAddress = fffff8000411a2e0	
00000015	9.08533764	SerialNumber = 62, FunctionName = NtAdjustPrivilegesToken, FunctionAddress = fffff8000414f334	
00000016	9.08930874	SerialNumber = 105, FunctionName = NtAlertResumeThread, FunctionAddress = fffff800042c1080

X86(一部分):

00000001	0.00000000	pdriver = \REGISTRY\MACHINE\SYSTEM\ControlSet001\services\GetSSDTFunctionIndex, , 85844798	
00000002	0.01811990	SerialNumber = 0	
00000003	0.02252340	FunctionName = NtAcceptConnectPort	
00000004	0.02708660	FunctioneAddress =    840c6c28	
00000005	0.03225730	SerialNumber = 1	
00000006	0.03527900	FunctionName = NtAccessCheck	
00000007	0.03983910	FunctioneAddress =    83f0d40d	
00000008	0.04433890	SerialNumber = 2	
00000009	0.04889340	FunctionName = NtAccessCheckAndAuditAlarm	
00000010	0.05347140	FunctioneAddress =    84056b68	
00000011	0.05801310	SerialNumber = 3	
00000012	0.06254450	FunctionName = NtAccessCheckByType	
00000013	0.06709350	FunctioneAddress =    83e7188a	
00000014	0.07159030	SerialNumber = 4	
00000015	0.07615380	FunctionName = NtAccessCheckByTypeAndAuditAlarm	
00000016	0.08075190	FunctioneAddress =    840c84ff	
00000017	0.08523520	SerialNumber = 5	
00000018	0.08981290	FunctionName = NtAccessCheckByTypeResultList	
00000019	0.09438350	FunctioneAddress =    83f4a3fa	
00000020	0.09887890	SerialNumber = 6	

迷迭香 – 回忆不想忘记得过去,纪念