├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── ErrnoRestorer.h ├── LICENSE ├── Log.cpp ├── Log.h ├── README.md ├── README_zhs.md ├── TimeUtils.cpp ├── TimeUtils.h ├── WsaPatch.cpp ├── WsaPatch.h ├── macros.h ├── original.dll.win11.22h2 └── x86_64 │ ├── icu.dll │ └── winhttp.dll └── pic └── screenshot_20221202.png /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build WsaPatch.dll 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | # Path to the solution file relative to the root of the project. 11 | SOLUTION_FILE_PATH: . 12 | BUILD_CONFIGURATION: Release 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | build: 19 | runs-on: windows-latest 20 | 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | 25 | - name: Configure 26 | run: | 27 | cmake -G "Visual Studio 17 2022" -A x64 -B build 28 | 29 | - name: Build Release 30 | run: cmake --build build --config Release --verbose 31 | 32 | - name: Upload WsaPatch.dll 33 | uses: actions/upload-artifact@v3 34 | with: 35 | name: WsaPatch-build 36 | path: .\build 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### C++ template 2 | # Prerequisites 3 | *.d 4 | 5 | # Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Fortran module files 16 | *.mod 17 | *.smod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | ### CMake template 31 | CMakeLists.txt.user 32 | CMakeCache.txt 33 | CMakeFiles 34 | CMakeScripts 35 | Testing 36 | Makefile 37 | cmake_install.cmake 38 | install_manifest.txt 39 | compile_commands.json 40 | CTestTestfile.cmake 41 | _deps 42 | 43 | ### VisualStudio template 44 | ## Ignore Visual Studio temporary files, build results, and 45 | ## files generated by popular Visual Studio add-ons. 46 | ## 47 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 48 | 49 | # User-specific files 50 | *.rsuser 51 | *.suo 52 | *.user 53 | *.userosscache 54 | *.sln.docstates 55 | 56 | # User-specific files (MonoDevelop/Xamarin Studio) 57 | *.userprefs 58 | 59 | # Mono auto generated files 60 | mono_crash.* 61 | 62 | # Build results 63 | [Dd]ebug/ 64 | [Dd]ebugPublic/ 65 | [Rr]elease/ 66 | [Rr]eleases/ 67 | x64/ 68 | x86/ 69 | [Ww][Ii][Nn]32/ 70 | [Aa][Rr][Mm]/ 71 | [Aa][Rr][Mm]64/ 72 | bld/ 73 | [Bb]in/ 74 | [Oo]bj/ 75 | [Ll]og/ 76 | [Ll]ogs/ 77 | 78 | # Visual Studio 2015/2017 cache/options directory 79 | .vs/ 80 | # Uncomment if you have tasks that create the project's static files in wwwroot 81 | #wwwroot/ 82 | 83 | # Visual Studio 2017 auto generated files 84 | Generated\ Files/ 85 | 86 | # MSTest test Results 87 | [Tt]est[Rr]esult*/ 88 | [Bb]uild[Ll]og.* 89 | 90 | # NUnit 91 | *.VisualState.xml 92 | TestResult.xml 93 | nunit-*.xml 94 | 95 | # Build Results of an ATL Project 96 | [Dd]ebugPS/ 97 | [Rr]eleasePS/ 98 | dlldata.c 99 | 100 | # Benchmark Results 101 | BenchmarkDotNet.Artifacts/ 102 | 103 | # .NET Core 104 | project.lock.json 105 | project.fragment.lock.json 106 | artifacts/ 107 | 108 | # ASP.NET Scaffolding 109 | ScaffoldingReadMe.txt 110 | 111 | # StyleCop 112 | StyleCopReport.xml 113 | 114 | # Files built by Visual Studio 115 | *_i.c 116 | *_p.c 117 | *_h.h 118 | *.ilk 119 | *.meta 120 | *.obj 121 | *.iobj 122 | *.pch 123 | *.pdb 124 | *.ipdb 125 | *.pgc 126 | *.pgd 127 | *.rsp 128 | *.sbr 129 | *.tlb 130 | *.tli 131 | *.tlh 132 | *.tmp 133 | *.tmp_proj 134 | *_wpftmp.csproj 135 | *.log 136 | *.vspscc 137 | *.vssscc 138 | .builds 139 | *.pidb 140 | *.svclog 141 | *.scc 142 | 143 | # Chutzpah Test files 144 | _Chutzpah* 145 | 146 | # Visual C++ cache files 147 | ipch/ 148 | *.aps 149 | *.ncb 150 | *.opendb 151 | *.opensdf 152 | *.sdf 153 | *.cachefile 154 | *.VC.db 155 | *.VC.VC.opendb 156 | 157 | # Visual Studio profiler 158 | *.psess 159 | *.vsp 160 | *.vspx 161 | *.sap 162 | 163 | # Visual Studio Trace Files 164 | *.e2e 165 | 166 | # TFS 2012 Local Workspace 167 | $tf/ 168 | 169 | # Guidance Automation Toolkit 170 | *.gpState 171 | 172 | # ReSharper is a .NET coding add-in 173 | _ReSharper*/ 174 | *.[Rr]e[Ss]harper 175 | *.DotSettings.user 176 | 177 | # TeamCity is a build add-in 178 | _TeamCity* 179 | 180 | # DotCover is a Code Coverage Tool 181 | *.dotCover 182 | 183 | # AxoCover is a Code Coverage Tool 184 | .axoCover/* 185 | !.axoCover/settings.json 186 | 187 | # Coverlet is a free, cross platform Code Coverage Tool 188 | coverage*.json 189 | coverage*.xml 190 | coverage*.info 191 | 192 | # Visual Studio code coverage results 193 | *.coverage 194 | *.coveragexml 195 | 196 | # NCrunch 197 | _NCrunch_* 198 | .*crunch*.local.xml 199 | nCrunchTemp_* 200 | 201 | # MightyMoose 202 | *.mm.* 203 | AutoTest.Net/ 204 | 205 | # Web workbench (sass) 206 | .sass-cache/ 207 | 208 | # Installshield output folder 209 | [Ee]xpress/ 210 | 211 | # DocProject is a documentation generator add-in 212 | DocProject/buildhelp/ 213 | DocProject/Help/*.HxT 214 | DocProject/Help/*.HxC 215 | DocProject/Help/*.hhc 216 | DocProject/Help/*.hhk 217 | DocProject/Help/*.hhp 218 | DocProject/Help/Html2 219 | DocProject/Help/html 220 | 221 | # Click-Once directory 222 | publish/ 223 | 224 | # Publish Web Output 225 | *.[Pp]ublish.xml 226 | *.azurePubxml 227 | # Note: Comment the next line if you want to checkin your web deploy settings, 228 | # but database connection strings (with potential passwords) will be unencrypted 229 | *.pubxml 230 | *.publishproj 231 | 232 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 233 | # checkin your Azure Web App publish settings, but sensitive information contained 234 | # in these scripts will be unencrypted 235 | PublishScripts/ 236 | 237 | # NuGet Packages 238 | *.nupkg 239 | # NuGet Symbol Packages 240 | *.snupkg 241 | # The packages folder can be ignored because of Package Restore 242 | **/[Pp]ackages/* 243 | # except build/, which is used as an MSBuild target. 244 | !**/[Pp]ackages/build/ 245 | # Uncomment if necessary however generally it will be regenerated when needed 246 | #!**/[Pp]ackages/repositories.config 247 | # NuGet v3's project.json files produces more ignorable files 248 | *.nuget.props 249 | *.nuget.targets 250 | 251 | # Microsoft Azure Build Output 252 | csx/ 253 | *.build.csdef 254 | 255 | # Microsoft Azure Emulator 256 | ecf/ 257 | rcf/ 258 | 259 | # Windows Store app package directories and files 260 | AppPackages/ 261 | BundleArtifacts/ 262 | Package.StoreAssociation.xml 263 | _pkginfo.txt 264 | *.appx 265 | *.appxbundle 266 | *.appxupload 267 | 268 | # Visual Studio cache files 269 | # files ending in .cache can be ignored 270 | *.[Cc]ache 271 | # but keep track of directories ending in .cache 272 | !?*.[Cc]ache/ 273 | 274 | # Others 275 | ClientBin/ 276 | ~$* 277 | *~ 278 | *.dbmdl 279 | *.dbproj.schemaview 280 | *.jfm 281 | *.pfx 282 | *.publishsettings 283 | orleans.codegen.cs 284 | 285 | # Including strong name files can present a security risk 286 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 287 | #*.snk 288 | 289 | # Since there are multiple workflows, uncomment next line to ignore bower_components 290 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 291 | #bower_components/ 292 | 293 | # RIA/Silverlight projects 294 | Generated_Code/ 295 | 296 | # Backup & report files from converting an old project file 297 | # to a newer Visual Studio version. Backup files are not needed, 298 | # because we have git ;-) 299 | _UpgradeReport_Files/ 300 | Backup*/ 301 | UpgradeLog*.XML 302 | UpgradeLog*.htm 303 | ServiceFabricBackup/ 304 | *.rptproj.bak 305 | 306 | # SQL Server files 307 | *.mdf 308 | *.ldf 309 | *.ndf 310 | 311 | # Business Intelligence projects 312 | *.rdl.data 313 | *.bim.layout 314 | *.bim_*.settings 315 | *.rptproj.rsuser 316 | *- [Bb]ackup.rdl 317 | *- [Bb]ackup ([0-9]).rdl 318 | *- [Bb]ackup ([0-9][0-9]).rdl 319 | 320 | # Microsoft Fakes 321 | FakesAssemblies/ 322 | 323 | # GhostDoc plugin setting file 324 | *.GhostDoc.xml 325 | 326 | # Node.js Tools for Visual Studio 327 | .ntvs_analysis.dat 328 | node_modules/ 329 | 330 | # Visual Studio 6 build log 331 | *.plg 332 | 333 | # Visual Studio 6 workspace options file 334 | *.opt 335 | 336 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 337 | *.vbw 338 | 339 | # Visual Studio LightSwitch build output 340 | **/*.HTMLClient/GeneratedArtifacts 341 | **/*.DesktopClient/GeneratedArtifacts 342 | **/*.DesktopClient/ModelManifest.xml 343 | **/*.Server/GeneratedArtifacts 344 | **/*.Server/ModelManifest.xml 345 | _Pvt_Extensions 346 | 347 | # Paket dependency manager 348 | .paket/paket.exe 349 | paket-files/ 350 | 351 | # FAKE - F# Make 352 | .fake/ 353 | 354 | # CodeRush personal settings 355 | .cr/personal 356 | 357 | # Python Tools for Visual Studio (PTVS) 358 | __pycache__/ 359 | *.pyc 360 | 361 | # Cake - Uncomment if you are using it 362 | # tools/** 363 | # !tools/packages.config 364 | 365 | # Tabs Studio 366 | *.tss 367 | 368 | # Telerik's JustMock configuration file 369 | *.jmconfig 370 | 371 | # BizTalk build output 372 | *.btp.cs 373 | *.btm.cs 374 | *.odx.cs 375 | *.xsd.cs 376 | 377 | # OpenCover UI analysis results 378 | OpenCover/ 379 | 380 | # Azure Stream Analytics local run output 381 | ASALocalRun/ 382 | 383 | # MSBuild Binary and Structured Log 384 | *.binlog 385 | 386 | # NVidia Nsight GPU debugger configuration file 387 | *.nvuser 388 | 389 | # MFractors (Xamarin productivity tool) working folder 390 | .mfractor/ 391 | 392 | # Local History for Visual Studio 393 | .localhistory/ 394 | 395 | # BeatPulse healthcheck temp database 396 | healthchecksdb 397 | 398 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 399 | MigrationBackup/ 400 | 401 | # Ionide (cross platform F# VS Code tools) working folder 402 | .ionide/ 403 | 404 | # Fody - auto-generated XML schema 405 | FodyWeavers.xsd 406 | 407 | ### Windows template 408 | # Windows thumbnail cache files 409 | Thumbs.db 410 | Thumbs.db:encryptable 411 | ehthumbs.db 412 | ehthumbs_vista.db 413 | 414 | # Dump file 415 | *.stackdump 416 | 417 | # Folder config file 418 | [Dd]esktop.ini 419 | 420 | # Recycle Bin used on file shares 421 | $RECYCLE.BIN/ 422 | 423 | # Windows Installer files 424 | *.cab 425 | *.msi 426 | *.msix 427 | *.msm 428 | *.msp 429 | 430 | # Windows shortcuts 431 | *.lnk 432 | 433 | /.idea 434 | /cmake-build-* 435 | /out 436 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.23) 2 | project(WsaPatch) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_C_STANDARD 11) 6 | 7 | add_library(WsaPatch SHARED WsaPatch.cpp Log.cpp TimeUtils.cpp ErrnoRestorer.h) 8 | -------------------------------------------------------------------------------- /ErrnoRestorer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2022.11.30. 3 | // 4 | 5 | #ifndef WSAPATCH_ERRNORESTORER_H 6 | #define WSAPATCH_ERRNORESTORER_H 7 | 8 | #include 9 | 10 | class ErrnoRestorer { 11 | public: 12 | ErrnoRestorer() : saved_errno_(GetLastError()) {} 13 | 14 | ~ErrnoRestorer() { 15 | SetLastError(saved_errno_); 16 | } 17 | 18 | // disable copy and assign 19 | 20 | ErrnoRestorer(const ErrnoRestorer &) = delete; 21 | 22 | ErrnoRestorer &operator=(const ErrnoRestorer &) = delete; 23 | 24 | ErrnoRestorer(ErrnoRestorer &&) = delete; 25 | 26 | ErrnoRestorer &operator=(ErrnoRestorer &&) = delete; 27 | 28 | // Allow this object to be used as part of && operation. 29 | explicit operator bool() const { return true; } 30 | 31 | private: 32 | const DWORD saved_errno_; 33 | }; 34 | 35 | #endif //WSAPATCH_ERRNORESTORER_H 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Log.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2022.11.30. 3 | // 4 | 5 | #include "Log.h" 6 | 7 | volatile Log::LogHandler Log::mHandler = nullptr; 8 | -------------------------------------------------------------------------------- /Log.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2022.11.30. 3 | // 4 | 5 | #ifndef WSAPATCH_LOG_H 6 | #define WSAPATCH_LOG_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef _MAC 15 | typedef wchar_t WCHAR; // wc, 16-bit UNICODE character 16 | #else 17 | // some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char 18 | typedef unsigned short WCHAR; // wc, 16-bit UNICODE character 19 | #endif 20 | 21 | #ifdef ERROR 22 | #define MACRO_PUSH_ERROR 1 23 | #pragma push_macro("ERROR") 24 | #undef ERROR 25 | #endif 26 | 27 | class Log { 28 | public: 29 | enum class Level { 30 | UNKNOWN = 0, 31 | VERBOSE = 2, 32 | DEBUG = 3, 33 | INFO = 4, 34 | WARN = 5, 35 | ERROR = 6 36 | }; 37 | using LogHandler = void (*)(Level level, const WCHAR *tag, const WCHAR *msg); 38 | private: 39 | static volatile LogHandler mHandler; 40 | public: 41 | static void format(Level level, const WCHAR *tag, _Printf_format_string_ const WCHAR *fmt, ...) { 42 | va_list varg1; 43 | LogHandler h = mHandler; 44 | if (h == nullptr || fmt == nullptr) { 45 | return; 46 | } 47 | WCHAR buffer[1024] = {}; 48 | va_start(varg1, fmt); 49 | StringCchVPrintfW(buffer, 1024, fmt, varg1); 50 | va_end(varg1); 51 | h(level, tag, buffer); 52 | } 53 | 54 | static void logBuffer(Level level, const WCHAR *tag, const WCHAR *msg) { 55 | LogHandler h = mHandler; 56 | if (h == nullptr) { 57 | return; 58 | } 59 | h(level, tag, msg); 60 | } 61 | 62 | static inline LogHandler getLogHandler() noexcept { 63 | return mHandler; 64 | } 65 | 66 | static inline void setLogHandler(LogHandler h) noexcept { 67 | mHandler = h; 68 | } 69 | 70 | static constexpr const WCHAR *levelName(Level level) noexcept { 71 | switch (level) { 72 | case Level::UNKNOWN: 73 | return L"UNKNOWN"; 74 | case Level::VERBOSE: 75 | return L"VERBOSE"; 76 | case Level::DEBUG: 77 | return L"DEBUG"; 78 | case Level::INFO: 79 | return L"INFO"; 80 | case Level::WARN: 81 | return L"WARN"; 82 | case Level::ERROR: 83 | return L"ERROR"; 84 | default: 85 | return L"UNKNOWN"; 86 | } 87 | } 88 | }; 89 | 90 | #define LOGE(...) Log::format(static_cast(6), LOG_TAG, __VA_ARGS__) 91 | #define LOGW(...) Log::format(Log::Level::WARN, LOG_TAG, __VA_ARGS__) 92 | #define LOGI(...) Log::format(Log::Level::INFO, LOG_TAG, __VA_ARGS__) 93 | #define LOGD(...) Log::format(Log::Level::DEBUG, LOG_TAG, __VA_ARGS__) 94 | #define LOGV(...) Log::format(Log::Level::VERBOSE, LOG_TAG, __VA_ARGS__) 95 | 96 | #ifdef MACRO_PUSH_ERROR 97 | #pragma pop_macro("ERROR") 98 | #endif 99 | 100 | #endif //WSAPATCH_LOG_H 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WSA patch for Windows 10 2 | 3 | [中文版本](./README_zhs.md) 4 | 5 | This is a patch for WSA to enable WSA (Windows Subsystem for Android) to run on Windows 10. 6 | 7 | ### Compatibility 8 | 9 | Below is a list of versions of WSA and versions of Windows 10 that have been tested together. 10 | 11 | | WSA version | Windows version | 12 | |-----------------|------------------------| 13 | | 2210.40000.7.0 | 10.0.19045.2311 (22H2) | 14 | | 2211.40000.10.0 | 10.0.19045.2364 (22H2) | 15 | | 2302.40000.9.0 | 10.0.19044.2728 (21H2) | 16 | 17 | In theory, this should work with any version of Windows 10 version 2004 (20H1) and above. This is because every update since then has been very minor, and the build number even still shows as 19041 (20H1's build number) in some places, such as the desktop build number watermark if that is enabled. 18 | 19 | ### Instructions 20 | 21 | 1. Make sure your Windows version is at least Windows 10 22H2 10.0.19045.2311. 22 | - You can check your Windows version with command `winver`. 23 | - If your Windows version is lower than 10.0.19045.2311, please update your Windows to at least 10.0.19045.2311. 24 | 2. Get WSA appx zip. You can do this by following instructions in https://github.com/LSPosed/MagiskOnWSALocal 25 | (You need to "build" this yourself with your local WSL2). 26 | 3. Get "icu.dll" from Windows 11 22H2. Note that you MUST use icu.dll from Windows 11. 27 | The icu.dll from Windows 10 will NOT work. 28 | (I have made a copy of these DLLs in the original.dll.win11.22h2 dir. They are digitally signed by Microsoft.) 29 | 4. Build WsaPatch.dll with source code in this repo. 30 | (Build with MSVC toolchain, not MinGW or something else.) 31 | 5. Patch icu.dll: add WsaPatch.dll as an import DLL as icu.dll. 32 | 6. Copy patched icu.dll and WsaPatch.dll to WsaClient dir. 33 | 7. Patch AppxManifest.xml. 34 | 1. Find TargetDeviceFamily node in AppxManifest.xml. 35 | ```xml 36 | 37 | ``` 38 | 39 | Change the `MinVersion` from `10.0.22000.120` to `10.0` plus the OS Build number that appears in `winver`. For example, if `winver` shows `Version 21H2 (OS Build 19044.2728)`, you'd change it to `10.0.19044.2728`. 40 | 41 | 2. Delete all nodes about "customInstall" extension (see below) in AppxManifest.xml. 42 | ```xml 43 | 44 | ``` 45 | 46 | ```xml 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ``` 58 | 59 | 8. Run "Run.bat" to register your WSA appx. 60 | 9. You should be able to run WSA now. 61 | 62 | If you don't want to build WsaPatch.dll and patch icu.dll yourself, 63 | you can download the prebuilt binaries from the [release page](https://github.com/cinit/WSAPatch/releases). 64 | 65 | ### Notice 66 | 67 | 1. You can only install WSA on a NTFS partition, not on an exFAT partition. 68 | 2. You can NOT delete the WSA installation folder. 69 | What `Add-AppxPackage -Register .\AppxManifest.xml` does is to register an appx package with some existing unpackaged files, 70 | so you need to keep them as long as you want to use WSA. 71 | Check https://learn.microsoft.com/en-us/powershell/module/appx/add-appxpackage?view=windowsserver2022-ps for more details. 72 | 3. You need to register your WSA appx package before you can run WSA (the 8th step in the instructions). 73 | For [MagiskOnWSALocal](https://github.com/LSPosed/MagiskOnWSALocal) users, you need to run `Run.bat` in the extracted dir. 74 | If the script fails, you can take the following steps for diagnosis (admin privilege required): 75 | 1. Open a PowerShell window and change working directory to your WSA directory. 76 | 2. Run `Add-AppxPackage -ForceApplicationShutdown -ForceUpdateFromAnyVersion -Register .\AppxManifest.xml` in PowerShell. 77 | This should fail with an ActivityID, which is a UUID required for the next step. 78 | 3. Run `Get-AppPackageLog -ActivityID ` in PowerShell. 79 | This should print the log of the failed operation. 80 | 4. Check the log for the reason of failure and fix it. 81 | 82 | #### About winhttp.dll 83 | 84 | - WsaClient.exe does use GetProcAddress to get some functions from winhttp.dll. 85 | - Some functions exist in winhttp.dll of Windows 11 22H2, but not in Windows 10 22H2. 86 | - If you create a file `EnableDebugConsole` in WsaClient directory or set `wsapatch::kDebug` in [WsaPatch.cpp](WsaPatch.cpp) to true, 87 | you will see the following message from log console. 88 | - If you copy a winhttp.dll from Windows 11 22H2 to WsaClient directory, WsaClient.exe will be able to find these functions. 89 | - WSA will still run even if you don't copy a winhttp.dll with these symbols. 90 | 91 | ```text 92 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpRegisterProxyChangeNotification, result=NULL 93 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpUnregisterProxyChangeNotification, result=NULL 94 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpGetProxySettingsEx, result=NULL 95 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpGetProxySettingsResultEx, result=NULL 96 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpFreeProxySettingsEx, result=NULL 97 | ``` 98 | 99 | ### Problems I met 100 | 101 | 1. When using WSA 2209.40000.26.0, I was able to run applications in WSA, 102 | but I was not able to connect to WSA ADB after enabling Developer Mode, 103 | since netstat shows that no process is listening on port 58526. 104 | After I upgraded to WSA 2210.40000.7.0, I was able to connect to WSA ADB. 105 | 2. The WSA settings window does not hava a draggable title, 106 | but you can move the WSA window if you hold the cursor left near the "minimize" button, 107 | or press Alt+Space, then click "Move" in the context 108 | menu. [#1](https://github.com/cinit/WSAPatch/issues/1) [#2](https://github.com/cinit/WSAPatch/issues/2) 109 | 3. If your WSA crashes(or suddenly disappears) when starting up, try to upgrade your Windows to Windows 10 22H2 10.0.19045.2311. 110 | (Someone has reported that WSA failed to start on 22H2 19045.2251, but worked after upgrading to 19045.2311.) 111 | 112 | If you encounter any problems or have any suggestions, please open an issue or pull request. 113 | 114 | ### Screenshot 115 | 116 | ![screenshot](./pic/screenshot_20221202.png) 117 | -------------------------------------------------------------------------------- /README_zhs.md: -------------------------------------------------------------------------------- 1 | # 适用于 Windows 10 的 WSA 补丁 2 | 3 | [English version](./README.md) 4 | 5 | 这是一个可以让 Windows 10 运行 WSA (Windows Subsystem for Android) 的补丁及其使用方法。 6 | 7 | 本人测试过 Windows 10 22H2 10.0.19045.2311 x64 + WSA 2210.40000.7.0 以及 10.0.19045.2364 + 2211.40000.10.0. 8 | 9 | ### 操作步骤 10 | 11 | 1. 将你的系统更新到 Windows 10 22H2 10.0.19045.2311 或更高版本. 12 | - 你可以通过 `winver` 命令查看您当前的 Windows 版本. 13 | - 如果您的 Windows 版本低于 10.0.19045.2311, 请将您的系统更新到 10.0.19045.2311 或以上. 14 | 2. 安装 WSL2 (我用的 Ubuntu 22.04 LTS, 当然你也可以选择你喜欢的发行版,下一步要用到). 15 | 3. 按照 https://github.com/LSPosed/MagiskOnWSALocal 的步骤,得到集成了 Magisk 的 WSA AppX 包. 16 | - 注:你需要在你的 WSL2 里进行操作,WSA 版本建议选 Insider Fast/Dev Channel. 17 | 4. 把你的 WSA AppX 包从 WSL2 里复制出来, 它应该是一个压缩包, 解压到你希望安装 WSA 的地方. 18 | - 位于 `MagiskOnWSALocal/output`, 文件名类似于 `WSA-with-magisk-stable-MindTheGapps_2210.40000.7.0_x64_Release-Nightly.7z`. 19 | 5. 用 Visual Studio 编译本仓库的代码,得到 WsaPatch.dll (用 MSVC 工具链编译,如果你不想自己编译,你可以去 release 里下). 20 | 6. 想办法从 Windows 11 22H2 的 System32 里复制一个 icu.dll 过来,找一个 PE32+ 编辑工具给它的导入表加一个 WsaPatch.dll. 21 | - 你必须用 Windows 11 22H2 里的 icu.dll, 因为 Windows 10 的 icu.dll 缺符号; 22 | - 如果你没有 Windows 11 22H2, 本仓库的 original.dll.win11.22h2 就有原版带微软签名的 icu.dll; 23 | - 如果你不想自己搞,你可以用 release 里已经修改过的 icu.dll. 24 | 7. 把编译好的 WsaPatch.dll 和修改过的 icu.dll 复制到 WSA 的 WsaClient 文件夹. 25 | 8. 按以下要求修改 AppxManifest.xml: 26 | 1. 在 `AppxManifest.xml` 找到 `TargetDeviceFamily` 节点 27 | ```xml 28 | 29 | ``` 30 | 31 | 把 `MinVersion` 从 `10.0.22000.120` 改成 `10.0.19045.2311`. 32 | 33 | 2. 在 `AppxManifest.xml` 删除 "customInstall" 相关节点,一共有两个. 34 | 找到以下内容,然后删掉. 35 | 36 | ```xml 37 | 38 | ``` 39 | 40 | ```xml 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ``` 52 | 53 | 9. 运行 `Run.bat` (需要管理员权限). 54 | 55 | 编译好的 WsaPatch.dll 和修改过的 icu.dll 可以从 [release 页面](https://github.com/cinit/WSAPatch/releases) 下载. 56 | 57 | ### 注意事项 58 | 59 | 1. WSA 只能安装在 NTFS 分区 (请勿安装在 exFAT 分区). 60 | 2. 在 `Add-AppxPackage -Register .\AppxManifest.xml` 完成 WSA 的安装后, 你原先解压 WSA 的文件夹不可以删除. 61 | 因为 `Add-AppxPackage -Register .\AppxManifest.xml` 的作用是注册 appx 应用包,它只登记注册,不会复制文件. 62 | 参考 https://learn.microsoft.com/en-us/powershell/module/appx/add-appxpackage?view=windowsserver2022-ps 63 | 3. 在解压 WSA 后第一次启动之前, 你需要注册 WSA appx 包 (上文第 9 步). 64 | 对于 [MagiskOnWSALocal](https://github.com/LSPosed/MagiskOnWSALocal) 用户, 你只需要运行解压目录下的 `Run.bat` 即可. 65 | 如果报错失败, 你可以进行以下操作进行错误诊断(需要管理员权限). 66 | 1. 以管理员身份打开 PowerShell, 切换工作目录到 WSA 的解压目录. 67 | 2. 在 PowerShell 中运行 `Add-AppxPackage -ForceApplicationShutdown -ForceUpdateFromAnyVersion -Register .\AppxManifest.xml`. 68 | 该命令应该会失败并提供一个这次错误的 ActivityID (是个 UUID). 69 | 3. 在 PowerShell 中运行 `Get-AppPackageLog -ActivityID ` 获取刚才的错误的日志. 70 | 4. 根据日志的内容进行修复. 71 | 72 | #### 关于 winhttp.dll 73 | 74 | - WsaClient.exe 会用 GetProcAddress 从 winhttp.dll 动态获取一些符号. 75 | - 有些符号是只有在 Windows 11 的 winhttp.dll 里才有,Windows 10 的 winhttp.dll 缺少这些符号. 76 | - 如果你在 WsaClient 文件夹建一个名为 `EnableDebugConsole` 的文件(没有扩展名)或者把 [WsaPatch.cpp](WsaPatch.cpp) 里的 `wsapatch::kDebug` 改成 true, 77 | 你能看到 GetProcAddress 有些结果是 NULL. 78 | - 如果你找一个 Windows 11 22H2 的 winhttp.dll 放到 WsaClient 文件夹里(或者 WSA 安装目录),WsaClient.exe 就能找到这些符号了. 79 | - 但是不管 WsaClient.exe 能否找得到这些符号,它都能用. 80 | 81 | ```text 82 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpRegisterProxyChangeNotification, result=NULL 83 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpUnregisterProxyChangeNotification, result=NULL 84 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpGetProxySettingsEx, result=NULL 85 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpGetProxySettingsResultEx, result=NULL 86 | 12-10 16:16:29.474 W WsaPatch: -GetProcAddress: hModule=C:\WINDOWS\SYSTEM32\WINHTTP.dll(00007FFC64780000), lpProcName=WinHttpFreeProxySettingsEx, result=NULL 87 | ``` 88 | 89 | ### 可能遇到的问题 90 | 91 | 1. 如果老版本的 WSA 2209.40000.26.0 开了开发者模式也连不上 ADB (端口没有进程监听), 更新到 WSA 2210.40000.7.0 就可以了. 92 | 2. 移动 WSA 的设置窗口可以通过按住窗口最小化按钮左边的一小块空白区域然后拖动, 也可以通过按快捷键 Alt+Space 然后点弹出菜单里的 "移动". 93 | [#1](https://github.com/cinit/WSAPatch/issues/1) [#2](https://github.com/cinit/WSAPatch/issues/2) 94 | 3. 如果你的 WSA 在启动时闪退且没有任何提示,请将 Windows 10 更新到 22H2 10.0.19045.2311 或更高版本. 95 | (有人反应过 WSA 在 22H2 19045.2251 闪退, 但更新到 22H2 19045.2311 就能用了). 96 | 97 | 如果你遇到了其他问题或者有什么建议,欢迎在 Issue/PR 中提出. 98 | 99 | ### 截图 100 | 101 | ![screenshot](./pic/screenshot_20221202.png) 102 | -------------------------------------------------------------------------------- /TimeUtils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2022.11.30. 3 | // 4 | 5 | #include "TimeUtils.h" 6 | 7 | #include 8 | 9 | u64 currentTimeMillis() { 10 | FILETIME ft = {}; 11 | GetSystemTimeAsFileTime(&ft); 12 | u64 ret = ft.dwHighDateTime; 13 | ret <<= 32; 14 | ret |= ft.dwLowDateTime; 15 | ret /= 10000LLU; 16 | return ret - 116444736000000000LLU; 17 | } 18 | 19 | u64 currentTimeSeconds() { 20 | return currentTimeMillis() / 1000LLU; 21 | } 22 | 23 | void timeMillisToUTCCalendar(u64 timeMillis, int *year, int *month, int *day, int *hour, int *minute, int *second, int *millisecond) { 24 | SYSTEMTIME st = {}; 25 | FILETIME ft = {}; 26 | u64 fileTime = (timeMillis + 116444736000000000LLU) * 10000LLU; 27 | ft.dwHighDateTime = (DWORD) (fileTime >> 32); 28 | ft.dwLowDateTime = (DWORD) fileTime; 29 | FileTimeToSystemTime(&ft, &st); 30 | if (year != nullptr) { 31 | *year = st.wYear; 32 | } 33 | if (month != nullptr) { 34 | *month = st.wMonth; 35 | } 36 | if (day != nullptr) { 37 | *day = st.wDay; 38 | } 39 | if (hour != nullptr) { 40 | *hour = st.wHour; 41 | } 42 | if (minute != nullptr) { 43 | *minute = st.wMinute; 44 | } 45 | if (second != nullptr) { 46 | *second = st.wSecond; 47 | } 48 | if (millisecond != nullptr) { 49 | *millisecond = st.wMilliseconds; 50 | } 51 | } 52 | 53 | static u64 gsTimeBiasSeconds = 0; 54 | static bool gsTimeBiasSecondsInitialized = false; 55 | 56 | s32 getLocalTimeBiasSeconds() { 57 | if (!gsTimeBiasSecondsInitialized) { 58 | TIME_ZONE_INFORMATION tzi = {}; 59 | GetTimeZoneInformation(&tzi); 60 | gsTimeBiasSeconds = -tzi.Bias * 60LU; 61 | gsTimeBiasSecondsInitialized = true; 62 | } 63 | return gsTimeBiasSeconds; 64 | } 65 | 66 | void timeMillisToLocalCalendar(u64 timeMillis, int *year, int *month, int *day, int *hour, int *minute, int *second, int *millisecond) { 67 | u64 timeWithBias = timeMillis + getLocalTimeBiasSeconds() * 1000; 68 | timeMillisToUTCCalendar(timeWithBias, year, month, day, hour, minute, second, millisecond); 69 | } 70 | -------------------------------------------------------------------------------- /TimeUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2022.11.30. 3 | // 4 | 5 | #ifndef WSAPATCH_TIMEUTILS_H 6 | #define WSAPATCH_TIMEUTILS_H 7 | 8 | #include "macros.h" 9 | 10 | u64 currentTimeMillis(); 11 | 12 | u64 currentTimeSeconds(); 13 | 14 | void timeMillisToLocalCalendar(u64 timeMillis, int *year, int *month, int *day, int *hour, int *minute, int *second, int *millisecond); 15 | 16 | void timeMillisToUTCCalendar(u64 timeMillis, int *year, int *month, int *day, int *hour, int *minute, int *second, int *millisecond); 17 | 18 | s32 getLocalTimeBiasSeconds(); 19 | 20 | #endif //WSAPATCH_TIMEUTILS_H 21 | -------------------------------------------------------------------------------- /WsaPatch.cpp: -------------------------------------------------------------------------------- 1 | #include "WsaPatch.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "Log.h" 9 | #include "ErrnoRestorer.h" 10 | #include "TimeUtils.h" 11 | 12 | static constexpr auto LOG_TAG = L"WsaPatch"; 13 | 14 | namespace wsapatch { 15 | 16 | bool kDebug = false; 17 | 18 | HINSTANCE gSelfInstance = nullptr; 19 | 20 | HANDLE gConsoleOutput = INVALID_HANDLE_VALUE; 21 | bool gConsoleIsAllocated = false; 22 | 23 | HMODULE hNtdll = nullptr; 24 | HMODULE hWsaClient = nullptr; 25 | 26 | RTL_OSVERSIONINFOEXW gOsVersionInfo = {0}; 27 | bool gIsPatchVersionNumber = false; 28 | bool gIsPatchProductType = false; 29 | 30 | #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) 31 | #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) 32 | 33 | void defaultLogHandler(Log::Level level, const WCHAR *tag, const WCHAR *msg) { 34 | HANDLE handle = gConsoleOutput; 35 | if (handle == nullptr || handle == INVALID_HANDLE_VALUE) { 36 | return; 37 | } 38 | WCHAR buffer[1024] = {}; 39 | u64 now = currentTimeMillis(); 40 | const WCHAR *levelName = Log::levelName(level); 41 | // MM-dd HH:mm:ss.SSS [level] tag: msg 42 | int month = 0, day = 0, hour = 0, minute = 0, second = 0, millisecond = 0; 43 | timeMillisToLocalCalendar(now, nullptr, &month, &day, &hour, &minute, &second, &millisecond); 44 | StringCchPrintfW(buffer, _countof(buffer), 45 | L"%02d-%02d %02d:%02d:%02d.%03d %c %s: %s\n", 46 | month, day, hour, minute, second, millisecond, 47 | WCHAR(*levelName), tag, msg); 48 | DWORD written = 0; 49 | WriteConsoleW(handle, buffer, static_cast(wcslen(buffer)), &written, nullptr); 50 | } 51 | 52 | using FuncRtlGetVersion = NTSYSAPI NTSTATUS(*)(PRTL_OSVERSIONINFOW lpVersionInformation); 53 | 54 | NTSTATUS WINAPI FakeRtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation) { 55 | // The minimal version is 10.0.22000.120 VER_NT_WORKSTATION 56 | LOGD(L"-FakeRtlGetVersion"); 57 | DWORD size = lpVersionInformation->dwOSVersionInfoSize; 58 | memcpy(lpVersionInformation, &gOsVersionInfo, size); 59 | lpVersionInformation->dwOSVersionInfoSize = size; 60 | if (gIsPatchVersionNumber) { 61 | lpVersionInformation->dwBuildNumber = 22000; 62 | } 63 | if (gIsPatchProductType && size >= sizeof(OSVERSIONINFOEXW)) { 64 | ((PRTL_OSVERSIONINFOEXW) lpVersionInformation)->wProductType = VER_NT_WORKSTATION; 65 | } 66 | return STATUS_SUCCESS; 67 | } 68 | 69 | FARPROC WINAPI BadGetProcAddress(_In_ HMODULE hModule, _In_ LPCSTR lpProcName) { 70 | FARPROC result; 71 | if (hModule == hNtdll && lpProcName != nullptr && strcmp(lpProcName, "RtlGetVersion") == 0) { 72 | result = reinterpret_cast(FakeRtlGetVersion); 73 | SetLastError(0); 74 | } else { 75 | result = GetProcAddress(hModule, lpProcName); 76 | if (result == nullptr) { 77 | ErrnoRestorer errnoRestorer; 78 | if (hModule != nullptr) { 79 | WCHAR buffer[1024] = {}; 80 | GetModuleFileNameW(hModule, buffer, _countof(buffer)); 81 | LOGW(L"-GetProcAddress: hModule=%s(%p), lpProcName=%hs, result=NULL", buffer, hModule, lpProcName); 82 | } else { 83 | LOGW(L"-GetProcAddress: hModule=NULL, lpProcName=%hs, result=NULL", lpProcName ? lpProcName : "NULL"); 84 | } 85 | } 86 | } 87 | return result; 88 | } 89 | 90 | usize kPageSize = 0; 91 | 92 | bool isAddressReadable(void *address) { 93 | if (kPageSize == 0) { 94 | SYSTEM_INFO si = {}; 95 | GetSystemInfo(&si); 96 | kPageSize = si.dwPageSize; 97 | } 98 | void *pageBase = reinterpret_cast(reinterpret_cast(address) & ~(kPageSize - 1)); 99 | MEMORY_BASIC_INFORMATION info = {}; 100 | if (VirtualQuery(pageBase, &info, sizeof(info)) == 0) { 101 | return false; 102 | } 103 | return (info.Protect & PAGE_READONLY) != 0 || (info.Protect & PAGE_READWRITE) != 0; 104 | } 105 | 106 | int HookIATProcedure(HMODULE hModule, LPCSTR procName, FARPROC replacement) { 107 | if (hModule == nullptr || procName == nullptr || replacement == nullptr) { 108 | LOGE(L"HookIATProcedure: invalid arguments: hModule=%p, procName=%hs, replacement=%p", 109 | hModule, procName ? procName : "(NULL)", replacement); 110 | return 0; 111 | } 112 | LOGI(L"HookIATProcedure: start, hModule=%p, procName=%hs, replacement=%p", hModule, procName, replacement); 113 | PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER) hModule; 114 | PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS) ((BYTE *) hModule + pDosHeader->e_lfanew); 115 | PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) 116 | ((BYTE *) hModule + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 117 | int count = 0; 118 | for (; pImportDesc->Name != 0; pImportDesc++) { 119 | PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA) ((BYTE *) hModule + pImportDesc->FirstThunk); 120 | PIMAGE_THUNK_DATA pOrgThunk = (PIMAGE_THUNK_DATA) ((BYTE *) hModule + pImportDesc->OriginalFirstThunk); 121 | for (; pThunk->u1.Function != 0; pThunk++, pOrgThunk++) { 122 | if (IMAGE_SNAP_BY_ORDINAL(pOrgThunk->u1.Ordinal)) { 123 | continue; 124 | } 125 | PIMAGE_IMPORT_BY_NAME pImport = (PIMAGE_IMPORT_BY_NAME) ((BYTE *) hModule + pOrgThunk->u1.AddressOfData); 126 | if (!isAddressReadable((char *) pImport->Name)) { 127 | LOGE(L"HookIATProcedure: pImport->Name is not readable: %p", pImport->Name); 128 | WCHAR buffer[1024] = {}; 129 | GetModuleFileNameW(hModule, buffer, _countof(buffer)); 130 | LOGE(L"HookIATProcedure: abort IAT hook, hModule=%s(%p), procName=%hs, replacement=%p", 131 | buffer, hModule, procName, replacement); 132 | return -1; 133 | } 134 | if (strcmp((char *) pImport->Name, procName) == 0) { 135 | LOGI(L"HookIATProcedure: Found %hs at %p", procName, pThunk); 136 | DWORD oldProtect = 0; 137 | VirtualProtect(pThunk, sizeof(PVOID), PAGE_READWRITE, &oldProtect); 138 | pThunk->u1.Function = (DWORD_PTR) replacement; 139 | VirtualProtect(pThunk, sizeof(PVOID), oldProtect, &oldProtect); 140 | count++; 141 | } 142 | } 143 | } 144 | LOGI(L"HookIATProcedure: end, count=%d", count); 145 | return count; 146 | } 147 | 148 | void checkEnableDebugConsole() { 149 | WCHAR exePath[1024] = {}; 150 | if (GetModuleFileNameW(nullptr, exePath, 1024) != 0) { 151 | std::wstring path = exePath; 152 | auto dirLen = path.find_last_of('\\'); 153 | if (dirLen == std::wstring::npos) { 154 | dirLen = 0; 155 | } 156 | std::wstring enableDebugConsole = path.substr(0, dirLen) + L"\\EnableDebugConsole"; 157 | if (GetFileAttributesW(enableDebugConsole.c_str()) != INVALID_FILE_ATTRIBUTES) { 158 | kDebug = true; 159 | } 160 | } 161 | } 162 | 163 | bool OnLoad(HINSTANCE hInstDLL) { 164 | gSelfInstance = hInstDLL; 165 | hNtdll = GetModuleHandleW(L"ntdll.dll"); 166 | if (hNtdll == nullptr) { 167 | return false; 168 | } 169 | checkEnableDebugConsole(); 170 | hWsaClient = GetModuleHandleW(L"WsaClient.exe"); 171 | if (kDebug) { 172 | if (AllocConsole()) { 173 | gConsoleIsAllocated = true; 174 | } 175 | gConsoleOutput = CreateFileW(L"CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); 176 | if (gConsoleOutput != INVALID_HANDLE_VALUE) { 177 | Log::setLogHandler(&defaultLogHandler); 178 | } else { 179 | std::wstring msg = std::wstring(L"Error CreateFileW CONOUT$, GetLastError=") + std::to_wstring(GetLastError()); 180 | MessageBoxW(nullptr, msg.c_str(), L"wsapatch.dll", MB_OK | MB_ICONERROR); 181 | } 182 | } 183 | LOGD(L"OnLoad, hInstDLL=%p, hNtdll=%p, hWsaClient=%p", hInstDLL, hNtdll, hWsaClient); 184 | if (hWsaClient == nullptr) { 185 | // check if we are loaded into the correct process 186 | WCHAR filename[MAX_PATH]; 187 | GetModuleFileNameW(nullptr, filename, MAX_PATH); 188 | // get lower case filename 189 | for (int i = 0; filename[i] != 0; i++) { 190 | filename[i] = towlower(filename[i]); 191 | } 192 | if (wcsstr(filename, L"\\wsaclinent.exe") == nullptr) { 193 | WCHAR buf[1024] = {}; 194 | StringCbPrintfW(buf, 1024, L"GetModuleHandleW(L\"WsaClient.exe\") is NULL.\nIs wsapatch.dll loaded into wrong process?\n%s", filename); 195 | MessageBoxW(nullptr, buf, L"wsapatch.dll", MB_OK | MB_ICONERROR); 196 | return false; 197 | } 198 | return false; 199 | } 200 | LOGD(L"ntdll.dll = %p", hNtdll); 201 | LOGD(L"WsaClient.exe = %p", hWsaClient); 202 | FuncRtlGetVersion funcRtlGetVersion = reinterpret_cast(GetProcAddress(hNtdll, "RtlGetVersion")); 203 | if (funcRtlGetVersion == nullptr) { 204 | LOGE(L"GetProcAddress(NTDLL.DLL, \"RtlGetVersion\") failed, GetLastError=%d", GetLastError()); 205 | return false; 206 | } 207 | gOsVersionInfo.dwOSVersionInfoSize = sizeof(gOsVersionInfo); 208 | NTSTATUS status = funcRtlGetVersion(reinterpret_cast(&gOsVersionInfo)); 209 | if (!NT_SUCCESS(status)) { 210 | LOGE(L"funcRtlGetVersion(&osVersionInfo) failed, status=%d", status); 211 | return false; 212 | } 213 | LOGD(L"RtlGetVersion: dwMajorVersion=%d, dwMinorVersion=%d, dwBuildNumber=%d, dwPlatformId=%d", 214 | gOsVersionInfo.dwMajorVersion, gOsVersionInfo.dwMinorVersion, gOsVersionInfo.dwBuildNumber, gOsVersionInfo.dwPlatformId); 215 | bool isWin11 = gOsVersionInfo.dwMajorVersion >= 10 && gOsVersionInfo.dwMinorVersion >= 0 && gOsVersionInfo.dwBuildNumber >= 22000; 216 | gIsPatchVersionNumber = !isWin11; 217 | gIsPatchProductType = (gOsVersionInfo.wProductType != VER_NT_WORKSTATION); 218 | if (!gIsPatchVersionNumber && !gIsPatchProductType) { 219 | LOGW(L"Windows 11 workstation detected, no need to patch"); 220 | return true; 221 | } 222 | LOGD(L"Need to patch, gIsPatchVersionNumber=%d, gIsPatchProductType=%d", gIsPatchVersionNumber, gIsPatchProductType); 223 | int count = HookIATProcedure(hWsaClient, "GetProcAddress", reinterpret_cast(&BadGetProcAddress)); 224 | if (count == 0) { 225 | LOGE(L"HookIATProcedure failed, count=%d", count); 226 | return false; 227 | } else { 228 | LOGI(L"HookIATProcedure success, count=%d", count); 229 | } 230 | // auxiliary hooks 231 | // HookIATProcedure(GetModuleHandleW(L"winhttp.dll"), "GetProcAddress", reinterpret_cast(&BadGetProcAddress)); 232 | // HookIATProcedure(GetModuleHandleW(L"icu.dll"), "GetProcAddress", reinterpret_cast(&BadGetProcAddress)); 233 | return true; 234 | } 235 | 236 | void OnUnload() { 237 | HANDLE handle = gConsoleOutput; 238 | if (handle != nullptr && handle != INVALID_HANDLE_VALUE) { 239 | CloseHandle(handle); 240 | if (gConsoleIsAllocated) { 241 | FreeConsole(); 242 | gConsoleIsAllocated = false; 243 | } 244 | gConsoleOutput = INVALID_HANDLE_VALUE; 245 | } 246 | gSelfInstance = nullptr; 247 | } 248 | 249 | } 250 | 251 | EXPORT_C void NS_WsaPatch_UnusedSymbol() { 252 | // nothing 253 | } 254 | 255 | BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) { 256 | // Perform actions based on the reason for calling. 257 | switch (fdwReason) { 258 | case DLL_PROCESS_ATTACH: 259 | return wsapatch::OnLoad(hInstDLL); 260 | case DLL_THREAD_ATTACH: 261 | case DLL_THREAD_DETACH: 262 | // no-op 263 | break; 264 | case DLL_PROCESS_DETACH: 265 | if (lpvReserved != nullptr) { 266 | // do not do cleanup if process termination scenario 267 | break; 268 | } 269 | wsapatch::OnUnload(); 270 | break; 271 | default: 272 | break; 273 | } 274 | return true; 275 | } 276 | -------------------------------------------------------------------------------- /WsaPatch.h: -------------------------------------------------------------------------------- 1 | #ifndef WSAPATCH_WSAPATCH_H 2 | #define WSAPATCH_WSAPATCH_H 3 | 4 | #include "macros.h" 5 | 6 | EXPORT_C void NS_WsaPatch_UnusedSymbol(); 7 | 8 | #endif //WSAPATCH_WSAPATCH_H 9 | -------------------------------------------------------------------------------- /macros.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef uint8_t u8; 4 | typedef uint16_t u16; 5 | typedef uint32_t u32; 6 | typedef uint64_t u64; 7 | 8 | typedef int8_t s8; 9 | typedef int16_t s16; 10 | typedef int32_t s32; 11 | typedef int64_t s64; 12 | 13 | typedef size_t usize; 14 | typedef ptrdiff_t isize; 15 | 16 | typedef uintptr_t uintptr; 17 | 18 | #ifdef __cplusplus 19 | #define EXTERN_C extern "C" 20 | #else 21 | #define EXTERN_C 22 | #endif 23 | 24 | 25 | #if defined(_MSC_VER) 26 | #define EXPORT __declspec(dllexport) 27 | #elif defined(__GNUC__) 28 | #define EXPORT __attribute__((visibility("default"), used)) 29 | #else 30 | #error "Unsupported compiler" 31 | #endif 32 | 33 | #define EXPORT_C EXTERN_C EXPORT 34 | -------------------------------------------------------------------------------- /original.dll.win11.22h2/x86_64/icu.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cinit/WSAPatch/bf77c3df600d44f8ca5a7eb17f84f3005fbc830d/original.dll.win11.22h2/x86_64/icu.dll -------------------------------------------------------------------------------- /original.dll.win11.22h2/x86_64/winhttp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cinit/WSAPatch/bf77c3df600d44f8ca5a7eb17f84f3005fbc830d/original.dll.win11.22h2/x86_64/winhttp.dll -------------------------------------------------------------------------------- /pic/screenshot_20221202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cinit/WSAPatch/bf77c3df600d44f8ca5a7eb17f84f3005fbc830d/pic/screenshot_20221202.png --------------------------------------------------------------------------------