Skip to content

Instantly share code, notes, and snippets.

@m417z
Last active December 17, 2024 06:08
Show Gist options
  • Save m417z/f0cdf071868a6f31210e84dd0d444055 to your computer and use it in GitHub Desktop.
Save m417z/f0cdf071868a6f31210e84dd0d444055 to your computer and use it in GitHub Desktop.
A mod that implements a workaround for Windhawk for https://github.com/mstorsjo/llvm-mingw/issues/459
// ==WindhawkMod==
// @id process-shutdown-message-box-fix
// @name Process shutdown message box fix
// @description Fixes message box and beep sound on process shutdown, e.g. when launching folders via an explorer shortcut
// @version 0.3
// @author m417z
// @github https://github.com/m417z
// @twitter https://twitter.com/m417z
// @homepage https://m417z.com/
// @include *
// ==/WindhawkMod==
#include <stdlib.h>
// Just to make the mod depend on libc++.
#include <vector>
std::vector<int> g_v;
using _errno_t = decltype(&_errno);
HMODULE g_libCpp;
void** g_ppSetlocale;
HMODULE g_msvcrtModule;
_errno_t g_msvcrtErrno;
void** FindImportPtr(HMODULE hFindInModule,
PCSTR pModuleName,
PCSTR pImportName) {
IMAGE_DOS_HEADER* pDosHeader;
IMAGE_NT_HEADERS* pNtHeader;
ULONG_PTR ImageBase;
IMAGE_IMPORT_DESCRIPTOR* pImportDescriptor;
ULONG_PTR* pOriginalFirstThunk;
ULONG_PTR* pFirstThunk;
ULONG_PTR ImageImportByName;
// Init
pDosHeader = (IMAGE_DOS_HEADER*)hFindInModule;
pNtHeader = (IMAGE_NT_HEADERS*)((char*)pDosHeader + pDosHeader->e_lfanew);
if (!pNtHeader->OptionalHeader.DataDirectory[1].VirtualAddress)
return nullptr;
ImageBase = (ULONG_PTR)hFindInModule;
pImportDescriptor =
(IMAGE_IMPORT_DESCRIPTOR*)(ImageBase +
pNtHeader->OptionalHeader.DataDirectory[1]
.VirtualAddress);
// Search!
while (pImportDescriptor->OriginalFirstThunk) {
if (lstrcmpiA((char*)(ImageBase + pImportDescriptor->Name),
pModuleName) == 0) {
pOriginalFirstThunk =
(ULONG_PTR*)(ImageBase + pImportDescriptor->OriginalFirstThunk);
ImageImportByName = *pOriginalFirstThunk;
pFirstThunk =
(ULONG_PTR*)(ImageBase + pImportDescriptor->FirstThunk);
while (ImageImportByName) {
if (!(ImageImportByName & IMAGE_ORDINAL_FLAG)) {
if ((ULONG_PTR)pImportName & ~0xFFFF) {
ImageImportByName += sizeof(WORD);
if (lstrcmpA((char*)(ImageBase + ImageImportByName),
pImportName) == 0)
return (void**)pFirstThunk;
}
} else {
if (((ULONG_PTR)pImportName & ~0xFFFF) == 0)
if ((ImageImportByName & 0xFFFF) ==
(ULONG_PTR)pImportName)
return (void**)pFirstThunk;
}
pOriginalFirstThunk++;
ImageImportByName = *pOriginalFirstThunk;
pFirstThunk++;
}
}
pImportDescriptor++;
}
return nullptr;
}
using SetLocale_t = char*(__cdecl*)(int category, const char* locale);
SetLocale_t SetLocale_Original;
char* __cdecl SetLocale_Wrapper(int category, const char* locale) {
// A workaround for https://github.com/mstorsjo/llvm-mingw/issues/459.
errno_t* err = g_msvcrtErrno();
HMODULE module;
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(PCWSTR)err, &module) &&
module == g_msvcrtModule) {
// Getting a process-wide errno from the module section instead of the
// thread errno from the heap means we have no PTD (Per-thread data) and
// setlocale will fail, likely with a message box and abort. Return NULL
// instead to let the caller handle it gracefully.
Wh_Log(L"Returning NULL for setlocale");
return nullptr;
}
return SetLocale_Original(category, locale);
}
bool InitGlobals() {
if (!GetModuleHandleEx(0, L"libc++.dll", &g_libCpp)) {
Wh_Log(L"No libc++.dll");
return false;
}
g_ppSetlocale = FindImportPtr(g_libCpp, "msvcrt.dll", "setlocale");
if (!g_ppSetlocale) {
Wh_Log(L"No setlocale");
return false;
}
if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(PCWSTR)*g_ppSetlocale, &g_msvcrtModule)) {
Wh_Log(L"No setlocale module");
return false;
}
if (g_msvcrtModule != GetModuleHandle(L"msvcrt.dll")) {
Wh_Log(L"Bad setlocale module, already patched?");
Wh_Log(L"%p %p", g_msvcrtModule, GetModuleHandle(L"msvcrt.dll"));
return false;
}
g_msvcrtErrno = (_errno_t)GetProcAddress(g_msvcrtModule, "_errno");
if (!g_msvcrtErrno) {
Wh_Log(L"No _errno");
return false;
}
return true;
}
BOOL Wh_ModInit() {
Wh_Log(L">");
if (!InitGlobals()) {
if (g_msvcrtModule) {
FreeLibrary(g_msvcrtModule);
}
if (g_libCpp) {
FreeLibrary(g_libCpp);
}
return FALSE;
}
DWORD dwOldProtect;
VirtualProtect(g_ppSetlocale, sizeof(*g_ppSetlocale),
PAGE_EXECUTE_READWRITE, &dwOldProtect);
SetLocale_Original = (SetLocale_t)*g_ppSetlocale;
*g_ppSetlocale = (void*)SetLocale_Wrapper;
VirtualProtect(g_ppSetlocale, sizeof(*g_ppSetlocale), dwOldProtect,
&dwOldProtect);
return TRUE;
}
void Wh_ModUninit() {
Wh_Log(L">");
DWORD dwOldProtect;
VirtualProtect(g_ppSetlocale, sizeof(*g_ppSetlocale),
PAGE_EXECUTE_READWRITE, &dwOldProtect);
*g_ppSetlocale = (void*)SetLocale_Original;
VirtualProtect(g_ppSetlocale, sizeof(*g_ppSetlocale), dwOldProtect,
&dwOldProtect);
FreeLibrary(g_msvcrtModule);
FreeLibrary(g_libCpp);
}
@mkacct
Copy link

