├── ep_dwm ├── resource.h ├── ep_dwm.vcxproj.filters ├── ep_dwm.rc ├── ep_dwm.h ├── ep_dwm.vcxproj ├── ep_dwm_x64.c └── ep_dwm.c ├── README.md ├── ep_dwm.sln ├── .gitignore └── LICENSE /ep_dwm/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by ep_dwm.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ep_dwm 2 | Implements a Windows service that removes the rounded corners for windows in Windows 11. 3 | 4 | Tested on Windows 11 build 22000.434. 5 | 6 | Pre-compiled binaries are available in the [ExplorerPatcher](https://github.com/valinet/ExplorerPatcher/releases) setup program (download the latest pre-release). You can install that program - it already includes this functionality built-in. Alternatively, to get only `ep_dwm_svc.exe` from the downloaded `ep_setup.exe`, run this: 7 | 8 | ``` 9 | ep_setup /extract C:\ep_dwm 10 | ``` 11 | 12 | The executable will be extracted in `C:\ep_dwm`. If you do not need them, you can delete the rest of the files in there and keep only `ep_dwm_svc.exe`. 13 | 14 | To register, type these commands in an elevated command window: 15 | 16 | ``` 17 | sc.exe create ep_dwm binPath= "\"C:\ep_dwm\ep_dwm_svc.exe\" ep_dwm Global\ep_dwm" DisplayName= "ep_dwm" start= auto 18 | sc.exe description ep_dwm "ep_dwm Service" 19 | sc.exe start ep_dwm 20 | ``` 21 | 22 | To unregister, type these commands in an elevated command window: 23 | 24 | ``` 25 | sc.exe stop ep_dwm 26 | sc.exe delete ep_dwm 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /ep_dwm/ep_dwm.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | 31 | 32 | Resource Files 33 | 34 | 35 | -------------------------------------------------------------------------------- /ep_dwm.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32112.339 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ep_dwm", "ep_dwm\ep_dwm.vcxproj", "{1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}.Debug|x64.ActiveCfg = Debug|x64 17 | {1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}.Debug|x64.Build.0 = Debug|x64 18 | {1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}.Debug|x86.ActiveCfg = Debug|Win32 19 | {1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}.Debug|x86.Build.0 = Debug|Win32 20 | {1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}.Release|x64.ActiveCfg = Release|x64 21 | {1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}.Release|x64.Build.0 = Release|x64 22 | {1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}.Release|x86.ActiveCfg = Release|Win32 23 | {1ECCAB38-61B6-4C85-BBB5-2E2232DA3A87}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {8A039276-40B0-4749-8CFC-2978DF848594} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /ep_dwm/ep_dwm.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Version 51 | // 52 | 53 | VS_VERSION_INFO VERSIONINFO 54 | FILEVERSION 2,0,0,0 55 | PRODUCTVERSION 2,0,0,0 56 | FILEFLAGSMASK 0x3fL 57 | #ifdef _DEBUG 58 | FILEFLAGS 0x1L 59 | #else 60 | FILEFLAGS 0x0L 61 | #endif 62 | FILEOS 0x40004L 63 | FILETYPE 0x1L 64 | FILESUBTYPE 0x0L 65 | BEGIN 66 | BLOCK "StringFileInfo" 67 | BEGIN 68 | BLOCK "040904b0" 69 | BEGIN 70 | VALUE "CompanyName", "VALINET Solutions SRL" 71 | VALUE "FileDescription", "ExplorerPatcher Desktop Window Manager Service Process" 72 | VALUE "FileVersion", "2.0.0.0" 73 | VALUE "InternalName", "ep_dwm_svc.exe" 74 | VALUE "LegalCopyright", "Copyright (C) 2006-2025 VALINET Solutions SRL. All rights reserved." 75 | VALUE "OriginalFilename", "ep_dwm_svc.exe" 76 | VALUE "ProductName", "ExplorerPatcher" 77 | VALUE "ProductVersion", "2.0.0.0" 78 | END 79 | END 80 | BLOCK "VarFileInfo" 81 | BEGIN 82 | VALUE "Translation", 0x409, 1200 83 | END 84 | END 85 | 86 | #endif // English (United States) resources 87 | ///////////////////////////////////////////////////////////////////////////// 88 | 89 | 90 | 91 | #ifndef APSTUDIO_INVOKED 92 | ///////////////////////////////////////////////////////////////////////////// 93 | // 94 | // Generated from the TEXTINCLUDE 3 resource. 95 | // 96 | 97 | 98 | ///////////////////////////////////////////////////////////////////////////// 99 | #endif // not APSTUDIO_INVOKED 100 | 101 | -------------------------------------------------------------------------------- /ep_dwm/ep_dwm.h: -------------------------------------------------------------------------------- 1 | #ifndef _H_DWM_H_ 2 | #define _H_DWM_H_ 3 | #pragma comment(linker,"\"/manifestdependency:type='win32' \ 4 | name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ 5 | processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 6 | #include 7 | #include 8 | #include 9 | #include 10 | #pragma comment(lib, "Comctl32.lib") 11 | #include 12 | #pragma comment(lib, "version.lib") 13 | 14 | void WINAPI ep_dwm_ServiceMain(DWORD argc, LPTSTR* argv); 15 | BOOL ep_dwm_StartService(LPWSTR wszServiceName, LPWSTR wszEventName); 16 | inline void* ep_dwm_memmem(void* haystack, size_t haystacklen, void* needle, size_t needlelen) 17 | { 18 | const char* text = (const char*)haystack; 19 | const char* pattern = (const char*)needle; 20 | const char* rv = NULL; 21 | 22 | size_t* out = calloc(needlelen, sizeof(size_t)); 23 | if (!out) 24 | { 25 | return NULL; 26 | } 27 | size_t j, i; 28 | 29 | j = 0, i = 1; 30 | while (i < needlelen) { 31 | if (text[j] != text[i]) 32 | { 33 | if (j > 0) 34 | { 35 | j = out[j - 1]; 36 | continue; 37 | } 38 | else j--; 39 | } 40 | j++; 41 | out[i] = j; 42 | i++; 43 | } 44 | 45 | i = 0, j = 0; 46 | for (i = 0; i <= haystacklen; i++) { 47 | if (text[i] == pattern[j]) { 48 | j++; 49 | if (j == needlelen) { 50 | rv = text + (size_t)(i - needlelen + 1); 51 | break; 52 | } 53 | } 54 | else { 55 | if (j != 0) { 56 | j = out[j - 1]; 57 | i--; 58 | } 59 | } 60 | } 61 | 62 | free(out); 63 | return rv; 64 | } 65 | 66 | BOOL ep_dwm_IsWindows11Version22H2OrHigher() 67 | { 68 | // Avoid manifesting the exe 69 | // https://stackoverflow.com/questions/25986331/how-to-determine-windows-version-in-future-proof-way 70 | 71 | static const wchar_t kernel32[] = L"\\kernel32.dll"; 72 | wchar_t* path = NULL; 73 | void* ver = NULL, * block; 74 | UINT n; 75 | BOOL r; 76 | DWORD versz, blocksz; 77 | VS_FIXEDFILEINFO* vinfo; 78 | 79 | path = malloc(sizeof(*path) * MAX_PATH); 80 | if (!path) 81 | return FALSE; 82 | 83 | n = GetSystemDirectoryW(path, MAX_PATH); 84 | if (n >= MAX_PATH || n == 0 || 85 | n > MAX_PATH - sizeof(kernel32) / sizeof(*kernel32)) 86 | return FALSE; 87 | memcpy(path + n, kernel32, sizeof(kernel32)); 88 | 89 | versz = GetFileVersionInfoSizeW(path, NULL); 90 | if (versz == 0) 91 | return FALSE; 92 | ver = malloc(versz); 93 | if (!ver) 94 | return FALSE; 95 | r = GetFileVersionInfoW(path, 0, versz, ver); 96 | if (!r) 97 | return FALSE; 98 | r = VerQueryValueW(ver, L"\\", &block, &blocksz); 99 | if (!r || blocksz < sizeof(VS_FIXEDFILEINFO)) 100 | return FALSE; 101 | vinfo = (VS_FIXEDFILEINFO*)block; 102 | BOOL result = ((int)HIWORD(vinfo->dwProductVersionLS) >= 22621); 103 | //printf( 104 | // "Windows version: %d.%d.%d.%d", 105 | // (int)HIWORD(vinfo->dwProductVersionMS), // 10 106 | // (int)LOWORD(vinfo->dwProductVersionMS), // 0 107 | // (int)HIWORD(vinfo->dwProductVersionLS), // 22000 108 | // (int)LOWORD(vinfo->dwProductVersionLS));// 708 109 | free(path); 110 | free(ver); 111 | return result; 112 | } 113 | #endif 114 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /ep_dwm/ep_dwm.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | Debug 22 | ARM64 23 | 24 | 25 | Release 26 | ARM64 27 | 28 | 29 | 30 | 16.0 31 | Win32Proj 32 | {1eccab38-61b6-4c85-bbb5-2e2232da3a87} 33 | epdwm 34 | 10.0 35 | 36 | 37 | 38 | Application 39 | true 40 | v143 41 | Unicode 42 | 43 | 44 | Application 45 | false 46 | v143 47 | true 48 | Unicode 49 | 50 | 51 | Application 52 | true 53 | v143 54 | Unicode 55 | 56 | 57 | Application 58 | false 59 | v143 60 | true 61 | Unicode 62 | 63 | 64 | Application 65 | true 66 | v143 67 | Unicode 68 | 69 | 70 | Application 71 | false 72 | v143 73 | true 74 | Unicode 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | true 102 | $(SolutionDir)\build\$(Configuration)\$(Platform)\ 103 | ep_dwm_svc 104 | 105 | 106 | false 107 | $(SolutionDir)\build\$(Configuration)\$(Platform)\ 108 | ep_dwm_svc 109 | 110 | 111 | true 112 | $(SolutionDir)\build\$(Configuration)\$(Platform)\ 113 | ep_dwm_svc 114 | 115 | 116 | false 117 | $(SolutionDir)\build\$(Configuration)\$(Platform)\ 118 | ep_dwm_svc 119 | 120 | 121 | true 122 | $(SolutionDir)\build\$(Configuration)\$(Platform)\ 123 | ep_dwm_svc 124 | 125 | 126 | false 127 | $(SolutionDir)\build\$(Configuration)\$(Platform)\ 128 | ep_dwm_svc 129 | 130 | 131 | 132 | Level3 133 | true 134 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 135 | true 136 | MultiThreadedDebug 137 | 138 | 139 | Windows 140 | true 141 | 142 | 143 | 144 | 145 | Level3 146 | true 147 | true 148 | true 149 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 150 | true 151 | MultiThreaded 152 | 153 | 154 | Windows 155 | true 156 | true 157 | true 158 | 159 | 160 | 161 | 162 | Level3 163 | true 164 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 165 | true 166 | MultiThreadedDebug 167 | 168 | 169 | Windows 170 | true 171 | 172 | 173 | 174 | 175 | Level3 176 | true 177 | true 178 | true 179 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 180 | true 181 | MultiThreaded 182 | 183 | 184 | Windows 185 | true 186 | true 187 | true 188 | 189 | 190 | 191 | 192 | Level3 193 | true 194 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 195 | true 196 | MultiThreadedDebug 197 | 198 | 199 | Windows 200 | true 201 | 202 | 203 | 204 | 205 | Level3 206 | true 207 | true 208 | true 209 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 210 | true 211 | MultiThreaded 212 | 213 | 214 | Windows 215 | true 216 | true 217 | true 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /ep_dwm/ep_dwm_x64.c: -------------------------------------------------------------------------------- 1 | #include "ep_dwm.h" 2 | 3 | LPWSTR ep_dwm_g_wszServiceName; 4 | LPWSTR ep_dwm_g_wszEventName; 5 | SERVICE_STATUS ep_dwm_g_ServiceStatus = { 0 }; 6 | SERVICE_STATUS_HANDLE ep_dwm_g_StatusHandle = NULL; 7 | HANDLE ep_dwm_g_Service = INVALID_HANDLE_VALUE; 8 | HANDLE ep_dwm_g_ServiceStopEvent = INVALID_HANDLE_VALUE; 9 | HANDLE ep_dwm_g_ServiceSessionChangeEvent = INVALID_HANDLE_VALUE; 10 | #define EP_DWM_NUM_EVENTS 2 11 | #define EP_DWM_SETUP_TIME 2000 12 | #define EP_DWM_GRACE_TIME 5000 13 | #define EP_DWM_GROW 10 14 | #define EP_DWM_MAX_NUM_MODULES 200 15 | #define EP_DWM_REASON_NONE 0 16 | #define EP_DWM_REASON_TERMINATION_BYUSER 1 17 | #define EP_DWM_REASON_EARLIER_ERROR 2 18 | char ep_dwm_pattern_data[1][20] = { {0x0F, 0x57, 0xF6, 0xF3, 0x48, 0x0F} }; 19 | // xorps xmm6, xmm6 20 | // cvtsi2ss xmm6, rax 21 | unsigned int ep_dwm_pattern_length[1] = { 6 }; 22 | // this patch always replaces 4 bytes coresponding to a "mov eax, [reg+offh+arg]" operation 23 | char ep_dwm_patch_data[1][20] = { {0x31, 0xC0, 0xFF, 0xC0} }; 24 | // xor eax, eax 25 | // inc eax 26 | unsigned int ep_dwm_patch_length[1] = { 4 }; 27 | 28 | unsigned int ep_dwm_expected_matches = 4; 29 | 30 | unsigned int ep_dwm_strategy = 0; 31 | int ep_dwm_strategy_1_order = -1; 32 | 33 | #define STRINGER_INTERNAL(x) #x 34 | #define STRINGER(x) STRINGER_INTERNAL(x) 35 | 36 | #define CLEAR(x) { \ 37 | DPA_DestroyCallback(dpaHandlesList, ep_dwm_DestroyHandle, dpaExclusionList); \ 38 | if (x == EP_DWM_REASON_TERMINATION_BYUSER) OutputDebugStringW(L"ep_dwm: Terminating as per user request (line " _T(STRINGER(__LINE__)) L").\n"); \ 39 | else if (x == EP_DWM_REASON_EARLIER_ERROR) OutputDebugStringW(L"ep_dwm: Terminating due to earlier failure (line " _T(STRINGER(__LINE__)) L")!\n"); \ 40 | } 41 | 42 | #define CLEAR_AND_DEPATCH(x) { \ 43 | for (unsigned int i = EP_DWM_NUM_EVENTS; i < DPA_GetPtrCount(dpaHandlesList); ++i) \ 44 | { \ 45 | ep_dwm_PatchProcess(wszUDWMPath, DPA_FastGetPtr(dpaHandlesList, i), dpaOffsetList, dpaOldCodeList); \ 46 | } \ 47 | CLEAR(x); \ 48 | } 49 | 50 | #define REPORT_ON_PROCESS(s, i) { \ 51 | DWORD dwExitCode = 0; \ 52 | GetExitCodeProcess(DPA_FastGetPtr(dpaHandlesList, i), &dwExitCode); \ 53 | if (dwExitCode && dwExitCode != 0xd00002fe) \ 54 | { \ 55 | if (s) { \ 56 | dwFailedNum = i; \ 57 | dwRes = dwExitCode; \ 58 | } \ 59 | OutputDebugStringW(L"ep_dwm: One of the processes has crashed (line " _T(STRINGER(__LINE__)) L")!\n"); \ 60 | } \ 61 | else \ 62 | { \ 63 | if (!dwRes || !s) \ 64 | { \ 65 | if (s) { \ 66 | dwFailedNum = i; \ 67 | dwRes = 0; \ 68 | } \ 69 | if (dwExitCode == 0xd00002fe) \ 70 | { \ 71 | OutputDebugStringW(L"ep_dwm: The Desktop Window Manager has exited with code (0xd00002fe) (line " _T(STRINGER(__LINE__)) L")!\n"); \ 72 | } \ 73 | else \ 74 | { \ 75 | OutputDebugStringW(L"ep_dwm: An instance of the Desktop Window Manager has closed (line " _T(STRINGER(__LINE__)) L")!\n"); \ 76 | } \ 77 | } \ 78 | } \ 79 | } 80 | 81 | static int ep_dwm_DestroyOldCode(void* p, void* pUnused) 82 | { 83 | free(p); 84 | return 1; 85 | } 86 | 87 | static int ep_dwm_DestroyHandle(HANDLE h, HDPA dpaExclusionList) 88 | { 89 | BOOL bShouldClose = TRUE; 90 | for (unsigned int i = 0; i < DPA_GetPtrCount(dpaExclusionList); ++i) 91 | { 92 | if (DPA_FastGetPtr(dpaExclusionList, i) == h) 93 | { 94 | bShouldClose = FALSE; 95 | } 96 | } 97 | if (bShouldClose) 98 | { 99 | CloseHandle(h); 100 | } 101 | return 1; 102 | } 103 | 104 | static DWORD ep_dwm_DeterminePatchAddresses(WCHAR* wszUDWMPath, HDPA dpaOffsetList, HDPA dpaPatchList) 105 | { 106 | DWORD dwRes = 0; 107 | 108 | HMODULE hModule = LoadLibraryW(wszUDWMPath); 109 | if (!hModule) 110 | { 111 | OutputDebugStringW(L"ep_dwm: Failed (LoadLibraryW) (line " _T(STRINGER(__LINE__)) L")!\n"); 112 | return __LINE__; 113 | } 114 | 115 | void* p = NULL; 116 | uintptr_t diff = 0; 117 | PIMAGE_DOS_HEADER dosHeader = hModule; 118 | if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) 119 | { 120 | PIMAGE_NT_HEADERS64 ntHeader = (PIMAGE_NT_HEADERS64)((u_char*)dosHeader + dosHeader->e_lfanew); 121 | if (ntHeader->Signature == IMAGE_NT_SIGNATURE) 122 | { 123 | PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntHeader); 124 | for (unsigned int i = 0; i < ntHeader->FileHeader.NumberOfSections; ++i) 125 | { 126 | if ((section->Characteristics & IMAGE_SCN_CNT_CODE) && section->SizeOfRawData) 127 | { 128 | char* pCandidate = NULL; 129 | while (TRUE) 130 | { 131 | pCandidate = ep_dwm_memmem( 132 | !pCandidate ? hModule + section->VirtualAddress : pCandidate, 133 | !pCandidate ? section->SizeOfRawData : (uintptr_t)section->SizeOfRawData - (uintptr_t)(pCandidate - (hModule + section->VirtualAddress)), 134 | ep_dwm_pattern_data[0], 135 | ep_dwm_pattern_length[0] 136 | ); 137 | if (!pCandidate) 138 | { 139 | break; 140 | } 141 | BOOL bContains = FALSE; 142 | for (unsigned j = 0; j < DPA_GetPtrCount(dpaOffsetList); ++j) 143 | { 144 | if (DPA_FastGetPtr(dpaOffsetList, j) == pCandidate - hModule) 145 | { 146 | bContains = TRUE; 147 | break; 148 | } 149 | } 150 | BOOL bPassedCheck = TRUE; 151 | if (ep_dwm_strategy == 1) 152 | { 153 | UINT32 offset = *(UINT32*)(pCandidate + 4); 154 | UINT32 value = *(UINT32*)(pCandidate + offset + 0x8); 155 | if (!(value == 0x41000000 /* 8.0 */ || value == 0x40800000 /* 4.0 */)) bPassedCheck = FALSE; 156 | } 157 | if (bPassedCheck && !bContains) 158 | { 159 | if (ep_dwm_strategy == 1 && (*(char*)(pCandidate + 8) & 0xFF) == 0x0f && (*(char*)(pCandidate + 9) & 0xFF) == 0x28 && (*(char*)(pCandidate + 10) & 0xFF) == 0xc6) ep_dwm_strategy_1_order = (DPA_GetPtrCount(dpaOffsetList) == 0 ? 0 : 1); 160 | DPA_AppendPtr(dpaOffsetList, pCandidate - hModule); 161 | char* pOldCode = malloc(ep_dwm_patch_length[0] * sizeof(char)); 162 | if (!pOldCode) 163 | { 164 | for (unsigned int k = 0; k < DPA_GetPtrCount(dpaPatchList); ++k) 165 | { 166 | free(DPA_FastGetPtr(dpaPatchList, k)); 167 | } 168 | return __LINE__; 169 | } 170 | int offset = (0 - ep_dwm_patch_length[0]); 171 | if (ep_dwm_strategy == 1) offset = 0; 172 | memcpy(pOldCode, pCandidate + offset, ep_dwm_patch_length[0]); 173 | DPA_AppendPtr(dpaPatchList, pOldCode); 174 | } 175 | pCandidate += ep_dwm_pattern_length[0]; 176 | if (pCandidate > hModule + section->VirtualAddress + section->SizeOfRawData) 177 | { 178 | break; 179 | } 180 | } 181 | } 182 | } 183 | } 184 | } 185 | 186 | FreeLibrary(hModule); 187 | 188 | if (DPA_GetPtrCount(dpaOffsetList) != DPA_GetPtrCount(dpaPatchList)) 189 | { 190 | OutputDebugStringW(L"ep_dwm: Different number of offsets and places to patch!\n"); 191 | dwRes == __LINE__; 192 | } 193 | if (DPA_GetPtrCount(dpaOffsetList) == 0) 194 | { 195 | OutputDebugStringW(L"ep_dwm: Unable to identify patch area!\n"); 196 | dwRes == __LINE__; 197 | } 198 | 199 | return dwRes; 200 | } 201 | 202 | static DWORD ep_dwm_PatchProcess(WCHAR* wszUDWMPath, HANDLE hProcess, HDPA dpaOffsetList, HDPA dpaOldCodeList) 203 | { 204 | DWORD dwRes = 0; 205 | 206 | MODULEENTRY32 me32; 207 | ZeroMemory(&me32, sizeof(MODULEENTRY32)); 208 | me32.dwSize = sizeof(MODULEENTRY32); 209 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(hProcess)); 210 | if (!hSnapshot) 211 | { 212 | OutputDebugStringW(L"ep_dwm: Failed (CreateToolhelp32Snapshot TH32CS_SNAPMODULE) (line " _T(STRINGER(__LINE__)) L")!\n"); 213 | return __LINE__; 214 | } 215 | if (Module32FirstW(hSnapshot, &me32) == TRUE) 216 | { 217 | do 218 | { 219 | if (!_wcsicmp(me32.szExePath, wszUDWMPath)) 220 | { 221 | for (unsigned int j = 0; j < DPA_GetPtrCount(dpaOffsetList, j); ++j) 222 | { 223 | DWORD dwOldProtect = 0; 224 | SIZE_T dwNumberOfBytesWritten = 0; 225 | int offset = (0 - ep_dwm_patch_length[0]); 226 | if (ep_dwm_strategy == 1) offset = 0; 227 | if (ep_dwm_strategy == 1 && (ep_dwm_strategy_1_order == 0 ? (j == 0 || j == 1) : (j == 2 || j == 3))) { 228 | ep_dwm_patch_data[0][0] = 0xB0; // change working register to al/rax 229 | ep_dwm_patch_data[0][6] = 0xF0; 230 | } 231 | if (!VirtualProtectEx(hProcess, me32.modBaseAddr + (uintptr_t)DPA_FastGetPtr(dpaOffsetList, j) + offset, ep_dwm_patch_length[0], PAGE_EXECUTE_READWRITE, &dwOldProtect)) 232 | { 233 | OutputDebugStringW(L"ep_dwm: Failed (VirtualProtectEx) (line " _T(STRINGER(__LINE__)) L")!\n"); 234 | dwRes = __LINE__; 235 | } 236 | else 237 | { 238 | WriteProcessMemory(hProcess, me32.modBaseAddr + (uintptr_t)DPA_FastGetPtr(dpaOffsetList, j) + offset, dpaOldCodeList ? DPA_FastGetPtr(dpaOldCodeList, j) : ep_dwm_patch_data[0], ep_dwm_patch_length[0], &dwNumberOfBytesWritten); 239 | if (!dwNumberOfBytesWritten || dwNumberOfBytesWritten != ep_dwm_patch_length[0]) 240 | { 241 | OutputDebugStringW(L"ep_dwm: Failed (WriteProcessMemory) (line " _T(STRINGER(__LINE__)) L")!\n"); 242 | dwRes = __LINE__; 243 | } 244 | VirtualProtectEx(hProcess, me32.modBaseAddr + (uintptr_t)DPA_FastGetPtr(dpaOffsetList, j) + offset, ep_dwm_patch_length[0], dwOldProtect, &dwOldProtect); 245 | } 246 | if (ep_dwm_strategy == 1 && (ep_dwm_strategy_1_order == 0 ? (j == 0 || j == 1) : (j == 2 || j == 3))) { 247 | ep_dwm_patch_data[0][0] = 0xB1; // revert change to register cl/rcx 248 | ep_dwm_patch_data[0][6] = 0xF1; 249 | } 250 | } 251 | break; 252 | } 253 | } while (Module32NextW(hSnapshot, &me32) == TRUE && dwRes == 0); 254 | } 255 | CloseHandle(hSnapshot); 256 | 257 | return dwRes; 258 | } 259 | 260 | static DWORD WINAPI ep_dwm_ServiceThread(LPVOID lpUnused) 261 | { 262 | DWORD dwRes = __LINE__; 263 | 264 | HANDLE hSnapshot = NULL; 265 | PROCESSENTRY32 pe32; 266 | HDPA dpaExclusionList = NULL; 267 | HDPA dpaHandlesList = NULL; 268 | HDPA dpaOffsetList = NULL; 269 | HDPA dpaOldCodeList = NULL; 270 | 271 | ep_dwm_strategy = (ep_dwm_IsWindows11Version22H2OrHigher() ? 1 : 0); 272 | if (ep_dwm_strategy == 1) 273 | { 274 | ep_dwm_pattern_data[0][0] = 0xF3; // movss xmm6, ... 275 | ep_dwm_pattern_data[0][1] = 0x0F; 276 | ep_dwm_pattern_data[0][2] = 0x10; 277 | ep_dwm_pattern_data[0][3] = 0x35; 278 | ep_dwm_pattern_length[0] = 4; 279 | ep_dwm_patch_data[0][0] = 0xB1; // mov cl, 1 280 | ep_dwm_patch_data[0][1] = 0x01; 281 | ep_dwm_patch_data[0][2] = 0xF3; // cvtsi2ss xmm6,rcx 282 | ep_dwm_patch_data[0][3] = 0x48; 283 | ep_dwm_patch_data[0][4] = 0x0F; 284 | ep_dwm_patch_data[0][5] = 0x2A; 285 | ep_dwm_patch_data[0][6] = 0xF1; 286 | ep_dwm_patch_data[0][7] = 0x90; 287 | ep_dwm_patch_length[0] = 8; 288 | } 289 | 290 | WCHAR wszDWMPath[MAX_PATH]; 291 | GetSystemDirectoryW(wszDWMPath, MAX_PATH); 292 | wcscat_s(wszDWMPath, MAX_PATH, L"\\dwm.exe"); 293 | 294 | WCHAR wszUDWMPath[MAX_PATH]; 295 | GetSystemDirectoryW(wszUDWMPath, MAX_PATH); 296 | wcscat_s(wszUDWMPath, MAX_PATH, L"\\uDWM.dll"); 297 | 298 | dpaOffsetList = DPA_Create(EP_DWM_GROW); 299 | if (!dpaOffsetList) 300 | { 301 | OutputDebugStringW(L"ep_dwm: Failed (DPA_Create) (line " _T(STRINGER(__LINE__)) L")!\n"); 302 | dwRes = __LINE__; 303 | return dwRes; 304 | } 305 | 306 | dpaOldCodeList = DPA_Create(EP_DWM_GROW); 307 | if (!dpaOldCodeList) 308 | { 309 | OutputDebugStringW(L"ep_dwm: Failed (DPA_Create) (line " _T(STRINGER(__LINE__)) L")!\n"); 310 | dwRes = __LINE__; 311 | return dwRes; 312 | } 313 | 314 | if (dwRes = ep_dwm_DeterminePatchAddresses(wszUDWMPath, dpaOffsetList, dpaOldCodeList)) 315 | { 316 | return dwRes; 317 | } 318 | 319 | dpaExclusionList = DPA_Create(EP_DWM_NUM_EVENTS); 320 | if (!dpaExclusionList) 321 | { 322 | dwRes = __LINE__; 323 | OutputDebugStringW(L"ep_dwm: Failed (DPA_Create) (line " _T(STRINGER(__LINE__)) L")!\n"); 324 | return dwRes; 325 | } 326 | DPA_AppendPtr(dpaExclusionList, ep_dwm_g_ServiceStopEvent); 327 | DPA_AppendPtr(dpaExclusionList, ep_dwm_g_ServiceSessionChangeEvent); 328 | 329 | while (DPA_GetPtrCount(dpaOffsetList) == ep_dwm_expected_matches) 330 | { 331 | DWORD dwFailedNum = 0; 332 | 333 | dpaHandlesList = DPA_Create(EP_DWM_GROW); 334 | if (!dpaHandlesList) 335 | { 336 | dwRes = __LINE__; 337 | OutputDebugStringW(L"ep_dwm: Failed (DPA_Create) (line " _T(STRINGER(__LINE__)) L")!\n"); 338 | break; 339 | } 340 | 341 | for (unsigned int i = 0; i < DPA_GetPtrCount(dpaExclusionList); ++i) 342 | { 343 | DPA_AppendPtr(dpaHandlesList, DPA_FastGetPtr(dpaExclusionList, i)); 344 | } 345 | 346 | // Make list of dwm.exe processes 347 | hSnapshot = NULL; 348 | ZeroMemory(&pe32, sizeof(PROCESSENTRY32)); 349 | pe32.dwSize = sizeof(PROCESSENTRY32); 350 | hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 351 | if (!hSnapshot) 352 | { 353 | DPA_Destroy(dpaHandlesList); 354 | OutputDebugStringW(L"ep_dwm: Failed (CreateToolhelp32Snapshot TH32CS_SNAPPROCESS) (line " _T(STRINGER(__LINE__)) L")!\n"); 355 | dwRes = __LINE__; 356 | break; 357 | } 358 | if (Process32FirstW(hSnapshot, &pe32) == TRUE) 359 | { 360 | do 361 | { 362 | if (!_wcsicmp(pe32.szExeFile, L"dwm.exe")) 363 | { 364 | HANDLE hProcess = OpenProcess( 365 | PROCESS_QUERY_LIMITED_INFORMATION | 366 | PROCESS_VM_OPERATION | 367 | PROCESS_VM_READ | 368 | PROCESS_VM_WRITE | 369 | //PROCESS_CREATE_THREAD | 370 | SYNCHRONIZE, 371 | FALSE, 372 | pe32.th32ProcessID 373 | ); 374 | if (!hProcess) 375 | { 376 | OutputDebugStringW(L"ep_dwm: Failed (OpenProcess) (line " _T(STRINGER(__LINE__)) L")!\n"); 377 | continue; 378 | } 379 | TCHAR wszProcessPath[MAX_PATH]; 380 | DWORD dwLength = MAX_PATH; 381 | QueryFullProcessImageNameW(hProcess, 0, wszProcessPath, &dwLength); 382 | if (!_wcsicmp(wszProcessPath, wszDWMPath)) 383 | { 384 | DPA_AppendPtr(dpaHandlesList, hProcess); 385 | } 386 | else 387 | { 388 | CloseHandle(hProcess); 389 | } 390 | } 391 | } while (Process32NextW(hSnapshot, &pe32) == TRUE); 392 | } 393 | CloseHandle(hSnapshot); 394 | 395 | // If process list is empty, retry 396 | if (DPA_GetPtrCount(dpaHandlesList) <= DPA_GetPtrCount(dpaExclusionList)) 397 | { 398 | DPA_Destroy(dpaHandlesList); 399 | OutputDebugStringW(L"ep_dwm: Desktop Window Manager is not running!\n"); 400 | OutputDebugStringW(L"ep_dwm: Retry (line " _T(STRINGER(__LINE__)) L").\n"); 401 | continue; 402 | } 403 | 404 | // Give processes a bit of time to start up 405 | OutputDebugStringW(L"ep_dwm: Waiting " _T(STRINGER(EP_DWM_SETUP_TIME)) L" ms for the processes to be ready.\n"); 406 | if (WaitForSingleObject(ep_dwm_g_ServiceStopEvent, EP_DWM_SETUP_TIME) == WAIT_OBJECT_0) 407 | { 408 | CLEAR(EP_DWM_REASON_TERMINATION_BYUSER); 409 | break; 410 | } 411 | 412 | // Attempt to patch each process 413 | for (unsigned int i = EP_DWM_NUM_EVENTS; i < DPA_GetPtrCount(dpaHandlesList); ++i) 414 | { 415 | if (ep_dwm_PatchProcess(wszUDWMPath, DPA_FastGetPtr(dpaHandlesList, i), dpaOffsetList, NULL)) 416 | { 417 | dwFailedNum = i; 418 | dwRes = __LINE__; 419 | break; 420 | } 421 | } 422 | if (dwFailedNum) 423 | { 424 | // If patching for a process failed, reverse the patch on the previous ones and give up 425 | CLEAR_AND_DEPATCH(EP_DWM_REASON_EARLIER_ERROR); 426 | break; 427 | } 428 | OutputDebugStringW(L"ep_dwm: Patched processes.\n"); 429 | 430 | // Give patch a bit of time in order to observe if it lead to program crash 431 | OutputDebugStringW(L"ep_dwm: Waiting " _T(STRINGER(EP_DWM_GRACE_TIME)) L" ms to determine if patch doesn't lead to crash.\n"); 432 | if (WaitForSingleObject(ep_dwm_g_ServiceStopEvent, EP_DWM_GRACE_TIME) == WAIT_OBJECT_0) 433 | { 434 | CLEAR_AND_DEPATCH(EP_DWM_REASON_TERMINATION_BYUSER); 435 | break; 436 | } 437 | 438 | // Check if any of the processes has terminated, then it's likely the patch crashed it 439 | for (unsigned int i = EP_DWM_NUM_EVENTS; i < DPA_GetPtrCount(dpaHandlesList); ++i) 440 | { 441 | if (WaitForSingleObject(DPA_FastGetPtr(dpaHandlesList, i), 0) == WAIT_OBJECT_0) 442 | { 443 | REPORT_ON_PROCESS(TRUE, i); 444 | } 445 | } 446 | if (dwFailedNum) 447 | { 448 | // If one of the processes just closed, wait a bit and repatch 449 | if (!dwRes) 450 | { 451 | if (WaitForSingleObject(ep_dwm_g_ServiceStopEvent, EP_DWM_SETUP_TIME) == WAIT_OBJECT_0) 452 | { 453 | CLEAR_AND_DEPATCH(EP_DWM_REASON_TERMINATION_BYUSER); 454 | break; 455 | } 456 | CLEAR(EP_DWM_REASON_NONE); 457 | OutputDebugStringW(L"ep_dwm: Retry (line " _T(STRINGER(__LINE__)) L").\n"); 458 | continue; 459 | } 460 | // If at least one process crashed, unpatch the rest and give up 461 | CLEAR_AND_DEPATCH(EP_DWM_REASON_EARLIER_ERROR); 462 | break; 463 | } 464 | 465 | // Wait for an external signal or for any of the processes to terminate 466 | OutputDebugStringW(L"ep_dwm: Waiting for a signal or process termination.\n"); 467 | DWORD dwRv = WaitForMultipleObjects(DPA_GetPtrCount(dpaHandlesList), DPA_GetPtrPtr(dpaHandlesList), FALSE, INFINITE); 468 | OutputDebugStringW(L"ep_dwm: Wait finished due to:\n"); 469 | if (dwRv == WAIT_OBJECT_0) 470 | { 471 | // Service is stopping by user action, so unpatch 472 | CLEAR_AND_DEPATCH(EP_DWM_REASON_TERMINATION_BYUSER); 473 | break; 474 | } 475 | else if (dwRv == WAIT_OBJECT_0 + 1) 476 | { 477 | // Another user logged on, likely to have a new DWM instance, wait a bit and then recreate the process list 478 | OutputDebugStringW(L"ep_dwm: User logon.\n"); 479 | if (WaitForSingleObject(ep_dwm_g_ServiceStopEvent, EP_DWM_SETUP_TIME) == WAIT_OBJECT_0) 480 | { 481 | CLEAR_AND_DEPATCH(EP_DWM_REASON_TERMINATION_BYUSER); 482 | break; 483 | } 484 | CLEAR(EP_DWM_REASON_NONE); 485 | } 486 | else 487 | { 488 | // One of the DWM processes has closed 489 | REPORT_ON_PROCESS(FALSE, dwRv - WAIT_OBJECT_0); 490 | CLEAR(EP_DWM_REASON_NONE); 491 | } 492 | OutputDebugStringW(L"ep_dwm: Retry (line " _T(STRINGER(__LINE__)) L").\n"); 493 | } 494 | 495 | DPA_DestroyCallback(dpaOldCodeList, ep_dwm_DestroyOldCode, NULL); 496 | DPA_Destroy(dpaOffsetList); 497 | DPA_Destroy(dpaExclusionList); 498 | OutputDebugStringW(L"ep_dwm: Exiting service thread.\n"); 499 | return dwRes; 500 | } 501 | 502 | static void WINAPI ep_dwm_ServiceCtrlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext) 503 | { 504 | switch (dwControl) 505 | { 506 | case SERVICE_CONTROL_SESSIONCHANGE: 507 | if (dwEventType == WTS_SESSION_LOGON) 508 | { 509 | OutputDebugStringW(L"ep_dwm: SERVICE_CONTROL_SESSIONCHANGE(WTS_SESSION_LOGON).\n"); 510 | SetEvent(ep_dwm_g_ServiceSessionChangeEvent); 511 | } 512 | break; 513 | case SERVICE_CONTROL_STOP: 514 | if (ep_dwm_g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) 515 | { 516 | OutputDebugStringW(L"ep_dwm: The user requested service termination, but the service is already terminating!\n"); 517 | break; 518 | } 519 | ep_dwm_g_ServiceStatus.dwControlsAccepted = 0; 520 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; 521 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 522 | ep_dwm_g_ServiceStatus.dwCheckPoint = 5; 523 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 524 | { 525 | // error 526 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 527 | } 528 | OutputDebugStringW(L"ep_dwm: User requested service termination.\n"); 529 | SetEvent(ep_dwm_g_ServiceStopEvent); 530 | break; 531 | default: 532 | break; 533 | } 534 | } 535 | 536 | void WINAPI ep_dwm_ServiceMain(DWORD argc, LPTSTR* argv) 537 | { 538 | // Signal interested processes that this service is running 539 | ep_dwm_g_Service = CreateEventW(NULL, FALSE, FALSE, ep_dwm_g_wszEventName); 540 | if (!ep_dwm_g_Service || GetLastError() == ERROR_ALREADY_EXISTS) 541 | { 542 | if (ep_dwm_g_Service) 543 | { 544 | CloseHandle(ep_dwm_g_Service); 545 | } 546 | OutputDebugStringW(L"ep_dwm: Service is already running!\n"); 547 | return; 548 | } 549 | 550 | // Register service with SCM 551 | ep_dwm_g_StatusHandle = RegisterServiceCtrlHandlerExW(ep_dwm_g_wszServiceName, ep_dwm_ServiceCtrlHandlerEx, NULL); 552 | if (ep_dwm_g_StatusHandle == NULL) 553 | { 554 | // error 555 | OutputDebugStringW(L"ep_dwm: Unable to register service with the SCM!\n"); 556 | CloseHandle(ep_dwm_g_Service); 557 | return; 558 | } 559 | 560 | // Inform SCM the service is starting 561 | ZeroMemory(&ep_dwm_g_ServiceStatus, sizeof(ep_dwm_g_ServiceStatus)); 562 | ep_dwm_g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 563 | ep_dwm_g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SESSIONCHANGE; 564 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 565 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 566 | ep_dwm_g_ServiceStatus.dwServiceSpecificExitCode = 0; 567 | ep_dwm_g_ServiceStatus.dwCheckPoint = 0; 568 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 569 | { 570 | // error 571 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 572 | CloseHandle(ep_dwm_g_Service); 573 | return; 574 | } 575 | 576 | // Create events to signal service status 577 | ep_dwm_g_ServiceStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 578 | ep_dwm_g_ServiceSessionChangeEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 579 | if (!ep_dwm_g_ServiceStopEvent || !ep_dwm_g_ServiceSessionChangeEvent) 580 | { 581 | if (ep_dwm_g_ServiceStopEvent) 582 | { 583 | CloseHandle(ep_dwm_g_ServiceStopEvent); 584 | } 585 | if (ep_dwm_g_ServiceSessionChangeEvent) 586 | { 587 | CloseHandle(ep_dwm_g_ServiceSessionChangeEvent); 588 | } 589 | ep_dwm_g_ServiceStatus.dwControlsAccepted = 0; 590 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 591 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 592 | ep_dwm_g_ServiceStatus.dwCheckPoint = 1; 593 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 594 | { 595 | // error 596 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 597 | } 598 | OutputDebugStringW(L"ep_dwm: Unable to create events!\n"); 599 | CloseHandle(ep_dwm_g_Service); 600 | return; 601 | } 602 | 603 | // Inform SCM the service has started 604 | ep_dwm_g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SESSIONCHANGE; 605 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; 606 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 607 | ep_dwm_g_ServiceStatus.dwCheckPoint = 2; 608 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 609 | { 610 | // error 611 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 612 | CloseHandle(ep_dwm_g_ServiceStopEvent); 613 | CloseHandle(ep_dwm_g_ServiceSessionChangeEvent); 614 | CloseHandle(ep_dwm_g_Service); 615 | return; 616 | } 617 | 618 | // Create service thread 619 | HANDLE hServiceThread = CreateThread(NULL, 0, ep_dwm_ServiceThread, NULL, 0, NULL); 620 | if (!hServiceThread) 621 | { 622 | ep_dwm_g_ServiceStatus.dwControlsAccepted = 0; 623 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 624 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 625 | ep_dwm_g_ServiceStatus.dwCheckPoint = 3; 626 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 627 | { 628 | // error 629 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 630 | CloseHandle(ep_dwm_g_ServiceStopEvent); 631 | CloseHandle(ep_dwm_g_ServiceSessionChangeEvent); 632 | } 633 | CloseHandle(ep_dwm_g_Service); 634 | OutputDebugStringW(L"ep_dwm: Unable to create service thread!\n"); 635 | return; 636 | } 637 | 638 | // Wait until our worker thread exits signaling that the service needs to stop 639 | WaitForSingleObject(hServiceThread, INFINITE); 640 | 641 | // Inform SCM the service has stopped 642 | ep_dwm_g_ServiceStatus.dwControlsAccepted = 0; 643 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 644 | GetExitCodeThread(hServiceThread, &ep_dwm_g_ServiceStatus.dwWin32ExitCode); 645 | ep_dwm_g_ServiceStatus.dwCheckPoint = 4; 646 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 647 | { 648 | // error 649 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 650 | } 651 | 652 | CloseHandle(hServiceThread); 653 | CloseHandle(ep_dwm_g_ServiceStopEvent); 654 | CloseHandle(ep_dwm_g_ServiceSessionChangeEvent); 655 | CloseHandle(ep_dwm_g_Service); 656 | 657 | OutputDebugStringW(L"ep_dwm: Service has terminated.\n"); 658 | return; 659 | } 660 | 661 | BOOL WINAPI ep_dwm_StartService(LPWSTR wszServiceName, LPWSTR wszEventName) 662 | { 663 | ep_dwm_g_wszServiceName = wszServiceName; 664 | ep_dwm_g_wszEventName = wszEventName; 665 | 666 | SERVICE_TABLE_ENTRY ServiceTable[] = 667 | { 668 | {wszServiceName, (LPSERVICE_MAIN_FUNCTION)ep_dwm_ServiceMain}, 669 | {NULL, NULL} 670 | }; 671 | 672 | return StartServiceCtrlDispatcherW(ServiceTable); 673 | } 674 | 675 | #ifndef EP_DWM_NO_WINMAIN 676 | int WINAPI wWinMain( 677 | _In_ HINSTANCE hInstance, 678 | _In_opt_ HINSTANCE hPrevInstance, 679 | _In_ LPWSTR lpCmdLine, 680 | _In_ int nShowCmd 681 | ) 682 | { 683 | int argc = 0; 684 | LPWSTR* wargv = CommandLineToArgvW(lpCmdLine, &argc); 685 | ZeroMemory(lpCmdLine, sizeof(WCHAR) * wcslen(lpCmdLine)); 686 | if (argc >= 2) 687 | { 688 | ep_dwm_StartService(wargv[0], wargv[1]); 689 | LocalFree(wargv); 690 | } 691 | return 0; 692 | } 693 | #endif 694 | -------------------------------------------------------------------------------- /ep_dwm/ep_dwm.c: -------------------------------------------------------------------------------- 1 | #include "ep_dwm.h" 2 | 3 | LPWSTR ep_dwm_g_wszServiceName; 4 | LPWSTR ep_dwm_g_wszEventName; 5 | SERVICE_STATUS ep_dwm_g_ServiceStatus = { 0 }; 6 | SERVICE_STATUS_HANDLE ep_dwm_g_StatusHandle = NULL; 7 | HANDLE ep_dwm_g_Service = INVALID_HANDLE_VALUE; 8 | HANDLE ep_dwm_g_ServiceStopEvent = INVALID_HANDLE_VALUE; 9 | HANDLE ep_dwm_g_ServiceSessionChangeEvent = INVALID_HANDLE_VALUE; 10 | #define EP_DWM_NUM_EVENTS 2 11 | #define EP_DWM_SETUP_TIME 2000 12 | #define EP_DWM_GRACE_TIME 5000 13 | #define EP_DWM_GROW 10 14 | #define EP_DWM_MAX_NUM_MODULES 200 15 | #define EP_DWM_REASON_NONE 0 16 | #define EP_DWM_REASON_TERMINATION_BYUSER 1 17 | #define EP_DWM_REASON_EARLIER_ERROR 2 18 | 19 | #define STRINGER_INTERNAL(x) #x 20 | #define STRINGER(x) STRINGER_INTERNAL(x) 21 | 22 | #define CLEAR(x) { \ 23 | DPA_DestroyCallback(dpaHandlesList, ep_dwm_DestroyHandle, dpaExclusionList); \ 24 | if (x == EP_DWM_REASON_TERMINATION_BYUSER) OutputDebugStringW(L"ep_dwm: Terminating as per user request (line " _T(STRINGER(__LINE__)) L").\n"); \ 25 | else if (x == EP_DWM_REASON_EARLIER_ERROR) OutputDebugStringW(L"ep_dwm: Terminating due to earlier failure (line " _T(STRINGER(__LINE__)) L")!\n"); \ 26 | } 27 | 28 | #define CLEAR_AND_DEPATCH(x) { \ 29 | for (unsigned int i = EP_DWM_NUM_EVENTS; i < DPA_GetPtrCount(dpaHandlesList); ++i) \ 30 | { \ 31 | ep_dwm_PatchProcess(wszUDWMPath, DPA_FastGetPtr(dpaHandlesList, i), dpaPatchSites, /*bRestore*/ TRUE); \ 32 | } \ 33 | CLEAR(x); \ 34 | } 35 | 36 | #define REPORT_ON_PROCESS(s, i) { \ 37 | DWORD dwExitCode = 0; \ 38 | GetExitCodeProcess(DPA_FastGetPtr(dpaHandlesList, i), &dwExitCode); \ 39 | if (dwExitCode && dwExitCode != 0xd00002fe) \ 40 | { \ 41 | if (s) { \ 42 | dwFailedNum = i; \ 43 | dwRes = dwExitCode; \ 44 | } \ 45 | OutputDebugStringW(L"ep_dwm: One of the processes has crashed (line " _T(STRINGER(__LINE__)) L")!\n"); \ 46 | } \ 47 | else \ 48 | { \ 49 | if (!dwRes || !s) \ 50 | { \ 51 | if (s) { \ 52 | dwFailedNum = i; \ 53 | dwRes = 0; \ 54 | } \ 55 | if (dwExitCode == 0xd00002fe) \ 56 | { \ 57 | OutputDebugStringW(L"ep_dwm: The Desktop Window Manager has exited with code (0xd00002fe) (line " _T(STRINGER(__LINE__)) L")!\n"); \ 58 | } \ 59 | else \ 60 | { \ 61 | OutputDebugStringW(L"ep_dwm: An instance of the Desktop Window Manager has closed (line " _T(STRINGER(__LINE__)) L")!\n"); \ 62 | } \ 63 | } \ 64 | } \ 65 | } 66 | 67 | typedef struct PatchSite 68 | { 69 | UINT_PTR offset; 70 | BYTE newCode[8]; 71 | BYTE oldCode[8]; 72 | DWORD codeLength; 73 | } PatchSite; 74 | 75 | static int ep_dwm_DestroyPatchSite(void* p, void* pUnused) 76 | { 77 | PatchSite* ps = p; 78 | free(ps); 79 | return 1; 80 | } 81 | 82 | static int ep_dwm_DestroyHandle(HANDLE h, HDPA dpaExclusionList) 83 | { 84 | BOOL bShouldClose = TRUE; 85 | for (unsigned int i = 0; i < DPA_GetPtrCount(dpaExclusionList); ++i) 86 | { 87 | if (DPA_FastGetPtr(dpaExclusionList, i) == h) 88 | { 89 | bShouldClose = FALSE; 90 | } 91 | } 92 | if (bShouldClose) 93 | { 94 | CloseHandle(h); 95 | } 96 | return 1; 97 | } 98 | 99 | static BOOL MaskCompare(PVOID pBuffer, LPCSTR lpPattern, LPCSTR lpMask) 100 | { 101 | for (PBYTE value = (PBYTE)pBuffer; *lpMask; ++lpPattern, ++lpMask, ++value) 102 | { 103 | if (*lpMask == 'x' && *(LPCBYTE)lpPattern != *value) 104 | return FALSE; 105 | } 106 | 107 | return TRUE; 108 | } 109 | 110 | static __declspec(noinline) PVOID FindPatternHelper(PVOID pBase, SIZE_T dwSize, LPCSTR lpPattern, LPCSTR lpMask) 111 | { 112 | for (SIZE_T index = 0; index < dwSize; ++index) 113 | { 114 | PBYTE pAddress = (PBYTE)pBase + index; 115 | 116 | if (MaskCompare(pAddress, lpPattern, lpMask)) 117 | return pAddress; 118 | } 119 | 120 | return NULL; 121 | } 122 | 123 | PVOID FindPattern(PVOID pBase, SIZE_T dwSize, LPCSTR lpPattern, LPCSTR lpMask) 124 | { 125 | dwSize -= strlen(lpMask); 126 | return FindPatternHelper(pBase, dwSize, lpPattern, lpMask); 127 | } 128 | 129 | #if defined(_M_ARM64) 130 | __forceinline DWORD ARM64_ReadBits(DWORD value, int h, int l) 131 | { 132 | return (value >> l) & ((1 << (h - l + 1)) - 1); 133 | } 134 | 135 | __forceinline int ARM64_SignExtend(DWORD value, int numBits) 136 | { 137 | DWORD mask = 1 << (numBits - 1); 138 | if (value & mask) 139 | value |= ~((1 << numBits) - 1); 140 | return (int)value; 141 | } 142 | 143 | __forceinline int ARM64_ReadBitsSignExtend(DWORD insn, int h, int l) 144 | { 145 | return ARM64_SignExtend(ARM64_ReadBits(insn, h, l), h - l + 1); 146 | } 147 | 148 | __forceinline BOOL ARM64_IsInRange(int value, int bitCount) 149 | { 150 | int minVal = -(1 << (bitCount - 1)); 151 | int maxVal = (1 << (bitCount - 1)) - 1; 152 | return value >= minVal && value <= maxVal; 153 | } 154 | 155 | __forceinline BOOL ARM64_IsBL(DWORD insn) { return ARM64_ReadBits(insn, 31, 26) == 0b100101; } 156 | 157 | __forceinline DWORD* ARM64_FollowBL(DWORD* pInsnBL) 158 | { 159 | DWORD insnBL = *pInsnBL; 160 | if (!ARM64_IsBL(insnBL)) 161 | return NULL; 162 | int imm26 = ARM64_ReadBitsSignExtend(insnBL, 25, 0); 163 | return pInsnBL + imm26; // offset = imm26 * 4 164 | } 165 | 166 | __forceinline DWORD ARM64_MakeB(int imm26) 167 | { 168 | if (!ARM64_IsInRange(imm26, 26)) 169 | return 0; 170 | return 0b000101 << 26 | imm26 & (1 << 26) - 1; 171 | } 172 | #endif 173 | 174 | static DWORD ep_dwm_DeterminePatchAddresses(const WCHAR* pwszUDWMPath, HDPA dpaPatchSites) 175 | { 176 | DWORD dwRes = 0; 177 | 178 | HMODULE hModule = LoadLibraryW(pwszUDWMPath); 179 | if (!hModule) 180 | { 181 | OutputDebugStringW(L"ep_dwm: Failed (LoadLibraryW) (line " _T(STRINGER(__LINE__)) L")!\n"); 182 | return __LINE__; 183 | } 184 | 185 | PBYTE beginText = NULL; 186 | DWORD sizeText = 0; 187 | 188 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule; 189 | if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) 190 | { 191 | PIMAGE_NT_HEADERS64 ntHeader = (PIMAGE_NT_HEADERS64)((u_char*)dosHeader + dosHeader->e_lfanew); 192 | if (ntHeader->Signature == IMAGE_NT_SIGNATURE) 193 | { 194 | PIMAGE_SECTION_HEADER firstSection = IMAGE_FIRST_SECTION(ntHeader); 195 | for (unsigned int i = 0; i < ntHeader->FileHeader.NumberOfSections; ++i) 196 | { 197 | PIMAGE_SECTION_HEADER section = firstSection + i; 198 | if (!strncmp(section->Name, ".text", 5)) 199 | { 200 | beginText = (PBYTE)dosHeader + section->VirtualAddress; 201 | sizeText = section->SizeOfRawData; 202 | break; 203 | } 204 | } 205 | } 206 | } 207 | if (!beginText || !sizeText) 208 | { 209 | return __LINE__; 210 | } 211 | 212 | #if defined(_M_X64) 213 | // CORNER_STYLE CTopLevelWindow::GetEffectiveCornerStyle() 214 | // 48 83 EC 38 0F 29 74 24 20 0F 57 F6 E8 ?? ?? ?? ?? 83 E8 02 215 | // ^^^^^^^^^^^ 216 | // -> 48 C7 C0 01 00 00 00 C3 217 | // Ref: float CTopLevelWindow::GetRadiusFromCornerStyle() 218 | PBYTE match = FindPattern( 219 | beginText, sizeText, 220 | "\x48\x83\xEC\x38\x0F\x29\x74\x24\x20\x0F\x57\xF6\xE8\x00\x00\x00\x00\x83\xE8\x02", 221 | "xxxxxxxxxxxxx????xxx" 222 | ); 223 | if (match) 224 | { 225 | match += 12; // Point to E8 226 | match += 5 + *(int*)(match + 1); 227 | } 228 | if (match) 229 | { 230 | PatchSite* site = calloc(1, sizeof(PatchSite)); 231 | if (site) 232 | { 233 | site->offset = match - (PBYTE)hModule; 234 | site->codeLength = 8; 235 | memcpy(site->newCode, "\x48\xC7\xC0\x01\x00\x00\x00\xC3", site->codeLength); 236 | memcpy(site->oldCode, match, site->codeLength); 237 | DPA_AppendPtr(dpaPatchSites, site); 238 | } 239 | } 240 | #elif defined(_M_ARM64) 241 | // CORNER_STYLE CTopLevelWindow::GetEffectiveCornerStyle() 242 | // E8 0F 1F FC FD 7B ?? A9 FD 03 00 91 08 E4 00 2F ?? ?? ?? ?? 1F ?? 00 71 243 | // ^^^^^^^^^^^ 244 | // -> 20 00 80 D2 C0 03 5F D6 245 | // Ref: float CTopLevelWindow::GetRadiusFromCornerStyle() 246 | PBYTE match = FindPattern( 247 | beginText, sizeText, 248 | "\xE8\x0F\x1F\xFC\xFD\x7B\x00\xA9\xFD\x03\x00\x91\x08\xE4\x00\x2F\x00\x00\x00\x00\x1F\x00\x00\x71", 249 | "xxxxxx?xxxxxxxxx????x?xx" 250 | ); 251 | if (match) 252 | { 253 | match += 16; 254 | match = (PBYTE)ARM64_FollowBL((DWORD*)match); 255 | } 256 | if (match) 257 | { 258 | PatchSite* site = calloc(1, sizeof(PatchSite)); 259 | if (site) 260 | { 261 | site->offset = match - (PBYTE)hModule; 262 | site->codeLength = 8; 263 | memcpy(site->newCode, "\x20\x00\x80\xD2\xC0\x03\x5F\xD6", site->codeLength); 264 | memcpy(site->oldCode, match, site->codeLength); 265 | DPA_AppendPtr(dpaPatchSites, site); 266 | } 267 | } 268 | 269 | // Inlined occurrences of the above function (can also be in the function itself) 270 | // 28 6D 40 39 68 00 00 34 28 75 40 39 ?? ?? 00 34 28 21 40 B9 1F 09 00 71 ?? ?? 00 54 271 | // ^^^^^^^^^^^ B.GE to B 272 | // Max 3 occurrences 273 | PBYTE cur = beginText; 274 | for (int i = 0; i < 3; ++i) 275 | { 276 | match = FindPattern( 277 | cur, sizeText - (cur - beginText), 278 | "\x28\x6D\x40\x39\x68\x00\x00\x34\x28\x75\x40\x39\x00\x00\x00\x34\x28\x21\x40\xB9\x1F\x09\x00\x71\x00\x00\x00\x54", 279 | "xxxxxxxxxxxx??xxxxxxxxxx??xx" 280 | ); 281 | if (!match) 282 | break; // No more matches 283 | 284 | cur = match + 28; 285 | match += 24; // Point to B.GE 286 | DWORD insnBCond = *(DWORD*)match; 287 | int cond = ARM64_ReadBits(insnBCond, 3, 0); 288 | if (cond != 0b1010) // GE 289 | { 290 | --i; // Not this one 291 | continue; 292 | } 293 | 294 | int imm19 = ARM64_ReadBitsSignExtend(insnBCond, 23, 5); 295 | DWORD newInsn = ARM64_MakeB(imm19); 296 | if (!newInsn) 297 | continue; 298 | 299 | PatchSite* site = calloc(1, sizeof(PatchSite)); 300 | if (site) 301 | { 302 | site->offset = match - (PBYTE)hModule; 303 | site->codeLength = 4; 304 | memcpy(site->newCode, &newInsn, site->codeLength); 305 | memcpy(site->oldCode, match, site->codeLength); 306 | DPA_AppendPtr(dpaPatchSites, site); 307 | } 308 | } 309 | #endif 310 | 311 | FreeLibrary(hModule); 312 | 313 | if (DPA_GetPtrCount(dpaPatchSites) == 0) 314 | { 315 | OutputDebugStringW(L"ep_dwm: Unable to identify patch area!\n"); 316 | dwRes = __LINE__; 317 | } 318 | 319 | return dwRes; 320 | } 321 | 322 | static DWORD ep_dwm_PatchProcess(const WCHAR* pwszUDWMPath, HANDLE hProcess, HDPA dpaPatchSites, BOOL bRestore) 323 | { 324 | DWORD dwRes = 0; 325 | 326 | MODULEENTRY32 me32; 327 | ZeroMemory(&me32, sizeof(MODULEENTRY32)); 328 | me32.dwSize = sizeof(MODULEENTRY32); 329 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(hProcess)); 330 | if (!hSnapshot) 331 | { 332 | OutputDebugStringW(L"ep_dwm: Failed (CreateToolhelp32Snapshot TH32CS_SNAPMODULE) (line " _T(STRINGER(__LINE__)) L")!\n"); 333 | return __LINE__; 334 | } 335 | if (Module32FirstW(hSnapshot, &me32) == TRUE) 336 | { 337 | do 338 | { 339 | if (!_wcsicmp(me32.szExePath, pwszUDWMPath)) 340 | { 341 | for (unsigned int j = 0; dwRes == 0 && j < DPA_GetPtrCount(dpaPatchSites); ++j) 342 | { 343 | PatchSite* ps = DPA_FastGetPtr(dpaPatchSites, j); 344 | 345 | UINT_PTR pfn = me32.modBaseAddr + ps->offset; 346 | DWORD dwOldProtect = 0; 347 | SIZE_T dwNumberOfBytesWritten = 0; 348 | if (!VirtualProtectEx(hProcess, (LPVOID)pfn, ps->codeLength, PAGE_EXECUTE_READWRITE, &dwOldProtect)) 349 | { 350 | OutputDebugStringW(L"ep_dwm: Failed (VirtualProtectEx) (line " _T(STRINGER(__LINE__)) L")!\n"); 351 | dwRes = __LINE__; 352 | break; 353 | } 354 | 355 | WriteProcessMemory(hProcess, (LPVOID)pfn, bRestore ? ps->oldCode : ps->newCode, ps->codeLength, &dwNumberOfBytesWritten); 356 | if (!dwNumberOfBytesWritten || dwNumberOfBytesWritten != ps->codeLength) 357 | { 358 | OutputDebugStringW(L"ep_dwm: Failed (WriteProcessMemory) (line " _T(STRINGER(__LINE__)) L")!\n"); 359 | dwRes = __LINE__; 360 | } 361 | 362 | VirtualProtectEx(hProcess, (LPVOID)pfn, ps->codeLength, dwOldProtect, &dwOldProtect); 363 | } 364 | break; 365 | } 366 | } while (dwRes == 0 && Module32NextW(hSnapshot, &me32) == TRUE); 367 | } 368 | CloseHandle(hSnapshot); 369 | 370 | return dwRes; 371 | } 372 | 373 | static DWORD WINAPI ep_dwm_ServiceThread(LPVOID lpUnused) 374 | { 375 | WCHAR wszDWMPath[MAX_PATH]; 376 | GetSystemDirectoryW(wszDWMPath, MAX_PATH); 377 | wcscat_s(wszDWMPath, MAX_PATH, L"\\dwm.exe"); 378 | 379 | WCHAR wszUDWMPath[MAX_PATH]; 380 | GetSystemDirectoryW(wszUDWMPath, MAX_PATH); 381 | wcscat_s(wszUDWMPath, MAX_PATH, L"\\uDWM.dll"); 382 | 383 | HDPA dpaPatchSites = DPA_Create(EP_DWM_GROW); 384 | if (!dpaPatchSites) 385 | { 386 | OutputDebugStringW(L"ep_dwm: Failed (DPA_Create) (line " _T(STRINGER(__LINE__)) L")!\n"); 387 | return __LINE__; 388 | } 389 | 390 | DWORD dwRes = ep_dwm_DeterminePatchAddresses(wszUDWMPath, dpaPatchSites); 391 | if (dwRes != 0) 392 | { 393 | return dwRes; 394 | } 395 | 396 | HDPA dpaExclusionList = DPA_Create(EP_DWM_NUM_EVENTS); 397 | if (!dpaExclusionList) 398 | { 399 | dwRes = __LINE__; 400 | OutputDebugStringW(L"ep_dwm: Failed (DPA_Create) (line " _T(STRINGER(__LINE__)) L")!\n"); 401 | return dwRes; 402 | } 403 | DPA_AppendPtr(dpaExclusionList, ep_dwm_g_ServiceStopEvent); 404 | DPA_AppendPtr(dpaExclusionList, ep_dwm_g_ServiceSessionChangeEvent); 405 | 406 | while (TRUE) 407 | { 408 | DWORD dwFailedNum = 0; 409 | 410 | HDPA dpaHandlesList = DPA_Create(EP_DWM_GROW); 411 | if (!dpaHandlesList) 412 | { 413 | dwRes = __LINE__; 414 | OutputDebugStringW(L"ep_dwm: Failed (DPA_Create) (line " _T(STRINGER(__LINE__)) L")!\n"); 415 | break; 416 | } 417 | 418 | for (unsigned int i = 0; i < DPA_GetPtrCount(dpaExclusionList); ++i) 419 | { 420 | DPA_AppendPtr(dpaHandlesList, DPA_FastGetPtr(dpaExclusionList, i)); 421 | } 422 | 423 | // Make list of dwm.exe processes 424 | PROCESSENTRY32 pe32; 425 | ZeroMemory(&pe32, sizeof(PROCESSENTRY32)); 426 | pe32.dwSize = sizeof(PROCESSENTRY32); 427 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 428 | if (!hSnapshot) 429 | { 430 | DPA_Destroy(dpaHandlesList); 431 | OutputDebugStringW(L"ep_dwm: Failed (CreateToolhelp32Snapshot TH32CS_SNAPPROCESS) (line " _T(STRINGER(__LINE__)) L")!\n"); 432 | dwRes = __LINE__; 433 | break; 434 | } 435 | if (Process32FirstW(hSnapshot, &pe32) == TRUE) 436 | { 437 | do 438 | { 439 | if (!_wcsicmp(pe32.szExeFile, L"dwm.exe")) 440 | { 441 | HANDLE hProcess = OpenProcess( 442 | PROCESS_QUERY_LIMITED_INFORMATION | 443 | PROCESS_VM_OPERATION | 444 | PROCESS_VM_READ | 445 | PROCESS_VM_WRITE | 446 | //PROCESS_CREATE_THREAD | 447 | SYNCHRONIZE, 448 | FALSE, 449 | pe32.th32ProcessID 450 | ); 451 | if (!hProcess) 452 | { 453 | OutputDebugStringW(L"ep_dwm: Failed (OpenProcess) (line " _T(STRINGER(__LINE__)) L")!\n"); 454 | continue; 455 | } 456 | TCHAR wszProcessPath[MAX_PATH]; 457 | DWORD dwLength = MAX_PATH; 458 | QueryFullProcessImageNameW(hProcess, 0, wszProcessPath, &dwLength); 459 | if (!_wcsicmp(wszProcessPath, wszDWMPath)) 460 | { 461 | DPA_AppendPtr(dpaHandlesList, hProcess); 462 | } 463 | else 464 | { 465 | CloseHandle(hProcess); 466 | } 467 | } 468 | } while (Process32NextW(hSnapshot, &pe32) == TRUE); 469 | } 470 | CloseHandle(hSnapshot); 471 | 472 | // If process list is empty, retry 473 | if (DPA_GetPtrCount(dpaHandlesList) <= DPA_GetPtrCount(dpaExclusionList)) 474 | { 475 | DPA_Destroy(dpaHandlesList); 476 | OutputDebugStringW(L"ep_dwm: Desktop Window Manager is not running!\n"); 477 | OutputDebugStringW(L"ep_dwm: Retry (line " _T(STRINGER(__LINE__)) L").\n"); 478 | continue; 479 | } 480 | 481 | // Give processes a bit of time to start up 482 | OutputDebugStringW(L"ep_dwm: Waiting " _T(STRINGER(EP_DWM_SETUP_TIME)) L" ms for the processes to be ready.\n"); 483 | if (WaitForSingleObject(ep_dwm_g_ServiceStopEvent, EP_DWM_SETUP_TIME) == WAIT_OBJECT_0) 484 | { 485 | CLEAR(EP_DWM_REASON_TERMINATION_BYUSER); 486 | break; 487 | } 488 | 489 | // Attempt to patch each process 490 | for (unsigned int i = EP_DWM_NUM_EVENTS; i < DPA_GetPtrCount(dpaHandlesList); ++i) 491 | { 492 | if (ep_dwm_PatchProcess(wszUDWMPath, DPA_FastGetPtr(dpaHandlesList, i), dpaPatchSites, /*bRestore*/ FALSE)) 493 | { 494 | dwFailedNum = i; 495 | dwRes = __LINE__; 496 | break; 497 | } 498 | } 499 | if (dwFailedNum) 500 | { 501 | // If patching for a process failed, reverse the patch on the previous ones and give up 502 | CLEAR_AND_DEPATCH(EP_DWM_REASON_EARLIER_ERROR); 503 | break; 504 | } 505 | OutputDebugStringW(L"ep_dwm: Patched processes.\n"); 506 | 507 | // Give patch a bit of time in order to observe if it lead to program crash 508 | OutputDebugStringW(L"ep_dwm: Waiting " _T(STRINGER(EP_DWM_GRACE_TIME)) L" ms to determine if patch doesn't lead to crash.\n"); 509 | if (WaitForSingleObject(ep_dwm_g_ServiceStopEvent, EP_DWM_GRACE_TIME) == WAIT_OBJECT_0) 510 | { 511 | CLEAR_AND_DEPATCH(EP_DWM_REASON_TERMINATION_BYUSER); 512 | break; 513 | } 514 | 515 | // Check if any of the processes has terminated, then it's likely the patch crashed it 516 | for (unsigned int i = EP_DWM_NUM_EVENTS; i < DPA_GetPtrCount(dpaHandlesList); ++i) 517 | { 518 | if (WaitForSingleObject(DPA_FastGetPtr(dpaHandlesList, i), 0) == WAIT_OBJECT_0) 519 | { 520 | REPORT_ON_PROCESS(TRUE, i); 521 | } 522 | } 523 | if (dwFailedNum) 524 | { 525 | // If one of the processes just closed, wait a bit and repatch 526 | if (!dwRes) 527 | { 528 | if (WaitForSingleObject(ep_dwm_g_ServiceStopEvent, EP_DWM_SETUP_TIME) == WAIT_OBJECT_0) 529 | { 530 | CLEAR_AND_DEPATCH(EP_DWM_REASON_TERMINATION_BYUSER); 531 | break; 532 | } 533 | CLEAR(EP_DWM_REASON_NONE); 534 | OutputDebugStringW(L"ep_dwm: Retry (line " _T(STRINGER(__LINE__)) L").\n"); 535 | continue; 536 | } 537 | // If at least one process crashed, unpatch the rest and give up 538 | CLEAR_AND_DEPATCH(EP_DWM_REASON_EARLIER_ERROR); 539 | break; 540 | } 541 | 542 | // Wait for an external signal or for any of the processes to terminate 543 | OutputDebugStringW(L"ep_dwm: Waiting for a signal or process termination.\n"); 544 | DWORD dwRv = WaitForMultipleObjects(DPA_GetPtrCount(dpaHandlesList), DPA_GetPtrPtr(dpaHandlesList), FALSE, INFINITE); 545 | OutputDebugStringW(L"ep_dwm: Wait finished due to:\n"); 546 | if (dwRv == WAIT_OBJECT_0) 547 | { 548 | // Service is stopping by user action, so unpatch 549 | CLEAR_AND_DEPATCH(EP_DWM_REASON_TERMINATION_BYUSER); 550 | break; 551 | } 552 | else if (dwRv == WAIT_OBJECT_0 + 1) 553 | { 554 | // Another user logged on, likely to have a new DWM instance, wait a bit and then recreate the process list 555 | OutputDebugStringW(L"ep_dwm: User logon.\n"); 556 | if (WaitForSingleObject(ep_dwm_g_ServiceStopEvent, EP_DWM_SETUP_TIME) == WAIT_OBJECT_0) 557 | { 558 | CLEAR_AND_DEPATCH(EP_DWM_REASON_TERMINATION_BYUSER); 559 | break; 560 | } 561 | CLEAR(EP_DWM_REASON_NONE); 562 | } 563 | else 564 | { 565 | // One of the DWM processes has closed 566 | REPORT_ON_PROCESS(FALSE, dwRv - WAIT_OBJECT_0); 567 | CLEAR(EP_DWM_REASON_NONE); 568 | } 569 | OutputDebugStringW(L"ep_dwm: Retry (line " _T(STRINGER(__LINE__)) L").\n"); 570 | } 571 | 572 | DPA_DestroyCallback(dpaPatchSites, ep_dwm_DestroyPatchSite, NULL); 573 | DPA_Destroy(dpaExclusionList); 574 | OutputDebugStringW(L"ep_dwm: Exiting service thread.\n"); 575 | return dwRes; 576 | } 577 | 578 | static void WINAPI ep_dwm_ServiceCtrlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext) 579 | { 580 | switch (dwControl) 581 | { 582 | case SERVICE_CONTROL_SESSIONCHANGE: 583 | if (dwEventType == WTS_SESSION_LOGON) 584 | { 585 | OutputDebugStringW(L"ep_dwm: SERVICE_CONTROL_SESSIONCHANGE(WTS_SESSION_LOGON).\n"); 586 | SetEvent(ep_dwm_g_ServiceSessionChangeEvent); 587 | } 588 | break; 589 | case SERVICE_CONTROL_STOP: 590 | if (ep_dwm_g_ServiceStatus.dwCurrentState != SERVICE_RUNNING) 591 | { 592 | OutputDebugStringW(L"ep_dwm: The user requested service termination, but the service is already terminating!\n"); 593 | break; 594 | } 595 | ep_dwm_g_ServiceStatus.dwControlsAccepted = 0; 596 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; 597 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 598 | ep_dwm_g_ServiceStatus.dwCheckPoint = 5; 599 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 600 | { 601 | // error 602 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 603 | } 604 | OutputDebugStringW(L"ep_dwm: User requested service termination.\n"); 605 | SetEvent(ep_dwm_g_ServiceStopEvent); 606 | break; 607 | default: 608 | break; 609 | } 610 | } 611 | 612 | void WINAPI ep_dwm_ServiceMain(DWORD argc, LPTSTR* argv) 613 | { 614 | // Signal interested processes that this service is running 615 | ep_dwm_g_Service = CreateEventW(NULL, FALSE, FALSE, ep_dwm_g_wszEventName); 616 | if (!ep_dwm_g_Service || GetLastError() == ERROR_ALREADY_EXISTS) 617 | { 618 | if (ep_dwm_g_Service) 619 | { 620 | CloseHandle(ep_dwm_g_Service); 621 | } 622 | OutputDebugStringW(L"ep_dwm: Service is already running!\n"); 623 | return; 624 | } 625 | 626 | // Register service with SCM 627 | ep_dwm_g_StatusHandle = RegisterServiceCtrlHandlerExW(ep_dwm_g_wszServiceName, ep_dwm_ServiceCtrlHandlerEx, NULL); 628 | if (ep_dwm_g_StatusHandle == NULL) 629 | { 630 | // error 631 | OutputDebugStringW(L"ep_dwm: Unable to register service with the SCM!\n"); 632 | CloseHandle(ep_dwm_g_Service); 633 | return; 634 | } 635 | 636 | // Inform SCM the service is starting 637 | ZeroMemory(&ep_dwm_g_ServiceStatus, sizeof(ep_dwm_g_ServiceStatus)); 638 | ep_dwm_g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 639 | ep_dwm_g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SESSIONCHANGE; 640 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 641 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 642 | ep_dwm_g_ServiceStatus.dwServiceSpecificExitCode = 0; 643 | ep_dwm_g_ServiceStatus.dwCheckPoint = 0; 644 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 645 | { 646 | // error 647 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 648 | CloseHandle(ep_dwm_g_Service); 649 | return; 650 | } 651 | 652 | // Create events to signal service status 653 | ep_dwm_g_ServiceStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 654 | ep_dwm_g_ServiceSessionChangeEvent = CreateEventW(NULL, FALSE, FALSE, NULL); 655 | if (!ep_dwm_g_ServiceStopEvent || !ep_dwm_g_ServiceSessionChangeEvent) 656 | { 657 | if (ep_dwm_g_ServiceStopEvent) 658 | { 659 | CloseHandle(ep_dwm_g_ServiceStopEvent); 660 | } 661 | if (ep_dwm_g_ServiceSessionChangeEvent) 662 | { 663 | CloseHandle(ep_dwm_g_ServiceSessionChangeEvent); 664 | } 665 | ep_dwm_g_ServiceStatus.dwControlsAccepted = 0; 666 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 667 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 668 | ep_dwm_g_ServiceStatus.dwCheckPoint = 1; 669 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 670 | { 671 | // error 672 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 673 | } 674 | OutputDebugStringW(L"ep_dwm: Unable to create events!\n"); 675 | CloseHandle(ep_dwm_g_Service); 676 | return; 677 | } 678 | 679 | // Inform SCM the service has started 680 | ep_dwm_g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SESSIONCHANGE; 681 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; 682 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 683 | ep_dwm_g_ServiceStatus.dwCheckPoint = 2; 684 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 685 | { 686 | // error 687 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 688 | CloseHandle(ep_dwm_g_ServiceStopEvent); 689 | CloseHandle(ep_dwm_g_ServiceSessionChangeEvent); 690 | CloseHandle(ep_dwm_g_Service); 691 | return; 692 | } 693 | 694 | // Create service thread 695 | HANDLE hServiceThread = CreateThread(NULL, 0, ep_dwm_ServiceThread, NULL, 0, NULL); 696 | if (!hServiceThread) 697 | { 698 | ep_dwm_g_ServiceStatus.dwControlsAccepted = 0; 699 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 700 | ep_dwm_g_ServiceStatus.dwWin32ExitCode = __LINE__; 701 | ep_dwm_g_ServiceStatus.dwCheckPoint = 3; 702 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 703 | { 704 | // error 705 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 706 | CloseHandle(ep_dwm_g_ServiceStopEvent); 707 | CloseHandle(ep_dwm_g_ServiceSessionChangeEvent); 708 | } 709 | CloseHandle(ep_dwm_g_Service); 710 | OutputDebugStringW(L"ep_dwm: Unable to create service thread!\n"); 711 | return; 712 | } 713 | 714 | // Wait until our worker thread exits signaling that the service needs to stop 715 | WaitForSingleObject(hServiceThread, INFINITE); 716 | 717 | // Inform SCM the service has stopped 718 | ep_dwm_g_ServiceStatus.dwControlsAccepted = 0; 719 | ep_dwm_g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; 720 | GetExitCodeThread(hServiceThread, &ep_dwm_g_ServiceStatus.dwWin32ExitCode); 721 | ep_dwm_g_ServiceStatus.dwCheckPoint = 4; 722 | if (SetServiceStatus(ep_dwm_g_StatusHandle, &ep_dwm_g_ServiceStatus) == FALSE) 723 | { 724 | // error 725 | OutputDebugStringW(L"ep_dwm: SetServiceStatus (line " _T(STRINGER(__LINE__)) L")!\n"); 726 | } 727 | 728 | CloseHandle(hServiceThread); 729 | CloseHandle(ep_dwm_g_ServiceStopEvent); 730 | CloseHandle(ep_dwm_g_ServiceSessionChangeEvent); 731 | CloseHandle(ep_dwm_g_Service); 732 | 733 | OutputDebugStringW(L"ep_dwm: Service has terminated.\n"); 734 | return; 735 | } 736 | 737 | BOOL WINAPI ep_dwm_StartService(LPWSTR wszServiceName, LPWSTR wszEventName) 738 | { 739 | ep_dwm_g_wszServiceName = wszServiceName; 740 | ep_dwm_g_wszEventName = wszEventName; 741 | 742 | SERVICE_TABLE_ENTRY ServiceTable[] = 743 | { 744 | {wszServiceName, (LPSERVICE_MAIN_FUNCTION)ep_dwm_ServiceMain}, 745 | {NULL, NULL} 746 | }; 747 | 748 | return StartServiceCtrlDispatcherW(ServiceTable); 749 | } 750 | 751 | #ifndef EP_DWM_NO_WINMAIN 752 | int WINAPI wWinMain( 753 | _In_ HINSTANCE hInstance, 754 | _In_opt_ HINSTANCE hPrevInstance, 755 | _In_ LPWSTR lpCmdLine, 756 | _In_ int nShowCmd 757 | ) 758 | { 759 | int argc = 0; 760 | LPWSTR* wargv = CommandLineToArgvW(lpCmdLine, &argc); 761 | ZeroMemory(lpCmdLine, sizeof(WCHAR) * wcslen(lpCmdLine)); 762 | if (argc >= 2) 763 | { 764 | ep_dwm_StartService(wargv[0], wargv[1]); 765 | LocalFree(wargv); 766 | } 767 | return 0; 768 | } 769 | #endif 770 | --------------------------------------------------------------------------------