diff --git a/CMakeLists.txt b/CMakeLists.txt index cda60af60..753ef5ab3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,6 +403,7 @@ set(ELFLOADER_SRC "${BOX64_ROOT}/src/tools/rbtree.c" "${BOX64_ROOT}/src/tools/env.c" "${BOX64_ROOT}/src/tools/wine_tools.c" + "${BOX64_ROOT}/src/tools/pe_tools.c" "${BOX64_ROOT}/src/wrapped/generated/wrapper.c" ) if(NOT STATICBUILD) diff --git a/src/include/pe_tools.h b/src/include/pe_tools.h new file mode 100644 index 000000000..54c3d42e6 --- /dev/null +++ b/src/include/pe_tools.h @@ -0,0 +1,7 @@ +#ifndef __PE_TOOLS_H__ +#define __PE_TOOLS_H__ + + +void ParseVolatileMetadata(char* filename, void* addr); + +#endif // __PE_TOOLS_H__ diff --git a/src/include/wine_tools.h b/src/include/wine_tools.h index 4bb0aa3a8..d8cefcbcc 100644 --- a/src/include/wine_tools.h +++ b/src/include/wine_tools.h @@ -10,6 +10,6 @@ void* get_wine_prereserve(void); void dynarec_wine_prereserve(void); #endif -void DetectUnityPlayer(int fd); +void DetectUnityPlayer(char* filename); #endif //__WINE_TOOLS_H__ diff --git a/src/tools/pe_tools.c b/src/tools/pe_tools.c new file mode 100644 index 000000000..46c8a2030 --- /dev/null +++ b/src/tools/pe_tools.c @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +typedef uint8_t BYTE; +typedef uint16_t WORD; +typedef uint32_t DWORD; +typedef int32_t LONG; +typedef uint32_t ULONG; +typedef uint64_t ULONGLONG; + +#define IMAGE_DOS_SIGNATURE 0x5A4D +#define IMAGE_NT_SIGNATURE 0x00004550 +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10B +#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20B + +typedef struct _IMAGE_DOS_HEADER { + WORD e_magic; + WORD e_cblp; + WORD e_cp; + WORD e_crlc; + WORD e_cparhdr; + WORD e_minalloc; + WORD e_maxalloc; + WORD e_ss; + WORD e_sp; + WORD e_csum; + WORD e_ip; + WORD e_cs; + WORD e_lfarlc; + WORD e_ovno; + WORD e_res[4]; + WORD e_oemid; + WORD e_oeminfo; + WORD e_res2[10]; + LONG e_lfanew; +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + +typedef struct _IMAGE_FILE_HEADER { + WORD Machine; + WORD NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + WORD SizeOfOptionalHeader; + WORD Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + +typedef struct _IMAGE_DATA_DIRECTORY { + DWORD VirtualAddress; + DWORD Size; +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; + +typedef struct _IMAGE_OPTIONAL_HEADER64 { + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + ULONGLONG ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + ULONGLONG SizeOfStackReserve; + ULONGLONG SizeOfStackCommit; + ULONGLONG SizeOfHeapReserve; + ULONGLONG SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; + +typedef struct _IMAGE_NT_HEADERS64 { + DWORD Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; + +typedef struct _IMAGE_SECTION_HEADER { + BYTE Name[8]; + union { + DWORD PhysicalAddress; + DWORD VirtualSize; + } Misc; + DWORD VirtualAddress; + DWORD SizeOfRawData; + DWORD PointerToRawData; + DWORD PointerToRelocations; + DWORD PointerToLinenumbers; + WORD NumberOfRelocations; + WORD NumberOfLinenumbers; + DWORD Characteristics; +} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; + +typedef struct _IMAGE_LOAD_CONFIG_DIRECTORY64 { + DWORD Size; + DWORD TimeDateStamp; + WORD MajorVersion; + WORD MinorVersion; + DWORD GlobalFlagsClear; + DWORD GlobalFlagsSet; + DWORD CriticalSectionDefaultTimeout; + ULONGLONG DeCommitFreeBlockThreshold; + ULONGLONG DeCommitTotalFreeThreshold; + ULONGLONG LockPrefixTable; + ULONGLONG MaximumAllocationSize; + ULONGLONG VirtualMemoryThreshold; + ULONGLONG ProcessAffinityMask; + DWORD ProcessHeapFlags; + WORD CSDVersion; + WORD DependentLoadFlags; + ULONGLONG EditList; + ULONGLONG SecurityCookie; + ULONGLONG SEHandlerTable; + ULONGLONG SEHandlerCount; + ULONGLONG GuardCFCheckFunctionPointer; + ULONGLONG GuardCFDispatchFunctionPointer; + ULONGLONG GuardCFFunctionTable; + ULONGLONG GuardCFFunctionCount; + DWORD GuardFlags; + ULONGLONG CodeIntegrity; + ULONGLONG GuardAddressTakenIatEntryTable; + ULONGLONG GuardAddressTakenIatEntryCount; + ULONGLONG GuardLongJumpTargetTable; + ULONGLONG GuardLongJumpTargetCount; + ULONGLONG DynamicValueRelocTable; + ULONGLONG CHPEMetadataPointer; + ULONGLONG GuardRFFailureRoutine; + ULONGLONG GuardRFFailureRoutineFunctionPointer; + DWORD DynamicValueRelocTableOffset; + WORD DynamicValueRelocTableSection; + WORD Reserved2; + ULONGLONG GuardRFVerifyStackPointerFunctionPointer; + DWORD HotPatchTableOffset; + DWORD Reserved3; + ULONGLONG EnclaveConfigurationPointer; + ULONGLONG VolatileMetadataPointer; + ULONGLONG GuardEHContinuationTable; + ULONGLONG GuardEHContinuationCount; + ULONGLONG GuardXFGCheckFunctionPointer; + ULONGLONG GuardXFGDispatchFunctionPointer; + ULONGLONG GuardXFGTableDispatchFunctionPointer; + ULONGLONG CastGuardOsDeterminedFailureMode; + ULONGLONG GuardMemcpyFunctionPointer; +} IMAGE_LOAD_CONFIG_DIRECTORY64, *PIMAGE_LOAD_CONFIG_DIRECTORY64; + +typedef struct _IMAGE_VOLATILE_METADATA { + DWORD Size; + DWORD Version; + DWORD VolatileAccessTable; + DWORD VolatileAccessTableSize; + DWORD VolatileInfoRangeTable; + DWORD VolatileInfoRangeTableSize; +} IMAGE_VOLATILE_METADATA, *PIMAGE_VOLATILE_METADATA; + +typedef struct _IMAGE_VOLATILE_RVA_METADATA { + ULONG Rva; +} IMAGE_VOLATILE_RVA_METADATA, *PIMAGE_VOLATILE_RVA_METADATA; + +typedef struct _IMAGE_VOLATILE_RANGE_METADATA { + ULONG Rva; + ULONG Size; +} IMAGE_VOLATILE_RANGE_METADATA, *PIMAGE_VOLATILE_RANGE_METADATA; + +static int HasSuffix(const char* str, const char* suffix) +{ + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + if (lensuffix > lenstr) return 0; + return strcmp(str + lenstr - lensuffix, suffix) == 0; +} + +DWORD RVAToFileOffset(PIMAGE_SECTION_HEADER sections, DWORD numberOfSections, DWORD rva, BYTE* fileBuffer, size_t fileSize) +{ + for (DWORD i = 0; i < numberOfSections; i++) { + PIMAGE_SECTION_HEADER section = §ions[i]; + if (rva >= section->VirtualAddress && rva < section->VirtualAddress + section->SizeOfRawData) { + DWORD offset = rva - section->VirtualAddress + section->PointerToRawData; + if (offset < fileSize) { + return offset; + } + } + } + return 0; +} + +void ParseVolatileMetadata(char* filename, void* addr) +{ + if (!filename) return; + if (!HasSuffix(filename, ".dll")) { + return; + } + + FILE* file = fopen(filename, "rb"); + if (!file) return; + + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + + char* buffer = (char*)malloc(size); + if (!buffer) { + fclose(file); + return; + } + + if (fread(buffer, 1, size, file) != size) { + free(buffer); + fclose(file); + return; + } + fclose(file); + + IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)buffer; + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { + free(buffer); + return; + } + + PIMAGE_NT_HEADERS64 ntHeaders64 = (PIMAGE_NT_HEADERS64)(buffer + dosHeader->e_lfanew); + if (ntHeaders64->Signature != IMAGE_NT_SIGNATURE || ntHeaders64->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + free(buffer); + return; + } + + int numberOfSections = ntHeaders64->FileHeader.NumberOfSections; + if (numberOfSections <= 0) { + free(buffer); + return; + } + IMAGE_DATA_DIRECTORY loadConfigDir = ntHeaders64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG]; + if (loadConfigDir.VirtualAddress == 0 || loadConfigDir.Size == 0) { + free(buffer); + return; + } + + PIMAGE_SECTION_HEADER sectionHeaders = (PIMAGE_SECTION_HEADER)((void*)ntHeaders64 + sizeof(IMAGE_NT_HEADERS64)); // immediately follows the optional header, if any. + DWORD loadConfigOffset = RVAToFileOffset(sectionHeaders, numberOfSections, loadConfigDir.VirtualAddress, (BYTE*)buffer, ntHeaders64->OptionalHeader.SizeOfImage); + if (loadConfigOffset == 0) { + free(buffer); + return; + } + + PIMAGE_LOAD_CONFIG_DIRECTORY64 loadConfig = (PIMAGE_LOAD_CONFIG_DIRECTORY64)(buffer + loadConfigOffset); + if (loadConfig->Size < offsetof(IMAGE_LOAD_CONFIG_DIRECTORY64, VolatileMetadataPointer) + sizeof(ULONGLONG)) { + free(buffer); + return; + } + DWORD volatileMetadataPointer = (DWORD)(loadConfig->VolatileMetadataPointer - ntHeaders64->OptionalHeader.ImageBase); + if (volatileMetadataPointer == 0) { + free(buffer); + return; + } + + DWORD volatileMetadataOffset = RVAToFileOffset(sectionHeaders, numberOfSections, volatileMetadataPointer, (BYTE*)buffer, ntHeaders64->OptionalHeader.SizeOfImage); + if (volatileMetadataOffset == 0) { + free(buffer); + return; + } + + PIMAGE_VOLATILE_METADATA volatileMetadata = (PIMAGE_VOLATILE_METADATA)(buffer + volatileMetadataOffset); + if (volatileMetadata->VolatileAccessTable && volatileMetadata->VolatileAccessTableSize) { + printf_log(LOG_INFO, "Parsing volatile metadata of file: %s\n", filename); + + DWORD volatileAccessTableOffset = RVAToFileOffset(sectionHeaders, numberOfSections, volatileMetadata->VolatileAccessTable, (BYTE*)buffer, ntHeaders64->OptionalHeader.SizeOfImage); + if (volatileAccessTableOffset == 0) { + free(buffer); + return; + } + + DWORD numEntries = volatileMetadata->VolatileAccessTableSize / sizeof(IMAGE_VOLATILE_RVA_METADATA); + PIMAGE_VOLATILE_RVA_METADATA volatileAccessTable = (PIMAGE_VOLATILE_RVA_METADATA)(buffer + volatileAccessTableOffset); + + for (DWORD i = 0; i < numEntries; i++) { + ULONG entry = volatileAccessTable[i].Rva; + printf_log(LOG_INFO, "Volatile Access Table Entry %d: %08X\n", i, entry); + } + } + + if (volatileMetadata->VolatileInfoRangeTable && volatileMetadata->VolatileInfoRangeTableSize) { + DWORD volatileInfoRangeTableOffset = RVAToFileOffset(sectionHeaders, numberOfSections, volatileMetadata->VolatileInfoRangeTable, (BYTE*)buffer, ntHeaders64->OptionalHeader.SizeOfImage); + if (volatileInfoRangeTableOffset == 0) { + free(buffer); + return; + } + + DWORD numEntries = volatileMetadata->VolatileInfoRangeTableSize / sizeof(IMAGE_VOLATILE_RANGE_METADATA); + PIMAGE_VOLATILE_RANGE_METADATA volatileRangeMetadata = (PIMAGE_VOLATILE_RANGE_METADATA)(buffer + volatileInfoRangeTableOffset); + + for (DWORD i = 0; i < numEntries; i++) { + ULONG Rva = volatileRangeMetadata[i].Rva; + ULONG Size = volatileRangeMetadata[i].Size; + printf_log(LOG_INFO, "Volatile Range Metadata Entry %d: %08X Size: %08X\n", i, Rva, Size); + } + } +} diff --git a/src/tools/wine_tools.c b/src/tools/wine_tools.c index 8b0edf14c..86a915980 100644 --- a/src/tools/wine_tools.c +++ b/src/tools/wine_tools.c @@ -136,16 +136,11 @@ void dynarec_wine_prereserve() } #endif -void DetectUnityPlayer(int fd) +void DetectUnityPlayer(char* filename) { static int unityplayer_detected = 0; - if (fd > 0 && BOX64ENV(unityplayer) && !unityplayer_detected) { - char filename[4096]; - char buf[128]; - sprintf(buf, "/proc/self/fd/%d", fd); - ssize_t r = readlink(buf, filename, sizeof(filename) - 1); - if (r != -1) filename[r] = 0; - if (r > 0 && strlen(filename) > strlen("UnityPlayer.dll") && !strcasecmp(filename + strlen(filename) - strlen("UnityPlayer.dll"), "UnityPlayer.dll")) { + if (!filename && BOX64ENV(unityplayer) && !unityplayer_detected) { + if (strlen(filename) > strlen("UnityPlayer.dll") && !strcasecmp(filename + strlen(filename) - strlen("UnityPlayer.dll"), "UnityPlayer.dll")) { printf_log(LOG_NONE, "Detected UnityPlayer.dll\n"); #ifdef DYNAREC if (!BOX64ENV(dynarec_strongmem)) { @@ -156,4 +151,4 @@ void DetectUnityPlayer(int fd) unityplayer_detected = 1; } } -} \ No newline at end of file +} diff --git a/src/wrapped/wrappedlibc.c b/src/wrapped/wrappedlibc.c index 58ecd5b42..3761f6099 100644 --- a/src/wrapped/wrappedlibc.c +++ b/src/wrapped/wrappedlibc.c @@ -68,6 +68,7 @@ #include "globalsymbols.h" #include "env.h" #include "wine_tools.h" +#include "pe_tools.h" #include "cleanup.h" #ifndef LOG_INFO #define LOG_INFO 1 @@ -3032,7 +3033,14 @@ EXPORT void* my_mmap64(x64emu_t* emu, void *addr, size_t length, int prot, int f #endif if(ret!=MAP_FAILED) { if(emu && !(flags&MAP_ANONYMOUS) && (fd>0)) { - DetectUnityPlayer(fd); + char filename[4096]; + char buf[128]; + sprintf(buf, "/proc/self/fd/%d", fd); + ssize_t r = readlink(buf, filename, sizeof(filename) - 1); + if (r != -1) filename[r] = 0; + + DetectUnityPlayer(filename); + // ParseVolatileMetadata(filename, addr); // the last_mmap will allow mmap created by wine, even those that have hole, to be fully tracked as one single mmap if((ret>=last_mmap_addr[0]) && ret+length<(last_mmap_addr[0]+last_mmap_len[0])) RecordEnvMappings((uintptr_t)last_mmap_addr[0], last_mmap_len[0], fd);