From 1f4b586f0312cab6b7e491d1feb65e4f4caaef65 Mon Sep 17 00:00:00 2001 From: Valentin Radu Date: Wed, 1 Mar 2023 20:27:44 +0200 Subject: [PATCH] ep_extra: Implemented an `ep_extra`-based loader ExplorerPatcher includes a mechanism which allows the user to load a single DLL, named `ep_extra.dll` and placed in `C:\Windows` when `explorer.exe` loads. This was long requested by some users who wanted to perform their own code/initializations/hooks when `explorer` started. In the mean time, I thought `ep_extra.dll` would work nicely as a loader for any number of modules. I could've included this functionality in the main ExplorerPatcher code, but I decided to take it up as a challenge instead and offer a robust implementation without changing ExplorerPatcher's main code. Thus, when using this as `ep_extra.dll`, it will also load DLLs that match the `ep_extra_*.dll` pattern. These DLLs must export a function called `setup` which the loader will execute on the thread that is loading the DLLs. Although not currently checked, return 0 from this function if your initialization succeeded, or some error code when it fails. What's up with the assembly and shell codes and weird threads that I create? Well, I realized I kind of did a mistake when coding ExplorerPatcher, in that I should have loaded and executed `ep_extra` on a seprate thread, not in `explorer`'s main thread. But since I said I'd do this challenge without changing EP, this was my solution towards having this `ep_extra` loader do its work, load the other DLLs, if any, and then unload itself from memory safely and (almost) cleanly without disturbing the main application right after it does its job. This way, you can update it seamlessly when `explorer` is running, which is much more convenient than having to kill `explorer`, replace the DLL, and then manually reload `explorer`. I don't know if this is the best way, but it is the way I thought about when realizing that I cannot call `FreeLibrary` simply, since the "next line" (which would have been a "return") is well outside of valid memory at that point. `FreeLibraryAndExitThread` also can't be used since I do not want to exit `explorer`'s main thread which the loader's function is called on. --- ep_extra/README.md | 5 + ep_extra/ep_extra.rc | 100 +++++++++++++++++ ep_extra/ep_extra.vcxproj | 179 ++++++++++++++++++++++++++++++ ep_extra/ep_extra.vcxproj.filters | 37 ++++++ ep_extra/main.asm | 18 +++ ep_extra/resource.h | 14 +++ ep_extra/worker.c | 82 ++++++++++++++ 7 files changed, 435 insertions(+) create mode 100644 ep_extra/README.md create mode 100644 ep_extra/ep_extra.rc create mode 100644 ep_extra/ep_extra.vcxproj create mode 100644 ep_extra/ep_extra.vcxproj.filters create mode 100644 ep_extra/main.asm create mode 100644 ep_extra/resource.h create mode 100644 ep_extra/worker.c diff --git a/ep_extra/README.md b/ep_extra/README.md new file mode 100644 index 0000000..580ef75 --- /dev/null +++ b/ep_extra/README.md @@ -0,0 +1,5 @@ +# ExplorerPatcher Custom Libraries Chainloader + +ExplorerPatcher has a simple, built-in mechanism that allows users to load their own DLL into `explorer.exe` right after ExplorerPatcher finishes initializing its hooks. Interested users hould place a DLL called `ep_extra.dll` in `C:\Windows`. When ExplorerPatcher finishes its setup, it loads the `ep_extra.dll` library and calls the `ep_extra_EntryPoint` function. Although this is very useful so that users can load their custom code, it is quite limited at the moment, as it loads just one DLL. + +This project is a solution to this issue. A chainloader is implemented here, that looks for other modules matching the `ep_extra_*.dll` pattern in `C:\Windows` as well, and loads them one after the other. diff --git a/ep_extra/ep_extra.rc b/ep_extra/ep_extra.rc new file mode 100644 index 0000000..0dca6b0 --- /dev/null +++ b/ep_extra/ep_extra.rc @@ -0,0 +1,100 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "VALINET Solutions SRL" + VALUE "FileDescription", "ExplorerPatcher Custom Libraries Chainloader" + VALUE "FileVersion", "1.0.0.0" + VALUE "InternalName", "ep_extra.dll" + VALUE "LegalCopyright", "Copyright (C) 2006-2022 VALINET Solutions SRL. All rights reserved." + VALUE "OriginalFilename", "ep_extra.dll" + VALUE "ProductName", "ExplorerPatcher" + VALUE "ProductVersion", "1.0.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/ep_extra/ep_extra.vcxproj b/ep_extra/ep_extra.vcxproj new file mode 100644 index 0000000..0e33346 --- /dev/null +++ b/ep_extra/ep_extra.vcxproj @@ -0,0 +1,179 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {93fa47cc-7753-4f86-b583-69048f51c5ab} + epextra + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(Configuration) + + + false + $(SolutionDir)\build\$(Configuration) + + + true + $(SolutionDir)\build\$(Configuration) + + + false + $(SolutionDir)\build\$(Configuration) + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + /EXPORT:ep_extra_EntryPoint %(AdditionalOptions) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + /EXPORT:ep_extra_EntryPoint %(AdditionalOptions) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Console + true + + + /EXPORT:ep_extra_EntryPoint %(AdditionalOptions) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + + + %(AdditionalOptions) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ep_extra/ep_extra.vcxproj.filters b/ep_extra/ep_extra.vcxproj.filters new file mode 100644 index 0000000..e30d842 --- /dev/null +++ b/ep_extra/ep_extra.vcxproj.filters @@ -0,0 +1,37 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {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 + + + + + Source Files + + + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/ep_extra/main.asm b/ep_extra/main.asm new file mode 100644 index 0000000..e57969a --- /dev/null +++ b/ep_extra/main.asm @@ -0,0 +1,18 @@ +EXTERN worker : PROC + +.CODE + +ep_extra_EntryPoint PROC EXPORT + PUSH RBP + MOV RBP, RSP + SUB RSP, 30H + CALL worker + CMP RAX, 0 + JE finish + JMP RAX +finish: + LEAVE + RET +ep_extra_EntryPoint ENDP + +END \ No newline at end of file diff --git a/ep_extra/resource.h b/ep_extra/resource.h new file mode 100644 index 0000000..bc8b44e --- /dev/null +++ b/ep_extra/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ep_extra.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/ep_extra/worker.c b/ep_extra/worker.c new file mode 100644 index 0000000..8f0b97f --- /dev/null +++ b/ep_extra/worker.c @@ -0,0 +1,82 @@ +#include +#include +#pragma comment(lib, "Shlwapi.lib") +#include + +HMODULE hModule = NULL; +HANDLE sigFinish = NULL; +void* pFinishProc = NULL; + +void done() { + WaitForSingleObject(sigFinish, INFINITE); + FreeLibraryAndExitThread(hModule, 0); +} + +void* worker() { + wchar_t pattern[MAX_PATH]; + GetWindowsDirectoryW(pattern, MAX_PATH); + wcscat_s(pattern, MAX_PATH, L"\\ep_extra_*.dll"); + + WIN32_FIND_DATA data; + HANDLE hFind = FindFirstFileW(pattern, &data); + if (hFind != INVALID_HANDLE_VALUE) { + do { + wprintf(L">> Found ep_extra library: \"%s\"\n", data.cFileName); + GetWindowsDirectoryW(pattern, MAX_PATH); + wcscat_s(pattern, MAX_PATH, L"\\"); + wcscat_s(pattern, MAX_PATH, data.cFileName); + HMODULE hLib = LoadLibraryW(pattern); + if (hLib) { + FARPROC proc = (FARPROC)(GetProcAddress(hLib, "setup")); + if (proc) { + if (!proc()) FreeLibrary(hLib); + } + else FreeLibrary(hLib); + } + } while (FindNextFileW(hFind, &data)); + FindClose(hFind); + } + + sigFinish = CreateEventW(NULL, FALSE, FALSE, NULL); + if (sigFinish) { + BYTE payload[] = { + 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rcx, sigFinish + 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, SetEvent + 0xFF, 0xD0, // call SetEvent + 0xC9, // leave + 0xC3 // ret + }; + *(INT64*)(payload + 2) = sigFinish; + *(INT64*)(payload + 12) = SetEvent; + + pFinishProc = VirtualAlloc(NULL, sizeof(payload), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (pFinishProc) { + memcpy(pFinishProc, payload, sizeof(payload)); + SHCreateThread(done, 0, CTF_NOADDREFLIB, NULL); + return pFinishProc; + } + } + return NULL; +} + +BOOL WINAPI DllMain( + _In_ HINSTANCE hinstDLL, + _In_ DWORD fdwReason, + _In_ LPVOID lpvReserved +) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + hModule = hinstDLL; + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +}