diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..f7a87b295 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "libs/libvalinet"] + path = libs/libvalinet + url = https://github.com/valinet/libvalinet +[submodule "libs/funchook"] + path = libs/funchook + url = https://github.com/kubo/funchook diff --git a/ExplorerPatcher.sln b/ExplorerPatcher.sln index f3ed24e23..458b305df 100644 --- a/ExplorerPatcher.sln +++ b/ExplorerPatcher.sln @@ -1,9 +1,11 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 -VisualStudioVersion = 16.0.31410.357 +VisualStudioVersion = 16.0.30002.166 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPatcher", "ExplorerPatcher\ExplorerPatcher.vcxproj", "{DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPatcher", "ExplorerPatcher\ExplorerPatcher.vcxproj", "{05EB9B16-4C34-47A2-97F5-E65E4955F71B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPatcherLibrary", "ExplorerPatcherLibrary\ExplorerPatcherLibrary.vcxproj", "{DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,19 +15,27 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}.Debug|x64.ActiveCfg = Debug|x64 - {DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}.Debug|x64.Build.0 = Debug|x64 - {DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}.Debug|x86.ActiveCfg = Debug|Win32 - {DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}.Debug|x86.Build.0 = Debug|Win32 - {DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}.Release|x64.ActiveCfg = Release|x64 - {DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}.Release|x64.Build.0 = Release|x64 - {DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}.Release|x86.ActiveCfg = Release|Win32 - {DED2A41B-0EA8-4D8E-8A02-31A29EF9F91C}.Release|x86.Build.0 = Release|Win32 + {05EB9B16-4C34-47A2-97F5-E65E4955F71B}.Debug|x64.ActiveCfg = Debug|x64 + {05EB9B16-4C34-47A2-97F5-E65E4955F71B}.Debug|x64.Build.0 = Debug|x64 + {05EB9B16-4C34-47A2-97F5-E65E4955F71B}.Debug|x86.ActiveCfg = Debug|Win32 + {05EB9B16-4C34-47A2-97F5-E65E4955F71B}.Debug|x86.Build.0 = Debug|Win32 + {05EB9B16-4C34-47A2-97F5-E65E4955F71B}.Release|x64.ActiveCfg = Release|x64 + {05EB9B16-4C34-47A2-97F5-E65E4955F71B}.Release|x64.Build.0 = Release|x64 + {05EB9B16-4C34-47A2-97F5-E65E4955F71B}.Release|x86.ActiveCfg = Release|Win32 + {05EB9B16-4C34-47A2-97F5-E65E4955F71B}.Release|x86.Build.0 = Release|Win32 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}.Debug|x64.ActiveCfg = Debug|x64 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}.Debug|x64.Build.0 = Debug|x64 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}.Debug|x86.ActiveCfg = Debug|Win32 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}.Debug|x86.Build.0 = Debug|Win32 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}.Release|x64.ActiveCfg = Release|x64 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}.Release|x64.Build.0 = Release|x64 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}.Release|x86.ActiveCfg = Release|Win32 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {1D4372C5-52FF-4B30-9C71-5ED6F36C1966} + SolutionGuid = {39EBC2F0-6949-46EC-9FC2-776591FEE2DA} EndGlobalSection EndGlobal diff --git a/ExplorerPatcher/ExplorerPatcher.rc b/ExplorerPatcher/ExplorerPatcher.rc new file mode 100644 index 000000000..b03d29c34 --- /dev/null +++ b/ExplorerPatcher/ExplorerPatcher.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 22000,1,0,1 + PRODUCTVERSION 22000,1,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "VALINET Solutions SRL" + VALUE "FileDescription", "ExplorerPatcher Daemon" + VALUE "FileVersion", "22000.1.0.1" + VALUE "InternalName", "ExplorerPatcher.exe" + VALUE "LegalCopyright", "Copyright (C) 2006-2021 VALINET Solutions SRL. All rights reserved." + VALUE "OriginalFilename", "ExplorerPatcher.exe" + VALUE "ProductName", "ExplorerPatcher" + VALUE "ProductVersion", "22000.1.0.1" + 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/ExplorerPatcher/ExplorerPatcher.vcxproj b/ExplorerPatcher/ExplorerPatcher.vcxproj index d7737e7c5..9b21582c0 100644 --- a/ExplorerPatcher/ExplorerPatcher.vcxproj +++ b/ExplorerPatcher/ExplorerPatcher.vcxproj @@ -18,21 +18,12 @@ x64 - - - - - - - - - 16.0 - Win32Proj - {ded2a41b-0ea8-4d8e-8a02-31a29ef9f91c} - ExplorerPatcher + {05EB9B16-4C34-47A2-97F5-E65E4955F71B} + CenterTitlebarText 10.0 + ExplorerPatcher @@ -82,55 +73,54 @@ true - - false - true + + false + false + $(SolutionDir)$(Platform)\$(Configuration)\ Level3 true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - MultiThreadedDebug - Windows + Console true - + Level3 - true - true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - MultiThreaded + $(SolutionDir)libs\libvalinet;%(AdditionalIncludeDirectories) Windows - true - true true - + Level3 + true + true true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - MultiThreadedDebug - Windows + Console + true + true true @@ -142,7 +132,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - MultiThreaded + $(SolutionDir)libs\libvalinet;%(AdditionalIncludeDirectories) Windows @@ -151,6 +141,15 @@ true + + + + + + + + + diff --git a/ExplorerPatcher/ExplorerPatcher.vcxproj.filters b/ExplorerPatcher/ExplorerPatcher.vcxproj.filters index 0794076f0..2e42052bf 100644 --- a/ExplorerPatcher/ExplorerPatcher.vcxproj.filters +++ b/ExplorerPatcher/ExplorerPatcher.vcxproj.filters @@ -3,7 +3,7 @@ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} @@ -14,19 +14,19 @@ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - Source Files - - Header Files - + Resource Files + + + Source Files + + \ No newline at end of file diff --git a/ExplorerPatcher/main.c b/ExplorerPatcher/main.c index 93ec12396..b534167b1 100644 --- a/ExplorerPatcher/main.c +++ b/ExplorerPatcher/main.c @@ -1,32 +1,63 @@ #pragma comment(linker,"\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#include #include #include #include #include +#include +#pragma comment(lib, "Shlwapi.lib") +#define CLASS_NAME TEXT("ExplorerPatcher") #define APP_NAME TEXT("Windows Explorer") #define NOP 0x90 #define PATCH_OFFSET 0x8cb33 +HANDLE hProcess = NULL; +HMODULE hMod = NULL; +LPVOID hInjection = NULL; +HWND hWnd = NULL; + +LRESULT CALLBACK WindowProc( + HWND hWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + switch (uMsg) + { + case WM_APP_CRASHED: + { + TerminateProcess(GetCurrentProcess(), 0); + } + } + return VnWindowProc( + hWnd, + uMsg, + wParam, + lParam + ); +} + // https://stackoverflow.com/questions/8046097/how-to-check-if-a-process-has-the-administrative-rights BOOL IsElevated() { BOOL fRet = FALSE; HANDLE hToken = NULL; if (OpenProcessToken( - GetCurrentProcess(), - TOKEN_QUERY, + GetCurrentProcess(), + TOKEN_QUERY, &hToken - )) + )) { TOKEN_ELEVATION Elevation; DWORD cbSize = sizeof(TOKEN_ELEVATION); if (GetTokenInformation( - hToken, + hToken, TokenElevation, - &Elevation, - sizeof(Elevation), + &Elevation, + sizeof(Elevation), &cbSize )) { fRet = Elevation.TokenIsElevated; @@ -146,6 +177,18 @@ int WINAPI wWinMain( MODULEENTRY32 me32 = { 0 }; THREADENTRY32 th32 = { 0 }; TCHAR szExplorerPath[MAX_PATH]; + FILE* conout; + TCHAR szLibPath[MAX_PATH]; + +#ifdef DEBUG + if (!AllocConsole()); + if (freopen_s( + &conout, + "CONOUT$", + "w", + stdout + )); +#endif if (install_uninstall()) { @@ -178,10 +221,10 @@ int WINAPI wWinMain( } if ((hExplorer = OpenProcess( - PROCESS_VM_READ | - PROCESS_VM_WRITE | - PROCESS_QUERY_INFORMATION | - PROCESS_VM_OPERATION | + PROCESS_VM_READ | + PROCESS_VM_WRITE | + PROCESS_QUERY_INFORMATION | + PROCESS_VM_OPERATION | SYNCHRONIZE, FALSE, dwExplorerPID @@ -195,7 +238,7 @@ int WINAPI wWinMain( CharLower(szExplorerPath); me32.dwSize = sizeof(MODULEENTRY32); hSnapshot = CreateToolhelp32Snapshot( - TH32CS_SNAPMODULE, + TH32CS_SNAPMODULE, dwExplorerPID ); if (Module32First(hSnapshot, &me32) == TRUE) @@ -248,7 +291,41 @@ int WINAPI wWinMain( { DebugActiveProcessStop(dwExplorerPID); } - return 0; + + GetModuleFileName( + GetModuleHandle(NULL), + szLibPath, + MAX_PATH + ); + PathRemoveFileSpec(szLibPath); + lstrcat( + szLibPath, + L"\\ExplorerPatcherLibrary.dll" + ); + Sleep(2000); + return VnInjectAndMonitorProcess( + szLibPath, + MAX_PATH, + "main", + TEXT("explorer.exe"), + CLASS_NAME, + NULL, + hInstance, + stdout, + 0, + WindowProc, + TRUE, + 0, + 0, + NULL, + &hProcess, + &hMod, + &hInjection, + NULL, + 0, + &hWnd, + &hWnd + ); } - return 0; + return 0; } \ No newline at end of file diff --git a/ExplorerPatcher/resource.h b/ExplorerPatcher/resource.h index 41050e091..90c388b17 100644 --- a/ExplorerPatcher/resource.h +++ b/ExplorerPatcher/resource.h @@ -1,6 +1,7 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by resource.rc +// Used by ExplorerPatcher.rc +// // Next default values for new objects // diff --git a/ExplorerPatcherLibrary/ExplorerPatcherLibrary.rc b/ExplorerPatcherLibrary/ExplorerPatcherLibrary.rc new file mode 100644 index 000000000..81aed482a --- /dev/null +++ b/ExplorerPatcherLibrary/ExplorerPatcherLibrary.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 22000,1,0,1 + PRODUCTVERSION 22000,1,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 Library" + VALUE "FileVersion", "22000.1.0.1" + VALUE "InternalName", "ExplorerPatcherLibrary.dll" + VALUE "LegalCopyright", "Copyright (C) 2006-2021 VALINET Solutions SRL. All rights reserved." + VALUE "OriginalFilename", "ExplorerPatcherLibrary.dll" + VALUE "ProductName", "WinOverview" + VALUE "ProductVersion", "22000.1.0.1" + 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/ExplorerPatcherLibrary/ExplorerPatcherLibrary.vcxproj b/ExplorerPatcherLibrary/ExplorerPatcherLibrary.vcxproj new file mode 100644 index 000000000..b41aee446 --- /dev/null +++ b/ExplorerPatcherLibrary/ExplorerPatcherLibrary.vcxproj @@ -0,0 +1,160 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {DB3E4319-2969-42B6-B7E8-BB57AA8C9FA9} + CenterTitlebarTextLibrary + 10.0 + ExplorerPatcherLibrary + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + false + $(SolutionDir)$(Platform)\$(Configuration)\ + + + true + + + true + + + false + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)libs\funchook\include;$(SolutionDir)libs\libvalinet;%(AdditionalIncludeDirectories) + + + Console + true + true + true + $(SolutionDir)libs\funchook\build\Release\funchook.lib;$(SolutionDir)libs\funchook\build\Release\distorm.lib;%(AdditionalLibraryDirectories) + $(SolutionDir)libs\funchook\build\Release\funchook.lib;$(SolutionDir)libs\funchook\build\Release\distorm.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(SolutionDir)libs\funchook\include;$(SolutionDir)libs\libvalinet;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)libs\funchook\build\Release\funchook.lib;$(SolutionDir)libs\funchook\build\Release\distorm.lib;%(AdditionalLibraryDirectories) + $(SolutionDir)libs\funchook\build\Release\funchook.lib;$(SolutionDir)libs\funchook\build\Release\distorm.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ExplorerPatcherLibrary/ExplorerPatcherLibrary.vcxproj.filters b/ExplorerPatcherLibrary/ExplorerPatcherLibrary.vcxproj.filters new file mode 100644 index 000000000..464c2dc48 --- /dev/null +++ b/ExplorerPatcherLibrary/ExplorerPatcherLibrary.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;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 + + + + + Header Files + + + + + Resource Files + + + + + Source Files + + + \ No newline at end of file diff --git a/ExplorerPatcherLibrary/dllmain.c b/ExplorerPatcherLibrary/dllmain.c new file mode 100644 index 000000000..c7add6c40 --- /dev/null +++ b/ExplorerPatcherLibrary/dllmain.c @@ -0,0 +1,467 @@ +#include +#include +#include +#include +#include +#pragma comment(lib, "Psapi.lib") // required by funchook +#include +#pragma comment(lib, "Shlwapi.lib") + + + +funchook_t* funchook = NULL; +HMODULE hModule = NULL; +HWND messageWindow = NULL; + + + +static HWND(WINAPI* CreateWindowInBand)( + _In_ DWORD dwExStyle, + _In_opt_ ATOM atom, + _In_opt_ LPCWSTR lpWindowName, + _In_ DWORD dwStyle, + _In_ int X, + _In_ int Y, + _In_ int nWidth, + _In_ int nHeight, + _In_opt_ HWND hWndParent, + _In_opt_ HMENU hMenu, + _In_opt_ HINSTANCE hInstance, + _In_opt_ LPVOID lpParam, + DWORD band + ); + + + +static INT64(*CLauncherTipContextMenu_ShowLauncherTipContextMenuFunc)( + void* _this, + POINT* pt + ); + +static void(*CLauncherTipContextMenu_ExecuteCommand)( + void* _this, + int a2 + ); + +static void(*CLauncherTipContextMenu_ExecuteShutdownCommand)( + void* _this, + void* a2 + ); + +static INT64(*InternalAddRef)( + void* a1, + INT64 a2 + ); + +static INT64(*ImmersiveContextMenuHelper_ApplyOwnerDrawToMenu)( + HMENU h1, + HMENU h2, + HWND a3, + unsigned int a4, + void* data + ); + +static INT64(*CLauncherTipContextMenu_GetMenuItemsAsync)( + void* _this, + void* rect, + void** iunk + ); + +DEFINE_GUID(IID_ILauncherTipContextMenu, + 0xb8c1db5f, + 0xcbb3, 0x48bc, 0xaf, 0xd9, + 0xce, 0x6b, 0x88, 0x0c, 0x79, 0xed +); + +#define CLASS_NAME L"LauncherTipWnd" + + + +static HRESULT(*CImmersiveHotkeyNotification_OnMessageFunc)( + void* _this, + INT64 msg, + INT wParam, + INT64 lParam + ); + +static INT64(*CImmersiveHotkeyNotification_GetMonitorForHotkeyNotificationFunc)( + void* _this, + void** a2, + HWND* a3 + ); + +static BOOL(*IsDesktopInputContextFunc)( + void* p1, + void* p2 + ); + + + + + + +HANDLE hThread; + +typedef struct +{ + void* _this; + POINT point; + IUnknown* iunk; +} ShowLauncherTipContextMenuParameters; + +DWORD ShowLauncherTipContextMenu( + ShowLauncherTipContextMenuParameters* params +) +{ + WNDCLASS wc = { 0 }; + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = DefWindowProc; // CLauncherTipContextMenu_WndProc + wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wc.hInstance = GetModuleHandle(NULL); + wc.lpszClassName = CLASS_NAME; + wc.hCursor = LoadCursorW(NULL, IDC_ARROW); + RegisterClass(&wc); + + HWND hWnd = CreateWindowInBand( + 0, + CLASS_NAME, + 0, + WS_POPUP, + 0, + 0, + 0, + 0, + 0, + 0, + GetModuleHandle(NULL), + params->_this, + 7 + ); + ShowWindow(hWnd, SW_SHOW); + SetForegroundWindow(hWnd); + + while (!(*((HMENU*)((char*)params->_this + 0xe8)))) + { + Sleep(1); + } + if (!(*((HMENU*)((char*)params->_this + 0xe8)))) + { + goto finalize; + } + + /* + void* v25 = 0; + ImmersiveContextMenuHelper_ApplyOwnerDrawToMenu( + *((HMENU*)((char*)params->_this + 0xe8)), + hWnd, + &(params->point), + 0xc, + &v25 + ); + */ + + BOOL res = TrackPopupMenu( + *((HMENU*)((char*)params->_this + 0xe8)), + TPM_RETURNCMD, + params->point.x, + params->point.y, + 0, + hWnd, + 0 + ); + if (res > 0) + { + if (res < 4000) + { + INT64 info = *(INT64*)((char*)(*(INT64*)((char*)params->_this + 0xa8 - 0x58)) + (INT64)res * 8 - 8); + CLauncherTipContextMenu_ExecuteCommand( + (char*)params->_this - 0x58, + &info + ); + } + else + { + INT64 info = *(INT64*)((char*)(*(INT64*)((char*)params->_this + 0xc8 - 0x58)) + ((INT64)res - 4000) * 8); + CLauncherTipContextMenu_ExecuteShutdownCommand( + (char*)params->_this - 0x58, + &info + ); + } + } + + // ImmersiveContextMenuHelper_RemoveOwnerDrawFromMenu + + finalize: + params->iunk->lpVtbl->Release(params->iunk); + SendMessage( + hWnd, + WM_CLOSE, + 0, + 0 + ); + free(params); + hThread = NULL; + return 0; +} + +INT64 CLauncherTipContextMenu_ShowLauncherTipContextMenuHook( + void* _this, + POINT* pt +) +{ + if (hThread) + { + goto finalize; + } + + POINT point; + if (pt) + { + point = *pt; + } + else + { + POINT ptCursor; + GetCursorPos(&ptCursor); + MONITORINFO mi; + mi.cbSize = sizeof(MONITORINFO); + GetMonitorInfo( + MonitorFromPoint( + ptCursor, + MONITOR_DEFAULTTONEAREST + ), + &mi + ); + // https://stackoverflow.com/questions/44746234/programatically-get-windows-taskbar-info-autohidden-state-taskbar-coordinates + APPBARDATA abd; + abd.cbSize = sizeof(APPBARDATA); + SHAppBarMessage(ABM_GETTASKBARPOS, &abd); + if (abd.rc.left < 5 && abd.rc.top > 5) + { + // TB_POS_BOTTOM + point.x = mi.rcMonitor.left; + point.y = mi.rcMonitor.bottom; + } + else if (abd.rc.left < 5 && abd.rc.top < 5 && abd.rc.right > abd.rc.bottom) + { + // TB_POS_TOP + point.x = mi.rcMonitor.left; + point.y = mi.rcMonitor.top; + } + else if (abd.rc.left < 5 && abd.rc.top < 5 && abd.rc.right < abd.rc.bottom) + { + // TB_POS_LEFT + point.x = mi.rcMonitor.left; + point.y = mi.rcMonitor.top; + } + else if (abd.rc.left > 5 && abd.rc.top < 5) + { + // TB_POS_RIGHT + point.x = mi.rcMonitor.right; + point.y = mi.rcMonitor.top; + } + } + + IUnknown* iunk; + INT64 r = CLauncherTipContextMenu_GetMenuItemsAsync( + _this, + &point, + &iunk + ); + iunk->lpVtbl->AddRef(iunk); + + ShowLauncherTipContextMenuParameters* params = malloc( + sizeof(ShowLauncherTipContextMenuParameters) + ); + params->_this = _this; + params->point = point; + params->iunk = iunk; + hThread = CreateThread( + 0, + 0, + ShowLauncherTipContextMenu, + params, + 0, + 0 + ); + + finalize: + return CLauncherTipContextMenu_ShowLauncherTipContextMenuFunc(_this, pt); +} + +HRESULT CImmersiveHotkeyNotification_OnMessageHook( + void* _this, + INT64 msg, + INT wParam, + INT64 lParam +) +{ + if (wParam == 28 && IsDesktopInputContextFunc(_this, msg)) // 15 + { + IUnknown* pMonitor; + HRESULT hr = CImmersiveHotkeyNotification_GetMonitorForHotkeyNotificationFunc( + (char*)_this - 0x68, + &pMonitor, + 0 + ); + if (SUCCEEDED(hr)) + { + IUnknown* pMenu; + IUnknown_QueryService( + pMonitor, + &IID_ILauncherTipContextMenu, + &IID_ILauncherTipContextMenu, + &pMenu + ); + if (pMenu) + { + CLauncherTipContextMenu_ShowLauncherTipContextMenuHook( + pMenu, + 0 + ); + pMenu->lpVtbl->Release(pMenu); + } + } + return 0; + } + + return CImmersiveHotkeyNotification_OnMessageFunc( + _this, + msg, + wParam, + lParam + ); +} + +__declspec(dllexport) DWORD WINAPI main( + _In_ LPVOID lpParameter +) +{ + /* + FILE* conout; + AllocConsole(); + freopen_s(&conout, "CONOUT$", "w", stdout); + */ + + int rv; + if (!funchook) + { + messageWindow = (HWND)lpParameter; + + + + funchook = funchook_create(); + + + + HANDLE hUser32 = GetModuleHandle(L"user32.dll"); + + if (hUser32) CreateWindowInBand = GetProcAddress(hUser32, "CreateWindowInBand"); + + + + HANDLE hTwinuiPcshell = GetModuleHandle(L"twinui.pcshell.dll"); + + InternalAddRef = (INT64(*)(void*, INT64)) + ((uintptr_t)hTwinuiPcshell + 0x46650); + + CLauncherTipContextMenu_GetMenuItemsAsync = (INT64(*)(void*, void*, void**)) + ((uintptr_t)hTwinuiPcshell + 0x5051F0); + + ImmersiveContextMenuHelper_ApplyOwnerDrawToMenu = (INT64(*)(HMENU, HMENU, HWND, unsigned int, void*)) + ((uintptr_t)hTwinuiPcshell + 0x535AF8); + + CLauncherTipContextMenu_ExecuteShutdownCommand = (void(*)(void*, void*)) + ((uintptr_t)hTwinuiPcshell + 0x514714); + + CLauncherTipContextMenu_ExecuteCommand = (void(*)(void*, int)) + ((uintptr_t)hTwinuiPcshell + 0x5143D0); + + CLauncherTipContextMenu_ShowLauncherTipContextMenuFunc = (INT64(*)(void*, POINT*)) + ((uintptr_t)hTwinuiPcshell + 0x506EE0); + rv = funchook_prepare( + funchook, + (void**)&CLauncherTipContextMenu_ShowLauncherTipContextMenuFunc, + CLauncherTipContextMenu_ShowLauncherTipContextMenuHook + ); + if (rv != 0) + { + FreeLibraryAndExitThread(hModule, rv); + return rv; + } + + + + HANDLE hTwinui = GetModuleHandle(L"twinui.dll"); + + CImmersiveHotkeyNotification_GetMonitorForHotkeyNotificationFunc = (INT64(*)(void*, void**, HWND*)) + ((uintptr_t)hTwinui + 0x24B4A8); + + IsDesktopInputContextFunc = (BOOL(*)(void*, void*)) + ((uintptr_t)hTwinui + 0x24A5C4); + + CImmersiveHotkeyNotification_OnMessageFunc = (HRESULT(*)(void*, INT64, INT, INT64)) + ((uintptr_t)hTwinui + 0xB2A70); + rv = funchook_prepare( + funchook, + (void**)&CImmersiveHotkeyNotification_OnMessageFunc, + CImmersiveHotkeyNotification_OnMessageHook + ); + if (rv != 0) + { + FreeLibraryAndExitThread(hModule, rv); + return rv; + } + + + + rv = funchook_install(funchook, 0); + if (rv != 0) + { + FreeLibraryAndExitThread(hModule, rv); + return rv; + } + } + else + { + rv = funchook_uninstall(funchook, 0); + if (rv != 0) + { + FreeLibraryAndExitThread(hModule, rv); + return rv; + } + + rv = funchook_destroy(funchook); + if (rv != 0) + { + FreeLibraryAndExitThread(hModule, rv); + return rv; + } + + FreeLibraryAndExitThread(hModule, 0); + } + + return 0; +} + +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; +} \ No newline at end of file diff --git a/ExplorerPatcherLibrary/resource.h b/ExplorerPatcherLibrary/resource.h new file mode 100644 index 000000000..68460ac1a --- /dev/null +++ b/ExplorerPatcherLibrary/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ExplorerPatcherLibrary.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/README.md b/README.md index 6be13ea64..8cd946480 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,95 @@ Explorer Patcher is a patcher that enables various stuff in Explorer. For the moment, it includes the following: * allows using the old taskbar in Windows 11 without the side effects of UndockingDisabled and with fully working search, modern apps showing properly, screen snip still working etc +* reenables the power menu (Win+X) when using the classic taskbar in Windows 11 + +This has been tested only on Windows 11 build 22000.1. It probably does not work on other builds due to different offsets in explorer.exe and its libraries. Once this matures, a solution will be offered for dynamically determining the necessary offsets. As it stands, the application is more in a proof of concept phase. A detailed description of how this works is available on my web site [here](https://valinet.ro/2021/08/09/Restore-Windows-11-to-working-Windows-10-UI.html). Precompiled binaries are available in [Releases](https://github.com/valinet/ExplorerPatcher/releases). +Please help me fix the known issues described below in order to get feature parity with regular Windows 10 releases. As it stands, the application works but it is not perfect, yet. + +## Installation + To install, save the executable in a safe directory, run it once as an administrator to have it register as [Taskman](https://www.geoffchappell.com/notes/windows/shell/explorer/taskman.htm) for Explorer and just restart Explorer or reboot. + +The application does not currently offer a way to configure its behavior. In the mean time, I recommend commenting out whatever you do not like and compile your own executable, as described below (instructions are very simple). + +## Known issues + +* The power menu (Win+X menu) is unskinned - it has the default menu appearance from Windows 11 instead of the look the clock, taskbar etc context menus get; at the moment, I think it boils down to correctly calling `ImmersiveContextMenuHelper::ApplyOwnerDrawToMenu`, but unfortunately I've yet to do it correctly +* The power menu (Win+X menu) is mapped to Win+F for the moment, as I've yet to have the time to investigate where exactly Win+X is handled (i.e. it is not in `CImmersiveHotkeyNotification::OnMessage`) + +## License + +Hooking is done using the excellent [funchook](https://github.com/kubo/funchook) library (GPLv2 with linking exception), which in turn is powered by the [diStorm3](https://github.com/gdabah/distorm/) (3-clause BSD) disassembler. Thus, I am offering this under GNU General Public License Version 2.0, which I believe is compatible. + +## Compiling + +The following prerequisites are necessary in order to compile this project: + +* Microsoft C/C++ Optimizing Compiler - this can be obtained by installing either of these packages: + + * Visual Studio - this is a fully featured IDE; you'll need to check "C/C++ application development role" when installing. If you do not require the full suite, use the package bellow. + * Build Tools for Visual Studio - this just installs the compiler, which you'll be able to use from the command line, or from other applications like CMake + + Download either of those [here](http://go.microsoft.com/fwlink/p/?LinkId=840931). The guide assumes you have installed either Visual Studio 2019, either Build Tools for Visual Studio 2019. + +* [CMake](https://cmake.org/) - for easier usage, make sure to have it added to PATH during installation + +* Git - you can use [Git for Windows](https://git-scm.com/download/win), or git command via the Windows Subsystem for Linux. + +Steps: + +1. Clone git repo along with all submodules + + ``` + git clone --recursive https://github.com/valinet/ExplorerPatcher + ``` + + If "git" is not found as a command, type its full path, or have its folder added to PATH, or open Git command window in the respective folder if using Git for Windows. + +2. Compile funchook + + ``` + cd libs + cd funchook + md build + cd build + cmake -G "Visual Studio 16 2019" -A x64 .. + cmake --build . --config Release + ``` + + If "cmake" is not found as a command, type its full path, or have its folder added to PATH. + + Type "Win32" instead of "x64" above, if compiling for x86. The command above works for x64. + +3. Compile ExplorerPatcher + + * Double click the ExplorerPatcher.sln to open the solution in Visual Studio. Choose Release and your processor architecture in the toolbar. Press F6 to compile. + + * Open an "x86 Native Tools Command Prompt for VS 2019" (for x86), or "x64 Native Tools Command Prompt for VS 2019" (for x64) (search that in Start), go to folder containing solution file and type: + + * For x86: + + ``` + msbuild ExplorerPatcher.sln /property:Configuration=Release /property:Platform=x86 + ``` + + * For x64: + + ``` + msbuild ExplorerPatcher.sln /property:Configuration=Release /property:Platform=x64 + ``` + + The resulting exe and dll will be in "Release" folder (if you chose x86), or "x64\Release" (if you chose x64) in the folder containing the solution file. + +That's it. later, if you want to recompile, make sure to update the repository and the submodules first: + +``` +git pull +git submodule update --init --recursive +``` + diff --git a/libs/funchook b/libs/funchook new file mode 160000 index 000000000..f3928f99f --- /dev/null +++ b/libs/funchook @@ -0,0 +1 @@ +Subproject commit f3928f99f5ec77bf750c8c7ebaec44e44ba34257 diff --git a/libs/libvalinet b/libs/libvalinet new file mode 160000 index 000000000..d345b62ab --- /dev/null +++ b/libs/libvalinet @@ -0,0 +1 @@ +Subproject commit d345b62ab1062b9414a1db934c69ce777d467169