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

#include

#include

#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

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

Previous

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

Next

Handle转换eProcess