PageRenderTime 29ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 1ms

/PortableExecutable.cpp

https://bitbucket.org/BlackLuny/crysearch-memory-scanner
C++ | 2276 lines | 1559 code | 364 blank | 353 comment | 302 complexity | 38c298094121c242bcddc7a5039cd75d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. #include "PortableExecutable.h"
  2. #include "BackendGlobalDef.h"
  3. // Defines seperately because the Windows SDK 7.1 headers do not yet include these two.
  4. #define _WIN32_WINNT_WIN8 0x0602
  5. #define _WIN32_WINNT_WINBLUE 0x0603
  6. #include <Shlwapi.h>
  7. #pragma comment(lib, "shlwapi.lib")
  8. #define EAT_ADDRESS_NOT_FOUND -1
  9. #define THREAD_HIJACK_MAGIC_PROBE_THRESHOLD 12
  10. // Checks whether a RVA points inside a section of the executable. Returns true if so and false if not.
  11. const bool RVAPointsInsideSection(const DWORD rva)
  12. {
  13. // Walk the sections in the target process.
  14. for (auto const& section : LoadedProcessPEInformation.ImageSections)
  15. {
  16. // Check if the specified RVA is within the bounds of a section.
  17. if (rva > section.BaseAddress && rva < (section.BaseAddress + section.RawSectionSize))
  18. {
  19. return true;
  20. }
  21. }
  22. return false;
  23. }
  24. // -------------------------------------------------------------------------------------------------------------------------------
  25. // Base class methods
  26. // -------------------------------------------------------------------------------------------------------------------------------
  27. // Default PE class constructor.
  28. PortableExecutable::PortableExecutable()
  29. {
  30. this->mProcessHandle = mMemoryScanner->GetHandle();
  31. this->mBaseAddress = mModuleManager->GetBaseAddress();
  32. }
  33. // Default PE class destructor. Virtual destructor, always use derived class' destructor to execute this one.
  34. PortableExecutable::~PortableExecutable()
  35. {
  36. LoadedProcessPEInformation.PEFields.Clear();
  37. LoadedProcessPEInformation.Reset();
  38. LoadedProcessPEInformation.ClearImportTable();
  39. }
  40. // Sets the base address.
  41. void PortableExecutable::SetBaseAddress(const SIZE_T baseAddress)
  42. {
  43. this->mBaseAddress = baseAddress;
  44. }
  45. // Gets the base address.
  46. const SIZE_T PortableExecutable::GetBaseAddress() const
  47. {
  48. return this->mBaseAddress;
  49. }
  50. // Retrieves the address of the Process Environment Block of the opened process.
  51. // Returns the PEB base address or NULL if the address was not succesfully retrieved.
  52. void* PortableExecutable::GetPebAddress() const
  53. {
  54. #ifdef WIN64
  55. if (mMemoryScanner->IsX86Process())
  56. {
  57. // If we run CrySearch x64 and we target an x86 process, we use NtQueryInformationProcess with a ULONG_PTR parameter.
  58. ULONG_PTR PebBaseAddress;
  59. if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessWow64Information, &PebBaseAddress, sizeof(ULONG_PTR), NULL) == STATUS_SUCCESS)
  60. {
  61. return (void*)PebBaseAddress;
  62. }
  63. }
  64. else
  65. {
  66. // If we run CrySearch x64 and we target an x64 process, we use NtQueryInformationProcess with a PROCESS_BASIC_INFORMATION parameter.
  67. PROCESS_BASIC_INFORMATION tInfo;
  68. if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessBasicInformation, &tInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL) == STATUS_SUCCESS)
  69. {
  70. return tInfo.PebBaseAddress;
  71. }
  72. }
  73. #else
  74. // If we run CrySearch x86, we can only target an x86 process and we use NtQueryInformationProcess with a PROCESS_BASIC_INFORMATION parameter.
  75. PROCESS_BASIC_INFORMATION tInfo;
  76. if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessBasicInformation, &tInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL) == STATUS_SUCCESS)
  77. {
  78. return tInfo.PebBaseAddress;
  79. }
  80. #endif
  81. return NULL;
  82. }
  83. // Parses input machine type and adds the parsed value to the global inventory for UI display.
  84. void PortableExecutable::ParseMachineType(const DWORD machineType) const
  85. {
  86. switch (machineType)
  87. {
  88. case IMAGE_FILE_MACHINE_I386:
  89. LoadedProcessPEInformation.PEFields.Add("Machine Type", "i386");
  90. break;
  91. case IMAGE_FILE_MACHINE_IA64:
  92. LoadedProcessPEInformation.PEFields.Add("Machine Type", "ia64");
  93. break;
  94. case IMAGE_FILE_MACHINE_AMD64:
  95. LoadedProcessPEInformation.PEFields.Add("Machine Type", "amd64");
  96. break;
  97. }
  98. }
  99. // Parses input subsystem type and adds the parsed value to the global inventory for UI display.
  100. void PortableExecutable::ParseSubsystemValue(const DWORD subSystem) const
  101. {
  102. switch (subSystem)
  103. {
  104. case IMAGE_SUBSYSTEM_UNKNOWN:
  105. LoadedProcessPEInformation.PEFields.Add("Subsystem", "Unknown");
  106. break;
  107. case IMAGE_SUBSYSTEM_NATIVE:
  108. LoadedProcessPEInformation.PEFields.Add("Subsystem", "Native");
  109. break;
  110. case IMAGE_SUBSYSTEM_WINDOWS_GUI:
  111. LoadedProcessPEInformation.PEFields.Add("Subsystem", "Windows GUI");
  112. break;
  113. case IMAGE_SUBSYSTEM_WINDOWS_CUI:
  114. LoadedProcessPEInformation.PEFields.Add("Subsystem", "Windows CUI");
  115. break;
  116. case IMAGE_SUBSYSTEM_OS2_CUI:
  117. LoadedProcessPEInformation.PEFields.Add("Subsystem", "OS/2 CUI");
  118. break;
  119. case IMAGE_SUBSYSTEM_POSIX_CUI:
  120. LoadedProcessPEInformation.PEFields.Add("Subsystem", "POSIX_CUI");
  121. break;
  122. case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI:
  123. LoadedProcessPEInformation.PEFields.Add("Subsystem", "Windows CE CUI");
  124. break;
  125. case IMAGE_SUBSYSTEM_EFI_APPLICATION:
  126. LoadedProcessPEInformation.PEFields.Add("Subsystem", "EFI");
  127. break;
  128. case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
  129. LoadedProcessPEInformation.PEFields.Add("Subsystem", "EFI Boot Driver");
  130. break;
  131. case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
  132. LoadedProcessPEInformation.PEFields.Add("Subsystem", "EFI Runtime Driver");
  133. break;
  134. case IMAGE_SUBSYSTEM_EFI_ROM:
  135. LoadedProcessPEInformation.PEFields.Add("Subsystem", "EFI ROM");
  136. break;
  137. case IMAGE_SUBSYSTEM_XBOX:
  138. LoadedProcessPEInformation.PEFields.Add("Subsystem", "Xbox system");
  139. break;
  140. case IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION:
  141. LoadedProcessPEInformation.PEFields.Add("Subsystem", "Boot application");
  142. break;
  143. }
  144. }
  145. // This function retrieves all sections from a specified PE file. The NT header is provided to create flexibility.
  146. // The input list is cleared before the retrieval is started.
  147. void PortableExecutable::GetImageSectionsList(const IMAGE_SECTION_HEADER* pSecHeader, const DWORD numberOfSections, Vector<Win32PESectionInformation>& list) const
  148. {
  149. list.Clear();
  150. // Iterate through sections and save them for application use.
  151. for (unsigned int i = 0; i < numberOfSections; ++i, ++pSecHeader)
  152. {
  153. // Sometimes PE files contain bogus sections at runtime due to packer activities. Remove bogus sections from the list.
  154. if (!pSecHeader->VirtualAddress)
  155. {
  156. continue;
  157. }
  158. // The name of a section can only be 8 characters long. A longer name has a different notation.
  159. // This is not taken into account because the chance of it appearing in an executable is very small.
  160. list.Add(Win32PESectionInformation((char*)pSecHeader->Name, pSecHeader->VirtualAddress, pSecHeader->Misc.VirtualSize == 0 ? pSecHeader->SizeOfRawData
  161. : pSecHeader->Misc.VirtualSize, pSecHeader->SizeOfRawData));
  162. }
  163. }
  164. // This function is declared seperately to reduce code in a few functions spreaded over this file.
  165. // This increases the code readability even though the function will probably be inlined by the compiler.
  166. wchar* PortableExecutable::InlineResolveApiSetSchema(const WString& str) const
  167. {
  168. // Check which version of Windows is running. We need this to differentiate between ApiSetSchema versions.
  169. Tuple2<int, int> winver;
  170. if (GetInlineWindowsVersion(&winver))
  171. {
  172. // Call the correct ApiSetSchema parsing function.
  173. if (winver.a == 6)
  174. {
  175. if (winver.b == 4)
  176. {
  177. // Call the Windows 10 version of the parser.
  178. return this->ResolveApiSetSchemaMapping10(str, str.GetLength());
  179. }
  180. else if (winver.b > 2)
  181. {
  182. // Call the Windows 8(.1) version of the parser.
  183. return this->ResolveApiSetSchemaMappingEx(str, str.GetLength());
  184. }
  185. else
  186. {
  187. // Call the Windows 7 version of the parser.
  188. return this->ResolveApiSetSchemaMapping(str, str.GetLength());
  189. }
  190. }
  191. else if (winver.a == 10)
  192. {
  193. // Call the Windows 10 version of the parser.
  194. return this->ResolveApiSetSchemaMapping10(str, str.GetLength());
  195. }
  196. }
  197. return NULL;
  198. }
  199. // Resolves Windows 6.x ApiSetSchema redirections found in the IAT. Usually they redirect to a common Windows DLL like advapi32.dll.
  200. // Returns the name of the redirected library, or NULL if the function failed. Beware that you still need to delete the buffer assigned to the return value!
  201. wchar* PortableExecutable::ResolveApiSetSchemaMapping(const wchar* ApiSetSchemaDll, const DWORD Length) const
  202. {
  203. // Retrieve PEB, the address of the map is there.
  204. #ifdef _WIN64
  205. APISETMAP* const apiSetSchemaBase = (APISETMAP*)((PPEB)__readgsqword(0x60))->ApiSetMap;
  206. #else
  207. APISETMAP* const apiSetSchemaBase = (APISETMAP*)((PPEB)__readfsdword(0x30))->ApiSetMap;
  208. #endif
  209. Byte* const apiSetSchemaFileBuffer = (Byte*)apiSetSchemaBase;
  210. DLLHOSTDESCRIPTOR* pDescriptor = apiSetSchemaBase->descriptors;
  211. // Iterate through the descriptor structs.
  212. for (unsigned int i = 0; i < apiSetSchemaBase->NumberOfHosts; ++i, ++pDescriptor)
  213. {
  214. // Compare virtual API with input.
  215. if (_wcsnicmp(ApiSetSchemaDll, (wchar*)(apiSetSchemaFileBuffer + pDescriptor->OffsetDllString), Length) == 0)
  216. {
  217. DLLREDIRECTOR* const directorStruct = (DLLREDIRECTOR*)(apiSetSchemaFileBuffer + pDescriptor->OffsetDllRedirector);
  218. // Iterate redirections for this api set.
  219. REDIRECTION* pRedirectionDescriptor = directorStruct->Redirection;
  220. const wchar* const redirectionString = (wchar*)(apiSetSchemaFileBuffer + pRedirectionDescriptor->OffsetRedirection2);
  221. // Redirection is found, create buffer to return to the caller and copy the logical dll name into it.
  222. const DWORD wcsLength = pRedirectionDescriptor->RedirectionLength2 / 2;
  223. wchar* const nameBuffer = new wchar[wcsLength + 1];
  224. memcpy(nameBuffer, redirectionString, pRedirectionDescriptor->RedirectionLength2);
  225. // Set null terminator in the string, otherwise the result contains the redirected dll name but the rest is undefined.
  226. nameBuffer[wcsLength] = NULL;
  227. return nameBuffer;
  228. }
  229. }
  230. return NULL;
  231. }
  232. // Compatibility with Windows 8.1 ApiSetSchema v2 is implemented since v1.04 of CrySearch.
  233. // Return value is the same as the PortableExecutable::ResolveApiSetSchemaMapping function.
  234. wchar* PortableExecutable::ResolveApiSetSchemaMappingEx(const wchar* ApiSetSchemaDll, const DWORD Length) const
  235. {
  236. // Retrieve PEB, the address of the map is there.
  237. #ifdef _WIN64
  238. API_SET_NAMESPACE_ARRAY_V2* const apiSetSchemaBase = (API_SET_NAMESPACE_ARRAY_V2*)((PPEB)__readgsqword(0x60))->ApiSetMap;
  239. #else
  240. API_SET_NAMESPACE_ARRAY_V2* const apiSetSchemaBase = (API_SET_NAMESPACE_ARRAY_V2*)((PPEB)__readfsdword(0x30))->ApiSetMap;
  241. #endif
  242. Byte* const apiSetSchemaFileBuffer = (Byte*)apiSetSchemaBase;
  243. API_SET_NAMESPACE_ENTRY_V2* pDescriptor = apiSetSchemaBase->Array;
  244. // Iterate through the descriptor structs.
  245. for (unsigned int i = 0; i < apiSetSchemaBase->Count; ++i, ++pDescriptor)
  246. {
  247. // Compare virtual API with input.
  248. if (_wcsnicmp(ApiSetSchemaDll, (wchar*)(apiSetSchemaFileBuffer + pDescriptor->NameOffset), Length) == 0)
  249. {
  250. API_SET_VALUE_ARRAY_V2* const directorStruct = (API_SET_VALUE_ARRAY_V2*)(apiSetSchemaFileBuffer + pDescriptor->DataOffset);
  251. // Iterate redirections for this api set.
  252. API_SET_VALUE_ENTRY_V2* pRedirectionDescriptor = directorStruct->Array;
  253. const wchar* const redirectionString = (wchar*)(apiSetSchemaFileBuffer + pRedirectionDescriptor->ValueOffset);
  254. // Apparently a virtual library may have two redirections. If the library being
  255. // processed matches the first API redirection, we must use the second one.
  256. const unsigned int totalLengthW = (unsigned int)wcslen(redirectionString);
  257. const DWORD wcsIndex = pRedirectionDescriptor->ValueLength / sizeof(wchar);
  258. wchar* const nameBuffer = new wchar[totalLengthW * sizeof(wchar)];
  259. if (_wcsnicmp(ApiSetSchemaDll, redirectionString + 4, Length) == 0)
  260. {
  261. // Redirection is found, create buffer to return to the caller and copy the logical dll name into it.
  262. const DWORD wcharCount = totalLengthW - wcsIndex;
  263. memcpy(nameBuffer, redirectionString + wcsIndex, wcharCount);
  264. nameBuffer[wcharCount] = NULL;
  265. }
  266. else
  267. {
  268. // Take the first redirection as result.
  269. memcpy(nameBuffer, redirectionString, pRedirectionDescriptor->ValueLength);
  270. nameBuffer[pRedirectionDescriptor->ValueLength / sizeof(wchar)] = NULL;
  271. }
  272. return nameBuffer;
  273. }
  274. }
  275. return NULL;
  276. }
  277. // Compatibility with Windows 10 ApiSetSchema is implemented since v2.0 of CrySearch.
  278. // Return value is the same as the PortableExecutable::ResolveApiSetSchemaMapping function.
  279. wchar* PortableExecutable::ResolveApiSetSchemaMapping10(const wchar* ApiSetSchemaDll, const DWORD Length ) const
  280. {
  281. // Retrieve PEB, the address of the map is there.
  282. #ifdef _WIN64
  283. API_SET_NAMESPACE_ARRAY_10* const apiSetSchemaBase = (API_SET_NAMESPACE_ARRAY_10*)((PPEB)__readgsqword(0x60))->ApiSetMap;
  284. #else
  285. API_SET_NAMESPACE_ARRAY_10* const apiSetSchemaBase = (API_SET_NAMESPACE_ARRAY_10*)((PPEB)__readfsdword(0x30))->ApiSetMap;
  286. #endif
  287. Byte* const apiSetSchemaFileBuffer = (Byte*)apiSetSchemaBase;
  288. API_SET_NAMESPACE_ENTRY_10* pDescriptor = (API_SET_NAMESPACE_ENTRY_10*)(apiSetSchemaFileBuffer + apiSetSchemaBase->End);
  289. // Iterate through the descriptor structs.
  290. for (unsigned int i = 0; i < apiSetSchemaBase->Count; ++i, ++pDescriptor)
  291. {
  292. // Retrieve the data associated with the current ApiSet schema entry.
  293. API_SET_VALUE_ARRAY_10* const directorStruct = (API_SET_VALUE_ARRAY_10*)(apiSetSchemaFileBuffer + apiSetSchemaBase->Start + sizeof(API_SET_VALUE_ARRAY_10) * pDescriptor->Size);
  294. // Compare virtual API with input. We need to add 4 words to the pointer because in Windows 10, the API names are not truncated to 'ms-win...'.
  295. if (_wcsnicmp(ApiSetSchemaDll, (wchar*)(apiSetSchemaFileBuffer + directorStruct->NameOffset + 8), Length - 1) == 0)
  296. {
  297. // Iterate redirections for this api set.
  298. API_SET_VALUE_ENTRY_10* pRedirectionDescriptor = (API_SET_VALUE_ENTRY_10*)(apiSetSchemaFileBuffer + directorStruct->DataOffset);
  299. const wchar* const redirectionString = (wchar*)(apiSetSchemaFileBuffer + pRedirectionDescriptor->ValueOffset);
  300. // Apparently a virtual library may have two redirections. If the library being
  301. // processed matches the first API redirection, we must use the second one.
  302. const unsigned int totalLengthW = (unsigned int)wcslen(redirectionString);
  303. const DWORD wcsIndex = pRedirectionDescriptor->ValueLength / sizeof(wchar);
  304. wchar* const nameBuffer = new wchar[totalLengthW * sizeof(wchar)];
  305. if (_wcsnicmp(ApiSetSchemaDll, redirectionString + 4, Length) == 0)
  306. {
  307. // Redirection is found, create buffer to return to the caller and copy the logical dll name into it.
  308. const DWORD wcharCount = totalLengthW - wcsIndex;
  309. memcpy(nameBuffer, redirectionString + wcsIndex, wcharCount);
  310. nameBuffer[wcharCount] = NULL;
  311. }
  312. else
  313. {
  314. // Take the first redirection as result.
  315. memcpy(nameBuffer, redirectionString, pRedirectionDescriptor->ValueLength);
  316. nameBuffer[pRedirectionDescriptor->ValueLength / sizeof(wchar)] = NULL;
  317. }
  318. return nameBuffer;
  319. }
  320. }
  321. return NULL;
  322. }
  323. // Reads the COM directory from a PE file header. Most likely this is the .NET header.
  324. // This function does not free the buffer pointed to by the parameter.
  325. void PortableExecutable::GetDotNetDirectoryInformation(const IMAGE_DATA_DIRECTORY* const netHeader) const
  326. {
  327. // Check if the executable contains a COM header.
  328. if (netHeader->VirtualAddress && netHeader->Size >= sizeof(IMAGE_COR20_HEADER))
  329. {
  330. SIZE_T bytesRead;
  331. // Read COR20 header from file.
  332. Byte* netDirBuffer = new Byte[netHeader->Size];
  333. bool b = CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + netHeader->VirtualAddress), netDirBuffer, netHeader->Size, &bytesRead);
  334. // Check if the section data was read succesfully.
  335. if (b && bytesRead == netHeader->Size)
  336. {
  337. // Save version information from COR20 header.
  338. IMAGE_DATA_DIRECTORY mdDir;
  339. memcpy(&mdDir, &((IMAGE_COR20_HEADER*)netDirBuffer)->MetaData, sizeof(IMAGE_DATA_DIRECTORY));
  340. delete[] netDirBuffer;
  341. // Save the offset to the metadata header to allow dumping of .NET sections later.
  342. LoadedProcessPEInformation.DotNetInformation.MetadataHeaderOffset = mdDir.VirtualAddress;
  343. // Read metadata from header.
  344. netDirBuffer = new Byte[mdDir.Size];
  345. b = CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + mdDir.VirtualAddress), netDirBuffer, mdDir.Size, &bytesRead);
  346. // Check whether the metadata was succesfully read into the local buffer.
  347. if (b && bytesRead == mdDir.Size)
  348. {
  349. // Dissect metadata. Since its a dynamic structure we cannot compile this into a struct.
  350. const DWORD vStrLength = *(DWORD*)(netDirBuffer + 12);
  351. const WORD streamCount = *(WORD*)(netDirBuffer + 18 + vStrLength);
  352. // Based on the stream count, dissect streams from the header.
  353. DWORD streamIterator = 0;
  354. for (const char* iterator = (char*)(netDirBuffer + 20 + vStrLength); streamIterator < streamCount; ++streamIterator)
  355. {
  356. // Get offset and size fields.
  357. const DWORD* const offsetPtr = (DWORD*)iterator;
  358. iterator += sizeof(DWORD);
  359. const DWORD* const sizePtr = (DWORD*)iterator;
  360. iterator += sizeof(DWORD);
  361. // Read the name of the stream.
  362. WORD str = 0;
  363. const char* const beginIterator = iterator;
  364. bool strEnded = false;
  365. while (1)
  366. {
  367. // First find the end of the string.
  368. if (!strEnded && *iterator == 0)
  369. {
  370. strEnded = true;
  371. }
  372. // Continue until the next 4 byte boundary is reached.
  373. else if (strEnded && ((SIZE_T)iterator % 4) == 0)
  374. {
  375. break;
  376. }
  377. ++str;
  378. ++iterator;
  379. }
  380. // String length was measured, now read it into a variable.
  381. Win32DotNetSectionInformation& newSect = LoadedProcessPEInformation.DotNetInformation.DotNetSections.Add();
  382. newSect.SectionName = String(beginIterator, str + 1);
  383. newSect.Offset = *offsetPtr;
  384. newSect.Size = *sizePtr;
  385. }
  386. }
  387. }
  388. delete[] netDirBuffer;
  389. }
  390. }
  391. // Resolves ApiSetSchema module names and returns a pointer to the resolved module.
  392. const Win32ModuleInformation* PortableExecutable::GetResolvedModule(const char* pModName, int* const recurseIndex, const char* NameOrdinal) const
  393. {
  394. // Find the first dot in the filename.
  395. String forwardedModName = pModName;
  396. *recurseIndex = forwardedModName.Find('.');
  397. // If the resulting filename starts with api-ms-win, we are dealing with ApiSetSchema libraries.
  398. WString apiSetDllName = ToLower(forwardedModName);
  399. if (apiSetDllName.StartsWith("api-ms-win") || apiSetDllName.StartsWith("ext-ms-win"))
  400. {
  401. apiSetDllName.Remove(0, 4);
  402. apiSetDllName.Remove(apiSetDllName.Find('.'), (int)strlen(NameOrdinal) + 1);
  403. const wchar* const outWString = this->InlineResolveApiSetSchema(apiSetDllName);
  404. forwardedModName = WString(outWString).ToString();
  405. delete[] outWString;
  406. }
  407. // Append .dll after the filename.
  408. const int dotIndex = forwardedModName.Find('.');
  409. if (dotIndex >= 0)
  410. {
  411. forwardedModName.Remove(dotIndex, forwardedModName.GetLength() - dotIndex);
  412. forwardedModName += ".dll";
  413. }
  414. // Resolve the module to a loaded one.
  415. return mModuleManager->FindModule(forwardedModName);
  416. }
  417. // -------------------------------------------------------------------------------------------------------------------------------
  418. // PE32 class methods
  419. // -------------------------------------------------------------------------------------------------------------------------------
  420. // Default PE32 destructor. Base class destructor is virtual so this destructor is executed with the base's.
  421. PortableExecutable32::~PortableExecutable32()
  422. {
  423. }
  424. // Retrieves PE header information from the loaded process. Information is saved in global storage that has process lifetime.
  425. // Note that IMAGE_NT_HEADERS and IMAGE_OPTIONAL_HEADER are explicitly defined as the 32 bit version. If compiled as 64 bit the structs differ.
  426. void PortableExecutable32::GetExecutablePeInformation() const
  427. {
  428. // Clear image sections before getting new ones.
  429. LoadedProcessPEInformation.Reset();
  430. // Read process memory into local buffer in order to load PE headers.
  431. Byte moduleBuffer[0x400];
  432. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)this->mBaseAddress, moduleBuffer, 0x400, NULL);
  433. // Load PE headers.
  434. IMAGE_DOS_HEADER* const pDosHeader = (IMAGE_DOS_HEADER*)moduleBuffer;
  435. IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(moduleBuffer + pDosHeader->e_lfanew);
  436. // When the PE Headers are destroyed at runtime the pointer to the headers may run out of the buffer's bounds.
  437. if ((Byte*)pNTHeader > (moduleBuffer + 0x400))
  438. {
  439. LoadedProcessPEInformation.PEFields.Clear();
  440. return;
  441. }
  442. // The headers should be fine, proceed loading the optional and file header.
  443. const IMAGE_OPTIONAL_HEADER32* const pOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader;
  444. const IMAGE_FILE_HEADER* const pFileHeader = &(pNTHeader->FileHeader);
  445. // Retrieve the type of machine the PE executable can run on.
  446. this->ParseMachineType(pFileHeader->Machine);
  447. // Retrieve PE fields and add them to the map.
  448. LoadedProcessPEInformation.PEFields.Add("Number of sections", pFileHeader->NumberOfSections);
  449. LoadedProcessPEInformation.PEFields.Add("Size of optional header", Format("%X", pFileHeader->SizeOfOptionalHeader));
  450. LoadedProcessPEInformation.PEFields.Add("Pointer to symbol table", (int)pFileHeader->PointerToSymbolTable);
  451. LoadedProcessPEInformation.PEFields.Add("Number of symbols", (int)pFileHeader->NumberOfSymbols);
  452. LoadedProcessPEInformation.PEFields.Add("Image base", Format("%lX", (int)pOptionalHeader->ImageBase));
  453. #ifndef _WIN64
  454. LoadedProcessPEInformation.PEFields.Add("Base of data", Format("%lX", (int)pOptionalHeader->BaseOfData));
  455. #endif
  456. LoadedProcessPEInformation.PEFields.Add("Base of code", Format("%lX", (int)pOptionalHeader->BaseOfCode));
  457. LoadedProcessPEInformation.PEFields.Add("Address of entrypoint", Format("%lX", (int)pOptionalHeader->AddressOfEntryPoint));
  458. LoadedProcessPEInformation.PEFields.Add("Size of code", Format("%lX", (int)pOptionalHeader->SizeOfCode));
  459. LoadedProcessPEInformation.PEFields.Add("Size of initialized data", Format("%lX", (int)pOptionalHeader->SizeOfInitializedData));
  460. LoadedProcessPEInformation.PEFields.Add("Size of uninitialized data", Format("%lX", (int)pOptionalHeader->SizeOfUninitializedData));
  461. LoadedProcessPEInformation.PEFields.Add("Section alignment", Format("%lX", (int)pOptionalHeader->SectionAlignment));
  462. LoadedProcessPEInformation.PEFields.Add("File alignment", Format("%lX", (int)pOptionalHeader->FileAlignment));
  463. LoadedProcessPEInformation.PEFields.Add("Size of image", Format("%lX", (int)pOptionalHeader->SizeOfImage));
  464. LoadedProcessPEInformation.PEFields.Add("Size of headers", Format("%lX", (int)pOptionalHeader->SizeOfHeaders));
  465. LoadedProcessPEInformation.PEFields.Add("Checksum", Format("%lX", (int)pOptionalHeader->CheckSum));
  466. LoadedProcessPEInformation.PEFields.Add("Linker version", Format("%i.%i", pOptionalHeader->MajorLinkerVersion, pOptionalHeader->MinorLinkerVersion));
  467. LoadedProcessPEInformation.PEFields.Add("OS version", Format("%i.%i", pOptionalHeader->MajorOperatingSystemVersion, pOptionalHeader->MinorOperatingSystemVersion));
  468. LoadedProcessPEInformation.PEFields.Add("Image version", Format("%i.%i", pOptionalHeader->MajorImageVersion, pOptionalHeader->MinorImageVersion));
  469. LoadedProcessPEInformation.PEFields.Add("Subsystem version", Format("%i.%i", pOptionalHeader->MajorSubsystemVersion, pOptionalHeader->MinorSubsystemVersion));
  470. LoadedProcessPEInformation.PEFields.Add("Number of data directories", Format("%lX", (int)pOptionalHeader->NumberOfRvaAndSizes));
  471. // Parse the last general property value of the PE header, save the section values and destroy the buffer.
  472. this->ParseSubsystemValue(pOptionalHeader->Subsystem);
  473. const DWORD sectionCount = pNTHeader->FileHeader.NumberOfSections;
  474. const DWORD sectionSizeBytes = sizeof(IMAGE_SECTION_HEADER) * sectionCount;
  475. const IMAGE_SECTION_HEADER* const firstSectionPtr = (IMAGE_SECTION_HEADER*)(this->mBaseAddress + ((Byte*)IMAGE_FIRST_SECTION(pNTHeader) - moduleBuffer));
  476. // Get the COM header from the PE file.
  477. this->GetDotNetDirectoryInformation(&pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]);
  478. // Attempt to load the sections inside the PE file.
  479. Byte* const sectionBuffer = new Byte[sectionSizeBytes];
  480. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, firstSectionPtr, sectionBuffer, sectionSizeBytes, NULL);
  481. this->GetImageSectionsList((IMAGE_SECTION_HEADER*)sectionBuffer, sectionCount, LoadedProcessPEInformation.ImageSections);
  482. delete[] sectionBuffer;
  483. }
  484. // Retrieves the address of a function in the export table of a module. Address can be returned for function by name or ordinal.
  485. // Returns the address of the function, created from the module base address added by the function RVA.
  486. // If the function is not found, the return value is 0xFFFFFFFF.
  487. // The NameLength parameter contains the length of the name if there is a name, or 0 if the function is ordinal-based.
  488. SIZE_T PortableExecutable32::GetAddressFromExportTable(const AddrStruct* addr, const char* NameOrdinal, const unsigned int NameLength) const
  489. {
  490. if (addr->ExportDirectory->AddressOfNameOrdinals)
  491. {
  492. int ResurseDotIndex = 0;
  493. const DWORD* funcAddrPtr = NULL;
  494. bool b;
  495. SIZE_T bytesRead;
  496. // Walk the exported functions in the target module.
  497. for (unsigned int i = 0; i < addr->ExportDirectory->NumberOfFunctions; ++i)
  498. {
  499. const WORD* const ordValue = (WORD*)((addr->BufferBaseAddress + addr->ExportDirectory->AddressOfNameOrdinals) + (i * sizeof(WORD)));
  500. if (!NameLength)
  501. {
  502. bool found = false;
  503. // Compare ordinal values without magic bitoperations!
  504. if ((addr->ExportDirectory->Base + *ordValue) == *reinterpret_cast<WORD*>(&NameOrdinal))
  505. {
  506. funcAddrPtr = (DWORD*)((addr->BufferBaseAddress + addr->ExportDirectory->AddressOfFunctions) + (sizeof(DWORD) * *ordValue));
  507. found = true;
  508. }
  509. // Skip the entry if it is not found.
  510. if (!found || ((Byte*)funcAddrPtr < addr->BufferBaseAddress || (Byte*)funcAddrPtr > addr->BufferEndAddress))
  511. {
  512. continue;
  513. }
  514. if (*funcAddrPtr > addr->DirectoryAddress->VirtualAddress && *funcAddrPtr < (addr->DirectoryAddress->VirtualAddress + addr->DirectoryAddress->Size))
  515. {
  516. const Win32ModuleInformation* modBaseAddr = this->GetResolvedModule((char*)(addr->BufferBaseAddress + *funcAddrPtr), &ResurseDotIndex, NameOrdinal);
  517. // Sometimes infinite redirecting causes stack overflowing. Terminate this sequence by returning not found.
  518. if (!modBaseAddr || (SIZE_T)addr->BaseAddress == modBaseAddr->BaseAddress)
  519. {
  520. return EAT_ADDRESS_NOT_FOUND;
  521. }
  522. Byte dllBuffer[0x400];
  523. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)modBaseAddr->BaseAddress, dllBuffer, 0x400, NULL);
  524. const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(dllBuffer + ((IMAGE_DOS_HEADER*)dllBuffer)->e_lfanew);
  525. IMAGE_DATA_DIRECTORY dataDir = *(&((IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader)->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
  526. Byte* const exportDirectoryBuffer = new Byte[dataDir.Size];
  527. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(modBaseAddr->BaseAddress + dataDir.VirtualAddress), exportDirectoryBuffer, dataDir.Size, NULL);
  528. Byte* const bufBase = exportDirectoryBuffer - dataDir.VirtualAddress;
  529. AddrStruct addrStruct((Byte*)modBaseAddr->BaseAddress, (exportDirectoryBuffer - dataDir.VirtualAddress), bufBase + dataDir.VirtualAddress + dataDir.Size
  530. , &dataDir, (IMAGE_EXPORT_DIRECTORY*)exportDirectoryBuffer);
  531. SIZE_T forwardedAddress = this->GetAddressFromExportTable(&addrStruct, (char*)addr->BufferBaseAddress + *funcAddrPtr + ResurseDotIndex + 2, NameLength);
  532. delete[] exportDirectoryBuffer;
  533. return forwardedAddress;
  534. }
  535. return (SIZE_T)(addr->BaseAddress + *funcAddrPtr);
  536. }
  537. else
  538. {
  539. const DWORD* const stringPtr = (DWORD*)((addr->BufferBaseAddress + addr->ExportDirectory->AddressOfNames) + (i * sizeof(DWORD)));
  540. const char* const functionName = (char*)(addr->BufferBaseAddress + *stringPtr);
  541. if ((functionName > (char*)addr->BufferBaseAddress + addr->DirectoryAddress->VirtualAddress && (functionName + NameLength + 1) < (char*)addr->BufferEndAddress) && memcmp(NameOrdinal, functionName, NameLength) == 0)
  542. {
  543. funcAddrPtr = (DWORD*)((addr->BufferBaseAddress + addr->ExportDirectory->AddressOfFunctions) + (sizeof(DWORD) * *ordValue));
  544. if (*funcAddrPtr > addr->DirectoryAddress->VirtualAddress && *funcAddrPtr < (addr->DirectoryAddress->VirtualAddress + addr->DirectoryAddress->Size))
  545. {
  546. const Win32ModuleInformation* modBaseAddr = this->GetResolvedModule((char*)(addr->BufferBaseAddress + *funcAddrPtr), &ResurseDotIndex, NameOrdinal);
  547. if (!modBaseAddr || (SIZE_T)addr->BaseAddress == modBaseAddr->BaseAddress)
  548. {
  549. return EAT_ADDRESS_NOT_FOUND;
  550. }
  551. Byte dllBuffer[0x400];
  552. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)modBaseAddr->BaseAddress, dllBuffer, 0x400, NULL);
  553. const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(dllBuffer + ((IMAGE_DOS_HEADER*)dllBuffer)->e_lfanew);
  554. IMAGE_DATA_DIRECTORY dataDir = *(&((IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader)->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
  555. // Check if the size of the data directory is valid.
  556. if (dataDir.Size)
  557. {
  558. // Read the export directory from memory.
  559. Byte* const exportDirectoryBuffer = new Byte[dataDir.Size];
  560. b = CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(modBaseAddr->BaseAddress + dataDir.VirtualAddress), exportDirectoryBuffer, dataDir.Size, &bytesRead);
  561. // Check whether it was read succesfully.
  562. if (b && bytesRead == dataDir.Size)
  563. {
  564. Byte* const bufBase = exportDirectoryBuffer - dataDir.VirtualAddress;
  565. AddrStruct addrStruct((Byte*)modBaseAddr->BaseAddress, (exportDirectoryBuffer - dataDir.VirtualAddress), bufBase + dataDir.VirtualAddress + dataDir.Size
  566. , &dataDir, (IMAGE_EXPORT_DIRECTORY*)exportDirectoryBuffer);
  567. SIZE_T forwardedAddress = this->GetAddressFromExportTable(&addrStruct, (char*)(addr->BufferBaseAddress + *funcAddrPtr + ResurseDotIndex + 1), NameLength);
  568. delete[] exportDirectoryBuffer;
  569. return forwardedAddress;
  570. }
  571. else
  572. {
  573. return EAT_ADDRESS_NOT_FOUND;
  574. }
  575. }
  576. else
  577. {
  578. return EAT_ADDRESS_NOT_FOUND;
  579. }
  580. }
  581. return (SIZE_T)(addr->BaseAddress + *funcAddrPtr);
  582. }
  583. }
  584. }
  585. }
  586. return EAT_ADDRESS_NOT_FOUND;
  587. }
  588. // Attempts to retrieve function name associated to ordinal import from the export table of the loaded module.
  589. // Returns a pointer to the function name if it exists. If the function is not found, the return value is NULL.
  590. const char* PortableExecutable32::GetOrdinalFunctionNameFromExportTable(const AddrStruct* addr, const WORD ordinal) const
  591. {
  592. const WORD* const ordinals = (WORD*)(addr->BufferBaseAddress + addr->ExportDirectory->AddressOfNameOrdinals);
  593. for (unsigned int i = 0; i < addr->ExportDirectory->NumberOfFunctions; ++i)
  594. {
  595. if ((addr->ExportDirectory->Base + ordinals[i]) == ordinal)
  596. {
  597. const DWORD* const stringPtr = (DWORD*)(addr->BufferBaseAddress + addr->ExportDirectory->AddressOfNames + i * sizeof(DWORD));
  598. const Byte* const absStringPtr = (Byte*)(addr->BufferBaseAddress + *stringPtr);
  599. // Make sure the string points inside of the buffer. Scrambled EAT would crash the application.
  600. if (absStringPtr > (addr->BufferBaseAddress + addr->DirectoryAddress->Size) && absStringPtr < addr->BufferEndAddress)
  601. {
  602. return (const char*)absStringPtr;
  603. }
  604. }
  605. }
  606. return NULL;
  607. }
  608. // Retrieves the import table from the PE header of the loaded process. This information is stored in the global storage that has process lifetime.
  609. // Note that IMAGE_NT_HEADERS, IMAGE_OPTIONAL_HEADER, IMAGE_THUNK_DATA and SIZE_T are explicitly defined as the 32 bit version. If compiled as 64 bit the structs differ.
  610. const bool PortableExecutable32::GetImportAddressTable() const
  611. {
  612. // We use a return value to indicate whether function names can actually be retrieved using OriginalFirstThunk.
  613. bool result = true;
  614. // Read process memory into local buffer in order to load IAT.
  615. Byte moduleBuffer[0x400];
  616. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)this->mBaseAddress, moduleBuffer, 0x400, NULL);
  617. const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(moduleBuffer + ((IMAGE_DOS_HEADER*)moduleBuffer)->e_lfanew);
  618. // The PE Headers are not valid, the pointer runs outside the bounds of the buffer.
  619. if ((Byte*)pNTHeader > (moduleBuffer + 0x400))
  620. {
  621. return false;
  622. }
  623. const IMAGE_OPTIONAL_HEADER32* const pOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader;
  624. // Read the import descriptor table into local memory.
  625. unsigned int counter = 0;
  626. IMAGE_IMPORT_DESCRIPTOR pDesc;
  627. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + (counter * sizeof(IMAGE_IMPORT_DESCRIPTOR))), &pDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR), NULL);
  628. // Check whether OriginalFirstThunk is non-zero. If it is zero, the target process executable may be packed. We can find
  629. // the function addresses, but we cannot find the function names directly.
  630. if (!pDesc.OriginalFirstThunk)
  631. {
  632. result = false;
  633. }
  634. while (pDesc.FirstThunk && pDesc.Name != 0xFFFF)
  635. {
  636. // Read DLL name from import descriptor entry.
  637. char dllName[48];
  638. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(pDesc.Name + this->mBaseAddress), dllName, 48, NULL);
  639. // Add new import descriptor to the table.
  640. ImportTableDescriptor& impDesc = LoadedProcessPEInformation.ImportAddressTable.Add();
  641. impDesc.ModuleName = dllName;
  642. // Get base address and length of desired DLL, and look up the function foreign name in the export table of that DLL.
  643. WString apiSetDllName = ToLower(dllName);
  644. const Win32ModuleInformation* modBaseAddr = NULL;
  645. // Check whether we have to deal with an ApiSet redirection or not.
  646. if (apiSetDllName.StartsWith("api-ms-win") || apiSetDllName.StartsWith("ext-ms-win"))
  647. {
  648. // Windows ApiSetSchema redirection detected. Remove the file extension before resolving.
  649. const int lastDot = apiSetDllName.ReverseFind('.');
  650. if (lastDot != -1)
  651. {
  652. apiSetDllName.Remove(lastDot, apiSetDllName.GetLength() - lastDot);
  653. }
  654. // Recursively resolve the redirection.
  655. while (!modBaseAddr)
  656. {
  657. // First remove the prefix, the resolving works without the prefix.
  658. apiSetDllName.Remove(0, 4);
  659. // Resolve the redirection.
  660. const wchar* const outWString = this->InlineResolveApiSetSchema(apiSetDllName);
  661. if (outWString)
  662. {
  663. apiSetDllName = outWString;
  664. delete[] outWString;
  665. // Get the module base address from the internal module list, and set the logical base address.
  666. modBaseAddr = mModuleManager->FindModule(apiSetDllName.ToString());
  667. impDesc.LogicalBaseAddress = modBaseAddr ? modBaseAddr->BaseAddress : 0;
  668. }
  669. }
  670. }
  671. else
  672. {
  673. // Get the module base address from the internal module list.
  674. modBaseAddr = mModuleManager->FindModule(apiSetDllName.ToString());
  675. impDesc.LogicalBaseAddress = 0;
  676. }
  677. // Does the module have a base address? If so, we parse its export table.
  678. if (modBaseAddr)
  679. {
  680. impDesc.ModulePointer = modBaseAddr;
  681. Byte dllBuffer[0x400];
  682. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)modBaseAddr->BaseAddress, dllBuffer, 0x400, NULL);
  683. const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(dllBuffer + ((IMAGE_DOS_HEADER*)dllBuffer)->e_lfanew);
  684. IMAGE_DATA_DIRECTORY dataDir = *(&((IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader)->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
  685. // Check whether the discovered module actually has an export table.
  686. if (dataDir.Size)
  687. {
  688. Byte* const exportDirectoryBuffer = new Byte[dataDir.Size];
  689. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(modBaseAddr->BaseAddress + dataDir.VirtualAddress), exportDirectoryBuffer, dataDir.Size, NULL);
  690. Byte* const bufBase = (exportDirectoryBuffer - dataDir.VirtualAddress);
  691. AddrStruct addrStruct((Byte*)modBaseAddr->BaseAddress, (exportDirectoryBuffer - dataDir.VirtualAddress), bufBase + dataDir.VirtualAddress + dataDir.Size
  692. , &dataDir, (IMAGE_EXPORT_DIRECTORY*)exportDirectoryBuffer);
  693. IMAGE_THUNK_DATA32 thunk;
  694. unsigned int count = 0;
  695. do
  696. {
  697. // Try to read current thunk into local memory.
  698. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + pDesc.OriginalFirstThunk + count * sizeof(DWORD)), &thunk, sizeof(IMAGE_THUNK_DATA32), NULL);
  699. ImportAddressTableEntry funcEntry;
  700. // Check for 32-bit ordinal magic flag.
  701. if (thunk.u1.Ordinal & IMAGE_ORDINAL_FLAG32)
  702. {
  703. funcEntry.Ordinal = IMAGE_ORDINAL32(thunk.u1.Ordinal);
  704. funcEntry.Hint = 0;
  705. if (addrStruct.ExportDirectory->AddressOfNames)
  706. {
  707. funcEntry.FunctionName = this->GetOrdinalFunctionNameFromExportTable(&addrStruct, funcEntry.Ordinal);
  708. }
  709. }
  710. else
  711. {
  712. // Read function name from thunk data.
  713. char funcName[96];
  714. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + thunk.u1.AddressOfData), funcName, 96, NULL);
  715. // Set ordinal value to 0, read function name and WORD sized hint from the first two read bytes sequence.
  716. funcEntry.Ordinal = 0;
  717. funcEntry.Hint = *(WORD*)funcName;
  718. funcEntry.FunctionName = funcName + sizeof(WORD);
  719. }
  720. // In a rare occasion the ordinal bit-flag is already removed. In this case the ordinal should be detected by section awareness.
  721. if (funcEntry.FunctionName.IsEmpty() && !RVAPointsInsideSection(thunk.u1.Ordinal))
  722. {
  723. funcEntry.Ordinal = (WORD)thunk.u1.Ordinal;
  724. funcEntry.Hint = 0;
  725. if (addrStruct.ExportDirectory->AddressOfNames)
  726. {
  727. funcEntry.FunctionName = this->GetOrdinalFunctionNameFromExportTable(&addrStruct, funcEntry.Ordinal);
  728. }
  729. }
  730. // If the function name is empty even after ordinal resolving, the function has no name. Give it an automated name.
  731. if (funcEntry.FunctionName.IsEmpty())
  732. {
  733. String localModName = dllName;
  734. const int dotIndex = localModName.ReverseFind('.');
  735. localModName.Remove(dotIndex, localModName.GetLength() - dotIndex);
  736. funcEntry.FunctionName = Format("%s.%i", localModName, funcEntry.Ordinal);
  737. }
  738. // Save the thunk address of the function.
  739. funcEntry.ThunkAddress = this->mBaseAddress + pDesc.FirstThunk + count++ * sizeof(DWORD);
  740. // Read function address from thunk data and increment function iteration counter.
  741. DWORD funcAddress;
  742. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)funcEntry.ThunkAddress, &funcAddress, sizeof(DWORD), NULL);
  743. if (!funcAddress)
  744. {
  745. continue;
  746. }
  747. funcEntry.VirtualAddress = funcAddress;
  748. funcEntry.Flag = 0;
  749. // Check whether actual address is equal to the address it should be, otherwise the IAT is hooked.
  750. const SIZE_T eatAddress = this->GetAddressFromExportTable(&addrStruct, funcEntry.Ordinal ? (char*)funcEntry.Ordinal : funcEntry.FunctionName.Begin(), funcEntry.Ordinal ? 0 : funcEntry.FunctionName.GetLength());
  751. if (eatAddress == EAT_ADDRESS_NOT_FOUND)
  752. {
  753. funcEntry.Flag = IAT_FLAG_NOT_FOUND;
  754. }
  755. else if (eatAddress != funcEntry.VirtualAddress)
  756. {
  757. funcEntry.Flag = IAT_FLAG_HOOKED;
  758. }
  759. // Add function to import descriptor.
  760. impDesc.FunctionList.Add(funcEntry);
  761. }
  762. while (thunk.u1.AddressOfData);
  763. delete[] exportDirectoryBuffer;
  764. }
  765. }
  766. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + (++counter * sizeof(IMAGE_IMPORT_DESCRIPTOR))), &pDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR), NULL);
  767. }
  768. return result;
  769. }
  770. // Places a hook in the IAT, replacing the function address with another one.
  771. // First parameter is either a pointer to a buffer containing the function name or an ordinal value.
  772. bool PortableExecutable32::PlaceIATHook(const Win32ModuleInformation* modBase, const char* NameOrdinal, const SIZE_T newAddress, bool IsOrdinal) const
  773. {
  774. bool result = false;
  775. Byte* const moduleBuffer = new Byte[modBase->Length];
  776. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)this->mBaseAddress, moduleBuffer, modBase->Length, NULL);
  777. const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(moduleBuffer + ((IMAGE_DOS_HEADER*)moduleBuffer)->e_lfanew);
  778. const IMAGE_OPTIONAL_HEADER32* const pOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader;
  779. const IMAGE_IMPORT_DESCRIPTOR* pDesc = (IMAGE_IMPORT_DESCRIPTOR*)(moduleBuffer + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
  780. // Additional sanity checking, to avoid pDesc to be located outside of the allocated buffer.
  781. if ((Byte*)pDesc > moduleBuffer && (Byte*)pDesc < moduleBuffer + modBase->Length)
  782. {
  783. while (pDesc->FirstThunk)
  784. {
  785. const char* dllName = (char*)(moduleBuffer + pDesc->Name);
  786. IMAGE_THUNK_DATA32* thunk;
  787. unsigned int count = 0;
  788. do
  789. {
  790. thunk = (IMAGE_THUNK_DATA32*)(moduleBuffer + pDesc->OriginalFirstThunk + count * sizeof(DWORD));
  791. void* const AddressAddr = (void*)(this->mBaseAddress + pDesc->FirstThunk + count++ * sizeof(DWORD));
  792. if (IsOrdinal)
  793. {
  794. if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
  795. {
  796. // Ordinal import detected, check whether ordinal matches input value.
  797. if (IMAGE_ORDINAL32(thunk->u1.Ordinal) == (SIZE_T)NameOrdinal)
  798. {
  799. DWORD dwOldProtect;
  800. CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, AddressAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);
  801. CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, AddressAddr, &newAddress, sizeof(DWORD), NULL);
  802. CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, AddressAddr, sizeof(DWORD), dwOldProtect, &dwOldProtect);
  803. result = true;
  804. break;
  805. }
  806. }
  807. }
  808. else
  809. {
  810. // Check if function is ordinal, because if it is, skip this one.
  811. if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
  812. {
  813. continue;
  814. }
  815. const char* funcName = (char*)(moduleBuffer + thunk->u1.AddressOfData + sizeof(WORD));
  816. // Named import detected, check whether import matches input name.
  817. if (strcmp(funcName, NameOrdinal) == 0)
  818. {
  819. DWORD dwOldProtect;
  820. CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, AddressAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);
  821. CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, AddressAddr, &newAddress, sizeof(DWORD), NULL);
  822. CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, AddressAddr, sizeof(DWORD), dwOldProtect, &dwOldProtect);
  823. result = true;
  824. break;
  825. }
  826. }
  827. }
  828. while (thunk->u1.AddressOfData);
  829. ++pDesc;
  830. }
  831. }
  832. // Success, free used buffers and return.
  833. delete[] moduleBuffer;
  834. return result;
  835. }
  836. // Attempts to restore the PE headers from a file on the harddisk to a module loaded in memory.
  837. // Retuns true if the operation succeeded and false if it did not succeed.
  838. bool PortableExecutable32::RestorePEHeaderFromFile(const String& fileName, const Win32ModuleInformation& module) const
  839. {
  840. bool result = true;
  841. // Create handle to file on the disk.
  842. HANDLE hFile = CreateFile(fileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  843. if (!hFile || hFile == INVALID_HANDLE_VALUE)
  844. {
  845. result = false;
  846. }
  847. // Get file size and read file into buffer.
  848. LARGE_INTEGER size;
  849. GetFileSizeEx(hFile, &size);
  850. DWORD bytesRead;
  851. Byte* const fileBuffer = new Byte[size.LowPart];
  852. if (!ReadFile(hFile, fileBuffer, size.LowPart, &bytesRead, NULL))
  853. {
  854. result = false;
  855. }
  856. // Read header size.
  857. const IMAGE_DOS_HEADER* const pDOSHeader = (IMAGE_DOS_HEADER*)fileBuffer;
  858. if (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE)
  859. {
  860. return false;
  861. }
  862. const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)((BYTE*)pDOSHeader + pDOSHeader->e_lfanew);
  863. if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
  864. {
  865. result = false;
  866. }
  867. const IMAGE_OPTIONAL_HEADER32* const pOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader;
  868. // Write header data into process memory at designated location.
  869. CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, (void*)module.BaseAddress, pOptionalHeader->SizeOfHeaders, PAGE_READWRITE, &bytesRead);
  870. if (!CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, (void*)module.BaseAddress, fileBuffer, pOptionalHeader->SizeOfHeaders, NULL))
  871. {
  872. result = false;
  873. }
  874. CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, (void*)module.BaseAddress, pOptionalHeader->SizeOfHeaders, bytesRead, &bytesRead);
  875. delete[] fileBuffer;
  876. CloseHandle(hFile);
  877. return result;
  878. }
  879. // Attempts to hide a module from the loaded process. Hiding means it not being visible for debuggers anymore.
  880. // Returns true if the operation succeeded, and false if it did not succeed.
  881. bool PortableExecutable32::HideModuleFromProcess(const Win32ModuleInformation& module) const
  882. {
  883. if (!CrySearchRoutines.NtQueryInformationProcess)
  884. {
  885. return false;
  886. }
  887. // Retrieve target process information using Nt function.
  888. #ifdef _WIN64
  889. ULONG_PTR pebAddr;
  890. if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessWow64Information, &pebAddr, sizeof(ULONG_PTR), NULL) != STATUS_SUCCESS)
  891. #else
  892. PROCESS_BASIC_INFORMATION procBlock;
  893. if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessBasicInformation, &procBlock, sizeof(PROCESS_BASIC_INFORMATION), NULL) != STATUS_SUCCESS)
  894. #endif
  895. {
  896. return false;
  897. }
  898. PEB_LDR_DATA32 peb;
  899. SIZE_T pebPtr;
  900. // Read process environment block and loader data from the process memory.
  901. #ifdef _WIN64
  902. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (unsigned char*)pebAddr + offsetof(PEB32, LoaderData), &pebPtr, sizeof(DWORD), NULL);
  903. #else
  904. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (unsigned char*)procBlock.PebBaseAddress + offsetof(PEB, LoaderData), &pebPtr, sizeof(DWORD), NULL);
  905. #endif
  906. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)pebPtr, &peb, sizeof(PEB_LDR_DATA), NULL);
  907. LDR_MODULE32 curModule;
  908. bool found = false;
  909. unsigned int retryCount = 0;
  910. int moduleCount = 0;
  911. SIZE_T Head = peb.InMemoryOrderModuleList.Flink;
  912. SIZE_T Node = Head;
  913. do
  914. {
  915. // Read current linked list module from the process memory.
  916. if (CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(Node -= sizeof(LIST_ENTRY32)), &curModule, sizeof(LDR_MODULE32), NULL))
  917. {
  918. if (curModule.BaseAddress)
  919. {
  920. // some applications cause an infinite loop. This is one way to help preventing it.
  921. ++moduleCount;
  922. // A valid module is found, read its base dll name from the process memory.
  923. wchar BaseDllName[MAX_PATH];
  924. CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)curModule.BaseDllName.Buffer, BaseDllName, curModule.BaseDllName.Length, NULL);
  925. BaseDllName[curModule.BaseDllName.Length / 2] = 0;
  926. PathStripPathW(BaseDllName);
  927. // Compare current module's base name and desired module name, if it matches, the desired one is found.
  928. String selModName = mModuleManager->GetModuleFilename(module.BaseAddress);
  929. if (memcmp(selModName.ToWString().Begin(), BaseDllName, selModName.GetLength() * sizeof(wchar)) == 0)
  930. {
  931. found = true;
  932. for (unsigned int index = 0; index < 3; (Node += sizeof(LIST_ENTRY32)), index++)
  933. {
  934. LIST_ENTRY32 current;
  935. // Read current, previous and next list entry from the process memory.
  936. BOOL localRes = CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)Node, &current, sizeof(LIST_ENTRY32), NULL);
  937. if (!localRes)
  938. {
  939. found = false;
  940. break;
  941. }
  942. const SIZE_T nextItemAddr = current.Flink;
  943. const SIZE_T prevItemAddr = current.Blink;
  944. // Overwrite the pointers of the previous and next list entry so the current one is effectively hidden.
  945. localRes = CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, (void*)current.Blink, &nextItemAddr, sizeof(DWORD), NULL);
  946. localRes = CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, (unsigned char*)current.Flink + sizeof(DWORD), &prevItemAddr, sizeof(DWORD), NULL);
  947. }
  948. break;
  949. }
  950. }
  951. }
  952. else
  953. {
  954. // Prevent infinite while looping. In most situations this code may be unnessecary.
  955. if (++retryCount == 3)
  956. {
  957. break;
  958. }
  959. }
  960. // Desired module was not yet found, traverse to the next one.
  961. Node = curModule.InMemoryOrderModuleList.Flink;
  962. }
  963. while(Head != Node && moduleCount < mModuleMan

Large files files are truncated, but you can click here to view the full file