mkacct commented Dec 16, 2024

On my machine this seems to render Windows Subsystem for Linux unusable (crashes on startup) for some reason...

@m417z
Copy link
Author

m417z commented Dec 16, 2024

@mkacct Can you post a crash dump? You should be able to get it with the following steps:

  • Open regedit
  • Go to: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting
  • Create a key named LocalDumps
  • Create a DWORD value named DumpType with value 2
  • Trigger the crash
  • Go to the %LocalAppData%\CrashDumps folder, you should see a dump file in there

@mkacct
Copy link

mkacct commented Dec 16, 2024

@m417z I don't seem to be able to do that (I guess it is not technically a crash in that sense), but I can provide more details regarding what happens.
When I try to start a WSL session, Windhawk shows this:
image
And then WSL prints this and exits:

The remote procedure call failed.
Error code: Wsl/Service/RPC_S_CALL_FAILED

@m417z
Copy link
Author

m417z commented Dec 17, 2024

@mkacct It seems to happen because the process exits really quickly after launching. This mod tries to implement a workaround, but it's not perfect. Rather than improving it, I'd rather invest my time in a new Windhawk version with a proper fix.

For now, I suggest to exclude wslservice.exe in Windhawk, I assume you don't have relevant mods for it. For excluding a process in Windhawk, check out the short video here.

Thanks for the feedback, and let me know if you need help with one of the steps.

@mkacct
Copy link

mkacct commented Dec 17, 2024

@m417z Okay thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment