动态获取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