1.内核重载:
2.内核重载的步骤
- 将内核文件加载到内存中
- 将文件展开到内存中的状态
- 通过LDR链获取ntkrnlpa.exe的基址
- 修复新内核的重定位
- 修复新的SSDT表
- 拦截系统调用,HOOK KiFastCallEntry,让自己的HOOK函数判断是走新内核还是老内核

注意:

#include <ntifs.h>#include <ntimage.h>PDRIVER_OBJECT g_pDriver = NULL;#pragma pack(1)typedef struct _ServiceDesriptorEntry{ ULONG *ServiceTableBase; // 服务表基址 ULONG *ServiceCounterTableBase; // 计数表基址 ULONG NumberOfServices; // 表中项的个数 UCHAR *ParamTableBase; // 参数表基址}SSDTEntry, *PSSDTEntry;#pragma pack()// 导入SSDTNTSYSAPI SSDTEntry KeServiceDescriptorTable;PSSDTEntry g_pNewSSDT;//新的SSDTULONG g_JmpPoint;PUCHAR pHookPoint;HANDLE KernelCreateFile( IN PUNICODE_STRING pstrFile, // 文件路径符号链接 IN BOOLEAN bIsDir) // 是否为文件夹{ HANDLE hFile = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; IO_STATUS_BLOCK StatusBlock = { 0 }; ULONG ulShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; ULONG ulCreateOpt = FILE_SYNCHRONOUS_IO_NONALERT; // 1. 初始化OBJECT_ATTRIBUTES的内容 OBJECT_ATTRIBUTES objAttrib = { 0 }; ULONG ulAttributes = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE; InitializeObjectAttributes( &objAttrib, // 返回初始化完毕的结构体 pstrFile, // 文件对象名称 ulAttributes, // 对象属性 NULL, NULL); // 一般为NULL // 2. 创建文件对象 ulCreateOpt |= bIsDir ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE; Status = ZwCreateFile( &hFile, // 返回文件句柄 GENERIC_ALL, // 文件操作描述 &objAttrib, // OBJECT_ATTRIBUTES &StatusBlock, // 接受函数的操作结果 0, // 初始文件大小 FILE_ATTRIBUTE_NORMAL, // 新建文件的属性 ulShareAccess, // 文件共享方式 FILE_OPEN_IF, // 文件存在则打开不存在则创建 ulCreateOpt, // 打开操作的附加标志位 NULL, // 扩展属性区 0); // 扩展属性区长度 if (!NT_SUCCESS(Status)) return (HANDLE)-1; return hFile;}ULONG64 KernelGetFileSize(IN HANDLE hfile){ // 查询文件状态 IO_STATUS_BLOCK StatusBlock = { 0 }; FILE_STANDARD_INFORMATION fsi = { 0 }; NTSTATUS Status = STATUS_UNSUCCESSFUL; Status = ZwQueryInformationFile( hfile, // 文件句柄 &StatusBlock, // 接受函数的操作结果 &fsi, // 根据最后一个参数的类型输出相关信息 sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation); if (!NT_SUCCESS(Status)) return 0; return fsi.EndOfFile.QuadPart;}ULONG64 KernelReadFile( IN HANDLE hfile, // 文件句柄 IN PLARGE_INTEGER Offset, // 从哪里开始读取 IN ULONG ulLength, // 读取多少字节 OUT PVOID pBuffer) // 保存数据的缓存{ // 1. 读取文件 IO_STATUS_BLOCK StatusBlock = { 0 }; NTSTATUS Status = STATUS_UNSUCCESSFUL; Status = ZwReadFile( hfile, // 文件句柄 NULL, // 信号状态(一般为NULL) NULL, NULL, // 保留 &StatusBlock, // 接受函数的操作结果 pBuffer, // 保存读取数据的缓存 ulLength, // 想要读取的长度 Offset, // 读取的起始偏移 NULL); // 一般为NULL if (!NT_SUCCESS(Status)) return 0; // 2. 返回实际读取的长度 return StatusBlock.Information;}typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; //双向链表 LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { struct { ULONG TimeDateStamp; }; struct { PVOID LoadedImports; }; };} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;void * SearchMemory(char * buf, int BufLenth, char * Mem, int MaxLenth){ int MemIndex = 0; int BufIndex = 0; for (MemIndex = 0; MemIndex < MaxLenth; MemIndex++) { BufIndex = 0; if (Mem[MemIndex] == buf[BufIndex] || buf[BufIndex] == '?') { int MemIndexTemp = MemIndex; do { MemIndexTemp++; BufIndex++; } while ((Mem[MemIndexTemp] == buf[BufIndex] || buf[BufIndex] == '?') && BufIndex < BufLenth); if (BufIndex == BufLenth) { return Mem + MemIndex; } } } return 0;}void OffProtected(){ __asm { //关闭内存保护 cli; push eax; mov eax, cr0; and eax, ~0x10000; mov cr0, eax; pop eax; }}void OnProtected(){ __asm { //恢复内存保护 push eax; mov eax, cr0; or eax, 0x10000; mov cr0, eax; pop eax; sti; }}ULONG32 MyGetModuleHandle(PUNICODE_STRING pModuleName){ PLDR_DATA_TABLE_ENTRY pLdr = (PLDR_DATA_TABLE_ENTRY)g_pDriver->DriverSection; LIST_ENTRY *pTemp = &pLdr->InLoadOrderLinks; do { PLDR_DATA_TABLE_ENTRY pDriverInfo = (PLDR_DATA_TABLE_ENTRY)pTemp; if (RtlCompareUnicodeString(pModuleName, &pDriverInfo->BaseDllName, FALSE) == 0) { return pDriverInfo->DllBase; } pTemp = pTemp->Blink; } while (pTemp != &pLdr->InLoadOrderLinks); return 0;}//windows根据不同的环境,会加载不同的内核文件//单核,开了PAE //单核,没开PAE//多核,开了PAE//多核,没开PAEvoid ReadKernelToBuf(PWCHAR pPath, PUCHAR* pBuf){ //----------------------------------------- UNICODE_STRING pKernelPath; //内核文件路径 HANDLE hFile = 0; //内核文件句柄 LARGE_INTEGER Offset = { 0 };//读取的偏移值 //----------------------------------------- //1 打开文件 RtlInitUnicodeString( &pKernelPath, pPath); hFile = KernelCreateFile(&pKernelPath, FALSE); //2 获取文件大小 ULONG64 ulFileSize = KernelGetFileSize(hFile); *pBuf = ExAllocatePool(NonPagedPool, ulFileSize); RtlZeroMemory(*pBuf, ulFileSize); //3 读取文件到内存 KernelReadFile(hFile, &Offset, ulFileSize, *pBuf);}void ZKKernel(PUCHAR * pZkBUf, PUCHAR buf){ _asm int //1 获得DOS头,继而获得NT头,再获得扩展头 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buf; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buf); ULONG uZkSize = pNt->OptionalHeader.SizeOfImage; //2 申请空间 *pZkBUf = ExAllocatePool(NonPagedPool, uZkSize); RtlZeroMemory(*pZkBUf, uZkSize); //3 开始展开 //3.1 先拷贝头部 memcpy(*pZkBUf, buf, pNt->OptionalHeader.SizeOfHeaders); //3.2再拷贝区段 PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt); for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) { memcpy( *pZkBUf + pSection[i].VirtualAddress,//本区段内存中的起始位置 buf + pSection[i].PointerToRawData, //本区段在文件中的位置 pSection[i].Misc.VirtualSize //本区段的大小 ); }}typedef struct _TYPE { USHORT Offset : 12; USHORT Type : 4;}TYPE, *PTYPE;void FixReloc(PUCHAR ZkBuf, PUCHAR OldBase){ //1 获得DOS头,继而获得NT头,再获得扩展头 PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ZkBuf; PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + ZkBuf); //2 获得重定位表 PIMAGE_DATA_DIRECTORY pRelocDir = (pNt->OptionalHeader.DataDirectory + 5); PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION) (pRelocDir->VirtualAddress + ZkBuf); //2.5 得到一个老内核与默认基址间的一个差值 ULONG uOffset = (ULONG)OldBase - pNt->OptionalHeader.ImageBase; //3 开始修复重定位 while (pReloc->SizeOfBlock != 0) { ULONG uCount = (pReloc->SizeOfBlock - 8) / 2;//本0x1000内,有多少需要重定位的地方 ULONG uBaseRva = pReloc->VirtualAddress; //本0x1000的起始位置 PTYPE pType = (PTYPE)(pReloc + 1); for (int i = 0; i < uCount; i++) { if (pType->Type == 3) { PULONG pRelocPoint = (uBaseRva + pType->Offset + ZkBuf); //重定位后的地址 - 新基址 = 没重定位的地址 - 默认基址 //所以:重定位后的地址 = 新基址 - 默认基址 + 没重定位的地址 *pRelocPoint = uOffset + *pRelocPoint; } pType++; } pReloc = (PIMAGE_BASE_RELOCATION)((ULONG)pReloc + pReloc->SizeOfBlock); }}void FixSSDT(PUCHAR pZKBuf, PUCHAR OldBase){ //新内核某位置1 - 新内核基址 = 老内核某位置1 - 老内核基址; //新内核某位置1 = 新内核基址 - 老内核基址 + 老内核某位置1; LONG Offset = (ULONG)pZKBuf - (ULONG)OldBase; //1 得到新内核中的SSDT g_pNewSSDT = (PSSDTEntry)((LONG)&KeServiceDescriptorTable + Offset); //2 填充系统服务个数 g_pNewSSDT->NumberOfServices = KeServiceDescriptorTable.NumberOfServices; //3 填充SSDT表 g_pNewSSDT->ServiceTableBase = (ULONG *)((PUCHAR)KeServiceDescriptorTable.ServiceTableBase + Offset); //让所有的SSDT中保存的函数地址,都指向新内核 for (int i = 0; i < g_pNewSSDT->NumberOfServices; i++) { g_pNewSSDT->ServiceTableBase[i] = g_pNewSSDT->ServiceTableBase[i] + Offset; } //4 填充参数表 g_pNewSSDT->ParamTableBase = (PULONG)((PUCHAR)KeServiceDescriptorTable.ParamTableBase + Offset); memcpy(g_pNewSSDT->ParamTableBase, KeServiceDescriptorTable.ParamTableBase, g_pNewSSDT->NumberOfServices );}ULONG GetKiFastCallEntry(){ ULONG uAddress = 0; _asm { push eax; push ecx; mov ecx, 0x176; rdmsr; mov uAddress, eax; pop ecx; pop eax; } return uAddress;}ULONG FilterFun(ULONG SSdtBase, PULONG OldFun, ULONG Id){ //如果相等,说明调用的是SSDT中的函数 if (SSdtBase == (ULONG)KeServiceDescriptorTable.ServiceTableBase) { //使用思路: //假如进程是OD,并且函数调用是190号,就走新内核中的函数,这样通过hookOpenProcess就无法拦住OD了。 return g_pNewSSDT->ServiceTableBase[Id]; } return OldFun;}_declspec(naked)void MyHookFun(){ //eax 里面是调用号,edx里面是老函数地址,edi里面是SSDT基址 _asm { pushad; pushfd; push eax; push edx; push edi; call FilterFun; mov dword ptr ds : [esp + 0x18], eax; popfd; popad; sub esp, ecx shr ecx, 2 jmp g_JmpPoint }}UCHAR Old_Code[5] = { 0 };void OnHookKiFastCallEntry(){ char buf[] = { 0x2b, 0xe1, 0xc1, 0xe9, 0x02 }; ULONG KiFastCallEntryAdd = GetKiFastCallEntry(); pHookPoint = SearchMemory(buf, 5, (char*)KiFastCallEntryAdd, 0x200); g_JmpPoint = (ULONG)(pHookPoint + 5); memcpy(Old_Code, pHookPoint, 5); OffProtected(); pHookPoint[0] = 0xE9; *(ULONG*)(&pHookPoint[1]) = (ULONG)MyHookFun - (ULONG)pHookPoint - 5; OnProtected();}void KernelReload(){ PUCHAR pBuf = NULL; PUCHAR pZKBuf = NULL; UNICODE_STRING KernelName; //1 首先把内核文件读取到内存里 //ReadKernelToBuf(L"\\??\\C:\\Windows\\System32\\ntoskrnl.exe", &pBuf); ReadKernelToBuf(L"\\??\\C:\\Windows\\System32\\ntoskrnl.exe", &pBuf); //2 把读到内存中的内核给展开成0x1000对齐 ZKKernel(&pZKBuf, pBuf); ExFreePool(pBuf); //3 修复新内核的重定位 RtlInitUnicodeString(&KernelName, L"ntoskrnl.exe"); ULONG32 uBase = MyGetModuleHandle(&KernelName); FixReloc(pZKBuf, (PUCHAR)uBase); //4 修复新的SSDT表 FixSSDT(pZKBuf, (PUCHAR)uBase); //5 Hook掉KiFastCallEntry,在自己的Hook函数中判断应该走新内核还是老内核 OnHookKiFastCallEntry();}void EnumDirver1(PDRIVER_OBJECT pDriver){ PLDR_DATA_TABLE_ENTRY pLdr = (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection; LIST_ENTRY *pTemp = &pLdr->InLoadOrderLinks; do { PLDR_DATA_TABLE_ENTRY pDriverInfo = (PLDR_DATA_TABLE_ENTRY)pTemp; KdPrint(("%wZ\n", &pDriverInfo->BaseDllName)); pTemp = pTemp->Blink; } while (pTemp != &pLdr->InLoadOrderLinks);}void UnHook(){ OffProtected(); memcpy(pHookPoint, Old_Code, 5); OnProtected();}VOID DriverUnload(PDRIVER_OBJECT pDriver){ pDriver; UnHook(); KdPrint(("Leave"));}NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath){ UNREFERENCED_PARAMETER(pPath); DbgBreakPoint(); g_pDriver = pDriver; //EnumDirver1(pDriver); KernelReload(); pDriver->DriverUnload = DriverUnload; return STATUS_SUCCESS;}