Skip to content

Commit 02f5e6e

Browse files
authored
Merge pull request x64dbg#3777 from 3rdit/fix/weird-bug
Fix module loading when file path is stale or file is locked
2 parents 773482d + d2a0ca9 commit 02f5e6e

File tree

4 files changed

+143
-19
lines changed

4 files changed

+143
-19
lines changed

src/dbg/_global.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,17 @@ bool GetFileNameFromModuleHandle(HANDLE hProcess, HMODULE hModule, char* szFileN
272272
auto result = false;
273273
if(GetMappedFileNameW(hProcess, hModule, wszDosFileName, _countof(wszDosFileName)))
274274
{
275-
if(!DevicePathToPathW(wszDosFileName, wszFileName, _countof(wszFileName)))
275+
if(DevicePathToPathW(wszDosFileName, wszFileName, _countof(wszFileName)))
276+
{
277+
// Verify the file exists - GetMappedFileNameW can return stale paths due to kernel section caching
278+
// https://github.com/x64dbg/x64dbg/issues/3756
279+
if(GetFileAttributesW(wszFileName) != INVALID_FILE_ATTRIBUTES)
280+
result = true;
281+
}
282+
283+
// Fall back to GetModuleFileNameExW if the path conversion failed or the file doesn't exist
284+
if(!result)
276285
result = !!GetModuleFileNameExW(hProcess, hModule, wszFileName, _countof(wszFileName));
277-
else
278-
result = true;
279286
}
280287
else
281288
result = !!GetModuleFileNameExW(hProcess, hModule, wszFileName, _countof(wszFileName));

src/dbg/debugger.cpp

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,14 +1844,44 @@ static void cbLoadDll(LOAD_DLL_DEBUG_INFO* LoadDll)
18441844
hActiveThread = ThreadGetHandle(GetDebugData()->dwThreadId);
18451845
void* base = LoadDll->lpBaseOfDll;
18461846

1847+
// Retrieve the DLL path using a fallback
1848+
// https://github.com/x64dbg/x64dbg/issues/3756
18471849
char DLLDebugFileName[MAX_PATH] = "";
1848-
if(!GetFileNameFromHandle(LoadDll->hFile, DLLDebugFileName, _countof(DLLDebugFileName)))
1850+
bool validPath = false;
1851+
1852+
//1 - Try kernel path from file handle
1853+
if(GetFileNameFromHandle(LoadDll->hFile, DLLDebugFileName, _countof(DLLDebugFileName)) &&
1854+
FileExists(DLLDebugFileName))
1855+
{
1856+
validPath = true;
1857+
}
1858+
1859+
//2 - Invalidate section cache and retry
1860+
// FSCTL_CHECK_FOR_SECTION releases cached image sections, fixing stale paths from kernel section caching.
1861+
// https://github.com/x64dbg/x64dbg/issues/3756
1862+
if(!validPath && LoadDll->hFile)
1863+
{
1864+
IO_STATUS_BLOCK iosb = {};
1865+
constexpr ULONG FSCTL_CHECK_FOR_SECTION = 0x90348;
1866+
NtFsControlFile(LoadDll->hFile, nullptr, nullptr, nullptr, &iosb, FSCTL_CHECK_FOR_SECTION, nullptr, 0, nullptr, 0);
1867+
if(GetFileNameFromHandle(LoadDll->hFile, DLLDebugFileName, _countof(DLLDebugFileName)) &&
1868+
FileExists(DLLDebugFileName))
1869+
{
1870+
validPath = true;
1871+
}
1872+
}
1873+
1874+
//3 - Try GetMappedFileNameW (with internal fallback to PEB)
1875+
if(!validPath)
18491876
{
1850-
if(!GetFileNameFromModuleHandle(fdProcessInfo->hProcess, HMODULE(base), DLLDebugFileName, _countof(DLLDebugFileName)))
1851-
strcpy_s(DLLDebugFileName, GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "??? (GetFileNameFromHandle failed)")));
1877+
validPath = GetFileNameFromModuleHandle(fdProcessInfo->hProcess, HMODULE(base), DLLDebugFileName, _countof(DLLDebugFileName));
18521878
}
18531879

1854-
ModLoad((duint)base, 1, DLLDebugFileName);
1880+
//Give up
1881+
if(!validPath)
1882+
strcpy_s(DLLDebugFileName, GuiTranslateText(QT_TRANSLATE_NOOP("DBG", "??? (GetFileNameFromHandle failed)")));
1883+
1884+
ModLoad((duint)base, 1, DLLDebugFileName, true, LoadDll->hFile);
18551885

18561886
// Update memory map
18571887
MemUpdateMapAsync();

src/dbg/module.cpp

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ void GetModuleInfo(MODINFO & Info, ULONG_PTR FileMapVA)
859859
#undef GetUnsafeModuleInfo
860860
}
861861

