获取SSTD函数调用索引号和ntoskrnl.exe函数地址

c++

Posted by YiMiTuMi on May 11, 2021

获取SSTD函数调用索引号

参考

Windows在调用系统API时,通过ntdll.dll文件中的函数进入内核,每个函数在进入内核时都会有一个调用编号,这个调用编号被存放在eax中,windows通过这个编号在SSTD中查找对应的函数地址。

注意:ntdll.dll 导出表的函数编号并不是SSTD的函数编号,两者并不对应。并且已经公开的函数大多数是以NT开头的,还有一些为公开的是ZW。

x64:

mov     r10, rcx ; NtSetEventBoostPriority
mov     eax, 2Dh ;  //其中2Dh就是调用编号

x32:

mov     eax, 2Dh ;  //其中2Dh就是调用编号

程序:

GetSSDTFunctionIndexAndAddress函数:

第一个参数接受一个PE文件路径

第二个参数接受一个指定函数名

第三个参数:当为TRUE时,返回SSTD函数编号,此时必须为ntdll.dll模块。当为FALST时,返回指定模块中的函数地址。

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

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

//查找函数
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, BOOL bNumberAndAdd)
{
	ULONGLONG  ulFunctionIndexAndAdd = 0; 	// Dos Header

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

			if (bNumberAndAdd)   //TRUE返回序号、FALSE返回地址
			{
				// 获取 SSDT 函数 Index
#ifdef _WIN64
				ulFunctionIndexAndAdd = *(ULONG*)((PUCHAR)lpFuncAddr + 4);
#else
				ulFunctionIndexAndAdd = *(ULONG*)((PUCHAR)lpFuncAddr + 1);
#endif
			}
			else
			{
				//返回函数地址
				ulFunctionIndexAndAdd = (ULONGLONG)lpFuncAddr;
			}

			DbgPrint("Function = %s, %d, %p\n", lpName, ulFunctionIndex, lpFuncAddr);

			break;
		}
	}

	return ulFunctionIndexAndAdd;
}

//获取函数名对应编号或地址
ULONGLONG GetSSDTFunctionIndexAndAddress(UNICODE_STRING ustrDllFileName, PCHAR pszFunctionName, BOOL bNumberAndAdd)
{
	ULONGLONG  ulFunctionIndexAndAdd = 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 ulFunctionIndexAndAdd;
	}

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

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

	return ulFunctionIndexAndAdd;
}


//卸载
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");
	ULONGLONG ulSSDTFunctionIndex = GetSSDTFunctionIndex(ustrDllFileName, "NtTerminateProcess", TRUE);

	//获取ntoskrnl.exe指定 函数地址
	UNICODE_STRING ustrDllFileNameNtoskrnl;
	RtlInitUnicodeString(&ustrDllFileNameNtoskrnl, L"\\??\\C:\\Windows\\System32\\ntoskrnl.exe");
	ULONGLONG ulSSDTFunctionIndex1Ntoskrnl = GetSSDTFunctionIndexAndAddress(ustrDllFileNameNtoskrnl, "_strnicmp", FALSE);


	return STATUS_SUCCESS;
}

蓝色妖姬 – 相守