Advertisement
G0dR4p3

Certspy.cpp

Sep 28th, 2020
403
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.14 KB | None | 0 0
  1. /*++
  2.  
  3. Copyright (c) Microsoft Corporation. All right reserved.
  4.  
  5. Module Name:
  6.  
  7. certspy.cpp
  8.  
  9. Abstract:
  10.  
  11. This module contains main entry-point of certspy debugger extension.
  12.  
  13. --*/
  14.  
  15. #include "certspyp.h"
  16.  
  17. //
  18. // Global notification session for module load notification from xbdm
  19. //
  20.  
  21. PDMN_SESSION DmSession;
  22.  
  23. //
  24. // Define bitmap that contains flag to intercept kernel thunk during startup
  25. //
  26.  
  27. BYTE KernelExportFlags[KernelExportSize];
  28.  
  29. //
  30. // Define array that contains intercepted kernel thunk index by ordinal
  31. //
  32.  
  33. PORDINAL_THUNK InterceptThunks[KernelExportSize];
  34.  
  35. //
  36. // Define array that contains image thunk data index by ordinal
  37. //
  38.  
  39. PIMAGE_THUNK_DATA ImageThunks[KernelExportSize];
  40.  
  41. //
  42. // Flag indicates that the XBE has been loaded by the kernel
  43. //
  44.  
  45. BOOLEAN CertSpyXbeHasBeenLoaded;
  46.  
  47. VOID
  48. FASTCALL
  49. CertSpyLogKernelCall(
  50. IN ULONG OrdinalNumber,
  51. IN PULONG StackFrame
  52. )
  53. {
  54. PUCHAR Params;
  55. BOOLEAN DecodeParams = FALSE;
  56. POBJECT_STRING ObjectString;
  57. const KERNEL_EXPORT_API* Api = &KernelExports[OrdinalNumber];
  58.  
  59. Params = (PUCHAR)Api->Parameters;
  60.  
  61. Params = (PUCHAR)Api->Parameters;
  62. StackFrame++; // Skip over return address
  63.  
  64. const SIZE_T BufferSize = 1024;
  65. SIZE_T BytesLeft = BufferSize;
  66. SIZE_T CharCount;
  67. CHAR TextBuffer[BufferSize];
  68. CHAR* Buffer = TextBuffer;
  69.  
  70. CharCount = _snprintf(Buffer, BytesLeft, "certspy!kcall %d", OrdinalNumber);
  71.  
  72. Buffer += CharCount;
  73. BytesLeft -= CharCount;
  74.  
  75. while (*Params && BytesLeft) {
  76.  
  77. CharCount = 0;
  78.  
  79. switch (*Params) {
  80.  
  81. case PT_PSTRING:
  82. CharCount = _snprintf(Buffer, BytesLeft, " %Z", *StackFrame);
  83. break;
  84.  
  85. case PT_POBJATTR:
  86. ObjectString = ((POBJECT_ATTRIBUTES)*StackFrame)->ObjectName;
  87. CharCount = _snprintf(Buffer, BytesLeft, " %Z", ObjectString);
  88. break;
  89.  
  90. case PT_LONGLONG:
  91. CharCount = _snprintf(Buffer, BytesLeft, " 0x%I64X", *StackFrame);
  92. StackFrame++;
  93. break;
  94.  
  95. default:
  96. CharCount = _snprintf(Buffer, BytesLeft, " 0x%X", *StackFrame);
  97. }
  98.  
  99. Buffer += CharCount;
  100. StackFrame++;
  101. Params++;
  102.  
  103. if (BytesLeft > CharCount) {
  104. BytesLeft -= CharCount;
  105. } else {
  106. BytesLeft = 0;
  107. }
  108. }
  109.  
  110. TextBuffer[BufferSize - 1] = 0;
  111. DmSendNotificationString(TextBuffer);
  112. }
  113.  
  114. PORDINAL_THUNK
  115. CertSpyBuildKernelThunk(
  116. IN ULONG OrdinalNumber,
  117. IN ULONG_PTR FunctionAddress
  118. )
  119. {
  120. PORDINAL_THUNK Thunk;
  121.  
  122. //
  123. // Allocate memory out of executable region. Note that executeable
  124. // region are only virtual address and debug region.
  125. //
  126.  
  127. Thunk = (PORDINAL_THUNK)DmAllocatePool(sizeof(*Thunk));
  128.  
  129. if (Thunk) {
  130.  
  131. //
  132. // Build the thunk by manually writing x86 op-code
  133. //
  134.  
  135. Thunk->__pushad = 0x60;
  136. Thunk->__lea_edx_esp_plus_32 = 0x2024548D;
  137. Thunk->__mov_ecx_immediate = 0xB9;
  138. Thunk->ImmediateValue = OrdinalNumber;
  139. Thunk->__call_LogFunction = 0xE8;
  140. Thunk->OffsetOfLogFunction = (ULONG_PTR)CertSpyLogKernelCall - \
  141. (ULONG_PTR)&Thunk->__popad;
  142. Thunk->__popad = 0x61;
  143. Thunk->__jmp_far = 0xE9;
  144. Thunk->ImmediateJmpAddress = (ULONG_PTR)FunctionAddress - \
  145. (ULONG_PTR)&Thunk->ImmediateJmpAddress - \
  146. sizeof(Thunk->ImmediateJmpAddress);
  147. }
  148.  
  149. return Thunk;
  150. }
  151.  
  152. ULONG
  153. CertSpyLookupOrdinalFromExportDirectory(
  154. IN ULONG ImageBase,
  155. IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
  156. IN ULONG AddressOfFunction
  157. )
  158. {
  159. ULONG Entry;
  160. PULONG AddressOfFunctions;
  161.  
  162. AddressOfFunctions = (PULONG)(ImageBase + \
  163. (ULONG)ExportDirectory->AddressOfFunctions);
  164.  
  165. for (Entry=0; Entry<ExportDirectory->NumberOfFunctions; Entry++) {
  166. if (AddressOfFunctions[Entry] == AddressOfFunction - \
  167. (ULONG_PTR)PsNtosImageBase) {
  168. return Entry + 1;
  169. }
  170. }
  171.  
  172. return ~0UL;
  173. }
  174.  
  175. NTSTATUS
  176. CertSpyHookKernelImportOrdinal(
  177. IN ULONG OrdinalNumber
  178. )
  179. {
  180. NTSTATUS Status;
  181. PIMAGE_THUNK_DATA ImageThunkData = NULL;
  182. PORDINAL_THUNK Thunk;
  183.  
  184. if (OrdinalNumber < KernelExportSize) {
  185. ImageThunkData = ImageThunks[OrdinalNumber];
  186. }
  187.  
  188. if (!ImageThunkData) {
  189.  
  190. if (CertSpyXbeHasBeenLoaded) {
  191. return STATUS_ORDINAL_NOT_FOUND;
  192. }
  193.  
  194. //
  195. // Add to defer list if image hasn't been loaded into memory
  196. //
  197.  
  198. KernelExportFlags[OrdinalNumber] |= KEF_INTERCEPT_AT_STARTUP;
  199. return STATUS_PENDING;
  200. }
  201.  
  202. if (InterceptThunks[OrdinalNumber]) {
  203. return STATUS_SUCCESS;
  204. }
  205.  
  206. Thunk = CertSpyBuildKernelThunk(OrdinalNumber, ImageThunkData->u1.Function);
  207.  
  208. if (!Thunk) {
  209. return STATUS_NO_MEMORY;
  210. }
  211.  
  212. //
  213. // Mark image thunk data read/write
  214. //
  215.  
  216. PVOID BaseAddress = (PVOID)&ImageThunkData->u1.Function;
  217. SIZE_T RegionSize = sizeof(ImageThunkData->u1.Function);
  218. ULONG OldProtect;
  219.  
  220. Status = NtProtectVirtualMemory(&BaseAddress, &RegionSize,
  221. PAGE_READWRITE, &OldProtect);
  222.  
  223. if (NT_SUCCESS(Status)) {
  224. ImageThunkData->u1.Function = (ULONG_PTR)Thunk;
  225. InterceptThunks[OrdinalNumber] = Thunk;
  226. CertSpyFlushICache();
  227. }
  228.  
  229. return Status;
  230. }
  231.  
  232. NTSTATUS
  233. CertSpyUnhookKernelImportOrdinal(
  234. IN ULONG OrdinalNumber
  235. )
  236. {
  237. NTSTATUS Status;
  238. PIMAGE_THUNK_DATA ImageThunkData = NULL;
  239. PORDINAL_THUNK Thunk;
  240.  
  241. if (OrdinalNumber < KernelExportSize) {
  242. ImageThunkData = ImageThunks[OrdinalNumber];
  243. }
  244.  
  245. if (!ImageThunkData) {
  246. return STATUS_ORDINAL_NOT_FOUND;
  247. }
  248.  
  249. Thunk = InterceptThunks[OrdinalNumber];
  250.  
  251. if (!Thunk) {
  252. return STATUS_SUCCESS;
  253. }
  254.  
  255. //
  256. // Mark image thunk data read/write
  257. //
  258.  
  259. PVOID BaseAddress = (PVOID)&ImageThunkData->u1.Function;
  260. SIZE_T RegionSize = sizeof(ImageThunkData->u1.Function);
  261. ULONG OldProtect;
  262.  
  263. Status = NtProtectVirtualMemory(&BaseAddress, &RegionSize,
  264. PAGE_READWRITE, &OldProtect);
  265.  
  266. if (NT_SUCCESS(Status)) {
  267. ImageThunkData->u1.Function = (ULONG_PTR)Thunk->ImmediateJmpAddress + \
  268. (ULONG_PTR)&Thunk->ImmediateJmpAddress + sizeof(Thunk->ImmediateJmpAddress);
  269. CertSpyFlushICache();
  270. InterceptThunks[OrdinalNumber] = NULL;
  271. DmFreePool(Thunk);
  272. }
  273.  
  274. return Status;
  275. }
  276.  
  277. NTSTATUS
  278. CertSpyHookKernelImportThunks(
  279. IN PIMAGE_THUNK_DATA ImageThunkData
  280. )
  281. {
  282. PIMAGE_EXPORT_DIRECTORY ExportDirectory;
  283. ULONG ExportDirectorySize;
  284. PULONG AddressOfFunctions;
  285. ULONG OrdinalNumber;
  286. PORDINAL_THUNK Thunk;
  287.  
  288. //
  289. // Lookup the export directory from the export executable.
  290. //
  291.  
  292. ExportDirectory =
  293. (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(PsNtosImageBase,
  294. TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportDirectorySize);
  295.  
  296. if (ExportDirectory == NULL) {
  297. return STATUS_ORDINAL_NOT_FOUND;
  298. }
  299.  
  300. ASSERT(ExportDirectory->NumberOfFunctions == KernelExportSize-1);
  301.  
  302. AddressOfFunctions = (PULONG)((ULONG)PsNtosImageBase +
  303. (ULONG)ExportDirectory->AddressOfFunctions);
  304.  
  305. //
  306. // Loop over the image thunks and look up the ordinal number
  307. //
  308.  
  309. while (ImageThunkData->u1.Function != 0) {
  310.  
  311. OrdinalNumber = CertSpyLookupOrdinalFromExportDirectory(
  312. (ULONG)PsNtosImageBase, ExportDirectory,
  313. ImageThunkData->u1.Function);
  314.  
  315. if (OrdinalNumber != ~0UL && \
  316. OrdinalNumber < KernelExportSize && \
  317. KernelExports[OrdinalNumber].FunctionName) {
  318.  
  319. if (ImageThunks[OrdinalNumber] == NULL) {
  320. ImageThunks[OrdinalNumber] = ImageThunkData;
  321. }
  322.  
  323. if ((KernelExportFlags[OrdinalNumber] & KEF_INTERCEPT_AT_STARTUP) && \
  324. InterceptThunks[OrdinalNumber] == NULL) {
  325.  
  326. Thunk = CertSpyBuildKernelThunk(OrdinalNumber, ImageThunkData->u1.Function);
  327.  
  328. if (Thunk) {
  329. ImageThunkData->u1.Function = (ULONG_PTR)Thunk;
  330. InterceptThunks[OrdinalNumber] = Thunk;
  331. }
  332. }
  333. }
  334.  
  335. ImageThunkData++;
  336. }
  337.  
  338. CertSpyFlushICache();
  339.  
  340. return STATUS_SUCCESS;
  341. }
  342.  
  343. DWORD
  344. NTAPI
  345. CertSpyNotificationCallback(
  346. ULONG Notification,
  347. DWORD Parameter
  348. )
  349. {
  350. PDMN_MODLOAD Module = (PDMN_MODLOAD)Parameter;
  351. PXBEIMAGE_HEADER XbeImageHeader;
  352.  
  353. if (Notification != DM_MODLOAD) {
  354. return 0;
  355. }
  356.  
  357. //
  358. // There is no way to distinguish between XBE and DLL from module-load
  359. // notification. One easy way to see if the XBE has just been loaded is
  360. // to look at XBEIMAGE_HEADER in memory but SEH isn't working at this
  361. // point because the interrupts are disabled. We have to check the XBE
  362. // file name first before trying to read the XBE image header.
  363. //
  364.  
  365. if (XeImageFileName->Length == 0 || XeImageFileName->Buffer == NULL) {
  366. return 0;
  367. }
  368.  
  369. //
  370. // Locate and validate XBE image header and check for kernel import thunks
  371. //
  372.  
  373. XbeImageHeader = (PXBEIMAGE_HEADER)XBEIMAGE_STANDARD_BASE_ADDRESS;
  374.  
  375. if (XbeImageHeader->Signature == XBEIMAGE_SIGNATURE &&
  376. XbeImageHeader->XboxKernelThunkData != NULL &&
  377. CertSpyXbeHasBeenLoaded == FALSE) {
  378. CertSpyXbeHasBeenLoaded = TRUE;
  379. CertSpyHookKernelImportThunks(XbeImageHeader->XboxKernelThunkData);
  380. }
  381.  
  382. return 0;
  383. }
  384.  
  385. VOID
  386. NTAPI
  387. DxtEntry(
  388. ULONG *pfUnload
  389. )
  390. {
  391. ULONG Result;
  392. HRESULT hr;
  393.  
  394. hr = DmOpenNotificationSession(DM_PERSISTENT, &DmSession);
  395.  
  396. if (SUCCEEDED(hr)) {
  397. hr = DmNotify(DmSession, DM_MODLOAD,
  398. (PDM_NOTIFY_FUNCTION)CertSpyNotificationCallback);
  399. }
  400.  
  401. if (SUCCEEDED(hr)) {
  402. hr = DmRegisterCommandProcessor("certspy", CertSpyCommandProcessor);
  403. }
  404.  
  405. if (SUCCEEDED(hr)) {
  406. *pfUnload = FALSE;
  407. } else {
  408. *pfUnload = TRUE;
  409. }
  410. }
  411.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement