测试环境Win7 x86
实现原理系统调用分析
1.重新加载一份按照PE格式拉伸后的内核文件到内存(避免当前内核已经被挂钩).
2.通过导出表获取HOOK函数系统服务号.
3.利用导出KeServiceDescriptorTable结构定位系统服务表实现替换函数(类似IAT_HOOK).
代码如下:
#include <ntifs.h>
#include <ntimage.h>
#include <ntstrsafe.h>
//获取系统目录
PWCHAR GetSystemFullPath();
//内核文件按照PE拉伸后格式映射到内存
PUCHAR FileMaping(PWCHAR SystemPath);
//释放文件映射
VOID UnFileMaping(PVOID mapBase);
//通过函数名查找导出函数
ULONG64 GetFuntionAddressByExportTableName(PUCHAR ImageBuffer, PUCHAR FunctionName);
//导出未文档化函数
NTSTATUS MmCreateSection(
__deref_out PVOID* SectionObject,
__in ACCESS_MASK DesiredAccess,
__in_opt POBJECT_ATTRIBUTES ObjectAttributes,
__in PLARGE_INTEGER InputMaximumSize,
__in ULONG SectionPageProtection,
__in ULONG AllocationAttributes,
__in_opt HANDLE FileHandle,
__in_opt PFILE_OBJECT FileObject
);
// 系统服务表
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // 函数地址表(SSDT)
PULONG ServiceCounterTableBase; // SSDT 函数被调用的次数
ULONG NumberOfService; // 函数个数
PULONG ParamTableBase; // 函数参数表(SSPT)
} KSYSTEM_SERVICE_TABLE, * PKSYSTEM_SERVICE_TABLE;
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // 内核函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 函数
KSYSTEM_SERVICE_TABLE unUsed1;
KSYSTEM_SERVICE_TABLE unUsed2;
} KSERVICE_TABLE_DESCRIPTOR, * PKSERVICE_TABLE_DESCRIPTOR;
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
PUCHAR G_MapNtdll = NULL;
//拷贝SSDT表
BOOLEAN SSDT_Init();
//释放SSDT表
VOID SSDT_Destroy();
//获取函数系统服务号
ULONG SSDT_GetFunIndex(PUCHAR szFunctionName);
//SSDTHOOK
ULONG_PTR SSDT_Hook(PUCHAR szFunctionName, ULONG_PTR FunctionAddr);
//关闭写保护以及中断
ULONG wpOff()
{
ULONG cr0 = __readcr0();
_disable();
__writecr0(cr0 & (~0x10000));
return cr0;
}
//恢复CR0默认数据
VOID wpOn(ULONG value)
{
__writecr0(value);
_enable();
}
//恢复HOOK时用到
ULONG G_OldFunAddr = NULL;
//函数指针
typedef NTSTATUS(NTAPI* OpenProcessProc)(_Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId);
//替换函数
NTSTATUS NTAPI MyOpenProcess(_Out_ PHANDLE ProcessHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PCLIENT_ID ClientId)
{
PUCHAR pEprocess = (PUCHAR)IoGetCurrentProcess();
DbgPrint("进程ID: [%d] 调用OpenProcess \r\n", *(PULONG)(pEprocess + 0xb4));
//TODO:
//获取参数,监控,修改返回值....
return ((OpenProcessProc)G_OldFunAddr)(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
NTSTATUS DriverUnload(PDRIVER_OBJECT pDriver)
{
DbgPrint("Driver Exit \r\n");
//恢复钩子
if (G_OldFunAddr)
{
SSDT_Hook("ZwOpenProcess", G_OldFunAddr);
}
//释放后延迟避免有进程还在执行我们函数释放导致蓝屏
SSDT_Destroy();
//延时
LARGE_INTEGER inTime = { 0 };
inTime.QuadPart = -10000 * 3000;
KeDelayExecutionThread(KernelMode, FALSE, &inTime);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
DbgPrint("Driver Load \r\n");
pDriver->DriverUnload = DriverUnload;
if (SSDT_Init())
{
G_OldFunAddr = SSDT_Hook("ZwOpenProcess", MyOpenProcess);
}
return STATUS_SUCCESS;
}
PWCHAR GetSystemFullPath()
{
//申请路径缓冲区
PWCHAR SystemPath = ExAllocatePool(PagedPool, PAGE_SIZE);
if (!SystemPath)
{
return NULL;
}
memset(SystemPath, 0, PAGE_SIZE);
//初始化路径
RtlStringCbPrintfW(SystemPath, PAGE_SIZE, L"\\??\\%s\\System32\\ntdll.dll", SharedUserData->NtSystemRoot);
DbgPrint("SystemPath -> [%ws] \r\n", SystemPath);
return SystemPath;
}
PUCHAR FileMaping(PWCHAR SystemPath)
{
//Initialize UnicodeString
UNICODE_STRING FileName = { 0 };
RtlInitUnicodeString(&FileName, SystemPath);
//Initialize ObjectAttribute
OBJECT_ATTRIBUTES objectFile = { 0 };
InitializeObjectAttributes(&objectFile, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
//CreateFile
HANDLE hFile = NULL;
IO_STACK_LOCATION iostacklocation = { 0 };
NTSTATUS ntstatus = ZwCreateFile(&hFile, GENERIC_READ, &objectFile, &iostacklocation, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, NULL);
if (!NT_SUCCESS(ntstatus))
{
DbgPrint("FileMaping ZwCreateFile Filed \r\n");
return NULL;
}
//Create Section
OBJECT_ATTRIBUTES objectSection = { 0 };
InitializeObjectAttributes(&objectSection, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
PVOID pSection = NULL;
LARGE_INTEGER InputMaximumSize = { 0 };
ntstatus = MmCreateSection(&pSection, SECTION_ALL_ACCESS, &objectSection, &InputMaximumSize, PAGE_EXECUTE_READWRITE, 0x1000000, hFile, NULL);
if (!NT_SUCCESS(ntstatus))
{
DbgPrint("FileMaping MmCreateSection Filed \r\n");
ZwClose(hFile);
return NULL;
}
PVOID pMapBase = NULL;
SIZE_T ViewSize = 0;
ntstatus = MmMapViewInSystemSpace(pSection, &pMapBase, &ViewSize);
ObDereferenceObject(pSection);
ZwClose(hFile);
if (NT_SUCCESS(ntstatus))
{
return pMapBase;
}
return NULL;
}
VOID UnFileMaping(PVOID pImage)
{
if (pImage)
{
MmUnmapViewInSystemSpace(pImage);
}
}
ULONG64 GetFuntionAddressByExportTableName(PUCHAR ImageBuffer, PUCHAR FunctionName)
{
//Headers
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ImageBuffer;
if (*(PUSHORT)pDos != IMAGE_DOS_SIGNATURE)
{
DbgPrint("Not PeFile \r\n");
return NULL;
}
PIMAGE_NT_HEADERS pNts = (PIMAGE_NT_HEADERS)(ImageBuffer + pDos->e_lfanew);
if (*(PULONG)pNts != IMAGE_NT_SIGNATURE)
{
DbgPrint("Not PeFile \r\n");
return NULL;
}
PIMAGE_FILE_HEADER pFil = (PIMAGE_FILE_HEADER)((ULONG)pNts + 0x4);
PIMAGE_OPTIONAL_HEADER pOpt = (PIMAGE_OPTIONAL_HEADER)((ULONG)pFil + IMAGE_SIZEOF_FILE_HEADER);
PIMAGE_EXPORT_DIRECTORY pExp = (PIMAGE_EXPORT_DIRECTORY)(ImageBuffer + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
//遍历导出表
ULONG64 FunctionAddr = NULL;
for (int i = 0; i < pExp->NumberOfNames; i++)
{
PULONG pAddressOfFuntion = ImageBuffer + pExp->AddressOfFunctions;
PULONG pAddressOfNames = ImageBuffer + pExp->AddressOfNames;
PUSHORT pAddressOfOrd = ImageBuffer + pExp->AddressOfNameOrdinals;
PUCHAR CurrentFunctionName = ImageBuffer + pAddressOfNames[i];
ULONG uIndex = -1;
if (strcmp(CurrentFunctionName, FunctionName) == 0)
{
uIndex = pAddressOfOrd[i];
}
if (uIndex != -1)
{
FunctionAddr = ImageBuffer + pAddressOfFuntion[uIndex];
break;
}
}
if (FunctionAddr)
{
DbgPrint("FindFunctionAddress FunName[%s] Addr[%p] \r\n", FunctionName, FunctionAddr);
}
else
{
DbgPrint("FindFunctionAddress Error FunName[%s] \r\n", FunctionName);
}
return FunctionAddr;
}
BOOLEAN SSDT_Init()
{
if (G_MapNtdll)
{
return TRUE;
}
PWCHAR szPath = GetSystemFullPath();
if (szPath == NULL)
{
return FALSE;
}
G_MapNtdll = FileMaping(szPath);
if (G_MapNtdll == NULL)
{
ExFreePool(szPath);
return FALSE;
}
ExFreePool(szPath);
return TRUE;
}
VOID SSDT_Destroy()
{
if (G_MapNtdll)
{
UnFileMaping(G_MapNtdll);
G_MapNtdll = NULL;
}
}
ULONG SSDT_GetFunIndex(PUCHAR szFunctionName)
{
//获取函数地址
PUCHAR pFunAddr = (PUCHAR)GetFuntionAddressByExportTableName(G_MapNtdll, szFunctionName);
if (pFunAddr == NULL)
{
return -1;
}
//获取函数系统服务号
return *(PULONG)(pFunAddr + 1);
}
ULONG_PTR SSDT_Hook(PUCHAR szFunctionName, ULONG_PTR FunctionAddr)
{
ULONG uIndex = SSDT_GetFunIndex(szFunctionName);
if (uIndex == -1)
{
return NULL;
}
//备份旧的地址
ULONG OldFuncAddr = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[uIndex];
//替换SSDT表中函数
ULONG cr0 = wpOff();
KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[uIndex] = FunctionAddr;
wpOn(cr0);
return OldFuncAddr;
}