Skip to content

Commit 3e63256

Browse files
committed
Fix logic that determines whether to spoof GetModuleFileNameW(nullptr)
It was depending on figuring out where it was called from, but the logic was unreliable and there is no apparent way to fix it. Now, it switches spoofing on just before loading SkyrimSE.exe (so the loaded DLLs see the path they expect). Requires a companion fix in TIltedCore:TiltedPhoques::GetPath() to persist the value found originally, so it doesn't change out from under with late callers. If you code has only late callers, just add a call before loading your game binary.
1 parent 806fa93 commit 3e63256

File tree

4 files changed

+16
-51
lines changed

4 files changed

+16
-51
lines changed

Code/immersive_launcher/Launcher.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ LaunchContext* GetLaunchContext()
4242
return g_context;
4343
}
4444

45+
bool LaunchContext::GetLoaded()
46+
{
47+
return isLoaded;
48+
}
49+
4550
// Everything is nothing, life is worth living, just look to the stars
4651
#define DIE_NOW(err) \
4752
{ \
@@ -126,6 +131,7 @@ bool LoadProgram(LaunchContext& LC)
126131
LC.Version = QueryFileVersion(LC.exePath.c_str());
127132
if (LC.Version.empty())
128133
DIE_NOW(L"Failed to query game version");
134+
LC.SetLoaded();
129135

130136
ExeLoader loader(CurrentTarget.exeLoadSz);
131137
if (!loader.Load(reinterpret_cast<uint8_t*>(content.data())))

Code/immersive_launcher/Launcher.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ struct LaunchContext
2121
fs::path exePath;
2222
fs::path gamePath;
2323
TiltedPhoques::String Version;
24+
bool isLoaded{false};
2425
ExeLoader::TEntryPoint gameMain = nullptr;
26+
27+
void SetLoaded() { isLoaded = true; } // If loaded, need to spoof GetModuleFileName*(nullptr)
28+
bool GetLoaded();
2529
};
2630

2731
LaunchContext* GetLaunchContext();

Code/immersive_launcher/stubs/FileMapping.cpp

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ inline bool IsUsingMO2()
3838
return GetModuleHandleW(L"usvfs_x64.dll");
3939
}
4040

41-
inline bool IsMyModule(HMODULE aHmod)
41+
bool IsMyModule(HMODULE aHmod)
4242
{
4343
return aHmod == nullptr || aHmod == NtInternal::ThePeb()->pImageBase;
4444
}
@@ -156,56 +156,19 @@ NTSTATUS WINAPI TP_LdrGetDllFullName(HMODULE Module, PUNICODE_STRING DllName)
156156
return RealLdrGetDllFullName(Module, DllName);
157157
}
158158

159-
bool NeedsToFool(void* pRbp, bool* wantsTruth = nullptr)
160-
{
161-
// game code/stub segment within this exe needs to be fooled
162-
if (IsThisExeAddress(static_cast<uint8_t*>(pRbp)))
163-
{
164-
return IsGameMemoryAddress(static_cast<uint8_t*>(pRbp));
165-
}
166-
167-
// this heuristic indicates hooked game code... that is still owned by us...
168-
// not recognized immedeatly, but still looks like game code...
169-
HMODULE hMod = HModFromAddress(pRbp);
170-
171-
// simple debug hook
172-
#if 0
173-
if (hMod == GetModuleHandleW(L"NvCameraAllowlisting64.dll"))
174-
{
175-
__debugbreak();
176-
}
177-
#endif
178-
179-
if (hMod == NtInternal::ThePeb()->pImageBase || hMod == nullptr /*This is a hook, virtual allocd, not owned by anybody, so we assign ownership to the ST directory*/)
180-
{
181-
if (wantsTruth)
182-
*wantsTruth = true;
183-
return false;
184-
}
185-
186-
return !IsLocalModulePath(hMod);
187-
}
188-
189159
DWORD WINAPI TP_GetModuleFileNameW(HMODULE aModule, LPWSTR alpFilename, DWORD aSize)
190160
{
191161
// trampoline space for USVFS
192162
TP_EMPTY_HOOK_PLACEHOLDER;
193163

194-
void* rbp = _ReturnAddress();
195-
// PrintOwnerNa me(rbp);
196-
197-
bool force = false;
198-
if (IsMyModule(aModule) && NeedsToFool(rbp, &force) && launcher::GetLaunchContext())
164+
if (IsMyModule(aModule) && launcher::GetLaunchContext() && launcher::GetLaunchContext()->GetLoaded())
199165
{
200166
auto& aExePath = launcher::GetLaunchContext()->exePath;
201167
StringCchCopyW(alpFilename, aSize, aExePath.c_str());
202168

203169
return static_cast<DWORD>(std::wcslen(alpFilename));
204170
}
205171

206-
if (force)
207-
return MYGetModuleFileNameW(aModule, alpFilename, aSize);
208-
209172
return RealGetModuleFileNameW(aModule, alpFilename, aSize);
210173
}
211174

@@ -228,15 +191,6 @@ DWORD WINAPI TP_GetModuleFileNameA(HMODULE aModule, char* alpFileName, DWORD aBu
228191
{
229192
TP_EMPTY_HOOK_PLACEHOLDER;
230193

231-
void* rbp = _ReturnAddress();
232-
if (IsMyModule(aModule) && NeedsToFool(rbp) && launcher::GetLaunchContext())
233-
{
234-
auto aExePath = launcher::GetLaunchContext()->exePath.string();
235-
StringCchCopyA(alpFileName, aBufferSize, aExePath.c_str());
236-
237-
return static_cast<DWORD>(std::strlen(alpFileName));
238-
}
239-
240194
ScopedOSHeapItem wideBuffer((aBufferSize * sizeof(wchar_t)) + 1);
241195

242196
wchar_t* pBuffer = static_cast<wchar_t*>(wideBuffer.m_pBlock);
@@ -246,8 +200,9 @@ DWORD WINAPI TP_GetModuleFileNameA(HMODULE aModule, char* alpFileName, DWORD aBu
246200
// of quitting, so just avoid the bug.
247201
// TODO: Further analysis of what is under MO2 USVFS and what is needed.
248202
DWORD result = 0;
249-
if (aModule != GetModuleHandleW(L"XAudio2_7.dll"))
250-
result = RealGetModuleFileNameW(aModule, pBuffer, aBufferSize * sizeof(wchar_t));
203+
HMODULE tMod = RealGetModuleHandleW(L"XAudio2_7.dll");
204+
if (tMod == nullptr || aModule != tMod)
205+
result = TP_GetModuleFileNameW(aModule, pBuffer, aBufferSize * sizeof(wchar_t)); // To make sure spoofing happens if needed
251206

252207
if (result == 0)
253208
{

xmake.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ end
3434
add_requires(
3535
"entt v3.10.0",
3636
"recastnavigation v1.6.0",
37-
"tiltedcore v0.2.7",
37+
"tiltedcore v0.2.8",
3838
"cryptopp 8.9.0",
3939
"spdlog v1.13.0",
4040
"cpp-httplib 0.14.0",

0 commit comments

Comments
 (0)