/PortableExecutable.cpp
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
- #include "PortableExecutable.h"
- #include "BackendGlobalDef.h"
- // Defines seperately because the Windows SDK 7.1 headers do not yet include these two.
- #define _WIN32_WINNT_WIN8 0x0602
- #define _WIN32_WINNT_WINBLUE 0x0603
- #include <Shlwapi.h>
- #pragma comment(lib, "shlwapi.lib")
- #define EAT_ADDRESS_NOT_FOUND -1
- #define THREAD_HIJACK_MAGIC_PROBE_THRESHOLD 12
- // Checks whether a RVA points inside a section of the executable. Returns true if so and false if not.
- const bool RVAPointsInsideSection(const DWORD rva)
- {
- // Walk the sections in the target process.
- for (auto const& section : LoadedProcessPEInformation.ImageSections)
- {
- // Check if the specified RVA is within the bounds of a section.
- if (rva > section.BaseAddress && rva < (section.BaseAddress + section.RawSectionSize))
- {
- return true;
- }
- }
-
- return false;
- }
- // -------------------------------------------------------------------------------------------------------------------------------
- // Base class methods
- // -------------------------------------------------------------------------------------------------------------------------------
- // Default PE class constructor.
- PortableExecutable::PortableExecutable()
- {
- this->mProcessHandle = mMemoryScanner->GetHandle();
- this->mBaseAddress = mModuleManager->GetBaseAddress();
- }
- // Default PE class destructor. Virtual destructor, always use derived class' destructor to execute this one.
- PortableExecutable::~PortableExecutable()
- {
- LoadedProcessPEInformation.PEFields.Clear();
- LoadedProcessPEInformation.Reset();
- LoadedProcessPEInformation.ClearImportTable();
- }
- // Sets the base address.
- void PortableExecutable::SetBaseAddress(const SIZE_T baseAddress)
- {
- this->mBaseAddress = baseAddress;
- }
- // Gets the base address.
- const SIZE_T PortableExecutable::GetBaseAddress() const
- {
- return this->mBaseAddress;
- }
- // Retrieves the address of the Process Environment Block of the opened process.
- // Returns the PEB base address or NULL if the address was not succesfully retrieved.
- void* PortableExecutable::GetPebAddress() const
- {
- #ifdef WIN64
- if (mMemoryScanner->IsX86Process())
- {
- // If we run CrySearch x64 and we target an x86 process, we use NtQueryInformationProcess with a ULONG_PTR parameter.
- ULONG_PTR PebBaseAddress;
- if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessWow64Information, &PebBaseAddress, sizeof(ULONG_PTR), NULL) == STATUS_SUCCESS)
- {
- return (void*)PebBaseAddress;
- }
- }
- else
- {
- // If we run CrySearch x64 and we target an x64 process, we use NtQueryInformationProcess with a PROCESS_BASIC_INFORMATION parameter.
- PROCESS_BASIC_INFORMATION tInfo;
- if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessBasicInformation, &tInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL) == STATUS_SUCCESS)
- {
- return tInfo.PebBaseAddress;
- }
- }
- #else
- // If we run CrySearch x86, we can only target an x86 process and we use NtQueryInformationProcess with a PROCESS_BASIC_INFORMATION parameter.
- PROCESS_BASIC_INFORMATION tInfo;
- if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessBasicInformation, &tInfo, sizeof(PROCESS_BASIC_INFORMATION), NULL) == STATUS_SUCCESS)
- {
- return tInfo.PebBaseAddress;
- }
- #endif
-
- return NULL;
- }
- // Parses input machine type and adds the parsed value to the global inventory for UI display.
- void PortableExecutable::ParseMachineType(const DWORD machineType) const
- {
- switch (machineType)
- {
- case IMAGE_FILE_MACHINE_I386:
- LoadedProcessPEInformation.PEFields.Add("Machine Type", "i386");
- break;
- case IMAGE_FILE_MACHINE_IA64:
- LoadedProcessPEInformation.PEFields.Add("Machine Type", "ia64");
- break;
- case IMAGE_FILE_MACHINE_AMD64:
- LoadedProcessPEInformation.PEFields.Add("Machine Type", "amd64");
- break;
- }
- }
- // Parses input subsystem type and adds the parsed value to the global inventory for UI display.
- void PortableExecutable::ParseSubsystemValue(const DWORD subSystem) const
- {
- switch (subSystem)
- {
- case IMAGE_SUBSYSTEM_UNKNOWN:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "Unknown");
- break;
- case IMAGE_SUBSYSTEM_NATIVE:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "Native");
- break;
- case IMAGE_SUBSYSTEM_WINDOWS_GUI:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "Windows GUI");
- break;
- case IMAGE_SUBSYSTEM_WINDOWS_CUI:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "Windows CUI");
- break;
- case IMAGE_SUBSYSTEM_OS2_CUI:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "OS/2 CUI");
- break;
- case IMAGE_SUBSYSTEM_POSIX_CUI:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "POSIX_CUI");
- break;
- case IMAGE_SUBSYSTEM_WINDOWS_CE_GUI:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "Windows CE CUI");
- break;
- case IMAGE_SUBSYSTEM_EFI_APPLICATION:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "EFI");
- break;
- case IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "EFI Boot Driver");
- break;
- case IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "EFI Runtime Driver");
- break;
- case IMAGE_SUBSYSTEM_EFI_ROM:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "EFI ROM");
- break;
- case IMAGE_SUBSYSTEM_XBOX:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "Xbox system");
- break;
- case IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION:
- LoadedProcessPEInformation.PEFields.Add("Subsystem", "Boot application");
- break;
- }
- }
- // This function retrieves all sections from a specified PE file. The NT header is provided to create flexibility.
- // The input list is cleared before the retrieval is started.
- void PortableExecutable::GetImageSectionsList(const IMAGE_SECTION_HEADER* pSecHeader, const DWORD numberOfSections, Vector<Win32PESectionInformation>& list) const
- {
- list.Clear();
-
- // Iterate through sections and save them for application use.
- for (unsigned int i = 0; i < numberOfSections; ++i, ++pSecHeader)
- {
- // Sometimes PE files contain bogus sections at runtime due to packer activities. Remove bogus sections from the list.
- if (!pSecHeader->VirtualAddress)
- {
- continue;
- }
-
- // The name of a section can only be 8 characters long. A longer name has a different notation.
- // This is not taken into account because the chance of it appearing in an executable is very small.
- list.Add(Win32PESectionInformation((char*)pSecHeader->Name, pSecHeader->VirtualAddress, pSecHeader->Misc.VirtualSize == 0 ? pSecHeader->SizeOfRawData
- : pSecHeader->Misc.VirtualSize, pSecHeader->SizeOfRawData));
- }
- }
- // This function is declared seperately to reduce code in a few functions spreaded over this file.
- // This increases the code readability even though the function will probably be inlined by the compiler.
- wchar* PortableExecutable::InlineResolveApiSetSchema(const WString& str) const
- {
- // Check which version of Windows is running. We need this to differentiate between ApiSetSchema versions.
- Tuple2<int, int> winver;
- if (GetInlineWindowsVersion(&winver))
- {
- // Call the correct ApiSetSchema parsing function.
- if (winver.a == 6)
- {
- if (winver.b == 4)
- {
- // Call the Windows 10 version of the parser.
- return this->ResolveApiSetSchemaMapping10(str, str.GetLength());
- }
- else if (winver.b > 2)
- {
- // Call the Windows 8(.1) version of the parser.
- return this->ResolveApiSetSchemaMappingEx(str, str.GetLength());
- }
- else
- {
- // Call the Windows 7 version of the parser.
- return this->ResolveApiSetSchemaMapping(str, str.GetLength());
- }
- }
- else if (winver.a == 10)
- {
- // Call the Windows 10 version of the parser.
- return this->ResolveApiSetSchemaMapping10(str, str.GetLength());
- }
- }
-
- return NULL;
- }
- // Resolves Windows 6.x ApiSetSchema redirections found in the IAT. Usually they redirect to a common Windows DLL like advapi32.dll.
- // 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!
- wchar* PortableExecutable::ResolveApiSetSchemaMapping(const wchar* ApiSetSchemaDll, const DWORD Length) const
- {
- // Retrieve PEB, the address of the map is there.
- #ifdef _WIN64
- APISETMAP* const apiSetSchemaBase = (APISETMAP*)((PPEB)__readgsqword(0x60))->ApiSetMap;
- #else
- APISETMAP* const apiSetSchemaBase = (APISETMAP*)((PPEB)__readfsdword(0x30))->ApiSetMap;
- #endif
-
- Byte* const apiSetSchemaFileBuffer = (Byte*)apiSetSchemaBase;
- DLLHOSTDESCRIPTOR* pDescriptor = apiSetSchemaBase->descriptors;
-
- // Iterate through the descriptor structs.
- for (unsigned int i = 0; i < apiSetSchemaBase->NumberOfHosts; ++i, ++pDescriptor)
- {
- // Compare virtual API with input.
- if (_wcsnicmp(ApiSetSchemaDll, (wchar*)(apiSetSchemaFileBuffer + pDescriptor->OffsetDllString), Length) == 0)
- {
- DLLREDIRECTOR* const directorStruct = (DLLREDIRECTOR*)(apiSetSchemaFileBuffer + pDescriptor->OffsetDllRedirector);
- // Iterate redirections for this api set.
- REDIRECTION* pRedirectionDescriptor = directorStruct->Redirection;
- const wchar* const redirectionString = (wchar*)(apiSetSchemaFileBuffer + pRedirectionDescriptor->OffsetRedirection2);
- // Redirection is found, create buffer to return to the caller and copy the logical dll name into it.
- const DWORD wcsLength = pRedirectionDescriptor->RedirectionLength2 / 2;
- wchar* const nameBuffer = new wchar[wcsLength + 1];
- memcpy(nameBuffer, redirectionString, pRedirectionDescriptor->RedirectionLength2);
- // Set null terminator in the string, otherwise the result contains the redirected dll name but the rest is undefined.
- nameBuffer[wcsLength] = NULL;
- return nameBuffer;
- }
- }
-
- return NULL;
- }
- // Compatibility with Windows 8.1 ApiSetSchema v2 is implemented since v1.04 of CrySearch.
- // Return value is the same as the PortableExecutable::ResolveApiSetSchemaMapping function.
- wchar* PortableExecutable::ResolveApiSetSchemaMappingEx(const wchar* ApiSetSchemaDll, const DWORD Length) const
- {
- // Retrieve PEB, the address of the map is there.
- #ifdef _WIN64
- API_SET_NAMESPACE_ARRAY_V2* const apiSetSchemaBase = (API_SET_NAMESPACE_ARRAY_V2*)((PPEB)__readgsqword(0x60))->ApiSetMap;
- #else
- API_SET_NAMESPACE_ARRAY_V2* const apiSetSchemaBase = (API_SET_NAMESPACE_ARRAY_V2*)((PPEB)__readfsdword(0x30))->ApiSetMap;
- #endif
-
- Byte* const apiSetSchemaFileBuffer = (Byte*)apiSetSchemaBase;
- API_SET_NAMESPACE_ENTRY_V2* pDescriptor = apiSetSchemaBase->Array;
-
- // Iterate through the descriptor structs.
- for (unsigned int i = 0; i < apiSetSchemaBase->Count; ++i, ++pDescriptor)
- {
- // Compare virtual API with input.
- if (_wcsnicmp(ApiSetSchemaDll, (wchar*)(apiSetSchemaFileBuffer + pDescriptor->NameOffset), Length) == 0)
- {
- API_SET_VALUE_ARRAY_V2* const directorStruct = (API_SET_VALUE_ARRAY_V2*)(apiSetSchemaFileBuffer + pDescriptor->DataOffset);
-
- // Iterate redirections for this api set.
- API_SET_VALUE_ENTRY_V2* pRedirectionDescriptor = directorStruct->Array;
- const wchar* const redirectionString = (wchar*)(apiSetSchemaFileBuffer + pRedirectionDescriptor->ValueOffset);
-
- // Apparently a virtual library may have two redirections. If the library being
- // processed matches the first API redirection, we must use the second one.
- const unsigned int totalLengthW = (unsigned int)wcslen(redirectionString);
- const DWORD wcsIndex = pRedirectionDescriptor->ValueLength / sizeof(wchar);
- wchar* const nameBuffer = new wchar[totalLengthW * sizeof(wchar)];
- if (_wcsnicmp(ApiSetSchemaDll, redirectionString + 4, Length) == 0)
- {
- // Redirection is found, create buffer to return to the caller and copy the logical dll name into it.
- const DWORD wcharCount = totalLengthW - wcsIndex;
- memcpy(nameBuffer, redirectionString + wcsIndex, wcharCount);
- nameBuffer[wcharCount] = NULL;
- }
- else
- {
- // Take the first redirection as result.
- memcpy(nameBuffer, redirectionString, pRedirectionDescriptor->ValueLength);
- nameBuffer[pRedirectionDescriptor->ValueLength / sizeof(wchar)] = NULL;
- }
-
- return nameBuffer;
- }
- }
-
- return NULL;
- }
- // Compatibility with Windows 10 ApiSetSchema is implemented since v2.0 of CrySearch.
- // Return value is the same as the PortableExecutable::ResolveApiSetSchemaMapping function.
- wchar* PortableExecutable::ResolveApiSetSchemaMapping10(const wchar* ApiSetSchemaDll, const DWORD Length ) const
- {
- // Retrieve PEB, the address of the map is there.
- #ifdef _WIN64
- API_SET_NAMESPACE_ARRAY_10* const apiSetSchemaBase = (API_SET_NAMESPACE_ARRAY_10*)((PPEB)__readgsqword(0x60))->ApiSetMap;
- #else
- API_SET_NAMESPACE_ARRAY_10* const apiSetSchemaBase = (API_SET_NAMESPACE_ARRAY_10*)((PPEB)__readfsdword(0x30))->ApiSetMap;
- #endif
-
- Byte* const apiSetSchemaFileBuffer = (Byte*)apiSetSchemaBase;
- API_SET_NAMESPACE_ENTRY_10* pDescriptor = (API_SET_NAMESPACE_ENTRY_10*)(apiSetSchemaFileBuffer + apiSetSchemaBase->End);
- // Iterate through the descriptor structs.
- for (unsigned int i = 0; i < apiSetSchemaBase->Count; ++i, ++pDescriptor)
- {
- // Retrieve the data associated with the current ApiSet schema entry.
- API_SET_VALUE_ARRAY_10* const directorStruct = (API_SET_VALUE_ARRAY_10*)(apiSetSchemaFileBuffer + apiSetSchemaBase->Start + sizeof(API_SET_VALUE_ARRAY_10) * pDescriptor->Size);
-
- // 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...'.
- if (_wcsnicmp(ApiSetSchemaDll, (wchar*)(apiSetSchemaFileBuffer + directorStruct->NameOffset + 8), Length - 1) == 0)
- {
- // Iterate redirections for this api set.
- API_SET_VALUE_ENTRY_10* pRedirectionDescriptor = (API_SET_VALUE_ENTRY_10*)(apiSetSchemaFileBuffer + directorStruct->DataOffset);
- const wchar* const redirectionString = (wchar*)(apiSetSchemaFileBuffer + pRedirectionDescriptor->ValueOffset);
-
- // Apparently a virtual library may have two redirections. If the library being
- // processed matches the first API redirection, we must use the second one.
- const unsigned int totalLengthW = (unsigned int)wcslen(redirectionString);
- const DWORD wcsIndex = pRedirectionDescriptor->ValueLength / sizeof(wchar);
- wchar* const nameBuffer = new wchar[totalLengthW * sizeof(wchar)];
- if (_wcsnicmp(ApiSetSchemaDll, redirectionString + 4, Length) == 0)
- {
- // Redirection is found, create buffer to return to the caller and copy the logical dll name into it.
- const DWORD wcharCount = totalLengthW - wcsIndex;
- memcpy(nameBuffer, redirectionString + wcsIndex, wcharCount);
- nameBuffer[wcharCount] = NULL;
- }
- else
- {
- // Take the first redirection as result.
- memcpy(nameBuffer, redirectionString, pRedirectionDescriptor->ValueLength);
- nameBuffer[pRedirectionDescriptor->ValueLength / sizeof(wchar)] = NULL;
- }
-
- return nameBuffer;
- }
- }
-
- return NULL;
- }
- // Reads the COM directory from a PE file header. Most likely this is the .NET header.
- // This function does not free the buffer pointed to by the parameter.
- void PortableExecutable::GetDotNetDirectoryInformation(const IMAGE_DATA_DIRECTORY* const netHeader) const
- {
- // Check if the executable contains a COM header.
- if (netHeader->VirtualAddress && netHeader->Size >= sizeof(IMAGE_COR20_HEADER))
- {
- SIZE_T bytesRead;
- // Read COR20 header from file.
- Byte* netDirBuffer = new Byte[netHeader->Size];
- bool b = CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + netHeader->VirtualAddress), netDirBuffer, netHeader->Size, &bytesRead);
- // Check if the section data was read succesfully.
- if (b && bytesRead == netHeader->Size)
- {
- // Save version information from COR20 header.
- IMAGE_DATA_DIRECTORY mdDir;
- memcpy(&mdDir, &((IMAGE_COR20_HEADER*)netDirBuffer)->MetaData, sizeof(IMAGE_DATA_DIRECTORY));
- delete[] netDirBuffer;
- // Save the offset to the metadata header to allow dumping of .NET sections later.
- LoadedProcessPEInformation.DotNetInformation.MetadataHeaderOffset = mdDir.VirtualAddress;
- // Read metadata from header.
- netDirBuffer = new Byte[mdDir.Size];
- b = CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + mdDir.VirtualAddress), netDirBuffer, mdDir.Size, &bytesRead);
- // Check whether the metadata was succesfully read into the local buffer.
- if (b && bytesRead == mdDir.Size)
- {
- // Dissect metadata. Since its a dynamic structure we cannot compile this into a struct.
- const DWORD vStrLength = *(DWORD*)(netDirBuffer + 12);
- const WORD streamCount = *(WORD*)(netDirBuffer + 18 + vStrLength);
- // Based on the stream count, dissect streams from the header.
- DWORD streamIterator = 0;
- for (const char* iterator = (char*)(netDirBuffer + 20 + vStrLength); streamIterator < streamCount; ++streamIterator)
- {
- // Get offset and size fields.
- const DWORD* const offsetPtr = (DWORD*)iterator;
- iterator += sizeof(DWORD);
- const DWORD* const sizePtr = (DWORD*)iterator;
- iterator += sizeof(DWORD);
- // Read the name of the stream.
- WORD str = 0;
- const char* const beginIterator = iterator;
- bool strEnded = false;
- while (1)
- {
- // First find the end of the string.
- if (!strEnded && *iterator == 0)
- {
- strEnded = true;
- }
- // Continue until the next 4 byte boundary is reached.
- else if (strEnded && ((SIZE_T)iterator % 4) == 0)
- {
- break;
- }
- ++str;
- ++iterator;
- }
- // String length was measured, now read it into a variable.
- Win32DotNetSectionInformation& newSect = LoadedProcessPEInformation.DotNetInformation.DotNetSections.Add();
- newSect.SectionName = String(beginIterator, str + 1);
- newSect.Offset = *offsetPtr;
- newSect.Size = *sizePtr;
- }
- }
- }
- delete[] netDirBuffer;
- }
- }
- // Resolves ApiSetSchema module names and returns a pointer to the resolved module.
- const Win32ModuleInformation* PortableExecutable::GetResolvedModule(const char* pModName, int* const recurseIndex, const char* NameOrdinal) const
- {
- // Find the first dot in the filename.
- String forwardedModName = pModName;
- *recurseIndex = forwardedModName.Find('.');
- // If the resulting filename starts with api-ms-win, we are dealing with ApiSetSchema libraries.
- WString apiSetDllName = ToLower(forwardedModName);
- if (apiSetDllName.StartsWith("api-ms-win") || apiSetDllName.StartsWith("ext-ms-win"))
- {
- apiSetDllName.Remove(0, 4);
- apiSetDllName.Remove(apiSetDllName.Find('.'), (int)strlen(NameOrdinal) + 1);
- const wchar* const outWString = this->InlineResolveApiSetSchema(apiSetDllName);
- forwardedModName = WString(outWString).ToString();
- delete[] outWString;
- }
- // Append .dll after the filename.
- const int dotIndex = forwardedModName.Find('.');
- if (dotIndex >= 0)
- {
- forwardedModName.Remove(dotIndex, forwardedModName.GetLength() - dotIndex);
- forwardedModName += ".dll";
- }
- // Resolve the module to a loaded one.
- return mModuleManager->FindModule(forwardedModName);
- }
- // -------------------------------------------------------------------------------------------------------------------------------
- // PE32 class methods
- // -------------------------------------------------------------------------------------------------------------------------------
- // Default PE32 destructor. Base class destructor is virtual so this destructor is executed with the base's.
- PortableExecutable32::~PortableExecutable32()
- {
-
- }
- // Retrieves PE header information from the loaded process. Information is saved in global storage that has process lifetime.
- // 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.
- void PortableExecutable32::GetExecutablePeInformation() const
- {
- // Clear image sections before getting new ones.
- LoadedProcessPEInformation.Reset();
-
- // Read process memory into local buffer in order to load PE headers.
- Byte moduleBuffer[0x400];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)this->mBaseAddress, moduleBuffer, 0x400, NULL);
-
- // Load PE headers.
- IMAGE_DOS_HEADER* const pDosHeader = (IMAGE_DOS_HEADER*)moduleBuffer;
- IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(moduleBuffer + pDosHeader->e_lfanew);
- // When the PE Headers are destroyed at runtime the pointer to the headers may run out of the buffer's bounds.
- if ((Byte*)pNTHeader > (moduleBuffer + 0x400))
- {
- LoadedProcessPEInformation.PEFields.Clear();
- return;
- }
-
- // The headers should be fine, proceed loading the optional and file header.
- const IMAGE_OPTIONAL_HEADER32* const pOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader;
- const IMAGE_FILE_HEADER* const pFileHeader = &(pNTHeader->FileHeader);
-
- // Retrieve the type of machine the PE executable can run on.
- this->ParseMachineType(pFileHeader->Machine);
-
- // Retrieve PE fields and add them to the map.
- LoadedProcessPEInformation.PEFields.Add("Number of sections", pFileHeader->NumberOfSections);
- LoadedProcessPEInformation.PEFields.Add("Size of optional header", Format("%X", pFileHeader->SizeOfOptionalHeader));
- LoadedProcessPEInformation.PEFields.Add("Pointer to symbol table", (int)pFileHeader->PointerToSymbolTable);
- LoadedProcessPEInformation.PEFields.Add("Number of symbols", (int)pFileHeader->NumberOfSymbols);
- LoadedProcessPEInformation.PEFields.Add("Image base", Format("%lX", (int)pOptionalHeader->ImageBase));
- #ifndef _WIN64
- LoadedProcessPEInformation.PEFields.Add("Base of data", Format("%lX", (int)pOptionalHeader->BaseOfData));
- #endif
- LoadedProcessPEInformation.PEFields.Add("Base of code", Format("%lX", (int)pOptionalHeader->BaseOfCode));
- LoadedProcessPEInformation.PEFields.Add("Address of entrypoint", Format("%lX", (int)pOptionalHeader->AddressOfEntryPoint));
- LoadedProcessPEInformation.PEFields.Add("Size of code", Format("%lX", (int)pOptionalHeader->SizeOfCode));
- LoadedProcessPEInformation.PEFields.Add("Size of initialized data", Format("%lX", (int)pOptionalHeader->SizeOfInitializedData));
- LoadedProcessPEInformation.PEFields.Add("Size of uninitialized data", Format("%lX", (int)pOptionalHeader->SizeOfUninitializedData));
- LoadedProcessPEInformation.PEFields.Add("Section alignment", Format("%lX", (int)pOptionalHeader->SectionAlignment));
- LoadedProcessPEInformation.PEFields.Add("File alignment", Format("%lX", (int)pOptionalHeader->FileAlignment));
- LoadedProcessPEInformation.PEFields.Add("Size of image", Format("%lX", (int)pOptionalHeader->SizeOfImage));
- LoadedProcessPEInformation.PEFields.Add("Size of headers", Format("%lX", (int)pOptionalHeader->SizeOfHeaders));
- LoadedProcessPEInformation.PEFields.Add("Checksum", Format("%lX", (int)pOptionalHeader->CheckSum));
- LoadedProcessPEInformation.PEFields.Add("Linker version", Format("%i.%i", pOptionalHeader->MajorLinkerVersion, pOptionalHeader->MinorLinkerVersion));
- LoadedProcessPEInformation.PEFields.Add("OS version", Format("%i.%i", pOptionalHeader->MajorOperatingSystemVersion, pOptionalHeader->MinorOperatingSystemVersion));
- LoadedProcessPEInformation.PEFields.Add("Image version", Format("%i.%i", pOptionalHeader->MajorImageVersion, pOptionalHeader->MinorImageVersion));
- LoadedProcessPEInformation.PEFields.Add("Subsystem version", Format("%i.%i", pOptionalHeader->MajorSubsystemVersion, pOptionalHeader->MinorSubsystemVersion));
- LoadedProcessPEInformation.PEFields.Add("Number of data directories", Format("%lX", (int)pOptionalHeader->NumberOfRvaAndSizes));
-
- // Parse the last general property value of the PE header, save the section values and destroy the buffer.
- this->ParseSubsystemValue(pOptionalHeader->Subsystem);
- const DWORD sectionCount = pNTHeader->FileHeader.NumberOfSections;
- const DWORD sectionSizeBytes = sizeof(IMAGE_SECTION_HEADER) * sectionCount;
- const IMAGE_SECTION_HEADER* const firstSectionPtr = (IMAGE_SECTION_HEADER*)(this->mBaseAddress + ((Byte*)IMAGE_FIRST_SECTION(pNTHeader) - moduleBuffer));
-
- // Get the COM header from the PE file.
- this->GetDotNetDirectoryInformation(&pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]);
-
- // Attempt to load the sections inside the PE file.
- Byte* const sectionBuffer = new Byte[sectionSizeBytes];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, firstSectionPtr, sectionBuffer, sectionSizeBytes, NULL);
- this->GetImageSectionsList((IMAGE_SECTION_HEADER*)sectionBuffer, sectionCount, LoadedProcessPEInformation.ImageSections);
- delete[] sectionBuffer;
- }
- // Retrieves the address of a function in the export table of a module. Address can be returned for function by name or ordinal.
- // Returns the address of the function, created from the module base address added by the function RVA.
- // If the function is not found, the return value is 0xFFFFFFFF.
- // The NameLength parameter contains the length of the name if there is a name, or 0 if the function is ordinal-based.
- SIZE_T PortableExecutable32::GetAddressFromExportTable(const AddrStruct* addr, const char* NameOrdinal, const unsigned int NameLength) const
- {
- if (addr->ExportDirectory->AddressOfNameOrdinals)
- {
- int ResurseDotIndex = 0;
- const DWORD* funcAddrPtr = NULL;
- bool b;
- SIZE_T bytesRead;
-
- // Walk the exported functions in the target module.
- for (unsigned int i = 0; i < addr->ExportDirectory->NumberOfFunctions; ++i)
- {
- const WORD* const ordValue = (WORD*)((addr->BufferBaseAddress + addr->ExportDirectory->AddressOfNameOrdinals) + (i * sizeof(WORD)));
- if (!NameLength)
- {
- bool found = false;
- // Compare ordinal values without magic bitoperations!
- if ((addr->ExportDirectory->Base + *ordValue) == *reinterpret_cast<WORD*>(&NameOrdinal))
- {
- funcAddrPtr = (DWORD*)((addr->BufferBaseAddress + addr->ExportDirectory->AddressOfFunctions) + (sizeof(DWORD) * *ordValue));
- found = true;
- }
-
- // Skip the entry if it is not found.
- if (!found || ((Byte*)funcAddrPtr < addr->BufferBaseAddress || (Byte*)funcAddrPtr > addr->BufferEndAddress))
- {
- continue;
- }
-
- if (*funcAddrPtr > addr->DirectoryAddress->VirtualAddress && *funcAddrPtr < (addr->DirectoryAddress->VirtualAddress + addr->DirectoryAddress->Size))
- {
- const Win32ModuleInformation* modBaseAddr = this->GetResolvedModule((char*)(addr->BufferBaseAddress + *funcAddrPtr), &ResurseDotIndex, NameOrdinal);
-
- // Sometimes infinite redirecting causes stack overflowing. Terminate this sequence by returning not found.
- if (!modBaseAddr || (SIZE_T)addr->BaseAddress == modBaseAddr->BaseAddress)
- {
- return EAT_ADDRESS_NOT_FOUND;
- }
-
- Byte dllBuffer[0x400];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)modBaseAddr->BaseAddress, dllBuffer, 0x400, NULL);
-
- const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(dllBuffer + ((IMAGE_DOS_HEADER*)dllBuffer)->e_lfanew);
- IMAGE_DATA_DIRECTORY dataDir = *(&((IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader)->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
-
- Byte* const exportDirectoryBuffer = new Byte[dataDir.Size];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(modBaseAddr->BaseAddress + dataDir.VirtualAddress), exportDirectoryBuffer, dataDir.Size, NULL);
-
- Byte* const bufBase = exportDirectoryBuffer - dataDir.VirtualAddress;
- AddrStruct addrStruct((Byte*)modBaseAddr->BaseAddress, (exportDirectoryBuffer - dataDir.VirtualAddress), bufBase + dataDir.VirtualAddress + dataDir.Size
- , &dataDir, (IMAGE_EXPORT_DIRECTORY*)exportDirectoryBuffer);
-
- SIZE_T forwardedAddress = this->GetAddressFromExportTable(&addrStruct, (char*)addr->BufferBaseAddress + *funcAddrPtr + ResurseDotIndex + 2, NameLength);
- delete[] exportDirectoryBuffer;
- return forwardedAddress;
- }
- return (SIZE_T)(addr->BaseAddress + *funcAddrPtr);
- }
- else
- {
- const DWORD* const stringPtr = (DWORD*)((addr->BufferBaseAddress + addr->ExportDirectory->AddressOfNames) + (i * sizeof(DWORD)));
- const char* const functionName = (char*)(addr->BufferBaseAddress + *stringPtr);
-
- if ((functionName > (char*)addr->BufferBaseAddress + addr->DirectoryAddress->VirtualAddress && (functionName + NameLength + 1) < (char*)addr->BufferEndAddress) && memcmp(NameOrdinal, functionName, NameLength) == 0)
- {
- funcAddrPtr = (DWORD*)((addr->BufferBaseAddress + addr->ExportDirectory->AddressOfFunctions) + (sizeof(DWORD) * *ordValue));
- if (*funcAddrPtr > addr->DirectoryAddress->VirtualAddress && *funcAddrPtr < (addr->DirectoryAddress->VirtualAddress + addr->DirectoryAddress->Size))
- {
- const Win32ModuleInformation* modBaseAddr = this->GetResolvedModule((char*)(addr->BufferBaseAddress + *funcAddrPtr), &ResurseDotIndex, NameOrdinal);
-
- if (!modBaseAddr || (SIZE_T)addr->BaseAddress == modBaseAddr->BaseAddress)
- {
- return EAT_ADDRESS_NOT_FOUND;
- }
-
- Byte dllBuffer[0x400];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)modBaseAddr->BaseAddress, dllBuffer, 0x400, NULL);
-
- const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(dllBuffer + ((IMAGE_DOS_HEADER*)dllBuffer)->e_lfanew);
- IMAGE_DATA_DIRECTORY dataDir = *(&((IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader)->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
-
- // Check if the size of the data directory is valid.
- if (dataDir.Size)
- {
- // Read the export directory from memory.
- Byte* const exportDirectoryBuffer = new Byte[dataDir.Size];
- b = CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(modBaseAddr->BaseAddress + dataDir.VirtualAddress), exportDirectoryBuffer, dataDir.Size, &bytesRead);
-
- // Check whether it was read succesfully.
- if (b && bytesRead == dataDir.Size)
- {
- Byte* const bufBase = exportDirectoryBuffer - dataDir.VirtualAddress;
- AddrStruct addrStruct((Byte*)modBaseAddr->BaseAddress, (exportDirectoryBuffer - dataDir.VirtualAddress), bufBase + dataDir.VirtualAddress + dataDir.Size
- , &dataDir, (IMAGE_EXPORT_DIRECTORY*)exportDirectoryBuffer);
-
- SIZE_T forwardedAddress = this->GetAddressFromExportTable(&addrStruct, (char*)(addr->BufferBaseAddress + *funcAddrPtr + ResurseDotIndex + 1), NameLength);
- delete[] exportDirectoryBuffer;
- return forwardedAddress;
- }
- else
- {
- return EAT_ADDRESS_NOT_FOUND;
- }
- }
- else
- {
- return EAT_ADDRESS_NOT_FOUND;
- }
- }
- return (SIZE_T)(addr->BaseAddress + *funcAddrPtr);
- }
- }
- }
- }
-
- return EAT_ADDRESS_NOT_FOUND;
- }
- // Attempts to retrieve function name associated to ordinal import from the export table of the loaded module.
- // Returns a pointer to the function name if it exists. If the function is not found, the return value is NULL.
- const char* PortableExecutable32::GetOrdinalFunctionNameFromExportTable(const AddrStruct* addr, const WORD ordinal) const
- {
- const WORD* const ordinals = (WORD*)(addr->BufferBaseAddress + addr->ExportDirectory->AddressOfNameOrdinals);
-
- for (unsigned int i = 0; i < addr->ExportDirectory->NumberOfFunctions; ++i)
- {
- if ((addr->ExportDirectory->Base + ordinals[i]) == ordinal)
- {
- const DWORD* const stringPtr = (DWORD*)(addr->BufferBaseAddress + addr->ExportDirectory->AddressOfNames + i * sizeof(DWORD));
- const Byte* const absStringPtr = (Byte*)(addr->BufferBaseAddress + *stringPtr);
-
- // Make sure the string points inside of the buffer. Scrambled EAT would crash the application.
- if (absStringPtr > (addr->BufferBaseAddress + addr->DirectoryAddress->Size) && absStringPtr < addr->BufferEndAddress)
- {
- return (const char*)absStringPtr;
- }
- }
- }
-
- return NULL;
- }
- // Retrieves the import table from the PE header of the loaded process. This information is stored in the global storage that has process lifetime.
- // 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.
- const bool PortableExecutable32::GetImportAddressTable() const
- {
- // We use a return value to indicate whether function names can actually be retrieved using OriginalFirstThunk.
- bool result = true;
- // Read process memory into local buffer in order to load IAT.
- Byte moduleBuffer[0x400];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)this->mBaseAddress, moduleBuffer, 0x400, NULL);
-
- const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(moduleBuffer + ((IMAGE_DOS_HEADER*)moduleBuffer)->e_lfanew);
- // The PE Headers are not valid, the pointer runs outside the bounds of the buffer.
- if ((Byte*)pNTHeader > (moduleBuffer + 0x400))
- {
- return false;
- }
- const IMAGE_OPTIONAL_HEADER32* const pOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader;
-
- // Read the import descriptor table into local memory.
- unsigned int counter = 0;
- IMAGE_IMPORT_DESCRIPTOR pDesc;
- 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);
- // Check whether OriginalFirstThunk is non-zero. If it is zero, the target process executable may be packed. We can find
- // the function addresses, but we cannot find the function names directly.
- if (!pDesc.OriginalFirstThunk)
- {
- result = false;
- }
- while (pDesc.FirstThunk && pDesc.Name != 0xFFFF)
- {
- // Read DLL name from import descriptor entry.
- char dllName[48];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(pDesc.Name + this->mBaseAddress), dllName, 48, NULL);
-
- // Add new import descriptor to the table.
- ImportTableDescriptor& impDesc = LoadedProcessPEInformation.ImportAddressTable.Add();
- impDesc.ModuleName = dllName;
-
- // Get base address and length of desired DLL, and look up the function foreign name in the export table of that DLL.
- WString apiSetDllName = ToLower(dllName);
- const Win32ModuleInformation* modBaseAddr = NULL;
- // Check whether we have to deal with an ApiSet redirection or not.
- if (apiSetDllName.StartsWith("api-ms-win") || apiSetDllName.StartsWith("ext-ms-win"))
- {
- // Windows ApiSetSchema redirection detected. Remove the file extension before resolving.
- const int lastDot = apiSetDllName.ReverseFind('.');
- if (lastDot != -1)
- {
- apiSetDllName.Remove(lastDot, apiSetDllName.GetLength() - lastDot);
- }
- // Recursively resolve the redirection.
- while (!modBaseAddr)
- {
- // First remove the prefix, the resolving works without the prefix.
- apiSetDllName.Remove(0, 4);
- // Resolve the redirection.
- const wchar* const outWString = this->InlineResolveApiSetSchema(apiSetDllName);
- if (outWString)
- {
- apiSetDllName = outWString;
- delete[] outWString;
- // Get the module base address from the internal module list, and set the logical base address.
- modBaseAddr = mModuleManager->FindModule(apiSetDllName.ToString());
- impDesc.LogicalBaseAddress = modBaseAddr ? modBaseAddr->BaseAddress : 0;
- }
- }
- }
- else
- {
- // Get the module base address from the internal module list.
- modBaseAddr = mModuleManager->FindModule(apiSetDllName.ToString());
- impDesc.LogicalBaseAddress = 0;
- }
-
- // Does the module have a base address? If so, we parse its export table.
- if (modBaseAddr)
- {
- impDesc.ModulePointer = modBaseAddr;
-
- Byte dllBuffer[0x400];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)modBaseAddr->BaseAddress, dllBuffer, 0x400, NULL);
-
- const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(dllBuffer + ((IMAGE_DOS_HEADER*)dllBuffer)->e_lfanew);
- IMAGE_DATA_DIRECTORY dataDir = *(&((IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader)->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
-
- // Check whether the discovered module actually has an export table.
- if (dataDir.Size)
- {
- Byte* const exportDirectoryBuffer = new Byte[dataDir.Size];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(modBaseAddr->BaseAddress + dataDir.VirtualAddress), exportDirectoryBuffer, dataDir.Size, NULL);
-
- Byte* const bufBase = (exportDirectoryBuffer - dataDir.VirtualAddress);
- AddrStruct addrStruct((Byte*)modBaseAddr->BaseAddress, (exportDirectoryBuffer - dataDir.VirtualAddress), bufBase + dataDir.VirtualAddress + dataDir.Size
- , &dataDir, (IMAGE_EXPORT_DIRECTORY*)exportDirectoryBuffer);
-
- IMAGE_THUNK_DATA32 thunk;
- unsigned int count = 0;
-
- do
- {
- // Try to read current thunk into local memory.
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + pDesc.OriginalFirstThunk + count * sizeof(DWORD)), &thunk, sizeof(IMAGE_THUNK_DATA32), NULL);
- ImportAddressTableEntry funcEntry;
-
- // Check for 32-bit ordinal magic flag.
- if (thunk.u1.Ordinal & IMAGE_ORDINAL_FLAG32)
- {
- funcEntry.Ordinal = IMAGE_ORDINAL32(thunk.u1.Ordinal);
- funcEntry.Hint = 0;
-
- if (addrStruct.ExportDirectory->AddressOfNames)
- {
- funcEntry.FunctionName = this->GetOrdinalFunctionNameFromExportTable(&addrStruct, funcEntry.Ordinal);
- }
- }
- else
- {
- // Read function name from thunk data.
- char funcName[96];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(this->mBaseAddress + thunk.u1.AddressOfData), funcName, 96, NULL);
-
- // Set ordinal value to 0, read function name and WORD sized hint from the first two read bytes sequence.
- funcEntry.Ordinal = 0;
- funcEntry.Hint = *(WORD*)funcName;
- funcEntry.FunctionName = funcName + sizeof(WORD);
- }
-
- // In a rare occasion the ordinal bit-flag is already removed. In this case the ordinal should be detected by section awareness.
- if (funcEntry.FunctionName.IsEmpty() && !RVAPointsInsideSection(thunk.u1.Ordinal))
- {
- funcEntry.Ordinal = (WORD)thunk.u1.Ordinal;
- funcEntry.Hint = 0;
-
- if (addrStruct.ExportDirectory->AddressOfNames)
- {
- funcEntry.FunctionName = this->GetOrdinalFunctionNameFromExportTable(&addrStruct, funcEntry.Ordinal);
- }
- }
-
- // If the function name is empty even after ordinal resolving, the function has no name. Give it an automated name.
- if (funcEntry.FunctionName.IsEmpty())
- {
- String localModName = dllName;
- const int dotIndex = localModName.ReverseFind('.');
- localModName.Remove(dotIndex, localModName.GetLength() - dotIndex);
- funcEntry.FunctionName = Format("%s.%i", localModName, funcEntry.Ordinal);
- }
-
- // Save the thunk address of the function.
- funcEntry.ThunkAddress = this->mBaseAddress + pDesc.FirstThunk + count++ * sizeof(DWORD);
-
- // Read function address from thunk data and increment function iteration counter.
- DWORD funcAddress;
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)funcEntry.ThunkAddress, &funcAddress, sizeof(DWORD), NULL);
-
- if (!funcAddress)
- {
- continue;
- }
-
- funcEntry.VirtualAddress = funcAddress;
- funcEntry.Flag = 0;
-
- // Check whether actual address is equal to the address it should be, otherwise the IAT is hooked.
- const SIZE_T eatAddress = this->GetAddressFromExportTable(&addrStruct, funcEntry.Ordinal ? (char*)funcEntry.Ordinal : funcEntry.FunctionName.Begin(), funcEntry.Ordinal ? 0 : funcEntry.FunctionName.GetLength());
- if (eatAddress == EAT_ADDRESS_NOT_FOUND)
- {
- funcEntry.Flag = IAT_FLAG_NOT_FOUND;
- }
- else if (eatAddress != funcEntry.VirtualAddress)
- {
- funcEntry.Flag = IAT_FLAG_HOOKED;
- }
-
- // Add function to import descriptor.
- impDesc.FunctionList.Add(funcEntry);
- }
- while (thunk.u1.AddressOfData);
-
- delete[] exportDirectoryBuffer;
- }
- }
- 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);
- }
- return result;
- }
- // Places a hook in the IAT, replacing the function address with another one.
- // First parameter is either a pointer to a buffer containing the function name or an ordinal value.
- bool PortableExecutable32::PlaceIATHook(const Win32ModuleInformation* modBase, const char* NameOrdinal, const SIZE_T newAddress, bool IsOrdinal) const
- {
- bool result = false;
- Byte* const moduleBuffer = new Byte[modBase->Length];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)this->mBaseAddress, moduleBuffer, modBase->Length, NULL);
-
- const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)(moduleBuffer + ((IMAGE_DOS_HEADER*)moduleBuffer)->e_lfanew);
- const IMAGE_OPTIONAL_HEADER32* const pOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader;
-
- const IMAGE_IMPORT_DESCRIPTOR* pDesc = (IMAGE_IMPORT_DESCRIPTOR*)(moduleBuffer + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
-
- // Additional sanity checking, to avoid pDesc to be located outside of the allocated buffer.
- if ((Byte*)pDesc > moduleBuffer && (Byte*)pDesc < moduleBuffer + modBase->Length)
- {
- while (pDesc->FirstThunk)
- {
- const char* dllName = (char*)(moduleBuffer + pDesc->Name);
-
- IMAGE_THUNK_DATA32* thunk;
- unsigned int count = 0;
-
- do
- {
- thunk = (IMAGE_THUNK_DATA32*)(moduleBuffer + pDesc->OriginalFirstThunk + count * sizeof(DWORD));
- void* const AddressAddr = (void*)(this->mBaseAddress + pDesc->FirstThunk + count++ * sizeof(DWORD));
-
- if (IsOrdinal)
- {
- if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
- {
- // Ordinal import detected, check whether ordinal matches input value.
- if (IMAGE_ORDINAL32(thunk->u1.Ordinal) == (SIZE_T)NameOrdinal)
- {
- DWORD dwOldProtect;
- CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, AddressAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);
- CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, AddressAddr, &newAddress, sizeof(DWORD), NULL);
- CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, AddressAddr, sizeof(DWORD), dwOldProtect, &dwOldProtect);
- result = true;
- break;
- }
- }
- }
- else
- {
- // Check if function is ordinal, because if it is, skip this one.
- if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
- {
- continue;
- }
-
- const char* funcName = (char*)(moduleBuffer + thunk->u1.AddressOfData + sizeof(WORD));
-
- // Named import detected, check whether import matches input name.
- if (strcmp(funcName, NameOrdinal) == 0)
- {
- DWORD dwOldProtect;
- CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, AddressAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);
- CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, AddressAddr, &newAddress, sizeof(DWORD), NULL);
- CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, AddressAddr, sizeof(DWORD), dwOldProtect, &dwOldProtect);
- result = true;
- break;
- }
- }
- }
- while (thunk->u1.AddressOfData);
-
- ++pDesc;
- }
- }
-
- // Success, free used buffers and return.
- delete[] moduleBuffer;
- return result;
- }
- // Attempts to restore the PE headers from a file on the harddisk to a module loaded in memory.
- // Retuns true if the operation succeeded and false if it did not succeed.
- bool PortableExecutable32::RestorePEHeaderFromFile(const String& fileName, const Win32ModuleInformation& module) const
- {
- bool result = true;
-
- // Create handle to file on the disk.
- HANDLE hFile = CreateFile(fileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
- if (!hFile || hFile == INVALID_HANDLE_VALUE)
- {
- result = false;
- }
-
- // Get file size and read file into buffer.
- LARGE_INTEGER size;
- GetFileSizeEx(hFile, &size);
-
- DWORD bytesRead;
- Byte* const fileBuffer = new Byte[size.LowPart];
- if (!ReadFile(hFile, fileBuffer, size.LowPart, &bytesRead, NULL))
- {
- result = false;
- }
-
- // Read header size.
- const IMAGE_DOS_HEADER* const pDOSHeader = (IMAGE_DOS_HEADER*)fileBuffer;
- if (pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE)
- {
- return false;
- }
-
- const IMAGE_NT_HEADERS32* const pNTHeader =(IMAGE_NT_HEADERS32*)((BYTE*)pDOSHeader + pDOSHeader->e_lfanew);
- if (pNTHeader->Signature != IMAGE_NT_SIGNATURE)
- {
- result = false;
- }
-
- const IMAGE_OPTIONAL_HEADER32* const pOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)&pNTHeader->OptionalHeader;
- // Write header data into process memory at designated location.
- CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, (void*)module.BaseAddress, pOptionalHeader->SizeOfHeaders, PAGE_READWRITE, &bytesRead);
- if (!CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, (void*)module.BaseAddress, fileBuffer, pOptionalHeader->SizeOfHeaders, NULL))
- {
- result = false;
- }
- CrySearchRoutines.CryProtectMemoryRoutine(this->mProcessHandle, (void*)module.BaseAddress, pOptionalHeader->SizeOfHeaders, bytesRead, &bytesRead);
-
- delete[] fileBuffer;
- CloseHandle(hFile);
-
- return result;
- }
- // Attempts to hide a module from the loaded process. Hiding means it not being visible for debuggers anymore.
- // Returns true if the operation succeeded, and false if it did not succeed.
- bool PortableExecutable32::HideModuleFromProcess(const Win32ModuleInformation& module) const
- {
- if (!CrySearchRoutines.NtQueryInformationProcess)
- {
- return false;
- }
-
- // Retrieve target process information using Nt function.
- #ifdef _WIN64
- ULONG_PTR pebAddr;
- if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessWow64Information, &pebAddr, sizeof(ULONG_PTR), NULL) != STATUS_SUCCESS)
- #else
- PROCESS_BASIC_INFORMATION procBlock;
- if (CrySearchRoutines.NtQueryInformationProcess(this->mProcessHandle, ProcessBasicInformation, &procBlock, sizeof(PROCESS_BASIC_INFORMATION), NULL) != STATUS_SUCCESS)
- #endif
- {
- return false;
- }
-
- PEB_LDR_DATA32 peb;
- SIZE_T pebPtr;
- // Read process environment block and loader data from the process memory.
- #ifdef _WIN64
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (unsigned char*)pebAddr + offsetof(PEB32, LoaderData), &pebPtr, sizeof(DWORD), NULL);
- #else
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (unsigned char*)procBlock.PebBaseAddress + offsetof(PEB, LoaderData), &pebPtr, sizeof(DWORD), NULL);
- #endif
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)pebPtr, &peb, sizeof(PEB_LDR_DATA), NULL);
- LDR_MODULE32 curModule;
- bool found = false;
- unsigned int retryCount = 0;
- int moduleCount = 0;
- SIZE_T Head = peb.InMemoryOrderModuleList.Flink;
- SIZE_T Node = Head;
- do
- {
- // Read current linked list module from the process memory.
- if (CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)(Node -= sizeof(LIST_ENTRY32)), &curModule, sizeof(LDR_MODULE32), NULL))
- {
- if (curModule.BaseAddress)
- {
- // some applications cause an infinite loop. This is one way to help preventing it.
- ++moduleCount;
- // A valid module is found, read its base dll name from the process memory.
- wchar BaseDllName[MAX_PATH];
- CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)curModule.BaseDllName.Buffer, BaseDllName, curModule.BaseDllName.Length, NULL);
- BaseDllName[curModule.BaseDllName.Length / 2] = 0;
- PathStripPathW(BaseDllName);
-
- // Compare current module's base name and desired module name, if it matches, the desired one is found.
- String selModName = mModuleManager->GetModuleFilename(module.BaseAddress);
- if (memcmp(selModName.ToWString().Begin(), BaseDllName, selModName.GetLength() * sizeof(wchar)) == 0)
- {
- found = true;
-
- for (unsigned int index = 0; index < 3; (Node += sizeof(LIST_ENTRY32)), index++)
- {
- LIST_ENTRY32 current;
- // Read current, previous and next list entry from the process memory.
- BOOL localRes = CrySearchRoutines.CryReadMemoryRoutine(this->mProcessHandle, (void*)Node, ¤t, sizeof(LIST_ENTRY32), NULL);
- if (!localRes)
- {
- found = false;
- break;
- }
-
- const SIZE_T nextItemAddr = current.Flink;
- const SIZE_T prevItemAddr = current.Blink;
-
- // Overwrite the pointers of the previous and next list entry so the current one is effectively hidden.
- localRes = CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, (void*)current.Blink, &nextItemAddr, sizeof(DWORD), NULL);
- localRes = CrySearchRoutines.CryWriteMemoryRoutine(this->mProcessHandle, (unsigned char*)current.Flink + sizeof(DWORD), &prevItemAddr, sizeof(DWORD), NULL);
- }
-
- break;
- }
- }
- }
- else
- {
- // Prevent infinite while looping. In most situations this code may be unnessecary.
- if (++retryCount == 3)
- {
- break;
- }
- }
-
- // Desired module was not yet found, traverse to the next one.
- Node = curModule.InMemoryOrderModuleList.Flink;
- }
- while(Head != Node && moduleCount < mModuleMan…
Large files files are truncated, but you can click here to view the full file