From bc89c30f1add45e519ab681a237b1e4d28894f00 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Thu, 21 May 2026 12:46:58 -0400 Subject: [PATCH] Add launcher project --- mhw-cs-plugin-loader.slnx | 4 + mhw-cs-plugin-loader/Launcher/Launcher.c | 94 ++++++++++++++++ mhw-cs-plugin-loader/Launcher/Launcher.ico | Bin 0 -> 4286 bytes mhw-cs-plugin-loader/Launcher/Launcher.rc | Bin 0 -> 3272 bytes .../Launcher/Launcher.vcxproj | 100 ++++++++++++++++++ .../Launcher/Launcher.vcxproj.filters | 33 ++++++ mhw-cs-plugin-loader/Launcher/create_icon.sh | 3 + mhw-cs-plugin-loader/Launcher/resource.h | 16 +++ 8 files changed, 250 insertions(+) create mode 100644 mhw-cs-plugin-loader/Launcher/Launcher.c create mode 100644 mhw-cs-plugin-loader/Launcher/Launcher.ico create mode 100755 mhw-cs-plugin-loader/Launcher/Launcher.rc create mode 100755 mhw-cs-plugin-loader/Launcher/Launcher.vcxproj create mode 100755 mhw-cs-plugin-loader/Launcher/Launcher.vcxproj.filters create mode 100755 mhw-cs-plugin-loader/Launcher/create_icon.sh create mode 100755 mhw-cs-plugin-loader/Launcher/resource.h diff --git a/mhw-cs-plugin-loader.slnx b/mhw-cs-plugin-loader.slnx index ecc1f8c..25e930f 100644 --- a/mhw-cs-plugin-loader.slnx +++ b/mhw-cs-plugin-loader.slnx @@ -62,6 +62,10 @@ + + + + diff --git a/mhw-cs-plugin-loader/Launcher/Launcher.c b/mhw-cs-plugin-loader/Launcher/Launcher.c new file mode 100644 index 0000000..29b68de --- /dev/null +++ b/mhw-cs-plugin-loader/Launcher/Launcher.c @@ -0,0 +1,94 @@ +// cl.exe Launcher.c kernel32.lib user32.lib /link /SUBSYSTEM:windows /NODEFAULTLIB /ENTRY:WinMainCRTStartup + +#include +#include + +#define TARGET_EXE "MonsterHunterWorld.exe" +#define TARGET_DLL "winmm.dll" + +static DWORD (*K32GetModuleBaseNameA)(IN HANDLE hProcess, IN HMODULE hModule, OUT LPSTR lpBaseName, IN DWORD nSize); +static BOOL (*K32EnumProcessModules)(IN HANDLE hProcess, OUT HMODULE *lphModule, IN DWORD cb, OUT LPDWORD lpcbNeeded); + +// MonsterHunterWorld.exe -> ntdll.dll -> KERNEL32.dll -> KERNELBASE.dll -> (apphelp.dll) -> TARGET_DLL + dependencies. +// () = Present on Windows and not Wine (Windows 11, Proton 11.0). +// Total modules varies between Windows and Wine, 64 is plenty in the case of MHW and likely most scenarios. +static HMODULE moduleHandles[64]; +static CHAR moduleName[MAX_MODULE_NAME32 + 1]; +static CHAR dllPath[MAX_PATH]; + +// https://git.musl-libc.org/cgit/musl/tree/src/string/strncmp.c +static int Strncmp(const char *_l, const char *_r, size_t n) +{ + const unsigned char *l=(void *)_l, *r=(void *)_r; + if (!n--) return 0; + for (; *l && *r && n && *l == *r ; l++, r++, n--); + return *l - *r; +} + +static BOOL DllWasInjected(HANDLE process) +{ + DWORD cbNeeded; + if (K32EnumProcessModules(process, moduleHandles, sizeof(moduleHandles), &cbNeeded)) { + DWORD processCount = cbNeeded / sizeof(HMODULE); + for (DWORD i = 0; i < processCount; i++) { + K32GetModuleBaseNameA(process, moduleHandles[i], moduleName, sizeof(moduleName)); + if (Strncmp(TARGET_DLL, moduleName, sizeof(TARGET_DLL)) == 0) { + return TRUE; + } + } + } + return FALSE; +} + +void __stdcall WinMainCRTStartup() +{ + HMODULE k32 = GetModuleHandleA("kernel32.dll"); + LPVOID LoadLibraryAddr = (LPVOID)GetProcAddress(k32, "LoadLibraryA"); + K32GetModuleBaseNameA = (LPVOID)GetProcAddress(k32, "K32GetModuleBaseNameA"); + K32EnumProcessModules = (LPVOID)GetProcAddress(k32, "K32EnumProcessModules"); + + STARTUPINFOA si = { .cb = sizeof(si) }; + PROCESS_INFORMATION pi = { 0 }; + if (CreateProcessA(NULL, TARGET_EXE, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi) == 0) { + MessageBoxA(NULL, + "Failed to create process. Is '" TARGET_EXE "' contained within this folder?", + "Launch Error", + MB_ICONERROR | MB_OK); + ExitProcess(1); + } + + DWORD arglen = GetFullPathNameA(TARGET_DLL, sizeof(dllPath), dllPath, NULL); + LPVOID buf = VirtualAllocEx(pi.hProcess, NULL, arglen, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + WriteProcessMemory(pi.hProcess, buf, dllPath, arglen, NULL); + + HANDLE hThread = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddr, buf, 0, NULL); + if (!hThread) { + MessageBoxA(NULL, + "Failed to CreateRemoteThread(), make sure the architecture of this exe matches " TARGET_EXE ".", + "Injection Failed", + MB_ICONWARNING | MB_OK); + } else { + WaitForSingleObject(hThread, INFINITE); + // The value of GetExitCodeThread() isn't usable, so try to manually check + // if the DLL got loaded (LoadLibrary() succeeded). + if (!DllWasInjected(pi.hProcess)) { + MessageBoxA(NULL, + "Failed to inject DLL. Is '" TARGET_DLL "' contained within this folder?", + "Injection Failed", + MB_ICONWARNING | MB_OK); + } + CloseHandle(hThread); + } + + VirtualFreeEx(pi.hProcess, buf, 0, MEM_RELEASE); + + ResumeThread(pi.hThread); + + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + FreeLibrary(k32); + + ExitProcess(0); +} diff --git a/mhw-cs-plugin-loader/Launcher/Launcher.ico b/mhw-cs-plugin-loader/Launcher/Launcher.ico new file mode 100644 index 0000000000000000000000000000000000000000..eac7be3d350b6d254b9e32b8676f5695a4177a86 GIT binary patch literal 4286 zcmb_gS!|U>6#fwtAN9!xc+dyh+q?gFU+i+bSg=Z=tw38^D%3((s)Z6a1a}3OxFjfU zK@%1YxFs4?lub$0M1wI%5vjfjnkZ^wqW1Iq{(GnXG^HT1DBhi--hkieqUoB zqx|;!us!C8`fKcOG)J95>iP>d=LR^72JH=Q`qyFm8TuamamfDbNN*VLcLcF@VE~&O z{En!vu_HZUNQb)L(PlG!Wd3U$1zcgz;954)mwVF$0Lyad(ZV#G>{5G*c&zbG&2 zYfN*}Q9QDdEqy#pUo(y0vn&uN<~qfXme8%J5x zmyfd$M;m<|rcbSl7-@rNVkmr{$@%??RoLRu&2h}8!#nZRO}~%#33jAM&Za5Jn*WrOKu4&H< z=f+m8`0sDZqBb3L*4$B-L?id2G9Ey8Lk5kN5p-Uca`t8M?6{-vwwF)&-o~617BO`; zwf^b+{`Ba|2wF?S@D+RV`_6d9n_G;(5Piv?;Ys;!=G2<+ns?iKW))#x+=Ju&PCh;( zY>JQgC%)*3;@!dC;0JfnsrEyZbtW zgE=p5s?3Y}L)?Q;xc zi<7wbU*GPWP+#?LJ9AonH!o4*6?$ zlc#&@g77l0o~ZX@lr>(wic`t++^go4$H8*r8*F#0*mQ>y|L<+wcVQZQad6w*6!vt+ z@Ks+FhkK(?eypFDLS1PZ*=Pit8iM$gc{EVrb#ikhbL&Oof3YHjh0GDAH7n=byv`=*5ne7Yivx#VA%!V@=rbJA$%en@9aYXW=8PjxDRSRep@aj}yZ%E@PW zUZ%nq2QL1Py7Nz1x0Cy?`y*IapF#_BQTcc`x!2g898*j$5&yI9C&MaF5SEwa?+@jk@_!=#;>qNn`}~<4P~JV#l*9p^3yI=8v^cvFi(>zTy^t)#)Tsp&)BUF%mqVZhY;F-bi|<5qqWTFLUr)<$!9B@vl}f zxWzqpu6fy2B;ttqeP`h7ZqVKn^8~T=PSyRh>HEP5dS|D{b8RBlg?_KiW>6Z7AmYvE zgzkjG+tA!Ux&w9p{_qw#5Qi6vHI-dWCWX>S$f<|Ka^Jtg*Zbq4Q$sc$Xh{C=94M|@ z%Kf}wg+Z*1;+^6=!PHV_Fs}XI@*SZ1W6u0kYdYUK`BEJ_PT#~hK&kmse)^EF0(O_vZBQH<)7)CtZr$gyp{;&@ZICbH_YANpz7+J8qxwyI zW5uI;TOvG-X-+!S9i`3FvZajY`4n(xohrnk-``%YrJsIx(*FqMSIXZ7QGbMUyQnij zT^~g@XZ33)uzniGENk6gmR0eGWnJ;JWnDUIS(og!tm*6dUk4KF0{@KtUiuoPPx%&x Ju!Je>e*neI#^nG2 literal 0 HcmV?d00001 diff --git a/mhw-cs-plugin-loader/Launcher/Launcher.rc b/mhw-cs-plugin-loader/Launcher/Launcher.rc new file mode 100755 index 0000000000000000000000000000000000000000..799a459ffda96ac6e1c5977e151abf433e411a48 GIT binary patch literal 3272 zcmdUxZ;ui&5XR@ViQl1eZ@g#@|9pZ2P9$<@4!CGSLI{YGz(QEinE2T}e@{!j1s06x zi#5$|+v!X@Go5)lcR$KjHfPs1v7s$(ZYkf`R?u#trM9+#O_^2g3C-Cj`)CQ@68egM z1Z_z#xizC9W5-OpO>6$)=d}A-Cy)W&Z zwXI`at69U|THkK$3TlmU%bNB9N}K<+RiHE($MdB_@mka__Ri|~W!J}Jyfn8N_OI}c z{LsPb_xu}JUByoM;|lr@jzqFpGKEW)!kAwey@PNHaM#@&0x|%fY`<>KexN^!Fd5B-KeY`1I58Ubz(ET#^&UA;Y0gY_fNF;ZapUZZuN~I@wOYV2 z@YR7T2EFFX4ets`=RK@yLobknb3qf*4Lzbwyf)6yvwDS>ZU&F4iWJG9cgkFv=pB_l zlF_bHnyQm;o7EY8cpIg01I%iGcB&(kQO8>3JI0e;D}iPM>{IQd_j}2?vTnXr=}6za zIQuuqpS|;9jbnVpUYIfVzBu%*@8?;uEXryV^D6d#Tj2@!j#qfmk6A5>HI{~laWx?? Nl(+l!-2T05r=LU8g`NNa literal 0 HcmV?d00001 diff --git a/mhw-cs-plugin-loader/Launcher/Launcher.vcxproj b/mhw-cs-plugin-loader/Launcher/Launcher.vcxproj new file mode 100755 index 0000000..662c6e8 --- /dev/null +++ b/mhw-cs-plugin-loader/Launcher/Launcher.vcxproj @@ -0,0 +1,100 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {1fa71114-1187-4059-9523-196a234c1b94} + Launcher + 10.0 + + + + Application + true + v143 + NotSet + + + Application + false + v143 + true + NotSet + + + + + + + + + + + + + + + SPLLauncher + false + + + SPLLauncher + false + + + + Level3 + false + _DEBUG;%(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN + true + false + Default + + + Windows + true + Yes + + + + + Level3 + true + true + false + NDEBUG;%(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN + true + false + + + Windows + true + Yes + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mhw-cs-plugin-loader/Launcher/Launcher.vcxproj.filters b/mhw-cs-plugin-loader/Launcher/Launcher.vcxproj.filters new file mode 100755 index 0000000..ab6603e --- /dev/null +++ b/mhw-cs-plugin-loader/Launcher/Launcher.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/mhw-cs-plugin-loader/Launcher/create_icon.sh b/mhw-cs-plugin-loader/Launcher/create_icon.sh new file mode 100755 index 0000000..5815f00 --- /dev/null +++ b/mhw-cs-plugin-loader/Launcher/create_icon.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env sh +# -define icon:auto-resize=256,128,96,64,48,32,16 +magick -density 512 -define icon:auto-resize=32 -background none ../../docs/images/SPL.svg Launcher.ico diff --git a/mhw-cs-plugin-loader/Launcher/resource.h b/mhw-cs-plugin-loader/Launcher/resource.h new file mode 100755 index 0000000..79dc64e --- /dev/null +++ b/mhw-cs-plugin-loader/Launcher/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Launcher.rc +// +#define IDI_ICON1 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif