int32 FWindowsPlatformStackWalk::GetProcessModuleSignatures(FStackWalkModuleInfo *ModuleSignatures, const int32 ModuleSignaturesSize) { FPlatformStackWalk::InitStackWalking(); HANDLE ProcessHandle = GetCurrentProcess(); // Enumerate process modules. HMODULE* ModuleHandlePointer = GetProcessModules(ProcessHandle); if (!ModuleHandlePointer) { return 0; } // Find out how many modules we need to load modules for. IMAGEHLP_MODULEW64 Img = {0}; Img.SizeOfStruct = sizeof(Img); int32 SignatureIndex = 0; // Load the modules. for( int32 ModuleIndex = 0; ModuleHandlePointer[ModuleIndex] && SignatureIndex < ModuleSignaturesSize; ModuleIndex++ ) { MODULEINFO ModuleInfo = {0}; #if WINVER > 0x502 WCHAR ModuleName[MAX_PATH] = {0}; WCHAR ImageName[MAX_PATH] = {0}; #else ANSICHAR ModuleName[MAX_PATH] = { 0 }; ANSICHAR ImageName[MAX_PATH] = { 0 }; #endif #if PLATFORM_64BITS static_assert(sizeof( MODULEINFO ) == 24, "Broken alignment for 64bit Windows include."); #else static_assert(sizeof( MODULEINFO ) == 12, "Broken alignment for 32bit Windows include."); #endif FGetModuleInformation( ProcessHandle, ModuleHandlePointer[ModuleIndex], &ModuleInfo, sizeof( ModuleInfo ) ); FGetModuleFileNameEx( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, MAX_PATH ); FGetModuleBaseName( ProcessHandle, ModuleHandlePointer[ModuleIndex], ModuleName, MAX_PATH ); // Load module. if(SymGetModuleInfoW64(ProcessHandle, (DWORD64)ModuleInfo.lpBaseOfDll, &Img)) { FStackWalkModuleInfo Info = {0}; Info.BaseOfImage = Img.BaseOfImage; FCString::Strcpy(Info.ImageName, Img.ImageName); Info.ImageSize = Img.ImageSize; FCString::Strcpy(Info.LoadedImageName, Img.LoadedImageName); FCString::Strcpy(Info.ModuleName, Img.ModuleName); Info.PdbAge = Img.PdbAge; Info.PdbSig = Img.PdbSig; FMemory::Memcpy(&Info.PdbSig70, &Img.PdbSig70, sizeof(GUID)); Info.TimeDateStamp = Img.TimeDateStamp; ModuleSignatures[SignatureIndex] = Info; ++SignatureIndex; } } // Free the module handle pointer allocated in case the static array was insufficient. FMemory::Free(ModuleHandlePointer); return SignatureIndex; }
/** * Upload locally built symbols to network symbol storage. * * Use case: * Game designers use game from source (without prebuild game .dll-files). * In this case all game .dll-files are compiled locally. * For post-mortem debug programmers need .dll and .pdb files from designers. */ bool FWindowsPlatformStackWalk::UploadLocalSymbols() { InitStackWalking(); #if WINVER > 0x502 // Upload locally compiled files to symbol storage. FString SymbolStorage; if (!GConfig->GetString( CrashReporterSettings, TEXT( "UploadSymbolsPath" ), SymbolStorage, GEditorPerProjectIni ) || SymbolStorage.IsEmpty()) { // Nothing to do. return true; } // Prepare string SymbolStorage.ReplaceInline( TEXT( "/" ), TEXT( "\\" ), ESearchCase::CaseSensitive ); SymbolStorage = TEXT( "SRV*" ) + SymbolStorage; int32 ErrorCode = 0; HANDLE ProcessHandle = GetCurrentProcess(); // Enumerate process modules. HMODULE* ModuleHandlePointer = GetProcessModules( ProcessHandle ); if (!ModuleHandlePointer) { ErrorCode = GetLastError(); return false; } #if WITH_EDITOR // Get Unreal Engine Editor directory for detecting non-game editor binaries. FString EnginePath = FPaths::ConvertRelativePathToFull( FPaths::EngineDir() ); FPaths::MakePlatformFilename( EnginePath ); #endif // Upload all locally built modules. for (int32 ModuleIndex = 0; ModuleHandlePointer[ModuleIndex]; ModuleIndex++) { WCHAR ImageName[MAX_PATH] = {0}; FGetModuleFileNameEx( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, MAX_PATH ); #if WITH_EDITOR WCHAR RelativePath[MAX_PATH]; // Skip binaries inside Unreal Engine Editor directory (non-game editor binaries) if (PathRelativePathTo( RelativePath, *EnginePath, FILE_ATTRIBUTE_DIRECTORY, ImageName, 0 ) && FCString::Strncmp( RelativePath, TEXT( "..\\" ), 3 )) { continue; } #endif WCHAR DebugName[MAX_PATH]; FCString::Strcpy( DebugName, ImageName ); if (PathRenameExtensionW( DebugName, L".pdb" )) { // Upload only if found .pdb file if (PathFileExistsW( DebugName )) { // Upload original file UE_LOG( LogWindows, Log, TEXT( "Uploading to symbol storage: %s" ), ImageName ); if (!SymSrvStoreFileW( ProcessHandle, *SymbolStorage, ImageName, SYMSTOREOPT_PASS_IF_EXISTS )) { UE_LOG( LogWindows, Warning, TEXT( "Uploading to symbol storage failed: %s. Error: %d" ), ImageName, GetLastError() ); } // Upload debug symbols UE_LOG( LogWindows, Log, TEXT( "Uploading to symbol storage: %s" ), DebugName ); if (!SymSrvStoreFileW( ProcessHandle, *SymbolStorage, DebugName, SYMSTOREOPT_PASS_IF_EXISTS )) { UE_LOG( LogWindows, Warning, TEXT( "Uploading to symbol storage failed: %s. Error: %d" ), DebugName, GetLastError() ); } } } } #else UE_LOG( LogWindows, Log, TEXT( "Symbol server not supported on Windows XP." ) ); #endif return true; }
/** * Loads modules for current process. */ static void LoadProcessModules(const FString &RemoteStorage) { int32 ErrorCode = 0; HANDLE ProcessHandle = GetCurrentProcess(); // Enumerate process modules. HMODULE* ModuleHandlePointer = GetProcessModules(ProcessHandle); if (!ModuleHandlePointer) { ErrorCode = GetLastError(); return; } // Load the modules. for( int32 ModuleIndex = 0; ModuleHandlePointer[ModuleIndex]; ModuleIndex++ ) { MODULEINFO ModuleInfo = {0}; #if WINVER > 0x502 WCHAR ModuleName[FProgramCounterSymbolInfo::MAX_NAME_LENGHT] = {0}; WCHAR ImageName[FProgramCounterSymbolInfo::MAX_NAME_LENGHT] = {0}; #else ANSICHAR ModuleName[FProgramCounterSymbolInfo::MAX_NAME_LENGHT] = { 0 }; ANSICHAR ImageName[FProgramCounterSymbolInfo::MAX_NAME_LENGHT] = { 0 }; #endif #if PLATFORM_64BITS static_assert(sizeof( MODULEINFO ) == 24, "Broken alignment for 64bit Windows include."); #else static_assert(sizeof( MODULEINFO ) == 12, "Broken alignment for 32bit Windows include."); #endif FGetModuleInformation( ProcessHandle, ModuleHandlePointer[ModuleIndex], &ModuleInfo, sizeof( ModuleInfo ) ); FGetModuleFileNameEx( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, FProgramCounterSymbolInfo::MAX_NAME_LENGHT ); FGetModuleBaseName( ProcessHandle, ModuleHandlePointer[ModuleIndex], ModuleName, FProgramCounterSymbolInfo::MAX_NAME_LENGHT ); // Set the search path to find PDBs in the same folder as the DLL. #if WINVER > 0x502 WCHAR SearchPath[MAX_PATH] = {0}; WCHAR* FileName = NULL; const auto Result = GetFullPathNameW( ImageName, MAX_PATH, SearchPath, &FileName ); #else ANSICHAR SearchPath[MAX_PATH] = { 0 }; ANSICHAR* FileName = NULL; const auto Result = GetFullPathNameA( ImageName, MAX_PATH, SearchPath, &FileName ); #endif FString SearchPathList; if (Result != 0 && Result < MAX_PATH) { *FileName = 0; #if WINVER > 0x502 SearchPathList = SearchPath; #else SearchPathList = ANSI_TO_TCHAR(SearchPath); #endif } if (!RemoteStorage.IsEmpty()) { if (!SearchPathList.IsEmpty()) { SearchPathList.AppendChar(';'); } SearchPathList.Append(RemoteStorage); } #if WINVER > 0x502 SymSetSearchPathW(ProcessHandle, *SearchPathList); // Load module. const DWORD64 BaseAddress = SymLoadModuleExW( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, ModuleName, (DWORD64) ModuleInfo.lpBaseOfDll, (uint32) ModuleInfo.SizeOfImage, NULL, 0 ); if( !BaseAddress ) { ErrorCode = GetLastError(); UE_LOG(LogWindows, Warning, TEXT("SymLoadModuleExW. Error: %d"), GetLastError()); } #else SymSetSearchPath(ProcessHandle, TCHAR_TO_ANSI(*SearchPathList)); // Load module. const DWORD64 BaseAddress = SymLoadModuleEx( ProcessHandle, ModuleHandlePointer[ModuleIndex], ImageName, ModuleName, (DWORD64)ModuleInfo.lpBaseOfDll, (uint32)ModuleInfo.SizeOfImage, NULL, 0 ); if (!BaseAddress) { ErrorCode = GetLastError(); UE_LOG(LogWindows, Warning, TEXT("SymLoadModuleEx. Error: %d"), GetLastError()); } #endif } // Free the module handle pointer allocated in case the static array was insufficient. FMemory::Free(ModuleHandlePointer); }
vector<EXPORT_ENTRY> Process::GetDLLExports(string p_sModule) { vector<EXPORT_ENTRY> vExports; BYTE *pcImageBase = NULL; HMODULE hResult = NULL; // Make sure library is loaded hResult = GetModuleHandle(p_sModule.c_str()); if(hResult == NULL) { DebugLog::LogString("[ERROR] Cannot get DLL handle: ", p_sModule); // Forcely load DLL, ugly but we make sure the DLL functions are hooked LoadLibrary(p_sModule.c_str()); } // Get modules vector<MODULEENTRY32> vModules = GetProcessModules(0); if(vModules.size() == 0) { DebugLog::LogString("[ERROR] Cannot get current process modules, searching for: ", p_sModule); return vExports; } // Case insensitive p_sModule = Utils::ToLower(p_sModule); // Find requested module for(size_t i = 0; i < vModules.size(); i++) { if(p_sModule.compare(Utils::ToLower(vModules[i].szModule)) == 0) { pcImageBase = vModules[i].modBaseAddr; // Parse PE headers IMAGE_DOS_HEADER oDOS; IMAGE_NT_HEADERS oNT; IMAGE_DATA_DIRECTORY oExportDirEntry; IMAGE_EXPORT_DIRECTORY oExportDirectory; // Parse EAT DWORD *pdwAddressOfFunctions = NULL; DWORD *pdwAddressOfNames = NULL; CHAR *pcFunctionName = NULL; DWORD dwFunctionAddress = 0; DWORD dwFunctionPointerLocation = 0; // Get Export directory memcpy(&oDOS, pcImageBase, sizeof(oDOS)); memcpy(&oNT, (BYTE *)((DWORD)pcImageBase + oDOS.e_lfanew), sizeof(oNT)); oExportDirEntry = oNT.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; memcpy(&oExportDirectory, (BYTE *)((DWORD)pcImageBase + oExportDirEntry.VirtualAddress), sizeof(oExportDirectory)); // Parse names pdwAddressOfNames = (DWORD *)((DWORD)pcImageBase + oExportDirectory.AddressOfNames); pdwAddressOfFunctions = (DWORD *)((DWORD)pcImageBase + oExportDirectory.AddressOfFunctions); for(DWORD nr = 0; nr < oExportDirectory.NumberOfFunctions; nr++) { EXPORT_ENTRY oExport; // Get function details pcFunctionName = (CHAR *)((DWORD)pcImageBase + (DWORD)(pdwAddressOfNames[nr])); dwFunctionAddress = (DWORD)pcImageBase + (DWORD)(pdwAddressOfFunctions[nr]); dwFunctionPointerLocation = (DWORD)pcImageBase + oExportDirectory.AddressOfFunctions + nr * sizeof(DWORD); // Save new function export oExport.dwAddress = dwFunctionAddress; oExport.dwPointerOfAddress = dwFunctionPointerLocation; oExport.sName = pcFunctionName; oExport.uOrdinal = (USHORT)nr + 1; vExports.push_back(oExport); } // Do not care about other modules break; } } return vExports; }