1.内核重载:

  • image.png

    2.内核重载的步骤

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

image.png

注意:

image.png

  1. #include <ntifs.h>
  2. #include <ntimage.h>
  3. PDRIVER_OBJECT g_pDriver = NULL;
  4. #pragma pack(1)
  5. typedef struct _ServiceDesriptorEntry
  6. {
  7. ULONG *ServiceTableBase; // 服务表基址
  8. ULONG *ServiceCounterTableBase; // 计数表基址
  9. ULONG NumberOfServices; // 表中项的个数
  10. UCHAR *ParamTableBase; // 参数表基址
  11. }SSDTEntry, *PSSDTEntry;
  12. #pragma pack()
  13. // 导入SSDT
  14. NTSYSAPI SSDTEntry KeServiceDescriptorTable;
  15. PSSDTEntry g_pNewSSDT;//新的SSDT
  16. ULONG g_JmpPoint;
  17. PUCHAR pHookPoint;
  18. HANDLE KernelCreateFile(
  19. IN PUNICODE_STRING pstrFile, // 文件路径符号链接
  20. IN BOOLEAN bIsDir) // 是否为文件夹
  21. {
  22. HANDLE hFile = NULL;
  23. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  24. IO_STATUS_BLOCK StatusBlock = { 0 };
  25. ULONG ulShareAccess =
  26. FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
  27. ULONG ulCreateOpt =
  28. FILE_SYNCHRONOUS_IO_NONALERT;
  29. // 1. 初始化OBJECT_ATTRIBUTES的内容
  30. OBJECT_ATTRIBUTES objAttrib = { 0 };
  31. ULONG ulAttributes =
  32. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE;
  33. InitializeObjectAttributes(
  34. &objAttrib, // 返回初始化完毕的结构体
  35. pstrFile, // 文件对象名称
  36. ulAttributes, // 对象属性
  37. NULL, NULL); // 一般为NULL
  38. // 2. 创建文件对象
  39. ulCreateOpt |= bIsDir ?
  40. FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE;
  41. Status = ZwCreateFile(
  42. &hFile, // 返回文件句柄
  43. GENERIC_ALL, // 文件操作描述
  44. &objAttrib, // OBJECT_ATTRIBUTES
  45. &StatusBlock, // 接受函数的操作结果
  46. 0, // 初始文件大小
  47. FILE_ATTRIBUTE_NORMAL, // 新建文件的属性
  48. ulShareAccess, // 文件共享方式
  49. FILE_OPEN_IF, // 文件存在则打开不存在则创建
  50. ulCreateOpt, // 打开操作的附加标志位
  51. NULL, // 扩展属性区
  52. 0); // 扩展属性区长度
  53. if (!NT_SUCCESS(Status))
  54. return (HANDLE)-1;
  55. return hFile;
  56. }
  57. ULONG64 KernelGetFileSize(IN HANDLE hfile)
  58. {
  59. // 查询文件状态
  60. IO_STATUS_BLOCK StatusBlock = { 0 };
  61. FILE_STANDARD_INFORMATION fsi = { 0 };
  62. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  63. Status = ZwQueryInformationFile(
  64. hfile, // 文件句柄
  65. &StatusBlock, // 接受函数的操作结果
  66. &fsi, // 根据最后一个参数的类型输出相关信息
  67. sizeof(FILE_STANDARD_INFORMATION),
  68. FileStandardInformation);
  69. if (!NT_SUCCESS(Status))
  70. return 0;
  71. return fsi.EndOfFile.QuadPart;
  72. }
  73. ULONG64 KernelReadFile(
  74. IN HANDLE hfile, // 文件句柄
  75. IN PLARGE_INTEGER Offset, // 从哪里开始读取
  76. IN ULONG ulLength, // 读取多少字节
  77. OUT PVOID pBuffer) // 保存数据的缓存
  78. {
  79. // 1. 读取文件
  80. IO_STATUS_BLOCK StatusBlock = { 0 };
  81. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  82. Status = ZwReadFile(
  83. hfile, // 文件句柄
  84. NULL, // 信号状态(一般为NULL)
  85. NULL, NULL, // 保留
  86. &StatusBlock, // 接受函数的操作结果
  87. pBuffer, // 保存读取数据的缓存
  88. ulLength, // 想要读取的长度
  89. Offset, // 读取的起始偏移
  90. NULL); // 一般为NULL
  91. if (!NT_SUCCESS(Status)) return 0;
  92. // 2. 返回实际读取的长度
  93. return StatusBlock.Information;
  94. }
  95. typedef struct _LDR_DATA_TABLE_ENTRY {
  96. LIST_ENTRY InLoadOrderLinks; //双向链表
  97. LIST_ENTRY InMemoryOrderLinks;
  98. LIST_ENTRY InInitializationOrderLinks;
  99. PVOID DllBase;
  100. PVOID EntryPoint;
  101. ULONG SizeOfImage;
  102. UNICODE_STRING FullDllName;
  103. UNICODE_STRING BaseDllName;
  104. ULONG Flags;
  105. USHORT LoadCount;
  106. USHORT TlsIndex;
  107. union {
  108. LIST_ENTRY HashLinks;
  109. struct {
  110. PVOID SectionPointer;
  111. ULONG CheckSum;
  112. };
  113. };
  114. union {
  115. struct {
  116. ULONG TimeDateStamp;
  117. };
  118. struct {
  119. PVOID LoadedImports;
  120. };
  121. };
  122. } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
  123. void * SearchMemory(char * buf, int BufLenth, char * Mem, int MaxLenth)
  124. {
  125. int MemIndex = 0;
  126. int BufIndex = 0;
  127. for (MemIndex = 0; MemIndex < MaxLenth; MemIndex++)
  128. {
  129. BufIndex = 0;
  130. if (Mem[MemIndex] == buf[BufIndex] || buf[BufIndex] == '?')
  131. {
  132. int MemIndexTemp = MemIndex;
  133. do
  134. {
  135. MemIndexTemp++;
  136. BufIndex++;
  137. } while ((Mem[MemIndexTemp] == buf[BufIndex] || buf[BufIndex] == '?') && BufIndex < BufLenth);
  138. if (BufIndex == BufLenth)
  139. {
  140. return Mem + MemIndex;
  141. }
  142. }
  143. }
  144. return 0;
  145. }
  146. void OffProtected()
  147. {
  148. __asm { //关闭内存保护
  149. cli;
  150. push eax;
  151. mov eax, cr0;
  152. and eax, ~0x10000;
  153. mov cr0, eax;
  154. pop eax;
  155. }
  156. }
  157. void OnProtected()
  158. {
  159. __asm { //恢复内存保护
  160. push eax;
  161. mov eax, cr0;
  162. or eax, 0x10000;
  163. mov cr0, eax;
  164. pop eax;
  165. sti;
  166. }
  167. }
  168. ULONG32 MyGetModuleHandle(PUNICODE_STRING pModuleName)
  169. {
  170. PLDR_DATA_TABLE_ENTRY pLdr =
  171. (PLDR_DATA_TABLE_ENTRY)g_pDriver->DriverSection;
  172. LIST_ENTRY *pTemp = &pLdr->InLoadOrderLinks;
  173. do
  174. {
  175. PLDR_DATA_TABLE_ENTRY pDriverInfo =
  176. (PLDR_DATA_TABLE_ENTRY)pTemp;
  177. if (RtlCompareUnicodeString(pModuleName, &pDriverInfo->BaseDllName, FALSE) == 0)
  178. {
  179. return pDriverInfo->DllBase;
  180. }
  181. pTemp = pTemp->Blink;
  182. } while (pTemp != &pLdr->InLoadOrderLinks);
  183. return 0;
  184. }
  185. //windows根据不同的环境,会加载不同的内核文件
  186. //单核,开了PAE
  187. //单核,没开PAE
  188. //多核,开了PAE
  189. //多核,没开PAE
  190. void ReadKernelToBuf(PWCHAR pPath, PUCHAR* pBuf)
  191. {
  192. //-----------------------------------------
  193. UNICODE_STRING pKernelPath; //内核文件路径
  194. HANDLE hFile = 0; //内核文件句柄
  195. LARGE_INTEGER Offset = { 0 };//读取的偏移值
  196. //-----------------------------------------
  197. //1 打开文件
  198. RtlInitUnicodeString(
  199. &pKernelPath,
  200. pPath);
  201. hFile = KernelCreateFile(&pKernelPath, FALSE);
  202. //2 获取文件大小
  203. ULONG64 ulFileSize = KernelGetFileSize(hFile);
  204. *pBuf = ExAllocatePool(NonPagedPool, ulFileSize);
  205. RtlZeroMemory(*pBuf, ulFileSize);
  206. //3 读取文件到内存
  207. KernelReadFile(hFile, &Offset, ulFileSize, *pBuf);
  208. }
  209. void ZKKernel(PUCHAR * pZkBUf, PUCHAR buf)
  210. {
  211. _asm int
  212. //1 获得DOS头,继而获得NT头,再获得扩展头
  213. PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buf;
  214. PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buf);
  215. ULONG uZkSize = pNt->OptionalHeader.SizeOfImage;
  216. //2 申请空间
  217. *pZkBUf = ExAllocatePool(NonPagedPool, uZkSize);
  218. RtlZeroMemory(*pZkBUf, uZkSize);
  219. //3 开始展开
  220. //3.1 先拷贝头部
  221. memcpy(*pZkBUf, buf, pNt->OptionalHeader.SizeOfHeaders);
  222. //3.2再拷贝区段
  223. PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
  224. for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)
  225. {
  226. memcpy(
  227. *pZkBUf + pSection[i].VirtualAddress,//本区段内存中的起始位置
  228. buf + pSection[i].PointerToRawData, //本区段在文件中的位置
  229. pSection[i].Misc.VirtualSize //本区段的大小
  230. );
  231. }
  232. }
  233. typedef struct _TYPE {
  234. USHORT Offset : 12;
  235. USHORT Type : 4;
  236. }TYPE, *PTYPE;
  237. void FixReloc(PUCHAR ZkBuf, PUCHAR OldBase)
  238. {
  239. //1 获得DOS头,继而获得NT头,再获得扩展头
  240. PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)ZkBuf;
  241. PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + ZkBuf);
  242. //2 获得重定位表
  243. PIMAGE_DATA_DIRECTORY pRelocDir = (pNt->OptionalHeader.DataDirectory + 5);
  244. PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)
  245. (pRelocDir->VirtualAddress + ZkBuf);
  246. //2.5 得到一个老内核与默认基址间的一个差值
  247. ULONG uOffset = (ULONG)OldBase - pNt->OptionalHeader.ImageBase;
  248. //3 开始修复重定位
  249. while (pReloc->SizeOfBlock != 0)
  250. {
  251. ULONG uCount = (pReloc->SizeOfBlock - 8) / 2;//本0x1000内,有多少需要重定位的地方
  252. ULONG uBaseRva = pReloc->VirtualAddress; //本0x1000的起始位置
  253. PTYPE pType = (PTYPE)(pReloc + 1);
  254. for (int i = 0; i < uCount; i++)
  255. {
  256. if (pType->Type == 3)
  257. {
  258. PULONG pRelocPoint = (uBaseRva + pType->Offset + ZkBuf);
  259. //重定位后的地址 - 新基址 = 没重定位的地址 - 默认基址
  260. //所以:重定位后的地址 = 新基址 - 默认基址 + 没重定位的地址
  261. *pRelocPoint = uOffset + *pRelocPoint;
  262. }
  263. pType++;
  264. }
  265. pReloc = (PIMAGE_BASE_RELOCATION)((ULONG)pReloc + pReloc->SizeOfBlock);
  266. }
  267. }
  268. void FixSSDT(PUCHAR pZKBuf, PUCHAR OldBase)
  269. {
  270. //新内核某位置1 - 新内核基址 = 老内核某位置1 - 老内核基址;
  271. //新内核某位置1 = 新内核基址 - 老内核基址 + 老内核某位置1;
  272. LONG Offset = (ULONG)pZKBuf - (ULONG)OldBase;
  273. //1 得到新内核中的SSDT
  274. g_pNewSSDT = (PSSDTEntry)((LONG)&KeServiceDescriptorTable + Offset);
  275. //2 填充系统服务个数
  276. g_pNewSSDT->NumberOfServices = KeServiceDescriptorTable.NumberOfServices;
  277. //3 填充SSDT表
  278. g_pNewSSDT->ServiceTableBase = (ULONG *)((PUCHAR)KeServiceDescriptorTable.ServiceTableBase + Offset);
  279. //让所有的SSDT中保存的函数地址,都指向新内核
  280. for (int i = 0; i < g_pNewSSDT->NumberOfServices; i++)
  281. {
  282. g_pNewSSDT->ServiceTableBase[i] = g_pNewSSDT->ServiceTableBase[i] + Offset;
  283. }
  284. //4 填充参数表
  285. g_pNewSSDT->ParamTableBase = (PULONG)((PUCHAR)KeServiceDescriptorTable.ParamTableBase + Offset);
  286. memcpy(g_pNewSSDT->ParamTableBase,
  287. KeServiceDescriptorTable.ParamTableBase,
  288. g_pNewSSDT->NumberOfServices
  289. );
  290. }
  291. ULONG GetKiFastCallEntry()
  292. {
  293. ULONG uAddress = 0;
  294. _asm
  295. {
  296. push eax;
  297. push ecx;
  298. mov ecx, 0x176;
  299. rdmsr;
  300. mov uAddress, eax;
  301. pop ecx;
  302. pop eax;
  303. }
  304. return uAddress;
  305. }
  306. ULONG FilterFun(ULONG SSdtBase, PULONG OldFun, ULONG Id)
  307. {
  308. //如果相等,说明调用的是SSDT中的函数
  309. if (SSdtBase == (ULONG)KeServiceDescriptorTable.ServiceTableBase)
  310. {
  311. //使用思路:
  312. //假如进程是OD,并且函数调用是190号,就走新内核中的函数,这样通过hookOpenProcess就无法拦住OD了。
  313. return g_pNewSSDT->ServiceTableBase[Id];
  314. }
  315. return OldFun;
  316. }
  317. _declspec(naked)void MyHookFun()
  318. {
  319. //eax 里面是调用号,edx里面是老函数地址,edi里面是SSDT基址
  320. _asm {
  321. pushad;
  322. pushfd;
  323. push eax;
  324. push edx;
  325. push edi;
  326. call FilterFun;
  327. mov dword ptr ds : [esp + 0x18], eax;
  328. popfd;
  329. popad;
  330. sub esp, ecx
  331. shr ecx, 2
  332. jmp g_JmpPoint
  333. }
  334. }
  335. UCHAR Old_Code[5] = { 0 };
  336. void OnHookKiFastCallEntry()
  337. {
  338. char buf[] = { 0x2b, 0xe1, 0xc1, 0xe9, 0x02 };
  339. ULONG KiFastCallEntryAdd = GetKiFastCallEntry();
  340. pHookPoint = SearchMemory(buf, 5, (char*)KiFastCallEntryAdd, 0x200);
  341. g_JmpPoint = (ULONG)(pHookPoint + 5);
  342. memcpy(Old_Code, pHookPoint, 5);
  343. OffProtected();
  344. pHookPoint[0] = 0xE9;
  345. *(ULONG*)(&pHookPoint[1]) = (ULONG)MyHookFun - (ULONG)pHookPoint - 5;
  346. OnProtected();
  347. }
  348. void KernelReload()
  349. {
  350. PUCHAR pBuf = NULL;
  351. PUCHAR pZKBuf = NULL;
  352. UNICODE_STRING KernelName;
  353. //1 首先把内核文件读取到内存里
  354. //ReadKernelToBuf(L"\\??\\C:\\Windows\\System32\\ntoskrnl.exe", &pBuf);
  355. ReadKernelToBuf(L"\\??\\C:\\Windows\\System32\\ntoskrnl.exe", &pBuf);
  356. //2 把读到内存中的内核给展开成0x1000对齐
  357. ZKKernel(&pZKBuf, pBuf);
  358. ExFreePool(pBuf);
  359. //3 修复新内核的重定位
  360. RtlInitUnicodeString(&KernelName, L"ntoskrnl.exe");
  361. ULONG32 uBase = MyGetModuleHandle(&KernelName);
  362. FixReloc(pZKBuf, (PUCHAR)uBase);
  363. //4 修复新的SSDT表
  364. FixSSDT(pZKBuf, (PUCHAR)uBase);
  365. //5 Hook掉KiFastCallEntry,在自己的Hook函数中判断应该走新内核还是老内核
  366. OnHookKiFastCallEntry();
  367. }
  368. void EnumDirver1(PDRIVER_OBJECT pDriver)
  369. {
  370. PLDR_DATA_TABLE_ENTRY pLdr =
  371. (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
  372. LIST_ENTRY *pTemp = &pLdr->InLoadOrderLinks;
  373. do
  374. {
  375. PLDR_DATA_TABLE_ENTRY pDriverInfo =
  376. (PLDR_DATA_TABLE_ENTRY)pTemp;
  377. KdPrint(("%wZ\n", &pDriverInfo->BaseDllName));
  378. pTemp = pTemp->Blink;
  379. } while (pTemp != &pLdr->InLoadOrderLinks);
  380. }
  381. void UnHook()
  382. {
  383. OffProtected();
  384. memcpy(pHookPoint, Old_Code, 5);
  385. OnProtected();
  386. }
  387. VOID DriverUnload(PDRIVER_OBJECT pDriver)
  388. {
  389. pDriver;
  390. UnHook();
  391. KdPrint(("Leave"));
  392. }
  393. NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath)
  394. {
  395. UNREFERENCED_PARAMETER(pPath);
  396. DbgBreakPoint();
  397. g_pDriver = pDriver;
  398. //EnumDirver1(pDriver);
  399. KernelReload();
  400. pDriver->DriverUnload = DriverUnload;
  401. return STATUS_SUCCESS;
  402. }