862-
bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols)
862+
bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols, HANDLE hFile)
863863
{
864864
// Handle a new module being loaded
865865
if(!Base || !Size || !FullPath)
@@ -928,25 +928,112 @@ bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols)
928928
if(!info.isVirtual)
929929
{
930930
auto wszFullPath = StringUtils::Utf8ToUtf16(FullPath);
931+
bool fileLoaded = false;
931932

932-
// Load the physical module from disk
933-
if(StaticFileLoadW(wszFullPath.c_str(), UE_ACCESS_READ, false, &info.fileHandle, &info.loadedSize, &info.fileMap, &info.fileMapVA))
933+
// 1. If we have a file handle from the debug event, try mapping from it first
934+
// https://github.com/x64dbg/x64dbg/issues/3756
935+
if(hFile)
936+
{
937+
DWORD fileSize = GetFileSize(hFile, nullptr);
938+
if(fileSize != INVALID_FILE_SIZE && fileSize > 0)
939+
{
940+
HANDLE hMap = CreateFileMappingW(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
941+
if(hMap)
942+
{
943+
LPVOID mapView = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
944+
if(mapView)
945+
{
946+
info.fileHandle = (HANDLE)1; // Non-zero for TitanEngine compatibility
947+
info.loadedSize = fileSize;
948+
info.fileMap = hMap;
949+
info.fileMapVA = (ULONG_PTR)mapView;
950+
fileLoaded = true;
951+
}
952+
else
953+
{
954+
CloseHandle(hMap);
955+
}
956+
}
957+
}
958+
}
959+
960+
// 2. If no file handle or mapping failed, try loading from disk path
961+
if(!fileLoaded && StaticFileLoadW(wszFullPath.c_str(), UE_ACCESS_READ, false, &info.fileHandle, &info.loadedSize, &info.fileMap, &info.fileMapVA))
934962
{
935-
// Fix an anti-debug trick, which opens exclusive access to the file
936963
CloseHandle(info.fileHandle);
937964
info.fileHandle = (HANDLE)1; // Set to non-zero for TitanEngine compatibility
965+
fileLoaded = true;
966+
dprintf(QT_TRANSLATE_NOOP("DBG", "Module %s%s loaded from file handle (path inaccessible)\n"), info.name, info.extension);
967+
}
938968

939-
GetModuleInfo(info, info.fileMapVA);
969+
// 3. If both failed, try reading from process memory as last resort
970+
if(!fileLoaded)
971+
{
972+
// The Size parameter is unreliable here (all pass 1 as a placeholder).
973+
// This is consistent with steps 1 and 2 which also determine size from their source.
974+
duint actualSize = 0;
975+
unsigned char headerBuf[0x1000] = {0};
976+
if(MemRead(Base, headerBuf, sizeof(headerBuf)))
977+
{
978+
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)headerBuf;
979+
if(dosHeader->e_magic == IMAGE_DOS_SIGNATURE &&
980+
dosHeader->e_lfanew > 0 &&
981+
dosHeader->e_lfanew < 0x1000 - sizeof(IMAGE_NT_HEADERS))
982+
{
983+
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(headerBuf + dosHeader->e_lfanew);
984+
if(ntHeaders->Signature == IMAGE_NT_SIGNATURE)
985+
actualSize = HEADER_FIELD(ntHeaders, SizeOfImage);
986+
}
987+
}
940988

941-
Size = HEADER_FIELD(info.headers, SizeOfImage);
942-
info.size = Size;
989+
//fallback if PE header parsing failed
990+
if(actualSize == 0)
991+
{
992+
MEMORY_BASIC_INFORMATION mbi;
993+
duint regionSize = 0;
994+
duint addr = Base;
995+
while(VirtualQueryEx(fdProcessInfo->hProcess, (LPCVOID)addr, &mbi, sizeof(mbi)))
996+
{
997+
if(mbi.AllocationBase != (PVOID)Base)
998+
break;
999+
regionSize += mbi.RegionSize;
1000+
addr += mbi.RegionSize;
1001+
}
1002+
actualSize = regionSize;
1003+
}
1004+
1005+
if(actualSize > 0)
1006+
{
1007+
info.mappedData.realloc(actualSize);
1008+
if(MemRead(Base, info.mappedData(), info.mappedData.size()))
1009+
{
1010+
info.isVirtual = true; // Process memory is SEC_IMAGE mapped
1011+
info.loadedSize = (DWORD)actualSize;
1012+
GetModuleInfo(info, (ULONG_PTR)info.mappedData());
1013+
info.size = HEADER_FIELD(info.headers, SizeOfImage);
1014+
dprintf(QT_TRANSLATE_NOOP("DBG", "Module %s%s loaded from process memory (file inaccessible)\n"), info.name, info.extension);
1015+
}
1016+
else
1017+
{
1018+
info.fileHandle = nullptr;
1019+
info.loadedSize = 0;
1020+
info.fileMap = nullptr;
1021+
info.fileMapVA = 0;
1022+
}
1023+
}
1024+
else
1025+
{
1026+
info.fileHandle = nullptr;
1027+
info.loadedSize = 0;
1028+
info.fileMap = nullptr;
1029+
info.fileMapVA = 0;
1030+
}
9431031
}
9441032
else
9451033
{
946-
info.fileHandle = nullptr;
947-
info.loadedSize = 0;
948-
info.fileMap = nullptr;
949-
info.fileMapVA = 0;
1034+
GetModuleInfo(info, info.fileMapVA);
1035+
Size = HEADER_FIELD(info.headers, SizeOfImage);
1036+
info.size = Size;
9501037
}
9511038
}
9521039
else

src/dbg/module.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ struct MODINFO
147147
};
148148

149149
ULONG64 ModRvaToOffset(ULONG64 base, PIMAGE_NT_HEADERS ntHeaders, ULONG64 rva);
150-
bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols = true);
150+
bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols = true, HANDLE hFile = nullptr);
151151
bool ModUnload(duint Base);
152152
void ModClear(bool updateGui = true);
153153
MODINFO* ModInfoFromAddr(duint Address);

0 commit comments

Comments
 (0)