├── .gitignore ├── README.md ├── Release_dlls ├── SteamAPICheckBypass.dll └── SteamAPICheckBypass_x32.dll ├── SteamAPICheckBypass.EXAMPLE.json ├── SteamAPICheckBypass.sln ├── SteamAPICheckBypass ├── dll_interface.vcxproj ├── dll_interface.vcxproj.filters ├── dllmain.cpp ├── exports.cpp ├── exports.def ├── exports.h ├── include │ └── nt_file_dupe.hpp ├── src │ └── nt_file_dupe.cpp ├── version.asm ├── winhttp.asm └── winmm.asm └── nt_file_dupe ├── README.md ├── include ├── Configs │ └── Configs.hpp ├── Helpers │ ├── Helpers.hpp │ └── dbglog.hpp ├── Hooks │ ├── Hooks.hpp │ ├── LdrGetDllHandle_hook │ │ └── LdrGetDllHandle_hook.hpp │ ├── LdrLoadDll_hook │ │ └── LdrLoadDll_hook.hpp │ ├── NtCreateDirectoryObjectEx_hook │ │ └── NtCreateDirectoryObjectEx_hook.hpp │ ├── NtCreateDirectoryObject_hook │ │ └── NtCreateDirectoryObject_hook.hpp │ ├── NtCreateFile_hook │ │ └── NtCreateFile_hook.hpp │ ├── NtDeleteFile_hook │ │ └── NtDeleteFile_hook.hpp │ ├── NtOpenDirectoryObject_hook │ │ └── NtOpenDirectoryObject_hook.hpp │ ├── NtOpenFile_hook │ │ └── NtOpenFile_hook.hpp │ ├── NtQueryAttributesFile_hook │ │ └── NtQueryAttributesFile_hook.hpp │ ├── NtQueryDirectoryFileEx_hook │ │ └── NtQueryDirectoryFileEx_hook.hpp │ ├── NtQueryDirectoryFile_hook │ │ └── NtQueryDirectoryFile_hook.hpp │ ├── NtQueryFullAttributesFile_hook │ │ └── NtQueryFullAttributesFile_hook.hpp │ ├── NtQueryInformationByName_hook │ │ └── NtQueryInformationByName_hook.hpp │ └── NtQueryInformationFile_hook │ │ └── NtQueryInformationFile_hook.hpp ├── NtApis │ ├── NtApis.hpp │ └── peb_helper.hpp └── lib_main │ └── lib_main.hpp ├── libs ├── Detours │ ├── CREDITS.md │ ├── LICENSE │ ├── LICENSE.md │ ├── README.md │ ├── SECURITY.md │ ├── creatwth.cpp │ ├── detours.cpp │ ├── detours.h │ ├── detver.h │ ├── disasm.cpp │ ├── disolarm.cpp │ ├── disolarm64.cpp │ ├── disolia64.cpp │ ├── disolx64.cpp │ ├── disolx86.cpp │ ├── image.cpp │ ├── modules.cpp │ └── uimports.cpp ├── Json │ └── nlohmann │ │ ├── LICENSE.MIT │ │ ├── README.md │ │ ├── json.hpp │ │ └── json_fwd.hpp └── README.md ├── nt_file_dupe.vcxproj ├── nt_file_dupe.vcxproj.filters └── src ├── Configs └── Configs.cpp ├── Helpers └── dbglog.cpp ├── Hooks ├── Hooks.cpp ├── LdrGetDllHandle_hook │ └── LdrGetDllHandle_hook.cpp ├── LdrLoadDll_hook │ └── LdrLoadDll_hook.cpp ├── NtCreateDirectoryObjectEx_hook │ └── NtCreateDirectoryObjectEx_hook.cpp ├── NtCreateDirectoryObject_hook │ └── NtCreateDirectoryObject_hook.cpp ├── NtCreateFile_hook │ └── NtCreateFile_hook.cpp ├── NtDeleteFile_hook │ └── NtDeleteFile_hook.cpp ├── NtOpenDirectoryObject_hook │ └── NtOpenDirectoryObject_hook.cpp ├── NtOpenFile_hook │ └── NtOpenFile_hook.cpp ├── NtQueryAttributesFile_hook │ └── NtQueryAttributesFile_hook.cpp ├── NtQueryDirectoryFileEx_hook │ └── NtQueryDirectoryFileEx_hook.cpp ├── NtQueryDirectoryFile_hook │ └── NtQueryDirectoryFile_hook.cpp ├── NtQueryFullAttributesFile_hook │ └── NtQueryFullAttributesFile_hook.cpp ├── NtQueryInformationByName_hook │ └── NtQueryInformationByName_hook.cpp └── NtQueryInformationFile_hook │ └── NtQueryInformationFile_hook.cpp ├── NtApis ├── NtApis.cpp └── peb_helper.cpp └── lib_main └── lib_main.cpp /.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/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Steam API Check Bypass 2 | 3 | Bypasses Steam API dll integrity/size check by hooking CreateFile API. 4 | 5 | ## Build 6 | 7 | * Build with Visual Studio 2022. 8 | 9 | ## Usage 10 | 11 | * If you are using x32 version please use `_x32` version dll. 12 | 13 | ### Method 1 14 | 15 | * Use CFF Explorer and add any dll import to game main exe, then put the `SteamAPICheckBypass(_x32).dll` dll beside game exe. 16 | 17 | ### Method 2 (VersionShim) (x64 for pre-built version.dll) 18 | 19 | * Rename `SteamAPICheckBypass(_x32).dll` to `version.dll`/`winmm.dll`/`winhttp.dll` and put it beside game exe. 20 | 21 | ## Configuration (Optional) 22 | 23 | * Create `SteamAPICheckBypass.json` and write file names you want to replace. Example: 24 | 25 | ```json 26 | { 27 | "steam_api64.dll": 28 | { 29 | "mode": "file_redirect", 30 | "to": "steam_api64.dll.bak", 31 | "hook_times_mode": "nth_time_only", 32 | "hook_time_n": [1,2,3], 33 | "bypass_loadlibrary": true 34 | }, 35 | "game.exe": 36 | { 37 | "mode": "file_redirect", 38 | "to": "game.exe.bak", 39 | "hook_times_mode": "nth_time_only", 40 | "hook_time_n": [1,2,3] 41 | } 42 | } 43 | ``` 44 | 45 | * `mode`: `file_redirect` or `file_hide`. 46 | * `to` : The target file relative path. 47 | * `hook_times_mode`: `all`, `nth_time_only` or `not_nth_time_only`. 48 | * `hook_time_n`: The list of nth time to hook / not hook. (Start from 1, LoadLibrary triggered hook is not counted) 49 | * `bypass_loadlibrary`: Bypass loadlibrary caused file read request. 50 | * The `hook time` option is useful for custom requirements. 51 | 52 | ## Internal Process 53 | 54 | * Please refer to [nt_file_dupe Readme](nt_file_dupe/README.md) for more information. 55 | 56 | ## Dependencies 57 | 58 | * 59 | 60 | ## Bugs 61 | 62 | If You Have Any Bugs, Please Submit a Issue On Github. 63 | cs.rin.ru thread: 64 | 65 | ## Donate 66 | 67 | Bitcoin: bc1qk39k55wxwx8yj4w35qu4vh3x4nhn90kq2mempn 68 | Bitcoin Cash: qzlc8qv59y5lssm9rct2rq5puznqpv9s4qhvhw3k7j 69 | Ethereum: 0xCBEF4582Fd0d049F3eBB7552027848f54C99cb38 70 | Stellar: GCMQOHLH6I6QZKCFXWS6VKTDHABFK5KA3CQIZW5JL6GBQYSDCFF5VL2E 71 | -------------------------------------------------------------------------------- /Release_dlls/SteamAPICheckBypass.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteamAutoCracks/Steam-API-Check-Bypass/5072cc81d928ba401eef33859ac541bf524b1d89/Release_dlls/SteamAPICheckBypass.dll -------------------------------------------------------------------------------- /Release_dlls/SteamAPICheckBypass_x32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SteamAutoCracks/Steam-API-Check-Bypass/5072cc81d928ba401eef33859ac541bf524b1d89/Release_dlls/SteamAPICheckBypass_x32.dll -------------------------------------------------------------------------------- /SteamAPICheckBypass.EXAMPLE.json: -------------------------------------------------------------------------------- 1 | { 2 | "steam_api64.dll": 3 | { 4 | "mode": "file_redirect", 5 | "to": "steam_api64.dll.bak", 6 | "hook_times_mode": "nth_time_only", 7 | "hook_time_n": [1,2,3], 8 | "bypass_loadlibrary": true 9 | }, 10 | "game.exe": 11 | { 12 | "mode": "file_redirect", 13 | "to": "game.exe.bak", 14 | "hook_times_mode": "nth_time_only", 15 | "hook_time_n": [1,2,3] 16 | } 17 | } -------------------------------------------------------------------------------- /SteamAPICheckBypass.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34202.233 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nt_file_dupe", "nt_file_dupe\nt_file_dupe.vcxproj", "{22E4EBEB-B621-4252-AE46-665835385D64}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dll_interface", "SteamAPICheckBypass\dll_interface.vcxproj", "{02A9F0E8-F3C9-407E-A7DC-CDC42430E626}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {22E4EBEB-B621-4252-AE46-665835385D64}.Debug|x64.ActiveCfg = Debug|x64 19 | {22E4EBEB-B621-4252-AE46-665835385D64}.Debug|x64.Build.0 = Debug|x64 20 | {22E4EBEB-B621-4252-AE46-665835385D64}.Debug|x86.ActiveCfg = Debug|Win32 21 | {22E4EBEB-B621-4252-AE46-665835385D64}.Debug|x86.Build.0 = Debug|Win32 22 | {22E4EBEB-B621-4252-AE46-665835385D64}.Release|x64.ActiveCfg = Release|x64 23 | {22E4EBEB-B621-4252-AE46-665835385D64}.Release|x64.Build.0 = Release|x64 24 | {22E4EBEB-B621-4252-AE46-665835385D64}.Release|x86.ActiveCfg = Release|Win32 25 | {22E4EBEB-B621-4252-AE46-665835385D64}.Release|x86.Build.0 = Release|Win32 26 | {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Debug|x64.ActiveCfg = Debug|x64 27 | {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Debug|x64.Build.0 = Debug|x64 28 | {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Debug|x86.ActiveCfg = Debug|Win32 29 | {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Debug|x86.Build.0 = Debug|Win32 30 | {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Release|x64.ActiveCfg = Release|x64 31 | {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Release|x64.Build.0 = Release|x64 32 | {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Release|x86.ActiveCfg = Release|Win32 33 | {02A9F0E8-F3C9-407E-A7DC-CDC42430E626}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {6AF09F83-CD97-4040-8F0E-DFFC4102F137} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /SteamAPICheckBypass/dll_interface.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {ebf2f168-126a-4813-8eaa-662709fb8740} 6 | 7 | 8 | 9 | 10 | Interface 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Interface 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /SteamAPICheckBypass/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | 4 | #include "lib_main/lib_main.hpp" 5 | #include "Helpers/Helpers.hpp" 6 | #include "Configs/Configs.hpp" 7 | 8 | #include 9 | #include 10 | 11 | #include "exports.h" 12 | 13 | std::vector initial_files = { 14 | // the current name of the dll is added here as a first entry 15 | L"steamapicheckbypass.json", 16 | L"steamapicheckbypass_config.json", 17 | L"SteamAPICheckBypass.json", 18 | L"SteamAPICheckBypass_config.json", 19 | L"nt_file_dupe.json", 20 | L"nt_file_dupe_config.json", 21 | L"nt_fs_dupe.json", 22 | L"nt_fs_dupe_config.json", 23 | L"nt_dupe.json", 24 | L"nt_dupe_config.json", 25 | }; 26 | 27 | void add_original_entries(const std::wstring& original_path) { 28 | std::wstring _original_path = ntfsdupe::helpers::to_absolute(original_path, ntfsdupe::cfgs::get_exe_dir()); 29 | std::filesystem::path path(_original_path); 30 | std::wstring extension = path.extension().wstring(); 31 | std::wstring stem = path.stem().wstring(); 32 | std::wstring parent_path = path.parent_path().wstring(); 33 | 34 | ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_redirect, _original_path, parent_path + L"\\" + stem + extension + L".bak", true); 35 | ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_redirect, _original_path, parent_path + L"\\" + stem + L".org", true); 36 | ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_redirect, _original_path, parent_path + L"\\" + stem + L"_o" + extension, true); 37 | } 38 | 39 | BOOL APIENTRY DllMain( 40 | HMODULE hModule, 41 | DWORD ul_reason_for_call, 42 | LPVOID lpReserved 43 | ) 44 | { 45 | switch (ul_reason_for_call) { 46 | case DLL_PROCESS_ATTACH: { 47 | if (!ntfsdupe::init()) return FALSE; 48 | 49 | std::wstring my_path_str(ntfsdupe::helpers::get_module_fullpath(hModule)); 50 | if (my_path_str.empty()) return FALSE; 51 | 52 | // hide ourself (on disk) 53 | if (!ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_hide, my_path_str)) return FALSE; 54 | 55 | auto my_path = std::filesystem::path(my_path_str); 56 | 57 | // trick to anti FreeLibrary 58 | GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_PIN, my_path.filename().string().c_str(), &hModule); 59 | 60 | // hide ourself (in memory) 61 | //if (!ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::module_hide_handle, my_path.filename().wstring())) return FALSE; 62 | 63 | std::wstring stem_lower = my_path.stem().wstring(); 64 | std::transform(stem_lower.begin(), stem_lower.end(), stem_lower.begin(), ::towlower); 65 | if (stem_lower != L"version" && stem_lower != L"winmm" && stem_lower != L"winhttp") { 66 | if (!ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::module_hide_handle, stem_lower)) return FALSE; 67 | } 68 | 69 | // add .json to the list 70 | initial_files.insert(initial_files.begin(), my_path.stem().wstring() + L".json"); 71 | 72 | // try to load some files by default 73 | auto my_dir = my_path.parent_path(); 74 | bool found_cfg_file = false; 75 | for (const auto &file : initial_files) { 76 | auto cfg_file = (my_dir / file).wstring(); 77 | if (ntfsdupe::cfgs::load_file(cfg_file.c_str())) { 78 | found_cfg_file = true; 79 | // hiding this file isn't really critical, right? 80 | ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_hide, cfg_file); 81 | break; 82 | } 83 | } 84 | 85 | if (!found_cfg_file) 86 | { 87 | // hide exe 88 | add_original_entries(ntfsdupe::helpers::get_module_fullpath(nullptr)); 89 | 90 | // hide steam_api.dll 91 | add_original_entries(L"steam_api.dll"); 92 | 93 | // hide steam_api64.dll 94 | add_original_entries(L"steam_api64.dll"); 95 | } 96 | 97 | } 98 | break; 99 | 100 | case DLL_THREAD_ATTACH: 101 | case DLL_THREAD_DETACH: 102 | break; 103 | 104 | case DLL_PROCESS_DETACH: 105 | ntfsdupe::deinit(); 106 | break; 107 | } 108 | 109 | return TRUE; 110 | } 111 | 112 | bool TlsOnce = false; 113 | void __stdcall TlsCallback(PVOID hModule, DWORD fdwReason, PVOID pContext) 114 | { 115 | if (!TlsOnce) 116 | { 117 | Load(); 118 | } 119 | } 120 | 121 | //linker spec 122 | #ifdef _M_IX86 123 | #pragma comment (linker, "/INCLUDE:__tls_used") 124 | #pragma comment (linker, "/INCLUDE:__tls_callback") 125 | #else 126 | #pragma comment (linker, "/INCLUDE:_tls_used") 127 | #pragma comment (linker, "/INCLUDE:_tls_callback") 128 | #endif 129 | EXTERN_C 130 | #ifdef _M_X64 131 | #pragma const_seg (".CRT$XLB") 132 | const 133 | #else 134 | #pragma data_seg (".CRT$XLB") 135 | #endif 136 | //end linker 137 | PIMAGE_TLS_CALLBACK _tls_callback = TlsCallback; 138 | 139 | #pragma data_seg () 140 | #pragma const_seg () -------------------------------------------------------------------------------- /SteamAPICheckBypass/exports.cpp: -------------------------------------------------------------------------------- 1 | #include "exports.h" 2 | #include 3 | #include 4 | 5 | FARPROC OriginalFuncs_version[17]; 6 | FARPROC OriginalFuncs_winmm[180]; 7 | FARPROC OriginalFuncs_winhttp[91]; 8 | 9 | enum DllType { 10 | DLL_VERSION, 11 | DLL_WINMM, 12 | DLL_WINHTTP, 13 | DLL_UNKNOWN 14 | }; 15 | 16 | DllType GetCurrentDllType() { 17 | char modulePath[MAX_PATH] = {0}; 18 | HMODULE hModule = NULL; 19 | 20 | GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | 21 | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 22 | (LPCTSTR)GetCurrentDllType, &hModule); 23 | 24 | GetModuleFileNameA(hModule, modulePath, MAX_PATH); 25 | 26 | char* fileName = strrchr(modulePath, '\\'); 27 | if (fileName) { 28 | fileName++; 29 | if (_stricmp(fileName, "version.dll") == 0) { 30 | return DLL_VERSION; 31 | } else if (_stricmp(fileName, "winmm.dll") == 0) { 32 | return DLL_WINMM; 33 | } else if (_stricmp(fileName, "winhttp.dll") == 0) { 34 | return DLL_WINMM; 35 | } 36 | } 37 | 38 | return DLL_UNKNOWN; 39 | } 40 | 41 | void Load() 42 | { 43 | DllType currentDll = GetCurrentDllType(); 44 | char szSystemDirectory[MAX_PATH] = {0}; 45 | GetSystemDirectoryA(szSystemDirectory, MAX_PATH); 46 | 47 | if (currentDll == DLL_VERSION) { 48 | char OriginalPath[MAX_PATH] = {0}; 49 | snprintf(OriginalPath, MAX_PATH, "%s\\version.dll", szSystemDirectory); 50 | 51 | HMODULE version = LoadLibraryA(OriginalPath); 52 | if (!version) { 53 | MessageBoxA(NULL, "Failed to load version.dll from system32\n", "Error", MB_ICONERROR); 54 | return; 55 | } 56 | 57 | for (int i = 0; i < 17; i++) { 58 | OriginalFuncs_version[i] = GetProcAddress(version, ExportNames_version[i]); 59 | if (!OriginalFuncs_version[i]) 60 | { 61 | continue; 62 | } 63 | } 64 | } 65 | else if (currentDll == DLL_WINMM) { 66 | char OriginalPath[MAX_PATH] = {0}; 67 | snprintf(OriginalPath, MAX_PATH, "%s\\winmm.dll", szSystemDirectory); 68 | 69 | HMODULE winmm = LoadLibraryA(OriginalPath); 70 | if (!winmm) { 71 | MessageBoxA(NULL, "Failed to load winmm.dll from system32\n", "Error", MB_ICONERROR); 72 | return; 73 | } 74 | 75 | for (int i = 0; i < 180; i++) { 76 | OriginalFuncs_winmm[i] = GetProcAddress(winmm, ExportNames_winmm[i]); 77 | if (!OriginalFuncs_winmm[i]) { 78 | continue; 79 | } 80 | } 81 | } 82 | else if (currentDll == DLL_WINHTTP) { 83 | char OriginalPath[MAX_PATH] = { 0 }; 84 | snprintf(OriginalPath, MAX_PATH, "%s\\winhttp.dll", szSystemDirectory); 85 | 86 | HMODULE winmm = LoadLibraryA(OriginalPath); 87 | if (!winmm) { 88 | MessageBoxA(NULL, "Failed to load winhttp.dll from system32\n", "Error", MB_ICONERROR); 89 | return; 90 | } 91 | 92 | for (int i = 0; i < 91; i++) { 93 | OriginalFuncs_winmm[i] = GetProcAddress(winmm, ExportNames_winmm[i]); 94 | if (!OriginalFuncs_winmm[i]) { 95 | continue; 96 | } 97 | } 98 | } 99 | else { 100 | return; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /SteamAPICheckBypass/exports.def: -------------------------------------------------------------------------------- 1 | LIBRARY SteamAPICheckBypass 2 | EXPORTS 3 | GetFileVersionInfoA 4 | GetFileVersionInfoByHandle 5 | GetFileVersionInfoExA 6 | GetFileVersionInfoExW 7 | GetFileVersionInfoSizeA 8 | GetFileVersionInfoSizeExA 9 | GetFileVersionInfoSizeExW 10 | GetFileVersionInfoSizeW 11 | GetFileVersionInfoW 12 | VerFindFileA 13 | VerFindFileW 14 | VerInstallFileA 15 | VerInstallFileW 16 | VerLanguageNameA 17 | VerLanguageNameW 18 | VerQueryValueA 19 | VerQueryValueW 20 | CloseDriver 21 | DefDriverProc 22 | DriverCallback 23 | DrvGetModuleHandle 24 | GetDriverModuleHandle 25 | OpenDriver 26 | PlaySound 27 | PlaySoundA 28 | PlaySoundW 29 | SendDriverMessage 30 | WOWAppExit 31 | auxGetDevCapsA 32 | auxGetDevCapsW 33 | auxGetNumDevs 34 | auxGetVolume 35 | auxOutMessage 36 | auxSetVolume 37 | joyConfigChanged 38 | joyGetDevCapsA 39 | joyGetDevCapsW 40 | joyGetNumDevs 41 | joyGetPos 42 | joyGetPosEx 43 | joyGetThreshold 44 | joyReleaseCapture 45 | joySetCapture 46 | joySetThreshold 47 | mciDriverNotify 48 | mciDriverYield 49 | mciExecute 50 | mciFreeCommandResource 51 | mciGetCreatorTask 52 | mciGetDeviceIDA 53 | mciGetDeviceIDFromElementIDA 54 | mciGetDeviceIDFromElementIDW 55 | mciGetDeviceIDW 56 | mciGetDriverData 57 | mciGetErrorStringA 58 | mciGetErrorStringW 59 | mciGetYieldProc 60 | mciLoadCommandResource 61 | mciSendCommandA 62 | mciSendCommandW 63 | mciSendStringA 64 | mciSendStringW 65 | mciSetDriverData 66 | mciSetYieldProc 67 | midiConnect 68 | midiDisconnect 69 | midiInAddBuffer 70 | midiInClose 71 | midiInGetDevCapsA 72 | midiInGetDevCapsW 73 | midiInGetErrorTextA 74 | midiInGetErrorTextW 75 | midiInGetID 76 | midiInGetNumDevs 77 | midiInMessage 78 | midiInOpen 79 | midiInPrepareHeader 80 | midiInReset 81 | midiInStart 82 | midiInStop 83 | midiInUnprepareHeader 84 | midiOutCacheDrumPatches 85 | midiOutCachePatches 86 | midiOutClose 87 | midiOutGetDevCapsA 88 | midiOutGetDevCapsW 89 | midiOutGetErrorTextA 90 | midiOutGetErrorTextW 91 | midiOutGetID 92 | midiOutGetNumDevs 93 | midiOutGetVolume 94 | midiOutLongMsg 95 | midiOutMessage 96 | midiOutOpen 97 | midiOutPrepareHeader 98 | midiOutReset 99 | midiOutSetVolume 100 | midiOutShortMsg 101 | midiOutUnprepareHeader 102 | midiStreamClose 103 | midiStreamOpen 104 | midiStreamOut 105 | midiStreamPause 106 | midiStreamPosition 107 | midiStreamProperty 108 | midiStreamRestart 109 | midiStreamStop 110 | mixerClose 111 | mixerGetControlDetailsA 112 | mixerGetControlDetailsW 113 | mixerGetDevCapsA 114 | mixerGetDevCapsW 115 | mixerGetID 116 | mixerGetLineControlsA 117 | mixerGetLineControlsW 118 | mixerGetLineInfoA 119 | mixerGetLineInfoW 120 | mixerGetNumDevs 121 | mixerMessage 122 | mixerOpen 123 | mixerSetControlDetails 124 | mmDrvInstall 125 | mmGetCurrentTask 126 | mmTaskBlock 127 | mmTaskCreate 128 | mmTaskSignal 129 | mmTaskYield 130 | mmioAdvance 131 | mmioAscend 132 | mmioClose 133 | mmioCreateChunk 134 | mmioDescend 135 | mmioFlush 136 | mmioGetInfo 137 | mmioInstallIOProcA 138 | mmioInstallIOProcW 139 | mmioOpenA 140 | mmioOpenW 141 | mmioRead 142 | mmioRenameA 143 | mmioRenameW 144 | mmioSeek 145 | mmioSendMessage 146 | mmioSetBuffer 147 | mmioSetInfo 148 | mmioStringToFOURCCA 149 | mmioStringToFOURCCW 150 | mmioWrite 151 | mmsystemGetVersion 152 | sndPlaySoundA 153 | sndPlaySoundW 154 | timeBeginPeriod 155 | timeEndPeriod 156 | timeGetDevCaps 157 | timeGetSystemTime 158 | timeGetTime 159 | timeKillEvent 160 | timeSetEvent 161 | waveInAddBuffer 162 | waveInClose 163 | waveInGetDevCapsA 164 | waveInGetDevCapsW 165 | waveInGetErrorTextA 166 | waveInGetErrorTextW 167 | waveInGetID 168 | waveInGetNumDevs 169 | waveInGetPosition 170 | waveInMessage 171 | waveInOpen 172 | waveInPrepareHeader 173 | waveInReset 174 | waveInStart 175 | waveInStop 176 | waveInUnprepareHeader 177 | waveOutBreakLoop 178 | waveOutClose 179 | waveOutGetDevCapsA 180 | waveOutGetDevCapsW 181 | waveOutGetErrorTextA 182 | waveOutGetErrorTextW 183 | waveOutGetID 184 | waveOutGetNumDevs 185 | waveOutGetPitch 186 | waveOutGetPlaybackRate 187 | waveOutGetPosition 188 | waveOutGetVolume 189 | waveOutMessage 190 | waveOutOpen 191 | waveOutPause 192 | waveOutPrepareHeader 193 | waveOutReset 194 | waveOutRestart 195 | waveOutSetPitch 196 | waveOutSetPlaybackRate 197 | waveOutSetVolume 198 | waveOutUnprepareHeader 199 | waveOutWrite 200 | DllCanUnloadNow 201 | DllGetClassObject 202 | Private1 203 | SvchostPushServiceGlobals 204 | WinHttpAddRequestHeaders 205 | WinHttpAddRequestHeadersEx 206 | WinHttpAutoProxySvcMain 207 | WinHttpCheckPlatform 208 | WinHttpCloseHandle 209 | WinHttpConnect 210 | WinHttpConnectionDeletePolicyEntries 211 | WinHttpConnectionDeleteProxyInfo 212 | WinHttpConnectionFreeNameList 213 | WinHttpConnectionFreeProxyInfo 214 | WinHttpConnectionFreeProxyList 215 | WinHttpConnectionGetNameList 216 | WinHttpConnectionGetProxyInfo 217 | WinHttpConnectionGetProxyList 218 | WinHttpConnectionOnlyConvert 219 | WinHttpConnectionOnlyReceive 220 | WinHttpConnectionOnlySend 221 | WinHttpConnectionSetPolicyEntries 222 | WinHttpConnectionSetProxyInfo 223 | WinHttpConnectionUpdateIfIndexTable 224 | WinHttpCrackUrl 225 | WinHttpCreateProxyList 226 | WinHttpCreateProxyManager 227 | WinHttpCreateProxyResolver 228 | WinHttpCreateProxyResult 229 | WinHttpCreateUiCompatibleProxyString 230 | WinHttpCreateUrl 231 | WinHttpDetectAutoProxyConfigUrl 232 | WinHttpFreeProxyResult 233 | WinHttpFreeProxyResultEx 234 | WinHttpFreeProxySettings 235 | WinHttpFreeProxySettingsEx 236 | WinHttpFreeQueryConnectionGroupResult 237 | WinHttpGetDefaultProxyConfiguration 238 | WinHttpGetIEProxyConfigForCurrentUser 239 | WinHttpGetProxyForUrl 240 | WinHttpGetProxyForUrlEx 241 | WinHttpGetProxyForUrlEx2 242 | WinHttpGetProxyForUrlHvsi 243 | WinHttpGetProxyResult 244 | WinHttpGetProxyResultEx 245 | WinHttpGetProxySettingsEx 246 | WinHttpGetProxySettingsResultEx 247 | WinHttpGetProxySettingsVersion 248 | WinHttpGetTunnelSocket 249 | WinHttpOpen 250 | WinHttpOpenRequest 251 | WinHttpPacJsWorkerMain 252 | WinHttpProbeConnectivity 253 | WinHttpProtocolCompleteUpgrade 254 | WinHttpProtocolReceive 255 | WinHttpProtocolSend 256 | WinHttpQueryAuthSchemes 257 | WinHttpQueryConnectionGroup 258 | WinHttpQueryDataAvailable 259 | WinHttpQueryHeaders 260 | WinHttpQueryHeadersEx 261 | WinHttpQueryOption 262 | WinHttpReadData 263 | WinHttpReadDataEx 264 | WinHttpReadProxySettings 265 | WinHttpReadProxySettingsHvsi 266 | WinHttpReceiveResponse 267 | WinHttpRefreshProxySettings 268 | WinHttpRegisterProxyChangeNotification 269 | WinHttpResetAutoProxy 270 | WinHttpResolverGetProxyForUrl 271 | WinHttpSaveProxyCredentials 272 | WinHttpSendRequest 273 | WinHttpSetCredentials 274 | WinHttpSetDefaultProxyConfiguration 275 | WinHttpSetOption 276 | WinHttpSetProxySettingsPerUser 277 | WinHttpSetSecureLegacyServersAppCompat 278 | WinHttpSetStatusCallback 279 | WinHttpSetTimeouts 280 | WinHttpTimeFromSystemTime 281 | WinHttpTimeToSystemTime 282 | WinHttpUnregisterProxyChangeNotification 283 | WinHttpWebSocketClose 284 | WinHttpWebSocketCompleteUpgrade 285 | WinHttpWebSocketQueryCloseStatus 286 | WinHttpWebSocketReceive 287 | WinHttpWebSocketSend 288 | WinHttpWebSocketShutdown 289 | WinHttpWriteData 290 | WinHttpWriteProxySettings 291 | ntfsdupe_add_entry 292 | ntfsdupe_load_file 293 | ntfsdupe_deinit -------------------------------------------------------------------------------- /SteamAPICheckBypass/exports.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | extern "C" FARPROC OriginalFuncs_version[17]; 5 | extern "C" FARPROC OriginalFuncs_winmm[180]; 6 | extern "C" FARPROC OriginalFuncs_winhttp[91]; 7 | 8 | inline LPCSTR ExportNames_version[] = { 9 | "GetFileVersionInfoA", 10 | "GetFileVersionInfoByHandle", 11 | "GetFileVersionInfoExA", 12 | "GetFileVersionInfoExW", 13 | "GetFileVersionInfoSizeA", 14 | "GetFileVersionInfoSizeExA", 15 | "GetFileVersionInfoSizeExW", 16 | "GetFileVersionInfoSizeW", 17 | "GetFileVersionInfoW", 18 | "VerFindFileA", 19 | "VerFindFileW", 20 | "VerInstallFileA", 21 | "VerInstallFileW", 22 | "VerLanguageNameA", 23 | "VerLanguageNameW", 24 | "VerQueryValueA", 25 | "VerQueryValueW" 26 | }; 27 | 28 | inline LPCSTR ExportNames_winmm[] = { 29 | "CloseDriver", 30 | "DefDriverProc", 31 | "DriverCallback", 32 | "DrvGetModuleHandle", 33 | "GetDriverModuleHandle", 34 | "OpenDriver", 35 | "PlaySound", 36 | "PlaySoundA", 37 | "PlaySoundW", 38 | "SendDriverMessage", 39 | "WOWAppExit", 40 | "auxGetDevCapsA", 41 | "auxGetDevCapsW", 42 | "auxGetNumDevs", 43 | "auxGetVolume", 44 | "auxOutMessage", 45 | "auxSetVolume", 46 | "joyConfigChanged", 47 | "joyGetDevCapsA", 48 | "joyGetDevCapsW", 49 | "joyGetNumDevs", 50 | "joyGetPos", 51 | "joyGetPosEx", 52 | "joyGetThreshold", 53 | "joyReleaseCapture", 54 | "joySetCapture", 55 | "joySetThreshold", 56 | "mciDriverNotify", 57 | "mciDriverYield", 58 | "mciExecute", 59 | "mciFreeCommandResource", 60 | "mciGetCreatorTask", 61 | "mciGetDeviceIDA", 62 | "mciGetDeviceIDFromElementIDA", 63 | "mciGetDeviceIDFromElementIDW", 64 | "mciGetDeviceIDW", 65 | "mciGetDriverData", 66 | "mciGetErrorStringA", 67 | "mciGetErrorStringW", 68 | "mciGetYieldProc", 69 | "mciLoadCommandResource", 70 | "mciSendCommandA", 71 | "mciSendCommandW", 72 | "mciSendStringA", 73 | "mciSendStringW", 74 | "mciSetDriverData", 75 | "mciSetYieldProc", 76 | "midiConnect", 77 | "midiDisconnect", 78 | "midiInAddBuffer", 79 | "midiInClose", 80 | "midiInGetDevCapsA", 81 | "midiInGetDevCapsW", 82 | "midiInGetErrorTextA", 83 | "midiInGetErrorTextW", 84 | "midiInGetID", 85 | "midiInGetNumDevs", 86 | "midiInMessage", 87 | "midiInOpen", 88 | "midiInPrepareHeader", 89 | "midiInReset", 90 | "midiInStart", 91 | "midiInStop", 92 | "midiInUnprepareHeader", 93 | "midiOutCacheDrumPatches", 94 | "midiOutCachePatches", 95 | "midiOutClose", 96 | "midiOutGetDevCapsA", 97 | "midiOutGetDevCapsW", 98 | "midiOutGetErrorTextA", 99 | "midiOutGetErrorTextW", 100 | "midiOutGetID", 101 | "midiOutGetNumDevs", 102 | "midiOutGetVolume", 103 | "midiOutLongMsg", 104 | "midiOutMessage", 105 | "midiOutOpen", 106 | "midiOutPrepareHeader", 107 | "midiOutReset", 108 | "midiOutSetVolume", 109 | "midiOutShortMsg", 110 | "midiOutUnprepareHeader", 111 | "midiStreamClose", 112 | "midiStreamOpen", 113 | "midiStreamOut", 114 | "midiStreamPause", 115 | "midiStreamPosition", 116 | "midiStreamProperty", 117 | "midiStreamRestart", 118 | "midiStreamStop", 119 | "mixerClose", 120 | "mixerGetControlDetailsA", 121 | "mixerGetControlDetailsW", 122 | "mixerGetDevCapsA", 123 | "mixerGetDevCapsW", 124 | "mixerGetID", 125 | "mixerGetLineControlsA", 126 | "mixerGetLineControlsW", 127 | "mixerGetLineInfoA", 128 | "mixerGetLineInfoW", 129 | "mixerGetNumDevs", 130 | "mixerMessage", 131 | "mixerOpen", 132 | "mixerSetControlDetails", 133 | "mmDrvInstall", 134 | "mmGetCurrentTask", 135 | "mmTaskBlock", 136 | "mmTaskCreate", 137 | "mmTaskSignal", 138 | "mmTaskYield", 139 | "mmioAdvance", 140 | "mmioAscend", 141 | "mmioClose", 142 | "mmioCreateChunk", 143 | "mmioDescend", 144 | "mmioFlush", 145 | "mmioGetInfo", 146 | "mmioInstallIOProcA", 147 | "mmioInstallIOProcW", 148 | "mmioOpenA", 149 | "mmioOpenW", 150 | "mmioRead", 151 | "mmioRenameA", 152 | "mmioRenameW", 153 | "mmioSeek", 154 | "mmioSendMessage", 155 | "mmioSetBuffer", 156 | "mmioSetInfo", 157 | "mmioStringToFOURCCA", 158 | "mmioStringToFOURCCW", 159 | "mmioWrite", 160 | "mmsystemGetVersion", 161 | "sndPlaySoundA", 162 | "sndPlaySoundW", 163 | "timeBeginPeriod", 164 | "timeEndPeriod", 165 | "timeGetDevCaps", 166 | "timeGetSystemTime", 167 | "timeGetTime", 168 | "timeKillEvent", 169 | "timeSetEvent", 170 | "waveInAddBuffer", 171 | "waveInClose", 172 | "waveInGetDevCapsA", 173 | "waveInGetDevCapsW", 174 | "waveInGetErrorTextA", 175 | "waveInGetErrorTextW", 176 | "waveInGetID", 177 | "waveInGetNumDevs", 178 | "waveInGetPosition", 179 | "waveInMessage", 180 | "waveInOpen", 181 | "waveInPrepareHeader", 182 | "waveInReset", 183 | "waveInStart", 184 | "waveInStop", 185 | "waveInUnprepareHeader", 186 | "waveOutBreakLoop", 187 | "waveOutClose", 188 | "waveOutGetDevCapsA", 189 | "waveOutGetDevCapsW", 190 | "waveOutGetErrorTextA", 191 | "waveOutGetErrorTextW", 192 | "waveOutGetID", 193 | "waveOutGetNumDevs", 194 | "waveOutGetPitch", 195 | "waveOutGetPlaybackRate", 196 | "waveOutGetPosition", 197 | "waveOutGetVolume", 198 | "waveOutMessage", 199 | "waveOutOpen", 200 | "waveOutPause", 201 | "waveOutPrepareHeader", 202 | "waveOutReset", 203 | "waveOutRestart", 204 | "waveOutSetPitch", 205 | "waveOutSetPlaybackRate", 206 | "waveOutSetVolume", 207 | "waveOutUnprepareHeader", 208 | "waveOutWrite", 209 | }; 210 | 211 | inline LPCSTR ExportNames_winhttp[] = { 212 | "DllCanUnloadNow", 213 | "DllGetClassObject", 214 | "Private1", 215 | "SvchostPushServiceGlobals", 216 | "WinHttpAddRequestHeaders", 217 | "WinHttpAddRequestHeadersEx", 218 | "WinHttpAutoProxySvcMain", 219 | "WinHttpCheckPlatform", 220 | "WinHttpCloseHandle", 221 | "WinHttpConnect", 222 | "WinHttpConnectionDeletePolicyEntries", 223 | "WinHttpConnectionDeleteProxyInfo", 224 | "WinHttpConnectionFreeNameList", 225 | "WinHttpConnectionFreeProxyInfo", 226 | "WinHttpConnectionFreeProxyList", 227 | "WinHttpConnectionGetNameList", 228 | "WinHttpConnectionGetProxyInfo", 229 | "WinHttpConnectionGetProxyList", 230 | "WinHttpConnectionOnlyConvert", 231 | "WinHttpConnectionOnlyReceive", 232 | "WinHttpConnectionOnlySend", 233 | "WinHttpConnectionSetPolicyEntries", 234 | "WinHttpConnectionSetProxyInfo", 235 | "WinHttpConnectionUpdateIfIndexTable", 236 | "WinHttpCrackUrl", 237 | "WinHttpCreateProxyList", 238 | "WinHttpCreateProxyManager", 239 | "WinHttpCreateProxyResolver", 240 | "WinHttpCreateProxyResult", 241 | "WinHttpCreateUiCompatibleProxyString", 242 | "WinHttpCreateUrl", 243 | "WinHttpDetectAutoProxyConfigUrl", 244 | "WinHttpFreeProxyResult", 245 | "WinHttpFreeProxyResultEx", 246 | "WinHttpFreeProxySettings", 247 | "WinHttpFreeProxySettingsEx", 248 | "WinHttpFreeQueryConnectionGroupResult", 249 | "WinHttpGetDefaultProxyConfiguration", 250 | "WinHttpGetIEProxyConfigForCurrentUser", 251 | "WinHttpGetProxyForUrl", 252 | "WinHttpGetProxyForUrlEx", 253 | "WinHttpGetProxyForUrlEx2", 254 | "WinHttpGetProxyForUrlHvsi", 255 | "WinHttpGetProxyResult", 256 | "WinHttpGetProxyResultEx", 257 | "WinHttpGetProxySettingsEx", 258 | "WinHttpGetProxySettingsResultEx", 259 | "WinHttpGetProxySettingsVersion", 260 | "WinHttpGetTunnelSocket", 261 | "WinHttpOpen", 262 | "WinHttpOpenRequest", 263 | "WinHttpPacJsWorkerMain", 264 | "WinHttpProbeConnectivity", 265 | "WinHttpProtocolCompleteUpgrade", 266 | "WinHttpProtocolReceive", 267 | "WinHttpProtocolSend", 268 | "WinHttpQueryAuthSchemes", 269 | "WinHttpQueryConnectionGroup", 270 | "WinHttpQueryDataAvailable", 271 | "WinHttpQueryHeaders", 272 | "WinHttpQueryHeadersEx", 273 | "WinHttpQueryOption", 274 | "WinHttpReadData", 275 | "WinHttpReadDataEx", 276 | "WinHttpReadProxySettings", 277 | "WinHttpReadProxySettingsHvsi", 278 | "WinHttpReceiveResponse", 279 | "WinHttpRefreshProxySettings", 280 | "WinHttpRegisterProxyChangeNotification", 281 | "WinHttpResetAutoProxy", 282 | "WinHttpResolverGetProxyForUrl", 283 | "WinHttpSaveProxyCredentials", 284 | "WinHttpSendRequest", 285 | "WinHttpSetCredentials", 286 | "WinHttpSetDefaultProxyConfiguration", 287 | "WinHttpSetOption", 288 | "WinHttpSetProxySettingsPerUser", 289 | "WinHttpSetSecureLegacyServersAppCompat", 290 | "WinHttpSetStatusCallback", 291 | "WinHttpSetTimeouts", 292 | "WinHttpTimeFromSystemTime", 293 | "WinHttpTimeToSystemTime", 294 | "WinHttpUnregisterProxyChangeNotification", 295 | "WinHttpWebSocketClose", 296 | "WinHttpWebSocketCompleteUpgrade", 297 | "WinHttpWebSocketQueryCloseStatus", 298 | "WinHttpWebSocketReceive", 299 | "WinHttpWebSocketSend", 300 | "WinHttpWebSocketShutdown", 301 | "WinHttpWriteData", 302 | "WinHttpWriteProxySettings" 303 | }; 304 | 305 | void Load(); -------------------------------------------------------------------------------- /SteamAPICheckBypass/include/nt_file_dupe.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #if defined(NTFSDUPE_EXPORTS) 5 | #define NTFSDUPE_API extern "C" 6 | #else 7 | #define NTFSDUPE_API extern "C" __declspec(dllimport) 8 | #endif 9 | 10 | #if defined(_WIN32) 11 | #define NTFSDUPE_DECL __stdcall 12 | #else 13 | #define NTFSDUPE_DECL __fastcall 14 | #endif 15 | #include 16 | 17 | 18 | // ntfsdupe::itf works for language >= C++17 19 | namespace ntfsdupe { 20 | namespace itf { 21 | // mirror of ntfsdupe::cfgs::Mode 22 | // to avoid including .hpp files from the static lib 23 | enum class Mode : char { 24 | file_redirect, 25 | file_hide, 26 | 27 | module_prevent_load, 28 | module_redirect, 29 | module_hide_handle, 30 | }; 31 | enum class HookTimesMode : char { 32 | all, 33 | nth_time_only, 34 | not_nth_time_only, 35 | }; 36 | } 37 | } 38 | 39 | NTFSDUPE_API bool NTFSDUPE_DECL ntfsdupe_add_entry( 40 | ntfsdupe::itf::Mode mode, 41 | const wchar_t *original, 42 | const wchar_t *target, 43 | bool file_must_exist, 44 | bool bypass_loadlibrary, 45 | ntfsdupe::itf::HookTimesMode hook_times_cfg, 46 | std::vector hook_time_n 47 | ); 48 | 49 | NTFSDUPE_API bool NTFSDUPE_DECL ntfsdupe_load_file(const wchar_t *file); 50 | 51 | NTFSDUPE_API void NTFSDUPE_DECL ntfsdupe_deinit(); 52 | -------------------------------------------------------------------------------- /SteamAPICheckBypass/src/nt_file_dupe.cpp: -------------------------------------------------------------------------------- 1 | #include "nt_file_dupe.hpp" 2 | 3 | #include "lib_main/lib_main.hpp" 4 | #include "Configs/Configs.hpp" 5 | #include "Helpers/Helpers.hpp" 6 | #include "Hooks/Hooks.hpp" 7 | 8 | #include 9 | #include 10 | 11 | 12 | bool NTFSDUPE_DECL ntfsdupe_add_entry( 13 | ntfsdupe::itf::Mode mode, 14 | const wchar_t *original, 15 | const wchar_t *target, 16 | bool file_must_exist, 17 | bool bypass_loadlibrary, 18 | ntfsdupe::itf::HookTimesMode hook_times_cfg, 19 | std::vector hook_time_n 20 | ) 21 | { 22 | if (!original || !original[0]) return false; 23 | 24 | std::wstring _target = target && target[0] 25 | ? std::wstring(target) 26 | : std::wstring(); 27 | return ntfsdupe::cfgs::add_entry((ntfsdupe::cfgs::Mode)mode, original, _target, file_must_exist, bypass_loadlibrary, (ntfsdupe::cfgs::HookTimesMode)hook_times_cfg, hook_time_n); 28 | } 29 | 30 | bool NTFSDUPE_DECL ntfsdupe_load_file(const wchar_t *file) 31 | { 32 | return ntfsdupe::cfgs::load_file(file); 33 | } 34 | 35 | void NTFSDUPE_DECL ntfsdupe_deinit() 36 | { 37 | ntfsdupe::deinit(); 38 | } 39 | -------------------------------------------------------------------------------- /SteamAPICheckBypass/version.asm: -------------------------------------------------------------------------------- 1 | ifdef RAX 2 | .code 3 | extern OriginalFuncs_version:QWORD 4 | GetFileVersionInfoA proc 5 | jmp QWORD ptr OriginalFuncs_version[0 * 8] 6 | GetFileVersionInfoA endp 7 | GetFileVersionInfoByHandle proc 8 | jmp QWORD ptr OriginalFuncs_version[1 * 8] 9 | GetFileVersionInfoByHandle endp 10 | GetFileVersionInfoExA proc 11 | jmp QWORD ptr OriginalFuncs_version[2 * 8] 12 | GetFileVersionInfoExA endp 13 | GetFileVersionInfoExW proc 14 | jmp QWORD ptr OriginalFuncs_version[3 * 8] 15 | GetFileVersionInfoExW endp 16 | GetFileVersionInfoSizeA proc 17 | jmp QWORD ptr OriginalFuncs_version[4 * 8] 18 | GetFileVersionInfoSizeA endp 19 | GetFileVersionInfoSizeExA proc 20 | jmp QWORD ptr OriginalFuncs_version[5 * 8] 21 | GetFileVersionInfoSizeExA endp 22 | GetFileVersionInfoSizeExW proc 23 | jmp QWORD ptr OriginalFuncs_version[6 * 8] 24 | GetFileVersionInfoSizeExW endp 25 | GetFileVersionInfoSizeW proc 26 | jmp QWORD ptr OriginalFuncs_version[7 * 8] 27 | GetFileVersionInfoSizeW endp 28 | GetFileVersionInfoW proc 29 | jmp QWORD ptr OriginalFuncs_version[8 * 8] 30 | GetFileVersionInfoW endp 31 | VerFindFileA proc 32 | jmp QWORD ptr OriginalFuncs_version[9 * 8] 33 | VerFindFileA endp 34 | VerFindFileW proc 35 | jmp QWORD ptr OriginalFuncs_version[10 * 8] 36 | VerFindFileW endp 37 | VerInstallFileA proc 38 | jmp QWORD ptr OriginalFuncs_version[11 * 8] 39 | VerInstallFileA endp 40 | VerInstallFileW proc 41 | jmp QWORD ptr OriginalFuncs_version[12 * 8] 42 | VerInstallFileW endp 43 | VerLanguageNameA proc 44 | jmp QWORD ptr OriginalFuncs_version[13 * 8] 45 | VerLanguageNameA endp 46 | VerLanguageNameW proc 47 | jmp QWORD ptr OriginalFuncs_version[14 * 8] 48 | VerLanguageNameW endp 49 | VerQueryValueA proc 50 | jmp QWORD ptr OriginalFuncs_version[15 * 8] 51 | VerQueryValueA endp 52 | VerQueryValueW proc 53 | jmp QWORD ptr OriginalFuncs_version[16 * 8] 54 | VerQueryValueW endp 55 | else 56 | .model flat, C 57 | .stack 4096 58 | .code 59 | extern OriginalFuncs_version:DWORD 60 | GetFileVersionInfoA proc 61 | jmp DWORD ptr OriginalFuncs_version[0 * 4] 62 | GetFileVersionInfoA endp 63 | GetFileVersionInfoByHandle proc 64 | jmp DWORD ptr OriginalFuncs_version[1 * 4] 65 | GetFileVersionInfoByHandle endp 66 | GetFileVersionInfoExA proc 67 | jmp DWORD ptr OriginalFuncs_version[2 * 4] 68 | GetFileVersionInfoExA endp 69 | GetFileVersionInfoExW proc 70 | jmp DWORD ptr OriginalFuncs_version[3 * 4] 71 | GetFileVersionInfoExW endp 72 | GetFileVersionInfoSizeA proc 73 | jmp DWORD ptr OriginalFuncs_version[4 * 4] 74 | GetFileVersionInfoSizeA endp 75 | GetFileVersionInfoSizeExA proc 76 | jmp DWORD ptr OriginalFuncs_version[5 * 4] 77 | GetFileVersionInfoSizeExA endp 78 | GetFileVersionInfoSizeExW proc 79 | jmp DWORD ptr OriginalFuncs_version[6 * 4] 80 | GetFileVersionInfoSizeExW endp 81 | GetFileVersionInfoSizeW proc 82 | jmp DWORD ptr OriginalFuncs_version[7 * 4] 83 | GetFileVersionInfoSizeW endp 84 | GetFileVersionInfoW proc 85 | jmp DWORD ptr OriginalFuncs_version[8 * 4] 86 | GetFileVersionInfoW endp 87 | VerFindFileA proc 88 | jmp DWORD ptr OriginalFuncs_version[9 * 4] 89 | VerFindFileA endp 90 | VerFindFileW proc 91 | jmp DWORD ptr OriginalFuncs_version[10 * 4] 92 | VerFindFileW endp 93 | VerInstallFileA proc 94 | jmp DWORD ptr OriginalFuncs_version[11 * 4] 95 | VerInstallFileA endp 96 | VerInstallFileW proc 97 | jmp DWORD ptr OriginalFuncs_version[12 * 4] 98 | VerInstallFileW endp 99 | VerLanguageNameA proc 100 | jmp DWORD ptr OriginalFuncs_version[13 * 4] 101 | VerLanguageNameA endp 102 | VerLanguageNameW proc 103 | jmp DWORD ptr OriginalFuncs_version[14 * 4] 104 | VerLanguageNameW endp 105 | VerQueryValueA proc 106 | jmp DWORD ptr OriginalFuncs_version[15 * 4] 107 | VerQueryValueA endp 108 | VerQueryValueW proc 109 | jmp DWORD ptr OriginalFuncs_version[16 * 4] 110 | VerQueryValueW endp 111 | endif 112 | end -------------------------------------------------------------------------------- /nt_file_dupe/README.md: -------------------------------------------------------------------------------- 1 | # Nt Filesystem Dupe 2 | A library for file & module redirection and hiding, by hooking various Nt APIs. 3 | This project is inspired by `CODEX` Steam emu and based on reversing it. Credits to them. 4 | 5 | --- 6 | 7 | ## Solution structure 8 | The solution is divided into 3 projects: 9 | 1. `nt_file_dupe`: The actual library, this is a static library (`.lib`) 10 | 2. `dll_interface`: A thin wrapper `.dll` project around the static library, exporting the necessary functions 11 | 3. `testxxx`: A simple console app to test the library + helpers 12 | 13 | ## JSON file format: 14 | ```json 15 | { 16 | "myfile.txt": { 17 | "mode": "file_redirect", 18 | "to": "myfile.org", 19 | "file_must_exist": true 20 | }, 21 | "path/myfile_22.txt": { 22 | "mode": "file_redirect", 23 | "to": "path/myfile_22.org", 24 | }, 25 | "../../folder/some_file.txt": { 26 | "mode": "file_redirect", 27 | "to": "../../folder/some_file.org", 28 | }, 29 | 30 | "hideme.txt": { 31 | "mode": "file_hide" 32 | }, 33 | "../hideme_22.txt": { 34 | "mode": "file_hide" 35 | }, 36 | 37 | "prevent_me.dll": { 38 | "mode": "module_prevent_load" 39 | }, 40 | "prevent_me": { 41 | "mode": "module_prevent_load" 42 | }, 43 | 44 | "my_module_org.dll": { 45 | "mode": "module_redirect", 46 | "to": "my_module_mod.dll" 47 | }, 48 | "my_module_org": { 49 | "mode": "module_redirect", 50 | "to": "my_module_mod" 51 | } 52 | } 53 | ``` 54 | Each JSON key is considered the *original* file, the value/object for that key defines the action for the original file. 55 | The entry type is determined by the `mode` JSON key. 56 | * `mode` 57 | 58 | --- 59 | 60 | - `file_redirect` 61 | Redirect original file creation/opening `to` a target file. 62 | Target files are always hidden. 63 | - `file_hide` 64 | Hide the file, as if it doesn't exist on disk. 65 | 66 | --- 67 | 68 | - `module_prevent_load` 69 | Prevent loading the module via `LoadLibrary()` and its variants. 70 | - `module_redirect` 71 | Redirect the loading operation `to` another target module. 72 | Target modules are always hidden and cannot be loaded by the process. 73 | - `module_hide_handle` 74 | Prevent `GetModuleHandle()` from succeeding, this won't affect `LoadLibrary()` and its variants. 75 | As if the module doesn't exist in the current process memory. 76 | 77 | * `to` 78 | Defines which target file/module to redirect the original file/module to. 79 | Only useful when `mode` is: 80 | - `file_redirect` 81 | - `module_redirect` 82 | 83 | * `file_must_exist` (default = `false`) 84 | When set to `true`, the JSON entry will be skipped if the original file doesn't exist, or the target file doesn't exist in `file_redirect` mode 85 | 86 | Check the example [sample.json](./example/sample.json) 87 | 88 | ## Behavior 89 | In case this is a **file** entry, the paths to the original or target files could be absolute or relative. 90 | Relative paths will be relative to the **location of the current `.exe`**, not the current directory. 91 | 92 | In case this is a **module** entry, the paths to the original or target modules must be just their filenames. 93 | 94 | Both the original and target files/modules must have the same filename length. 95 | Additionally, if this is a **file** entry, they must exist in the same dir. 96 | 97 | Target files/modules are hidden by default, for example, in the JSON file defined above: 98 | * `myfile.org` Will be hidden and cannot be opened/created 99 | * `my_module_mod.dll` Will be hidden and cannot be loaded 100 | 101 | If `file_must_exist` = `true`, then this JSON entry will be ignored without an error if: 102 | * The original file was missing 103 | * The `mode` = `file_redirect` and the target file, defined by the JSON key `to`, is missing 104 | 105 | The dll will try to load only one of the following files upon startup in that order: 106 | * A JSON file with the same name as the `.dll` itself 107 | * `nt_file_dupe.json` 108 | * `nt_file_dupe_config.json` 109 | * `nt_fs_dupe.json` 110 | * `nt_fs_dupe_config.json` 111 | * `nt_dupe.json` 112 | * `nt_dupe_config.json` 113 | 114 | Any of these files must exist **beside** the `.dll`, not the current running `.exe` 115 | 116 | Upon startup, the dll will try to hide itself, both on disk and in memory. 117 | Additionally in the debug build, it will try to hide the log file on disk. 118 | 119 | ## How use the pre-built .dll: 120 | 1. Create a `.json` file with some entries as shown above 121 | 2. Rename the `.json` file to the same name of the `.dll`, for example if the `.dll` is called `nt_file_dupe.dll`, then the `.json` file must be named `nt_file_dupe.json` 122 | 3. Place both the `.dll` and the `.json` files beside each other in the same folder 123 | 4. Load the `.dll` inside your target, either modify the imports table with something like `CFF Explorer`, or use any dll loader/injector 124 | 125 | 126 | Note that the functions to add entries are not thread safe. 127 | 128 | 129 | ## How to link and use as a static lib (.lib): 130 | 1. Open the Visual Studio solution file `nt_file_dupe.sln` and build the project `nt_file_dupe`. 131 | Make sure to select the right architecture (`x64` or `x86`) and build type (`release` or `debug`) 132 | 2. Assuming for example you've selected `Debug | x64`, the library will be built in 133 | ```batch 134 | bin\x64\nt_file_dupe\nt_file_dupe_static.lib 135 | ``` 136 | 3. In your own Visual Studio project, you must use C++ language version >= `C++17`, 137 | add the static `.lib` file as an input to the linker: [.lib files as linker input](https://learn.microsoft.com/en-us/cpp/build/reference/dot-lib-files-as-linker-input#to-add-lib-files-as-linker-input-in-the-development-environment) 138 | 4. Finally, add the folder `nt_file_dupe\include` as an extra include directory: [Additional include directories](https://learn.microsoft.com/en-us/cpp/build/reference/i-additional-include-directories#to-set-this-compiler-option-in-the-visual-studio-development-environment) 139 | 5. Everything will be under the namespace `ntfsdupe::` 140 | 6. Check how the `.dll` wrapper project is importing the required `.hpp` files, and using the library in its [DllMain](./dll_interface/dllmain.cpp) 141 | 142 | ## How to link and use as a dynamic lib (.dll): 143 | 1. Open the Visual Studio solution file `nt_file_dupe.sln` and build the project `dll_interface`. 144 | Make sure to select the right architecture (`x64` or `x86`) and build type (`release` or `debug`) 145 | 2. Assuming for example you've selected `Debug | x64`, the library will be built as 2 parts in 146 | ```batch 147 | bin\Debug\x64\dll_interface\nt_file_dupe.dll 148 | bin\Debug\x64\dll_interface\nt_file_dupe.lib 149 | ``` 150 | Notice that the `.dll` file must be accompanied by a small-sized `.lib` file which we'll use next 151 | 3. In your own Visual Studio project, you must use C++ language version >= `C++17`, 152 | add the static `.lib` file as an input to the linker: [.lib files as linker input](https://learn.microsoft.com/en-us/cpp/build/reference/dot-lib-files-as-linker-input#to-add-lib-files-as-linker-input-in-the-development-environment) 153 | 4. Finally, add the folder `dll_interface\include` as an extra include directory: [Additional include directories](https://learn.microsoft.com/en-us/cpp/build/reference/i-additional-include-directories#to-set-this-compiler-option-in-the-visual-studio-development-environment) 154 | 5. All available exports will have this prefix `ntfsdupe_` 155 | ```c++ 156 | #include "nt_file_dupe.hpp" 157 | 158 | int main() { 159 | ntfsdupe_load_file(L"myfile.json"); 160 | ntfsdupe_add_entry(ntfsdupe::itf::Mode::file_hide, L"some_file.dll", nullptr); 161 | return 0; 162 | } 163 | ``` 164 | 6. The file `nt_file_dupe.dll` will be added to your imports table, so make sure to copy this `.dll` file beside your project's build output 165 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Configs/Configs.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace ntfsdupe::cfgs { 8 | // input from file 9 | enum class Mode : char { 10 | file_redirect, 11 | file_hide, 12 | 13 | module_prevent_load, 14 | module_redirect, 15 | module_hide_handle, 16 | }; 17 | 18 | enum class HookTimesMode : char { 19 | all, 20 | nth_time_only, 21 | not_nth_time_only, 22 | }; 23 | 24 | struct HookTimes { 25 | // only works when mode = file_redirect 26 | HookTimesMode mode{ HookTimesMode::all }; 27 | std::vector hook_time_n {}; 28 | }; 29 | 30 | // file operation/action type 31 | enum class FileType : char { 32 | original, // current file to redirect (file.dll) 33 | target, // target file after redirection (file.org) 34 | 35 | hide, // hide this file 36 | }; 37 | 38 | struct FileCfgEntry { 39 | FileType mode{}; 40 | 41 | // strong ref holder 42 | std::wstring original{}; 43 | const wchar_t *original_filename{}; 44 | 45 | // strong ref holder 46 | std::wstring target{}; 47 | const wchar_t *target_filename{}; 48 | 49 | unsigned short filename_bytes{}; 50 | 51 | HookTimes hook_times{ }; 52 | 53 | bool bypass_loadlibrary{ true }; // bypass LoadLibrary() for the file 54 | }; 55 | 56 | // module operation/action type 57 | enum class ModuleType : char { 58 | prevent_load, // prevent dynamic load via LoadLibrary() 59 | 60 | // --- both used when mode = redirect 61 | original, // current file to redirect (file.dll) 62 | target, // target file after redirection (file.org) 63 | // --- 64 | 65 | hide_handle, // prevent GetModuleHandle(), but allow LoadLibrary() 66 | }; 67 | 68 | struct ModuleCfgEntry { 69 | ModuleType mode{}; 70 | 71 | std::wstring original_filename{}; 72 | std::wstring target_filename{}; 73 | 74 | unsigned short filename_bytes{}; 75 | 76 | HookTimes hook_times{ }; 77 | }; 78 | 79 | 80 | bool init(); 81 | 82 | void deinit(); 83 | 84 | const std::wstring& get_exe_dir() noexcept; 85 | 86 | bool add_entry(Mode mode, const std::wstring &original, const std::wstring &target = std::wstring(), bool file_must_exist = false, bool bypass_loadlibrary = true, HookTimesMode hook_times_cfg = HookTimesMode::all, std::vector hook_time_n = std::vector()); 87 | 88 | bool load_file(const wchar_t *file); 89 | 90 | const FileCfgEntry* find_file_entry(const std::wstring_view &str) noexcept; 91 | 92 | const ModuleCfgEntry* find_module_entry(const std::wstring_view &str) noexcept; 93 | 94 | void add_bypass(const std::wstring_view &str) noexcept; 95 | 96 | void remove_bypass(const std::wstring_view &str) noexcept; 97 | 98 | bool is_bypassed(const std::wstring_view &str) noexcept; 99 | 100 | bool is_count_bypass(const ntfsdupe::cfgs::FileCfgEntry* cfg) noexcept; 101 | 102 | bool is_count_bypass(const ntfsdupe::cfgs::ModuleCfgEntry* cfg) noexcept; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Helpers/Helpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define WIN32_LEAN_AND_MEAN 10 | #include 11 | 12 | #define unique_ptr_stack(Type, size) \ 13 | std::unique_ptr( \ 14 | (Type*)_malloca(size), \ 15 | [](void* const p) -> void { _freea(p); }) 16 | 17 | #define null_unique_ptr_stack(Type) \ 18 | std::unique_ptr(nullptr, nullptr) 19 | 20 | namespace ntfsdupe::helpers 21 | { 22 | __forceinline static std::wstring upper(const std::wstring& wstr) 23 | { 24 | std::wstring _wstr(wstr); 25 | std::transform( 26 | _wstr.begin(), _wstr.end(), _wstr.begin(), 27 | [](auto c) { return c == L'/' ? L'\\' : std::toupper(c); } 28 | ); 29 | 30 | return _wstr; 31 | } 32 | 33 | __forceinline static std::wstring str_to_wstr(const std::string &str) 34 | { 35 | if (str.empty()) return {}; 36 | 37 | int chars = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), nullptr, 0); 38 | if (!chars) return {}; 39 | 40 | std::wstring wstr(chars, 0); 41 | chars = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &wstr[0], (int)wstr.size()); 42 | if (!chars) return {}; 43 | 44 | return wstr; 45 | } 46 | 47 | __forceinline static bool file_exist(const std::filesystem::path &filepath) 48 | { 49 | if (std::filesystem::is_directory(filepath)) return false; 50 | else if (std::filesystem::exists(filepath)) return true; 51 | 52 | return false; 53 | } 54 | 55 | __forceinline static bool file_exist(const std::string &filepath) 56 | { 57 | if (filepath.empty()) return false; 58 | 59 | return file_exist(std::filesystem::u8path(filepath)); 60 | } 61 | 62 | __forceinline static bool file_exist(const std::wstring &filepath) 63 | { 64 | if (filepath.empty()) return false; 65 | 66 | return file_exist(std::filesystem::path(filepath)); 67 | } 68 | 69 | __forceinline static std::filesystem::path to_absolute(const std::filesystem::path &path, const std::filesystem::path &base) 70 | { 71 | if (path.is_absolute()) return path; 72 | 73 | return std::filesystem::absolute(base / path); 74 | } 75 | 76 | __forceinline static std::wstring to_absolute(const std::wstring& path, const std::wstring& base) 77 | { 78 | if (path.empty()) return path; 79 | auto path_abs = to_absolute( 80 | std::filesystem::path(path), 81 | base.empty() ? std::filesystem::current_path() : std::filesystem::path(base) 82 | ); 83 | return path_abs.wstring(); 84 | } 85 | 86 | __forceinline static wchar_t* upper(wchar_t* wstr, int count) 87 | { 88 | if (!wstr || !wstr[0] || !count) return wstr; 89 | 90 | while (count) { 91 | *wstr = (wchar_t)std::toupper(*wstr); 92 | wstr++; 93 | count--; 94 | } 95 | return wstr; 96 | } 97 | 98 | __forceinline static std::wstring get_module_fullpath(HMODULE hModule) 99 | { 100 | size_t chars = 512; 101 | std::wstring mod_path{}; 102 | do { 103 | chars *= 2; 104 | mod_path.resize(chars); 105 | // from docs: 106 | // The string returned will use the same format that was specified when the module was loaded. 107 | // Therefore, the path can be a long or short file name, and can use the prefix "\?" 108 | chars = GetModuleFileNameW(hModule, &mod_path[0], (DWORD)mod_path.size()); 109 | if (!chars) return {}; 110 | } while (chars == mod_path.size()); 111 | if (!chars) return {}; 112 | 113 | return mod_path.substr(0, chars); 114 | } 115 | 116 | __forceinline static std::wstring get_current_module_fullpath() 117 | { 118 | HMODULE hModule = GetModuleHandleW(nullptr); 119 | if (!hModule) return {}; 120 | return get_module_fullpath(hModule); 121 | } 122 | 123 | __forceinline static std::wstring get_module_name(HMODULE hModule) 124 | { 125 | std::wstring full_path = get_module_fullpath(hModule); 126 | if (full_path.empty()) return {}; 127 | 128 | std::filesystem::path path(full_path); 129 | return path.filename().wstring(); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Helpers/dbglog.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if !defined(NT_FS_DUPE_RELEASE) 4 | 5 | #include 6 | 7 | namespace ntfsdupe::helpers::dbglog { 8 | 9 | bool init(); 10 | 11 | void write(const wchar_t *fmt, ...); 12 | void write(const std::wstring &str); 13 | void write(const std::string &str); 14 | 15 | void close(); 16 | 17 | } 18 | 19 | #define NTFSDUPE_DBG_INIT() ntfsdupe::helpers::dbglog::init() 20 | #define NTFSDUPE_DBG_CLOSE() ntfsdupe::helpers::dbglog::close() 21 | #define NTFSDUPE_DBG(fmt, ...) ntfsdupe::helpers::dbglog::write(fmt, __VA_ARGS__) 22 | 23 | #else 24 | 25 | #define NTFSDUPE_DBG_INIT() 26 | #define NTFSDUPE_DBG_CLOSE() 27 | #define NTFSDUPE_DBG(...) 28 | 29 | #endif // NT_FS_DUPE_RELEASE 30 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/LdrGetDllHandle_hook/LdrGetDllHandle_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | // https://doxygen.reactos.org/d7/d55/ldrapi_8c.html#a048f061547bbe72e1b884b368cf526a2 5 | // https://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FExecutable%20Images%2FLdrGetDllHandle.html 6 | NTSTATUS NTAPI LdrGetDllHandle_hook( 7 | _In_opt_ PWSTR DllPath, 8 | _In_opt_ PULONG DllCharacteristics, 9 | _In_ PUNICODE_STRING DllName, 10 | _Out_ PHANDLE DllHandle 11 | ); 12 | 13 | extern decltype(LdrGetDllHandle_hook) *LdrGetDllHandle_original; 14 | } 15 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/LdrLoadDll_hook/LdrLoadDll_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | // https://doxygen.reactos.org/d7/d55/ldrapi_8c.html#a8838a6bd5ee2987045215ee7129c3a2c 5 | // https://github.com/wine-mirror/wine/blob/86557b9e0ba8a783f1b0d0918b1ddec7e0a7749e/dlls/ntdll/loader.c#L3372 6 | // Note: both docs seem incorrect, the first arg is the dll flags from LoadLibraryEx() | 1 7 | // ex: LOAD_LIBRARY_SEARCH_SYSTEM32 | 1 8 | // also on x64, first arg is sometimes used as ecx (4 bytes) and sometimes rcx (8 bytes) 9 | NTSTATUS NTAPI LdrLoadDll_hook( 10 | __in_opt LPVOID DllCharacteristics, 11 | __in_opt LPDWORD Unknown, 12 | __in PUNICODE_STRING DllName, 13 | _Out_ HMODULE* BaseAddress 14 | ); 15 | 16 | extern decltype(LdrLoadDll_hook) *LdrLoadDll_original; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtCreateDirectoryObjectEx_hook/NtCreateDirectoryObjectEx_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks 4 | { 5 | // https://googleprojectzero.blogspot.com/2016/08/ 6 | // https://docs.rs/ntapi/latest/ntapi/ntobapi/fn.NtCreateDirectoryObjectEx.html 7 | NTSTATUS NTAPI NtCreateDirectoryObjectEx_hook( 8 | __out PHANDLE DirectoryHandle, 9 | __in ACCESS_MASK DesiredAccess, 10 | __in POBJECT_ATTRIBUTES ObjectAttributes, 11 | __in_opt HANDLE ShadowDirectoryHandle, 12 | __in ULONG Flags 13 | ); 14 | 15 | extern decltype(NtCreateDirectoryObjectEx_hook) *NtCreateDirectoryObjectEx_original; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtCreateDirectoryObject_hook/NtCreateDirectoryObject_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtCreateDirectoryObject_hook( 5 | __out PHANDLE DirectoryHandle, 6 | __in ACCESS_MASK DesiredAccess, 7 | __in POBJECT_ATTRIBUTES ObjectAttributes 8 | ); 9 | 10 | extern decltype(NtCreateDirectoryObject_hook) *NtCreateDirectoryObject_original; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtCreateFile_hook/NtCreateFile_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtCreateFile_hook( 5 | __out PHANDLE FileHandle, 6 | __in ACCESS_MASK DesiredAccess, 7 | __in POBJECT_ATTRIBUTES ObjectAttributes, 8 | __out PIO_STATUS_BLOCK IoStatusBlock, 9 | __in_opt PLARGE_INTEGER AllocationSize, 10 | __in ULONG FileAttributes, 11 | __in ULONG ShareAccess, 12 | __in ULONG CreateDisposition, 13 | __in ULONG CreateOptions, 14 | __in_opt PVOID EaBuffer, 15 | __in ULONG EaLength 16 | ); 17 | 18 | extern decltype(NtCreateFile_hook) *NtCreateFile_original; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtDeleteFile_hook/NtDeleteFile_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtDeleteFile_hook( 5 | __in POBJECT_ATTRIBUTES ObjectAttributes 6 | ); 7 | 8 | extern decltype(NtDeleteFile_hook) *NtDeleteFile_original; 9 | } 10 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtOpenDirectoryObject_hook/NtOpenDirectoryObject_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtOpenDirectoryObject_hook( 5 | __out PHANDLE DirectoryHandle, 6 | __in ACCESS_MASK DesiredAccess, 7 | __in POBJECT_ATTRIBUTES ObjectAttributes 8 | ); 9 | 10 | extern decltype(NtOpenDirectoryObject_hook) *NtOpenDirectoryObject_original; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtOpenFile_hook/NtOpenFile_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtOpenFile_hook( 5 | __out PHANDLE FileHandle, 6 | __in ACCESS_MASK DesiredAccess, 7 | __in POBJECT_ATTRIBUTES ObjectAttributes, 8 | __out PIO_STATUS_BLOCK IoStatusBlock, 9 | __in ULONG ShareAccess, 10 | __in ULONG OpenOptions 11 | ); 12 | 13 | extern decltype(NtOpenFile_hook) *NtOpenFile_original; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtQueryAttributesFile_hook/NtQueryAttributesFile_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_basic_information 5 | typedef struct _FILE_BASIC_INFORMATION { 6 | LARGE_INTEGER CreationTime; 7 | LARGE_INTEGER LastAccessTime; 8 | LARGE_INTEGER LastWriteTime; 9 | LARGE_INTEGER ChangeTime; 10 | ULONG FileAttributes; 11 | } FILE_BASIC_INFORMATION, * PFILE_BASIC_INFORMATION; 12 | 13 | NTSTATUS NTAPI NtQueryAttributesFile_hook( 14 | __in POBJECT_ATTRIBUTES ObjectAttributes, 15 | __out PFILE_BASIC_INFORMATION FileInformation 16 | ); 17 | 18 | extern decltype(NtQueryAttributesFile_hook) *NtQueryAttributesFile_original; 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtQueryDirectoryFileEx_hook/NtQueryDirectoryFileEx_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtQueryDirectoryFileEx_hook( 5 | __in HANDLE FileHandle, 6 | __in_opt HANDLE Event, 7 | __in_opt PIO_APC_ROUTINE ApcRoutine, 8 | __in_opt PVOID ApcContext, 9 | __out PIO_STATUS_BLOCK IoStatusBlock, 10 | __out PVOID FileInformation, 11 | __in ULONG Length, 12 | __in FILE_INFORMATION_CLASS FileInformationClass, 13 | __in ULONG QueryFlags, 14 | __in_opt PUNICODE_STRING FileName 15 | ); 16 | 17 | extern decltype(NtQueryDirectoryFileEx_hook) *NtQueryDirectoryFileEx_original; 18 | } 19 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtQueryDirectoryFile_hook/NtQueryDirectoryFile_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtQueryDirectoryFile_hook( 5 | __in HANDLE FileHandle, 6 | __in_opt HANDLE Event, 7 | __in_opt PIO_APC_ROUTINE ApcRoutine, 8 | __in_opt PVOID ApcContext, 9 | __out PIO_STATUS_BLOCK IoStatusBlock, 10 | __out PVOID FileInformation, 11 | __in ULONG Length, 12 | __in FILE_INFORMATION_CLASS FileInformationClass, 13 | __in BOOLEAN ReturnSingleEntry, 14 | __in_opt PUNICODE_STRING FileName, 15 | __in BOOLEAN RestartScan 16 | ); 17 | 18 | extern decltype(NtQueryDirectoryFile_hook) *NtQueryDirectoryFile_original; 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtQueryFullAttributesFile_hook/NtQueryFullAttributesFile_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_file_network_open_information 5 | typedef struct _FILE_NETWORK_OPEN_INFORMATION { 6 | LARGE_INTEGER CreationTime; 7 | LARGE_INTEGER LastAccessTime; 8 | LARGE_INTEGER LastWriteTime; 9 | LARGE_INTEGER ChangeTime; 10 | LARGE_INTEGER AllocationSize; 11 | LARGE_INTEGER EndOfFile; 12 | ULONG FileAttributes; 13 | } FILE_NETWORK_OPEN_INFORMATION, * PFILE_NETWORK_OPEN_INFORMATION; 14 | 15 | NTSTATUS NTAPI NtQueryFullAttributesFile_hook( 16 | __in POBJECT_ATTRIBUTES ObjectAttributes, 17 | __out PFILE_NETWORK_OPEN_INFORMATION FileInformation 18 | ); 19 | 20 | extern decltype(NtQueryFullAttributesFile_hook) *NtQueryFullAttributesFile_original; 21 | } 22 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtQueryInformationByName_hook/NtQueryInformationByName_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtQueryInformationByName_hook( 5 | __in POBJECT_ATTRIBUTES ObjectAttributes, 6 | __out PIO_STATUS_BLOCK IoStatusBlock, 7 | __out PVOID FileInformation, 8 | __in ULONG Length, 9 | __in FILE_INFORMATION_CLASS FileInformationClass 10 | ); 11 | 12 | extern decltype(NtQueryInformationByName_hook) *NtQueryInformationByName_original; 13 | } 14 | -------------------------------------------------------------------------------- /nt_file_dupe/include/Hooks/NtQueryInformationFile_hook/NtQueryInformationFile_hook.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe::hooks { 4 | NTSTATUS NTAPI NtQueryInformationFile_hook( 5 | __in HANDLE FileHandle, 6 | __out PIO_STATUS_BLOCK IoStatusBlock, 7 | __out PVOID FileInformation, 8 | __in ULONG Length, 9 | __in FILE_INFORMATION_CLASS FileInformationClass 10 | ); 11 | 12 | extern decltype(NtQueryInformationFile_hook) *NtQueryInformationFile_original; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /nt_file_dupe/include/NtApis/NtApis.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | 7 | #ifndef NT_STATUS 8 | #define NT_STATUS(s) (s >= 0) 9 | #endif 10 | 11 | namespace ntfsdupe::ntapis { 12 | bool init(void); 13 | 14 | bool NtPathToDosPath( 15 | PWSTR dosPath, USHORT* dosPathBytes, 16 | PCWSTR ntPath, const USHORT ntPathBytes 17 | ); 18 | 19 | bool GetFullDosPath(PWSTR fullPath, USHORT* fullPathBytes, PWSTR path); 20 | 21 | HANDLE DuplicateHandle(HANDLE original); 22 | 23 | NTSTATUS CloseHandle(HANDLE handle); 24 | 25 | void LockPeb(); 26 | void ReleasePeb(); 27 | } 28 | -------------------------------------------------------------------------------- /nt_file_dupe/include/NtApis/peb_helper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | namespace ntfsdupe::ntapis::peb_helper { 7 | bool remove_from_peb(HMODULE hModule); 8 | } 9 | -------------------------------------------------------------------------------- /nt_file_dupe/include/lib_main/lib_main.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace ntfsdupe { 4 | bool init(); 5 | void deinit(); 6 | } 7 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/CREDITS.md: -------------------------------------------------------------------------------- 1 | # Detours Contributor Credits 2 | 3 | The following individuals have helped identify specific bugs and improvements 4 | in Detours. The entire Detours community has benefited from their help. 5 | 6 | * Jay Krell: Identified error in DetourFindPayload that caused a 7 | incorrect failure when pcbData is NULL. (Build_342) 8 | 9 | * Jay Krell: Identified issue with VirtualSize == 0 files created in 10 | NT 3.1 images. (Build_339) 11 | 12 | * Igor Odnovorov: Identified an issue with the placement of the trampoline 13 | region when a function is detoured twice and the second 14 | trampoline region is outside of the +/- 2GB range of 15 | the target. (Build_337) 16 | 17 | * Jay Krell: Identified need for some programs to enumerate the 18 | address of IAT entries. (Build_336) 19 | 20 | * Calvin Hsia: Identified need for some program to change the excluded 21 | system region. (Build_336) 22 | 23 | * Adam Smith: Identified error in failure handling when VirtualProect 24 | cannot make pages executable because the Prohibit 25 | Dynamic Code Generation mitigation policy has been 26 | applied to a process. (Build_335) 27 | 28 | * Ben Faull: Identified fix to detour_alloc_region_from_lo and 29 | detour_alloc_region_from_hi that preserves ASLR entropy. 30 | (Build_334) 31 | 32 | * Shaoxiang Su: Reported errors building with Visual Studio 2015. 33 | (Build_332) 34 | 35 | * Jay Krell: Identified and resolved significant gaps in the X86, X64 36 | and IA64 disassemblers for instruction found in code, 37 | but seldom found in function prologues. (Build_331) 38 | 39 | * Allan Murphy: Identify error in rep and jmp ds: encodings. (Build_331) 40 | 41 | * Philip Bacon: Identified incorrect entry point return for pure 42 | resource-only binaries. (Build_330) 43 | 44 | * Jay Krell: Identified failure in DetourAttachEx to update nAlign. 45 | (Build_330) 46 | 47 | * Sumit Sarin: Helped debug error with packed binaries. 48 | (Build_329) 49 | 50 | * Nitya Kumar Sharma: Reported bug in DetourAfterWithDll for 32/64 agnostic 51 | EXEs. 52 | (Build_327) 53 | 54 | * Richard Black: Identified a large number of typos in documentation. 55 | (Build_326) 56 | 57 | * Michael Bilodeau: Identified bug in DetourUpdateProcessWithDll when the 58 | target process contains a Detours payload *after* all 59 | valid PE binaries. 60 | (Build_324) 61 | 62 | * Meera Jindal: Reported bug in identification of target address in 63 | DetourCopyInstruction for jmp[] and call[] on x86 & x64, 64 | the ff15 and ff25 opcodes. 65 | (Build_323) 66 | 67 | * Ken Johnson: Assistance with SAL 2.0 annotations. 68 | (Build_319) 69 | 70 | * Nick Wood: Identified bug in DetourFindFunction on ARM. 71 | (Build_314) 72 | 73 | * Mark Russinovich: Helped debug DetourCreateProcessWithDllEx. 74 | (Build_314) 75 | 76 | * John Lin: Implementation idea for DetoursCreateProcessWithDllEx. 77 | (Build_314) 78 | 79 | * Andrew Zawadowskiy Reported an improper memory page permissions 80 | vulnerability in Detours 2.1. (Vulnerability does not 81 | exist in versions later than Detours 2.1.) 82 | (Build_223) 83 | 84 | * Nightxie: Identified bug in detour_alloc_round_up_to_region. 85 | (Build_310) 86 | 87 | * Diana Milirud: Identified bug in B* instructions on ARM. 88 | (Build_309) 89 | 90 | * Juan Carlos Identified correct MSIL entry point for unsigned MSIL. 91 | Luciani: (Build_308) 92 | 93 | * Lee Hunt Suggested improvements in algorithm for allocation of 94 | Lawrence Landauer trampoline regions on x64 to avoid collisions with 95 | Joe Laughlin: system DLLs. 96 | (Build_307) 97 | 98 | * Tyler Sims Identified bug in handling of "anycpu" MSIL binaries 99 | Darren Kennedy: on x64. 100 | (Build_307) 101 | 102 | * Andre Vachon: Help with optimized binaries. 103 | (Build 301) 104 | 105 | * Chris Mann: Identified fix not forward ported from 2.2 to 3.0. 106 | (Build_301) 107 | 108 | * Mark Irving: Identified bug with EXEs missing second import table. 109 | (Build_300) 110 | 111 | * Ben Schwarz: Identified bug in handling of multi-byte NOPs. 112 | (Build_300) 113 | 114 | * Aaron Giles Coded initial ARM/Thumb2 disassembler. 115 | Jared Henderson: (Build_300) 116 | 117 | * Doug Brubacher: Coded initial x86 disassembler. 118 | (Build_100) 119 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Microsoft Corporation. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/LICENSE.md: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation 2 | 3 | All rights reserved. 4 | 5 | # MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 11 | of the Software, and to permit persons to whom the Software is furnished to do 12 | so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/README.md: -------------------------------------------------------------------------------- 1 | # Microsoft Research Detours Package 2 | 3 | Detours is a software package for monitoring and instrumenting API calls on Windows. Detours 4 | has been used by many ISVs and is also used by product teams at Microsoft. Detours is now available under 5 | a standard open source license ([MIT](https://github.com/microsoft/Detours/blob/master/LICENSE.md)). This simplifies licensing for programmers using Detours 6 | and allows the community to support Detours using open source tools and processes. 7 | 8 | Detours is compatible with the Windows NT family of 9 | operating systems: Windows NT, Windows XP, Windows Server 2003, Windows 7, 10 | Windows 8, and Windows 10. It cannot be used by Windows Store apps 11 | because Detours requires APIs not available to those applications. 12 | This repo contains the source code for version 4.0.1 of Detours. 13 | 14 | For technical documentation on Detours, see the [Detours Wiki](https://github.com/microsoft/Detours/wiki). 15 | For directions on how to build and run samples, see the 16 | samples [README.txt](https://github.com/Microsoft/Detours/blob/master/samples/README.TXT) file. 17 | 18 | ## Contributing 19 | 20 | The [`Detours`](https://github.com/microsoft/detours) repository is where development is done. 21 | Here are some ways you can participate in the project: 22 | 23 | * [Answer questions](https://github.com/microsoft/detours/issues) about using Detours. 24 | * [Improve the Wiki](https://github.com/microsoft/detours/wiki). 25 | * [Submit bugs](https://github.com/microsoft/detours/issues) and help us verify fixes and changes as they are checked in. 26 | * Review [source code changes](https://github.com/microsoft/detours/pulls). 27 | 28 | Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that 29 | you have the right to, and actually do, grant us the rights to use your contribution. 30 | For details, visit https://cla.opensource.microsoft.com. 31 | 32 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 33 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 34 | provided by the bot. You will only need to do this once across all repos using our CLA. 35 | 36 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 37 | 38 | ## Issues, questions, and feedback 39 | 40 | * Open an issue on [GitHub Issues](https://github.com/Microsoft/detours/issues). 41 | 42 | ## Mailing list for announcements 43 | 44 | The detours-announce mailing list is a low-traffic email list for important announcements 45 | about the project, such as the availability of new versions of Detours. To join it, send 46 | an email to listserv@lists.research.microsoft.com with a 47 | message body containing only the text SUBSCRIBE DETOURS-ANNOUNCE. 48 | To leave it, send an email to listserv@lists.research.microsoft.com with a 49 | message body containing only the text UNSUBSCRIBE DETOURS-ANNOUNCE. 50 | 51 | 52 | ## License 53 | 54 | Copyright (c) Microsoft Corporation. All rights reserved. 55 | 56 | Licensed under the [MIT](LICENSE.md) License. 57 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/detver.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Common version parameters. 4 | // 5 | // Microsoft Research Detours Package, Version 4.0.1 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved. 8 | // 9 | 10 | #define _USING_V110_SDK71_ 1 11 | #include "winver.h" 12 | #if 0 13 | #include 14 | #include 15 | #else 16 | #ifndef DETOURS_STRINGIFY 17 | #define DETOURS_STRINGIFY_(x) #x 18 | #define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) 19 | #endif 20 | 21 | #define VER_FILEFLAGSMASK 0x3fL 22 | #define VER_FILEFLAGS 0x0L 23 | #define VER_FILEOS 0x00040004L 24 | #define VER_FILETYPE 0x00000002L 25 | #define VER_FILESUBTYPE 0x00000000L 26 | #endif 27 | #define VER_DETOURS_BITS DETOURS_STRINGIFY(DETOURS_BITS) 28 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/disolarm.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_ARM_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/disolarm64.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_ARM64_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/disolia64.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_IA64_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/disolx64.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_X64_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/disolx86.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_X86_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Detours/uimports.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Add DLLs to a module import table (uimports.cpp of detours.lib) 4 | // 5 | // Microsoft Research Detours Package, Version 4.0.1 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved. 8 | // 9 | // Note that this file is included into creatwth.cpp one or more times 10 | // (once for each supported module format). 11 | // 12 | 13 | #if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH 14 | #error detours.h version mismatch 15 | #endif 16 | 17 | // UpdateImports32 aka UpdateImports64 18 | static BOOL UPDATE_IMPORTS_XX(HANDLE hProcess, 19 | HMODULE hModule, 20 | __in_ecount(nDlls) LPCSTR *plpDlls, 21 | DWORD nDlls) 22 | { 23 | BOOL fSucceeded = FALSE; 24 | DWORD cbNew = 0; 25 | 26 | BYTE * pbNew = NULL; 27 | DWORD i; 28 | SIZE_T cbRead; 29 | DWORD n; 30 | 31 | PBYTE pbModule = (PBYTE)hModule; 32 | 33 | IMAGE_DOS_HEADER idh; 34 | ZeroMemory(&idh, sizeof(idh)); 35 | if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), &cbRead) 36 | || cbRead < sizeof(idh)) { 37 | 38 | DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n", 39 | pbModule, pbModule + sizeof(idh), GetLastError())); 40 | 41 | finish: 42 | if (pbNew != NULL) { 43 | delete[] pbNew; 44 | pbNew = NULL; 45 | } 46 | return fSucceeded; 47 | } 48 | 49 | IMAGE_NT_HEADERS_XX inh; 50 | ZeroMemory(&inh, sizeof(inh)); 51 | 52 | if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), &cbRead) 53 | || cbRead < sizeof(inh)) { 54 | DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n", 55 | pbModule + idh.e_lfanew, 56 | pbModule + idh.e_lfanew + sizeof(inh), 57 | GetLastError())); 58 | goto finish; 59 | } 60 | 61 | if (inh.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC_XX) { 62 | DETOUR_TRACE(("Wrong size image (%04x != %04x).\n", 63 | inh.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR_MAGIC_XX)); 64 | SetLastError(ERROR_INVALID_BLOCK); 65 | goto finish; 66 | } 67 | 68 | // Zero out the bound table so loader doesn't use it instead of our new table. 69 | inh.BOUND_DIRECTORY.VirtualAddress = 0; 70 | inh.BOUND_DIRECTORY.Size = 0; 71 | 72 | // Find the size of the mapped file. 73 | DWORD dwSec = idh.e_lfanew + 74 | FIELD_OFFSET(IMAGE_NT_HEADERS_XX, OptionalHeader) + 75 | inh.FileHeader.SizeOfOptionalHeader; 76 | 77 | for (i = 0; i < inh.FileHeader.NumberOfSections; i++) { 78 | IMAGE_SECTION_HEADER ish; 79 | ZeroMemory(&ish, sizeof(ish)); 80 | 81 | if (!ReadProcessMemory(hProcess, pbModule + dwSec + sizeof(ish) * i, &ish, 82 | sizeof(ish), &cbRead) 83 | || cbRead < sizeof(ish)) { 84 | 85 | DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n", 86 | pbModule + dwSec + sizeof(ish) * i, 87 | pbModule + dwSec + sizeof(ish) * (i + 1), 88 | GetLastError())); 89 | goto finish; 90 | } 91 | 92 | DETOUR_TRACE(("ish[%lu] : va=%08lx sr=%lu\n", i, ish.VirtualAddress, ish.SizeOfRawData)); 93 | 94 | // If the linker didn't suggest an IAT in the data directories, the 95 | // loader will look for the section of the import directory to be used 96 | // for this instead. Since we put out new IMPORT_DIRECTORY outside any 97 | // section boundary, the loader will not find it. So we provide one 98 | // explicitly to avoid the search. 99 | // 100 | if (inh.IAT_DIRECTORY.VirtualAddress == 0 && 101 | inh.IMPORT_DIRECTORY.VirtualAddress >= ish.VirtualAddress && 102 | inh.IMPORT_DIRECTORY.VirtualAddress < ish.VirtualAddress + ish.SizeOfRawData) { 103 | 104 | inh.IAT_DIRECTORY.VirtualAddress = ish.VirtualAddress; 105 | inh.IAT_DIRECTORY.Size = ish.SizeOfRawData; 106 | } 107 | } 108 | 109 | if (inh.IMPORT_DIRECTORY.VirtualAddress != 0 && inh.IMPORT_DIRECTORY.Size == 0) { 110 | 111 | // Don't worry about changing the PE file, 112 | // because the load information of the original PE header has been saved and will be restored. 113 | // The change here is just for the following code to work normally 114 | 115 | PIMAGE_IMPORT_DESCRIPTOR pImageImport = (PIMAGE_IMPORT_DESCRIPTOR)(pbModule + inh.IMPORT_DIRECTORY.VirtualAddress); 116 | 117 | do { 118 | IMAGE_IMPORT_DESCRIPTOR ImageImport; 119 | if (!ReadProcessMemory(hProcess, pImageImport, &ImageImport, sizeof(ImageImport), NULL)) { 120 | DETOUR_TRACE(("ReadProcessMemory failed: %lu\n", GetLastError())); 121 | goto finish; 122 | } 123 | inh.IMPORT_DIRECTORY.Size += sizeof(IMAGE_IMPORT_DESCRIPTOR); 124 | if (!ImageImport.Name) { 125 | break; 126 | } 127 | ++pImageImport; 128 | } while (TRUE); 129 | 130 | DWORD dwLastError = GetLastError(); 131 | OutputDebugString(TEXT("[This PE file has an import table, but the import table size is marked as 0. This is an error.") 132 | TEXT("If it is not repaired, the launched program will not work properly, Detours has automatically repaired its import table size for you! ! !]\r\n")); 133 | if (GetLastError() != dwLastError) { 134 | SetLastError(dwLastError); 135 | } 136 | } 137 | 138 | DETOUR_TRACE((" Imports: %p..%p\n", 139 | pbModule + inh.IMPORT_DIRECTORY.VirtualAddress, 140 | pbModule + inh.IMPORT_DIRECTORY.VirtualAddress + 141 | inh.IMPORT_DIRECTORY.Size)); 142 | 143 | // Calculate new import directory size. Note that since inh is from another 144 | // process, inh could have been corrupted. We need to protect against 145 | // integer overflow in allocation calculations. 146 | DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR); 147 | DWORD obRem; 148 | if (DWordMult(sizeof(IMAGE_IMPORT_DESCRIPTOR), nDlls, &obRem) != S_OK) { 149 | DETOUR_TRACE(("too many new DLLs.\n")); 150 | goto finish; 151 | } 152 | DWORD obOld; 153 | if (DWordAdd(obRem, sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls, &obOld) != S_OK) { 154 | DETOUR_TRACE(("DLL entries overflow.\n")); 155 | goto finish; 156 | } 157 | DWORD obTab = PadToDwordPtr(obOld); 158 | // Check for integer overflow. 159 | if (obTab < obOld) { 160 | DETOUR_TRACE(("DLL entries padding overflow.\n")); 161 | goto finish; 162 | } 163 | DWORD stSize; 164 | if (DWordMult(sizeof(DWORD_XX) * 4, nDlls, &stSize) != S_OK) { 165 | DETOUR_TRACE(("String table overflow.\n")); 166 | goto finish; 167 | } 168 | DWORD obDll; 169 | if (DWordAdd(obTab, stSize, &obDll) != S_OK) { 170 | DETOUR_TRACE(("Import table size overflow\n")); 171 | goto finish; 172 | } 173 | DWORD obStr = obDll; 174 | cbNew = obStr; 175 | for (n = 0; n < nDlls; n++) { 176 | if (DWordAdd(cbNew, PadToDword((DWORD)strlen(plpDlls[n]) + 1), &cbNew) != S_OK) { 177 | DETOUR_TRACE(("Overflow adding string table entry\n")); 178 | goto finish; 179 | } 180 | } 181 | pbNew = new BYTE [cbNew]; 182 | if (pbNew == NULL) { 183 | DETOUR_TRACE(("new BYTE [cbNew] failed.\n")); 184 | goto finish; 185 | } 186 | ZeroMemory(pbNew, cbNew); 187 | 188 | PBYTE pbBase = pbModule; 189 | PBYTE pbNext = pbBase 190 | + inh.OptionalHeader.BaseOfCode 191 | + inh.OptionalHeader.SizeOfCode 192 | + inh.OptionalHeader.SizeOfInitializedData 193 | + inh.OptionalHeader.SizeOfUninitializedData; 194 | if (pbBase < pbNext) { 195 | pbBase = pbNext; 196 | } 197 | DETOUR_TRACE(("pbBase = %p\n", pbBase)); 198 | 199 | PBYTE pbNewIid = FindAndAllocateNearBase(hProcess, pbModule, pbBase, cbNew); 200 | if (pbNewIid == NULL) { 201 | DETOUR_TRACE(("FindAndAllocateNearBase failed.\n")); 202 | goto finish; 203 | } 204 | 205 | PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)pbNew; 206 | IMAGE_THUNK_DATAXX *pt = NULL; 207 | 208 | DWORD obBase = (DWORD)(pbNewIid - pbModule); 209 | DWORD dwProtect = 0; 210 | 211 | if (inh.IMPORT_DIRECTORY.VirtualAddress != 0) { 212 | // Read the old import directory if it exists. 213 | DETOUR_TRACE(("IMPORT_DIRECTORY perms=%lx\n", dwProtect)); 214 | 215 | if (!ReadProcessMemory(hProcess, 216 | pbModule + inh.IMPORT_DIRECTORY.VirtualAddress, 217 | &piid[nDlls], 218 | nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR), &cbRead) 219 | || cbRead < nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR)) { 220 | 221 | DETOUR_TRACE(("ReadProcessMemory(imports) failed: %lu\n", GetLastError())); 222 | goto finish; 223 | } 224 | } 225 | 226 | for (n = 0; n < nDlls; n++) { 227 | HRESULT hrRet = StringCchCopyA((char*)pbNew + obStr, cbNew - obStr, plpDlls[n]); 228 | if (FAILED(hrRet)) { 229 | DETOUR_TRACE(("StringCchCopyA failed: %08lx\n", hrRet)); 230 | goto finish; 231 | } 232 | 233 | // After copying the string, we patch up the size "??" bits if any. 234 | hrRet = ReplaceOptionalSizeA((char*)pbNew + obStr, 235 | cbNew - obStr, 236 | DETOURS_STRINGIFY(DETOURS_BITS_XX)); 237 | if (FAILED(hrRet)) { 238 | DETOUR_TRACE(("ReplaceOptionalSizeA failed: %08lx\n", hrRet)); 239 | goto finish; 240 | } 241 | 242 | DWORD nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * (4 * n)); 243 | piid[n].OriginalFirstThunk = obBase + nOffset; 244 | 245 | // We need 2 thunks for the import table and 2 thunks for the IAT. 246 | // One for an ordinal import and one to mark the end of the list. 247 | pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset)); 248 | pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1; 249 | pt[1].u1.Ordinal = 0; 250 | 251 | nOffset = obTab + (sizeof(IMAGE_THUNK_DATAXX) * ((4 * n) + 2)); 252 | piid[n].FirstThunk = obBase + nOffset; 253 | pt = ((IMAGE_THUNK_DATAXX*)(pbNew + nOffset)); 254 | pt[0].u1.Ordinal = IMAGE_ORDINAL_FLAG_XX + 1; 255 | pt[1].u1.Ordinal = 0; 256 | piid[n].TimeDateStamp = 0; 257 | piid[n].ForwarderChain = 0; 258 | piid[n].Name = obBase + obStr; 259 | 260 | obStr += PadToDword((DWORD)strlen(plpDlls[n]) + 1); 261 | } 262 | _Analysis_assume_(obStr <= cbNew); 263 | 264 | #if 0 265 | for (i = 0; i < nDlls + nOldDlls; i++) { 266 | DETOUR_TRACE(("%8d. Look=%08x Time=%08x Fore=%08x Name=%08x Addr=%08x\n", 267 | i, 268 | piid[i].OriginalFirstThunk, 269 | piid[i].TimeDateStamp, 270 | piid[i].ForwarderChain, 271 | piid[i].Name, 272 | piid[i].FirstThunk)); 273 | if (piid[i].OriginalFirstThunk == 0 && piid[i].FirstThunk == 0) { 274 | break; 275 | } 276 | } 277 | #endif 278 | 279 | if (!WriteProcessMemory(hProcess, pbNewIid, pbNew, obStr, NULL)) { 280 | DETOUR_TRACE(("WriteProcessMemory(iid) failed: %lu\n", GetLastError())); 281 | goto finish; 282 | } 283 | 284 | DETOUR_TRACE(("obBaseBef = %08lx..%08lx\n", 285 | inh.IMPORT_DIRECTORY.VirtualAddress, 286 | inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size)); 287 | DETOUR_TRACE(("obBaseAft = %08lx..%08lx\n", obBase, obBase + obStr)); 288 | 289 | // In this case the file didn't have an import directory in first place, 290 | // so we couldn't fix the missing IAT above. We still need to explicitly 291 | // provide an IAT to prevent to loader from looking for one. 292 | // 293 | if (inh.IAT_DIRECTORY.VirtualAddress == 0) { 294 | inh.IAT_DIRECTORY.VirtualAddress = obBase; 295 | inh.IAT_DIRECTORY.Size = cbNew; 296 | } 297 | 298 | inh.IMPORT_DIRECTORY.VirtualAddress = obBase; 299 | inh.IMPORT_DIRECTORY.Size = cbNew; 300 | 301 | /////////////////////// Update the NT header for the new import directory. 302 | // 303 | if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders, 304 | PAGE_EXECUTE_READWRITE, &dwProtect)) { 305 | DETOUR_TRACE(("VirtualProtectEx(inh) write failed: %lu\n", GetLastError())); 306 | goto finish; 307 | } 308 | 309 | inh.OptionalHeader.CheckSum = 0; 310 | 311 | if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { 312 | DETOUR_TRACE(("WriteProcessMemory(idh) failed: %lu\n", GetLastError())); 313 | goto finish; 314 | } 315 | DETOUR_TRACE(("WriteProcessMemory(idh:%p..%p)\n", pbModule, pbModule + sizeof(idh))); 316 | 317 | if (!WriteProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), NULL)) { 318 | DETOUR_TRACE(("WriteProcessMemory(inh) failed: %lu\n", GetLastError())); 319 | goto finish; 320 | } 321 | DETOUR_TRACE(("WriteProcessMemory(inh:%p..%p)\n", 322 | pbModule + idh.e_lfanew, 323 | pbModule + idh.e_lfanew + sizeof(inh))); 324 | 325 | if (!VirtualProtectEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders, 326 | dwProtect, &dwProtect)) { 327 | DETOUR_TRACE(("VirtualProtectEx(idh) restore failed: %lu\n", GetLastError())); 328 | goto finish; 329 | } 330 | 331 | fSucceeded = TRUE; 332 | goto finish; 333 | } 334 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Json/nlohmann/LICENSE.MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2013-2022 Niels Lohmann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/Json/nlohmann/json_fwd.hpp: -------------------------------------------------------------------------------- 1 | // __ _____ _____ _____ 2 | // __| | __| | | | JSON for Modern C++ 3 | // | | |__ | | | | | | version 3.11.3 4 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 5 | // 6 | // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann 7 | // SPDX-License-Identifier: MIT 8 | 9 | #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ 10 | #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ 11 | 12 | #include // int64_t, uint64_t 13 | #include // map 14 | #include // allocator 15 | #include // string 16 | #include // vector 17 | 18 | // #include 19 | // __ _____ _____ _____ 20 | // __| | __| | | | JSON for Modern C++ 21 | // | | |__ | | | | | | version 3.11.3 22 | // |_____|_____|_____|_|___| https://github.com/nlohmann/json 23 | // 24 | // SPDX-FileCopyrightText: 2013-2023 Niels Lohmann 25 | // SPDX-License-Identifier: MIT 26 | 27 | 28 | 29 | // This file contains all macro definitions affecting or depending on the ABI 30 | 31 | #ifndef JSON_SKIP_LIBRARY_VERSION_CHECK 32 | #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) 33 | #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3 34 | #warning "Already included a different version of the library!" 35 | #endif 36 | #endif 37 | #endif 38 | 39 | #define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) 40 | #define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum) 41 | #define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum) 42 | 43 | #ifndef JSON_DIAGNOSTICS 44 | #define JSON_DIAGNOSTICS 0 45 | #endif 46 | 47 | #ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 48 | #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 49 | #endif 50 | 51 | #if JSON_DIAGNOSTICS 52 | #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag 53 | #else 54 | #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS 55 | #endif 56 | 57 | #if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 58 | #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp 59 | #else 60 | #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON 61 | #endif 62 | 63 | #ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION 64 | #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 65 | #endif 66 | 67 | // Construct the namespace ABI tags component 68 | #define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b 69 | #define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \ 70 | NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) 71 | 72 | #define NLOHMANN_JSON_ABI_TAGS \ 73 | NLOHMANN_JSON_ABI_TAGS_CONCAT( \ 74 | NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ 75 | NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON) 76 | 77 | // Construct the namespace version component 78 | #define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ 79 | _v ## major ## _ ## minor ## _ ## patch 80 | #define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ 81 | NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) 82 | 83 | #if NLOHMANN_JSON_NAMESPACE_NO_VERSION 84 | #define NLOHMANN_JSON_NAMESPACE_VERSION 85 | #else 86 | #define NLOHMANN_JSON_NAMESPACE_VERSION \ 87 | NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ 88 | NLOHMANN_JSON_VERSION_MINOR, \ 89 | NLOHMANN_JSON_VERSION_PATCH) 90 | #endif 91 | 92 | // Combine namespace components 93 | #define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b 94 | #define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ 95 | NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) 96 | 97 | #ifndef NLOHMANN_JSON_NAMESPACE 98 | #define NLOHMANN_JSON_NAMESPACE \ 99 | nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ 100 | NLOHMANN_JSON_ABI_TAGS, \ 101 | NLOHMANN_JSON_NAMESPACE_VERSION) 102 | #endif 103 | 104 | #ifndef NLOHMANN_JSON_NAMESPACE_BEGIN 105 | #define NLOHMANN_JSON_NAMESPACE_BEGIN \ 106 | namespace nlohmann \ 107 | { \ 108 | inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ 109 | NLOHMANN_JSON_ABI_TAGS, \ 110 | NLOHMANN_JSON_NAMESPACE_VERSION) \ 111 | { 112 | #endif 113 | 114 | #ifndef NLOHMANN_JSON_NAMESPACE_END 115 | #define NLOHMANN_JSON_NAMESPACE_END \ 116 | } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ 117 | } // namespace nlohmann 118 | #endif 119 | 120 | 121 | /*! 122 | @brief namespace for Niels Lohmann 123 | @see https://github.com/nlohmann 124 | @since version 1.0.0 125 | */ 126 | NLOHMANN_JSON_NAMESPACE_BEGIN 127 | 128 | /*! 129 | @brief default JSONSerializer template argument 130 | 131 | This serializer ignores the template arguments and uses ADL 132 | ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) 133 | for serialization. 134 | */ 135 | template 136 | struct adl_serializer; 137 | 138 | /// a class to store JSON values 139 | /// @sa https://json.nlohmann.me/api/basic_json/ 140 | template class ObjectType = 141 | std::map, 142 | template class ArrayType = std::vector, 143 | class StringType = std::string, class BooleanType = bool, 144 | class NumberIntegerType = std::int64_t, 145 | class NumberUnsignedType = std::uint64_t, 146 | class NumberFloatType = double, 147 | template class AllocatorType = std::allocator, 148 | template class JSONSerializer = 149 | adl_serializer, 150 | class BinaryType = std::vector, // cppcheck-suppress syntaxError 151 | class CustomBaseClass = void> 152 | class basic_json; 153 | 154 | /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document 155 | /// @sa https://json.nlohmann.me/api/json_pointer/ 156 | template 157 | class json_pointer; 158 | 159 | /*! 160 | @brief default specialization 161 | @sa https://json.nlohmann.me/api/json/ 162 | */ 163 | using json = basic_json<>; 164 | 165 | /// @brief a minimal map-like container that preserves insertion order 166 | /// @sa https://json.nlohmann.me/api/ordered_map/ 167 | template 168 | struct ordered_map; 169 | 170 | /// @brief specialization that maintains the insertion order of object keys 171 | /// @sa https://json.nlohmann.me/api/ordered_json/ 172 | using ordered_json = basic_json; 173 | 174 | NLOHMANN_JSON_NAMESPACE_END 175 | 176 | #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ 177 | -------------------------------------------------------------------------------- /nt_file_dupe/libs/README.md: -------------------------------------------------------------------------------- 1 | External libraries 2 | -------------------------------------------------------------------------------- /nt_file_dupe/nt_file_dupe.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {5bc16b5c-bafc-4899-b5d4-0e2963b077b6} 6 | 7 | 8 | {8e3ccce4-f784-4a8b-9353-e9e8f04533f3} 9 | 10 | 11 | {8a01e19e-dd42-47cd-9795-77a79964d956} 12 | 13 | 14 | {8c8290fa-89b8-4d04-bf3c-c09522bdf129} 15 | 16 | 17 | {590454ba-fc14-4091-be17-1302646e1723} 18 | 19 | 20 | {e8cee40f-4a11-46bd-9843-b4008c52e19e} 21 | 22 | 23 | {9fb606f3-3e69-4ea3-bf2c-911572f14698} 24 | 25 | 26 | {a6969531-3a77-4332-8ec8-945df19a8d06} 27 | 28 | 29 | {c2902eed-400a-4843-a432-29ae6d49ac53} 30 | 31 | 32 | {87a6614f-7d69-41fe-adec-1cd8af3fe234} 33 | 34 | 35 | {755db213-8e29-4421-a4f4-88c37a16b2c7} 36 | 37 | 38 | {099ed076-9cdb-423f-8ba6-6d8aa754fafd} 39 | 40 | 41 | {7f7b144f-c07d-4a36-af70-9cb950de8643} 42 | 43 | 44 | {3136f89e-277d-410b-b58e-c55723bcc950} 45 | 46 | 47 | {ab07afcc-98b1-4fa1-9e41-173146a9b9c7} 48 | 49 | 50 | {bd3b3971-f874-4cad-97cb-8d1711b685a6} 51 | 52 | 53 | {75cf0e8c-c7c6-4adc-a863-a08b0387c949} 54 | 55 | 56 | {e3fecc77-9d31-4e79-8425-b29fa30b516f} 57 | 58 | 59 | {b5b69105-3f0c-4b33-bf87-abfe034353fa} 60 | 61 | 62 | {ea3adc20-7aca-4960-853b-4069bca884e0} 63 | 64 | 65 | {d001949c-3fb8-4277-9bec-dd5950f9069d} 66 | 67 | 68 | {880696e8-c988-41e3-afcb-88fe2a5f46f7} 69 | 70 | 71 | {681f8939-0f73-41f9-84c6-28a25d073362} 72 | 73 | 74 | 75 | 76 | Hooks 77 | 78 | 79 | NtApis 80 | 81 | 82 | Configs 83 | 84 | 85 | Hooks\NtCreateDirectoryObject_hook 86 | 87 | 88 | Hooks\NtCreateDirectoryObjectEx_hook 89 | 90 | 91 | Hooks\NtCreateFile_hook 92 | 93 | 94 | Hooks\NtDeleteFile_hook 95 | 96 | 97 | Hooks\NtOpenDirectoryObject_hook 98 | 99 | 100 | Hooks\NtOpenFile_hook 101 | 102 | 103 | Hooks\NtQueryAttributesFile_hook 104 | 105 | 106 | Hooks\NtQueryDirectoryFile_hook 107 | 108 | 109 | Hooks\NtQueryDirectoryFileEx_hook 110 | 111 | 112 | Hooks\NtQueryFullAttributesFile_hook 113 | 114 | 115 | Hooks\NtQueryInformationByName_hook 116 | 117 | 118 | Hooks\NtQueryInformationFile_hook 119 | 120 | 121 | libs\Detours 122 | 123 | 124 | libs\Detours 125 | 126 | 127 | libs\Detours 128 | 129 | 130 | libs\Detours 131 | 132 | 133 | libs\Detours 134 | 135 | 136 | libs\Detours 137 | 138 | 139 | libs\Detours 140 | 141 | 142 | libs\Detours 143 | 144 | 145 | libs\Detours 146 | 147 | 148 | libs\Detours 149 | 150 | 151 | libs\Detours 152 | 153 | 154 | Hooks\LdrLoadDll_hook 155 | 156 | 157 | Helpers 158 | 159 | 160 | lib_main 161 | 162 | 163 | Hooks\LdrGetDllHandle 164 | 165 | 166 | NtApis 167 | 168 | 169 | 170 | 171 | libs\Json\nlohmann 172 | 173 | 174 | Helpers 175 | 176 | 177 | Hooks 178 | 179 | 180 | NtApis 181 | 182 | 183 | Configs 184 | 185 | 186 | Hooks\NtCreateDirectoryObject_hook 187 | 188 | 189 | Hooks\NtCreateDirectoryObjectEx_hook 190 | 191 | 192 | Hooks\NtCreateFile_hook 193 | 194 | 195 | Hooks\NtDeleteFile_hook 196 | 197 | 198 | Hooks\NtOpenDirectoryObject_hook 199 | 200 | 201 | Hooks\NtOpenFile_hook 202 | 203 | 204 | Hooks\NtQueryAttributesFile_hook 205 | 206 | 207 | Hooks\NtQueryDirectoryFile_hook 208 | 209 | 210 | Hooks\NtQueryDirectoryFileEx_hook 211 | 212 | 213 | Hooks\NtQueryFullAttributesFile_hook 214 | 215 | 216 | Hooks\NtQueryInformationByName_hook 217 | 218 | 219 | Hooks\NtQueryInformationFile_hook 220 | 221 | 222 | libs\Json\nlohmann 223 | 224 | 225 | libs\Detours 226 | 227 | 228 | libs\Detours 229 | 230 | 231 | Hooks\LdrLoadDll_hook 232 | 233 | 234 | Helpers 235 | 236 | 237 | lib_main 238 | 239 | 240 | Hooks\LdrGetDllHandle 241 | 242 | 243 | NtApis 244 | 245 | 246 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Helpers/dbglog.cpp: -------------------------------------------------------------------------------- 1 | #if !defined(NT_FS_DUPE_RELEASE) 2 | 3 | #include "Helpers/dbglog.hpp" 4 | #include "Helpers/Helpers.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static const auto start_time = std::chrono::system_clock::now(); 13 | static std::recursive_mutex f_mtx{}; 14 | static FILE *out_file = nullptr; 15 | 16 | bool ntfsdupe::helpers::dbglog::init() 17 | { 18 | std::lock_guard lk(f_mtx); 19 | if (!out_file) { 20 | auto path = ntfsdupe::helpers::get_module_fullpath(nullptr) + L".NT_FS_DUPE.log"; 21 | auto err = _wfopen_s(&out_file, path.c_str(), L"a, ccs=UTF-8"); 22 | if (err == 0) { 23 | return true; 24 | } else { 25 | out_file = nullptr; 26 | } 27 | } 28 | 29 | return false; 30 | } 31 | 32 | void ntfsdupe::helpers::dbglog::write(const wchar_t* fmt, ...) 33 | { 34 | std::lock_guard lk(f_mtx); 35 | auto elapsed = std::chrono::system_clock::now() - start_time; 36 | std::wstringstream ss{}; 37 | ss << "[SteamAPICheckBypass]" << "[" << std::chrono::duration_cast(elapsed).count() << " ms] [tid: " << std::this_thread::get_id() << "] "; 38 | auto ss_str = ss.str(); 39 | std::va_list args{}; 40 | va_start(args, fmt); 41 | wchar_t buffer[1024]; 42 | std::vswprintf(buffer, sizeof(buffer) / sizeof(wchar_t), fmt, args); 43 | va_end(args); 44 | if (out_file) { 45 | std::fwprintf(out_file, L"%s%s\n", ss_str.c_str(), buffer); 46 | std::fflush(out_file); 47 | } 48 | OutputDebugStringW((ss_str + buffer).c_str()); 49 | } 50 | 51 | void ntfsdupe::helpers::dbglog::write(const std::wstring &str) 52 | { 53 | std::lock_guard lk(f_mtx); 54 | if (out_file) { 55 | write(str.c_str()); 56 | } 57 | } 58 | 59 | void ntfsdupe::helpers::dbglog::write(const std::string &str) 60 | { 61 | std::lock_guard lk(f_mtx); 62 | if (out_file) { 63 | std::wstring wstr(ntfsdupe::helpers::str_to_wstr(str)); 64 | ntfsdupe::helpers::dbglog::write(wstr.c_str()); 65 | } 66 | } 67 | 68 | void ntfsdupe::helpers::dbglog::close() 69 | { 70 | std::lock_guard lk(f_mtx); 71 | if (out_file) { 72 | std::fwprintf(out_file, L"\n[SteamAPICheckBypass] Log file closed\n\n"); 73 | std::fclose(out_file); 74 | out_file = nullptr; 75 | } 76 | } 77 | 78 | #endif // NT_FS_DUPE_RELEASE 79 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/LdrGetDllHandle_hook/LdrGetDllHandle_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(LdrGetDllHandle_hook) *LdrGetDllHandle_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::LdrGetDllHandle_hook( 10 | _In_opt_ PWSTR DllPath, 11 | _In_opt_ PULONG DllCharacteristics, 12 | _In_ PUNICODE_STRING DllName, 13 | _Out_ PHANDLE DllHandle 14 | ) 15 | { 16 | try { 17 | if (DllName && DllName->Length && DllName->Buffer && DllName->Buffer[0]) { 18 | auto cfg = ntfsdupe::hooks::find_module_obj_filename(DllName); 19 | if (cfg) switch (cfg->mode) 20 | { 21 | // we only care about this case 22 | case ntfsdupe::cfgs::ModuleType::prevent_load: // this doesn't make sense here, but just in case 23 | case ntfsdupe::cfgs::ModuleType::target: // target modules are invisible to the process 24 | case ntfsdupe::cfgs::ModuleType::hide_handle: { 25 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 26 | return LdrGetDllHandle_original( 27 | DllPath, 28 | DllCharacteristics, 29 | DllName, 30 | DllHandle 31 | ); 32 | } 33 | NTFSDUPE_DBG( 34 | L"ntfsdupe::hooks::LdrGetDllHandle_hook prevent_load/target/hide_handle '%s'", 35 | std::wstring(DllName->Buffer, DllName->Length / sizeof(wchar_t)).c_str() 36 | ); 37 | // this is what the original API returns when you pass a dll name that doesn't exist 38 | return STATUS_DLL_NOT_FOUND; 39 | } 40 | 41 | case ntfsdupe::cfgs::ModuleType::original: { 42 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 43 | return LdrGetDllHandle_original( 44 | DllPath, 45 | DllCharacteristics, 46 | DllName, 47 | DllHandle 48 | ); 49 | } 50 | NTFSDUPE_DBG( 51 | L"ntfsdupe::hooks::LdrGetDllHandle_hook original '%s'", 52 | std::wstring(DllName->Buffer, DllName->Length / sizeof(wchar_t)).c_str() 53 | ); 54 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 55 | auto len = wcslen(cfg->target_filename.c_str()) * sizeof(wchar_t) + sizeof(wchar_t); 56 | auto dllname_new = unique_ptr_stack(wchar_t, len); 57 | if (dllname_new) { 58 | ntfsdupe::hooks::copy_new_module_target(dllname_new.get(), DllName, cfg); 59 | 60 | ntfsdupe::helpers::upper(dllname_new.get(), len / sizeof(wchar_t)); 61 | std::wstring_view wsv(dllname_new.get(), len / sizeof(wchar_t)); 62 | wsv = wsv.substr(wsv.find_last_of(L'\\') + 1); 63 | 64 | ntfsdupe::cfgs::add_bypass(wsv); 65 | // backup original buffer 66 | const auto buffer_backup = DllName->Buffer; 67 | const auto length_backup = DllName->Length; 68 | const auto max_length_backup = DllName->MaximumLength; 69 | // set new buffer 70 | DllName->Buffer = dllname_new.get(); 71 | DllName->Length = static_cast(wcslen(DllName->Buffer) * sizeof(wchar_t)); 72 | DllName->MaximumLength = static_cast(len); 73 | // call original API 74 | const auto result = LdrGetDllHandle_original( 75 | DllPath, 76 | DllCharacteristics, 77 | DllName, 78 | DllHandle 79 | ); 80 | // restore original buffer 81 | DllName->Buffer = buffer_backup; 82 | DllName->Length = length_backup; 83 | DllName->MaximumLength = max_length_backup; 84 | ntfsdupe::cfgs::remove_bypass(wsv); 85 | 86 | return result; 87 | } 88 | } 89 | break; 90 | 91 | default: break; 92 | } 93 | 94 | // if redirection/hiding didn't do anything, then bypass this dll to avoid later redirection by NtOpenFile or NtCreateFile 95 | auto terminated_name = unique_ptr_stack(wchar_t, DllName->Length + sizeof(wchar_t)); 96 | if (terminated_name) { 97 | memcpy(terminated_name.get(), DllName->Buffer, DllName->Length); 98 | terminated_name.get()[DllName->Length / sizeof(wchar_t)] = L'\0'; 99 | auto len = DllName->Length; 100 | ntfsdupe::helpers::upper(terminated_name.get(), len / sizeof(wchar_t)); 101 | std::wstring_view wsv(terminated_name.get(), len / sizeof(wchar_t)); 102 | wsv = wsv.substr(wsv.find_last_of(L'\\') + 1); 103 | 104 | ntfsdupe::cfgs::add_bypass(wsv); 105 | const auto result = LdrGetDllHandle_original( 106 | DllPath, 107 | DllCharacteristics, 108 | DllName, 109 | DllHandle 110 | ); 111 | ntfsdupe::cfgs::remove_bypass(wsv); 112 | return result; 113 | } 114 | } 115 | 116 | // if everything above failed 117 | return LdrGetDllHandle_original( 118 | DllPath, 119 | DllCharacteristics, 120 | DllName, 121 | DllHandle 122 | ); 123 | } 124 | catch (...) { 125 | NTFSDUPE_DBG(L"ntfsdupe::hooks::LdrGetDllHandle_hook exception occurred"); 126 | return LdrGetDllHandle_original( 127 | DllPath, 128 | DllCharacteristics, 129 | DllName, 130 | DllHandle 131 | ); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/LdrLoadDll_hook/LdrLoadDll_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(LdrLoadDll_hook) *LdrLoadDll_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::LdrLoadDll_hook( 10 | __in_opt LPVOID DllCharacteristics, 11 | __in_opt LPDWORD Unknown, 12 | __in PUNICODE_STRING DllName, 13 | _Out_ HMODULE* BaseAddress 14 | ) 15 | { 16 | try { 17 | if (DllName && DllName->Length && DllName->Buffer && DllName->Buffer[0]) { 18 | // try redirection/hiding first 19 | auto cfg = ntfsdupe::hooks::find_module_obj_filename(DllName); 20 | if (cfg) switch (cfg->mode) 21 | { 22 | // in the case 'hide_handle' we just want to hide it in memory, not prevent loading 23 | 24 | case ntfsdupe::cfgs::ModuleType::prevent_load: 25 | case ntfsdupe::cfgs::ModuleType::target: { // target files are invisible to the process 26 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 27 | return LdrLoadDll_original( 28 | DllCharacteristics, 29 | Unknown, 30 | DllName, 31 | BaseAddress 32 | ); 33 | } 34 | NTFSDUPE_DBG( 35 | L"ntfsdupe::hooks::LdrLoadDll_hook prevent_load/target '%s'", 36 | std::wstring(DllName->Buffer, DllName->Length / sizeof(wchar_t)).c_str() 37 | ); 38 | // this is what the original API returns when you pass a dll name that doesn't exist 39 | return STATUS_DLL_NOT_FOUND; 40 | } 41 | 42 | case ntfsdupe::cfgs::ModuleType::original: { 43 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 44 | return LdrLoadDll_original( 45 | DllCharacteristics, 46 | Unknown, 47 | DllName, 48 | BaseAddress 49 | ); 50 | } 51 | NTFSDUPE_DBG( 52 | L"ntfsdupe::hooks::LdrLoadDll_hook original '%s'", 53 | std::wstring(DllName->Buffer, DllName->Length / sizeof(wchar_t)).c_str() 54 | ); 55 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 56 | auto len = wcslen(cfg->target_filename.c_str()) * sizeof(wchar_t) + sizeof(wchar_t); 57 | auto dllname_new = unique_ptr_stack(wchar_t, len); 58 | if (dllname_new) { 59 | ntfsdupe::hooks::copy_new_module_target(dllname_new.get(), DllName, cfg); 60 | 61 | ntfsdupe::helpers::upper(dllname_new.get(), len / sizeof(wchar_t)); 62 | std::wstring_view wsv(dllname_new.get(), len / sizeof(wchar_t)); 63 | wsv = wsv.substr(wsv.find_last_of(L'\\') + 1); 64 | 65 | ntfsdupe::cfgs::add_bypass(wsv); 66 | // backup original buffer 67 | const auto buffer_backup = DllName->Buffer; 68 | const auto length_backup = DllName->Length; 69 | const auto max_length_backup = DllName->MaximumLength; 70 | // set new buffer 71 | DllName->Buffer = dllname_new.get(); 72 | DllName->Length = static_cast(wcslen(DllName->Buffer) * sizeof(wchar_t)); 73 | DllName->MaximumLength = static_cast(len); 74 | // call original API 75 | const auto result = LdrLoadDll_original( 76 | DllCharacteristics, 77 | Unknown, 78 | DllName, 79 | BaseAddress 80 | ); 81 | // restore original buffer 82 | DllName->Buffer = buffer_backup; 83 | DllName->Length = length_backup; 84 | DllName->MaximumLength = max_length_backup; 85 | ntfsdupe::cfgs::remove_bypass(wsv); 86 | 87 | return result; 88 | } 89 | } 90 | break; 91 | 92 | default: break; 93 | } 94 | 95 | // if redirection/hiding didn't do anything, then bypass this dll to avoid later redirection by NtOpenFile or NtCreateFile 96 | auto terminated_name = unique_ptr_stack(wchar_t, DllName->Length + sizeof(wchar_t)); 97 | if (terminated_name) { 98 | memcpy(terminated_name.get(), DllName->Buffer, DllName->Length); 99 | terminated_name.get()[DllName->Length / sizeof(wchar_t)] = L'\0'; 100 | auto len = DllName->Length; 101 | ntfsdupe::helpers::upper(terminated_name.get(), len / sizeof(wchar_t)); 102 | std::wstring_view wsv(terminated_name.get(), len / sizeof(wchar_t)); 103 | wsv = wsv.substr(wsv.find_last_of(L'\\') + 1); 104 | 105 | ntfsdupe::cfgs::add_bypass(wsv); 106 | const auto result = LdrLoadDll_original( 107 | DllCharacteristics, 108 | Unknown, 109 | DllName, 110 | BaseAddress 111 | ); 112 | ntfsdupe::cfgs::remove_bypass(wsv); 113 | return result; 114 | } 115 | } 116 | 117 | // if everything above failed 118 | return LdrLoadDll_original( 119 | DllCharacteristics, 120 | Unknown, 121 | DllName, 122 | BaseAddress 123 | ); 124 | } 125 | catch (...) { 126 | NTFSDUPE_DBG(L"ntfsdupe::hooks::LdrLoadDll_hook exception occurred"); 127 | return LdrLoadDll_original( 128 | DllCharacteristics, 129 | Unknown, 130 | DllName, 131 | BaseAddress 132 | ); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtCreateDirectoryObjectEx_hook/NtCreateDirectoryObjectEx_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtCreateDirectoryObjectEx_hook) *NtCreateDirectoryObjectEx_original = nullptr; 6 | } 7 | 8 | 9 | // // https://googleprojectzero.blogspot.com/2016/08/ 10 | NTSTATUS NTAPI ntfsdupe::hooks::NtCreateDirectoryObjectEx_hook( 11 | __out PHANDLE DirectoryHandle, 12 | __in ACCESS_MASK DesiredAccess, 13 | __in POBJECT_ATTRIBUTES ObjectAttributes, 14 | __in_opt HANDLE ShadowDirectoryHandle, 15 | __in ULONG Flags 16 | ) 17 | { 18 | try { 19 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 20 | if (cfg) { 21 | switch (cfg->mode) { 22 | case ntfsdupe::cfgs::FileType::hide: 23 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 24 | NTFSDUPE_DBG( 25 | L"ntfsdupe::hooks::NtCreateDirectoryObjectEx_hook hide/target '%s'", 26 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 27 | ); 28 | return STATUS_OBJECT_NAME_NOT_FOUND; 29 | } 30 | 31 | case ntfsdupe::cfgs::FileType::original: { 32 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 33 | return NtCreateDirectoryObjectEx_original( 34 | DirectoryHandle, 35 | DesiredAccess, 36 | ObjectAttributes, 37 | ShadowDirectoryHandle, 38 | Flags 39 | ); 40 | } 41 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtCreateDirectoryObjectEx_hook original '%s'", cfg->original.c_str()); 42 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 43 | 44 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t);; 45 | auto object_name_new = unique_ptr_stack(wchar_t, len); 46 | if (object_name_new) { 47 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 48 | 49 | // backup original buffer 50 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 51 | const auto length_backup = ObjectAttributes->ObjectName->Length; 52 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 53 | // set new buffer 54 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 55 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 56 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 57 | const auto result = NtCreateDirectoryObjectEx_original( 58 | DirectoryHandle, 59 | DesiredAccess, 60 | ObjectAttributes, 61 | ShadowDirectoryHandle, 62 | Flags 63 | ); 64 | // restore original buffer 65 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 66 | ObjectAttributes->ObjectName->Length = length_backup; 67 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 68 | return result; 69 | } 70 | } 71 | break; 72 | 73 | default: break; 74 | } 75 | 76 | } 77 | 78 | return NtCreateDirectoryObjectEx_original( 79 | DirectoryHandle, 80 | DesiredAccess, 81 | ObjectAttributes, 82 | ShadowDirectoryHandle, 83 | Flags 84 | ); 85 | } 86 | catch (...) { 87 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtCreateDirectoryObjectEx_hook exception occurred"); 88 | return NtCreateDirectoryObjectEx_original( 89 | DirectoryHandle, 90 | DesiredAccess, 91 | ObjectAttributes, 92 | ShadowDirectoryHandle, 93 | Flags 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtCreateDirectoryObject_hook/NtCreateDirectoryObject_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtCreateDirectoryObject_hook) *NtCreateDirectoryObject_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::NtCreateDirectoryObject_hook( 10 | __out PHANDLE DirectoryHandle, 11 | __in ACCESS_MASK DesiredAccess, 12 | __in POBJECT_ATTRIBUTES ObjectAttributes 13 | ) 14 | { 15 | try{ 16 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 17 | if (cfg) { 18 | switch (cfg->mode) { 19 | case ntfsdupe::cfgs::FileType::hide: 20 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 21 | NTFSDUPE_DBG( 22 | L"ntfsdupe::hooks::NtCreateDirectoryObject_hook hide/target '%s'", 23 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 24 | ); 25 | return STATUS_OBJECT_NAME_NOT_FOUND; 26 | } 27 | 28 | case ntfsdupe::cfgs::FileType::original: { 29 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 30 | return NtCreateDirectoryObject_original( 31 | DirectoryHandle, 32 | DesiredAccess, 33 | ObjectAttributes 34 | ); 35 | } 36 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtCreateDirectoryObject_hook original '%s'", cfg->original.c_str()); 37 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 38 | 39 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t); 40 | auto object_name_new = unique_ptr_stack(wchar_t, len); 41 | if (object_name_new) { 42 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 43 | 44 | // backup original buffer 45 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 46 | const auto length_backup = ObjectAttributes->ObjectName->Length; 47 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 48 | // set new buffer 49 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 50 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 51 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 52 | const auto result = NtCreateDirectoryObject_original( 53 | DirectoryHandle, 54 | DesiredAccess, 55 | ObjectAttributes 56 | ); 57 | // restore original buffer 58 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 59 | ObjectAttributes->ObjectName->Length = length_backup; 60 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 61 | return result; 62 | } 63 | } 64 | break; 65 | 66 | default: break; 67 | } 68 | } 69 | 70 | return NtCreateDirectoryObject_original( 71 | DirectoryHandle, 72 | DesiredAccess, 73 | ObjectAttributes 74 | ); 75 | } 76 | catch(...){ 77 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtCreateDirectoryObject_hook exception occurred"); 78 | return NtCreateDirectoryObject_original( 79 | DirectoryHandle, 80 | DesiredAccess, 81 | ObjectAttributes 82 | ); 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtCreateFile_hook/NtCreateFile_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtCreateFile_hook) *NtCreateFile_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::NtCreateFile_hook( 10 | __out PHANDLE FileHandle, 11 | __in ACCESS_MASK DesiredAccess, 12 | __in POBJECT_ATTRIBUTES ObjectAttributes, 13 | __out PIO_STATUS_BLOCK IoStatusBlock, 14 | __in_opt PLARGE_INTEGER AllocationSize, 15 | __in ULONG FileAttributes, 16 | __in ULONG ShareAccess, 17 | __in ULONG CreateDisposition, 18 | __in ULONG CreateOptions, 19 | __in_opt PVOID EaBuffer, 20 | __in ULONG EaLength 21 | ) 22 | { 23 | try { 24 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 25 | if (cfg) { 26 | switch (cfg->mode) { 27 | case ntfsdupe::cfgs::FileType::hide: 28 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 29 | NTFSDUPE_DBG( 30 | L"ntfsdupe::hooks::NtCreateFile_hook hide/target '%s'", 31 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 32 | ); 33 | // the original API doesn't change IoStatusBlock or FileHandle on failure 34 | // it returns STATUS_OBJECT_NAME_NOT_FOUND 35 | return STATUS_OBJECT_NAME_NOT_FOUND; 36 | } 37 | 38 | case ntfsdupe::cfgs::FileType::original: { 39 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 40 | return NtCreateFile_original( 41 | FileHandle, DesiredAccess, 42 | ObjectAttributes, IoStatusBlock, 43 | AllocationSize, FileAttributes, 44 | ShareAccess, CreateDisposition, 45 | CreateOptions, EaBuffer, EaLength 46 | ); 47 | } 48 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtCreateFile_hook original '%s'", cfg->original.c_str()); 49 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 50 | 51 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t);; 52 | auto object_name_new = unique_ptr_stack(wchar_t, len); 53 | if (object_name_new) { 54 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 55 | 56 | // backup original buffer 57 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 58 | const auto length_backup = ObjectAttributes->ObjectName->Length; 59 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 60 | // set new buffer 61 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 62 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 63 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 64 | const auto result = NtCreateFile_original( 65 | FileHandle, DesiredAccess, 66 | ObjectAttributes, IoStatusBlock, 67 | AllocationSize, FileAttributes, 68 | ShareAccess, CreateDisposition, 69 | CreateOptions, EaBuffer, EaLength 70 | ); 71 | // restore original buffer 72 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 73 | ObjectAttributes->ObjectName->Length = length_backup; 74 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 75 | return result; 76 | } 77 | } 78 | break; 79 | 80 | default: break; 81 | } 82 | 83 | } 84 | 85 | return NtCreateFile_original( 86 | FileHandle, DesiredAccess, 87 | ObjectAttributes, IoStatusBlock, 88 | AllocationSize, FileAttributes, 89 | ShareAccess, CreateDisposition, 90 | CreateOptions, EaBuffer, EaLength 91 | ); 92 | } 93 | catch (...) { 94 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtCreateFile_hook exception occurred"); 95 | return NtCreateFile_original( 96 | FileHandle, DesiredAccess, 97 | ObjectAttributes, IoStatusBlock, 98 | AllocationSize, FileAttributes, 99 | ShareAccess, CreateDisposition, 100 | CreateOptions, EaBuffer, EaLength 101 | ); 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtDeleteFile_hook/NtDeleteFile_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtDeleteFile_hook) *NtDeleteFile_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::NtDeleteFile_hook( 10 | __in POBJECT_ATTRIBUTES ObjectAttributes 11 | ) 12 | { 13 | try { 14 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 15 | if (cfg) { 16 | switch (cfg->mode) { 17 | case ntfsdupe::cfgs::FileType::hide: 18 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 19 | NTFSDUPE_DBG( 20 | L"ntfsdupe::hooks::NtDeleteFile_hook hide/target '%s'", 21 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 22 | ); 23 | // the original API doesn't change IoStatusBlock or FileHandle on failure 24 | // it returns STATUS_OBJECT_NAME_NOT_FOUND 25 | return STATUS_OBJECT_NAME_NOT_FOUND; 26 | } 27 | 28 | case ntfsdupe::cfgs::FileType::original: { 29 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 30 | return NtDeleteFile_original(ObjectAttributes); 31 | } 32 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtDeleteFile_hook original '%s'", cfg->original.c_str()); 33 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 34 | 35 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t);; 36 | auto object_name_new = unique_ptr_stack(wchar_t, len); 37 | if (object_name_new) { 38 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 39 | 40 | // backup original buffer 41 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 42 | const auto length_backup = ObjectAttributes->ObjectName->Length; 43 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 44 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 45 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 46 | // set new buffer 47 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 48 | const auto result = NtDeleteFile_original(ObjectAttributes); 49 | // restore original buffer 50 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 51 | ObjectAttributes->ObjectName->Length = length_backup; 52 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 53 | return result; 54 | } 55 | } 56 | break; 57 | 58 | default: break; 59 | } 60 | 61 | } 62 | 63 | return NtDeleteFile_original(ObjectAttributes); 64 | } 65 | catch (...) { 66 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtDeleteFile_hook exception occurred"); 67 | return NtDeleteFile_original(ObjectAttributes); 68 | } 69 | } -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtOpenDirectoryObject_hook/NtOpenDirectoryObject_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtOpenDirectoryObject_hook) *NtOpenDirectoryObject_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::NtOpenDirectoryObject_hook( 10 | __out PHANDLE DirectoryHandle, 11 | __in ACCESS_MASK DesiredAccess, 12 | __in POBJECT_ATTRIBUTES ObjectAttributes 13 | ) 14 | { 15 | try { 16 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 17 | if (cfg) { 18 | switch (cfg->mode) { 19 | case ntfsdupe::cfgs::FileType::hide: 20 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 21 | NTFSDUPE_DBG( 22 | L"ntfsdupe::hooks::NtOpenDirectoryObject_hook hide/target '%s'", 23 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 24 | ); 25 | return STATUS_OBJECT_NAME_NOT_FOUND; 26 | } 27 | 28 | case ntfsdupe::cfgs::FileType::original: { 29 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 30 | return NtOpenDirectoryObject_original( 31 | DirectoryHandle, 32 | DesiredAccess, 33 | ObjectAttributes 34 | ); 35 | } 36 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtOpenDirectoryObject_hook original '%s'", cfg->original.c_str()); 37 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 38 | 39 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t);; 40 | auto object_name_new = unique_ptr_stack(wchar_t, len); 41 | if (object_name_new) { 42 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 43 | 44 | // backup original buffer 45 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 46 | const auto length_backup = ObjectAttributes->ObjectName->Length; 47 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 48 | // set new buffer 49 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 50 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 51 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 52 | const auto result = NtOpenDirectoryObject_original( 53 | DirectoryHandle, 54 | DesiredAccess, 55 | ObjectAttributes 56 | ); 57 | // restore original buffer 58 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 59 | ObjectAttributes->ObjectName->Length = length_backup; 60 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 61 | return result; 62 | } 63 | } 64 | break; 65 | 66 | default: break; 67 | } 68 | 69 | } 70 | 71 | return NtOpenDirectoryObject_original( 72 | DirectoryHandle, 73 | DesiredAccess, 74 | ObjectAttributes 75 | ); 76 | } 77 | catch (...) { 78 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtOpenDirectoryObject_hook exception occurred"); 79 | return NtOpenDirectoryObject_original( 80 | DirectoryHandle, 81 | DesiredAccess, 82 | ObjectAttributes 83 | ); 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtOpenFile_hook/NtOpenFile_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtOpenFile_hook) *NtOpenFile_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::NtOpenFile_hook( 10 | __out PHANDLE FileHandle, 11 | __in ACCESS_MASK DesiredAccess, 12 | __in POBJECT_ATTRIBUTES ObjectAttributes, 13 | __out PIO_STATUS_BLOCK IoStatusBlock, 14 | __in ULONG ShareAccess, 15 | __in ULONG OpenOptions 16 | ) 17 | { 18 | try { 19 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 20 | if (cfg) { 21 | switch (cfg->mode) { 22 | case ntfsdupe::cfgs::FileType::hide: 23 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 24 | NTFSDUPE_DBG( 25 | L"ntfsdupe::hooks::NtOpenFile_hook hide/target '%s'", 26 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 27 | ); 28 | return STATUS_OBJECT_NAME_NOT_FOUND; 29 | } 30 | 31 | case ntfsdupe::cfgs::FileType::original: { 32 | if (ntfsdupe::cfgs::is_count_bypass(cfg)) { 33 | return NtOpenFile_original( 34 | FileHandle, DesiredAccess, 35 | ObjectAttributes, IoStatusBlock, 36 | ShareAccess, OpenOptions 37 | ); 38 | } 39 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtOpenFile_hook original '%s'", cfg->original.c_str()); 40 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 41 | 42 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t);; 43 | auto object_name_new = unique_ptr_stack(wchar_t, len); 44 | if (object_name_new) { 45 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 46 | 47 | // backup original buffer 48 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 49 | const auto length_backup = ObjectAttributes->ObjectName->Length; 50 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 51 | // set new buffer 52 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 53 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 54 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 55 | const auto result = NtOpenFile_original( 56 | FileHandle, DesiredAccess, 57 | ObjectAttributes, IoStatusBlock, 58 | ShareAccess, OpenOptions 59 | ); 60 | // restore original buffer 61 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 62 | ObjectAttributes->ObjectName->Length = length_backup; 63 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 64 | return result; 65 | } 66 | } 67 | break; 68 | 69 | default: break; 70 | } 71 | 72 | } 73 | 74 | return NtOpenFile_original( 75 | FileHandle, DesiredAccess, 76 | ObjectAttributes, IoStatusBlock, 77 | ShareAccess, OpenOptions 78 | ); 79 | } 80 | catch (...) { 81 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtOpenFile_hook exception occurred"); 82 | return NtOpenFile_original( 83 | FileHandle, DesiredAccess, 84 | ObjectAttributes, IoStatusBlock, 85 | ShareAccess, OpenOptions 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtQueryAttributesFile_hook/NtQueryAttributesFile_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtQueryAttributesFile_hook) *NtQueryAttributesFile_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::NtQueryAttributesFile_hook( 10 | __in POBJECT_ATTRIBUTES ObjectAttributes, 11 | __out PFILE_BASIC_INFORMATION FileInformation 12 | ) 13 | { 14 | try { 15 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 16 | if (cfg) { 17 | switch (cfg->mode) { 18 | case ntfsdupe::cfgs::FileType::hide: 19 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 20 | NTFSDUPE_DBG( 21 | L"ntfsdupe::hooks::NtQueryAttributesFile_hook hide/target '%s'", 22 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 23 | ); 24 | return STATUS_OBJECT_NAME_NOT_FOUND; 25 | } 26 | 27 | case ntfsdupe::cfgs::FileType::original: { 28 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryAttributesFile_hook original '%s'", cfg->original.c_str()); 29 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 30 | 31 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t);; 32 | auto object_name_new = unique_ptr_stack(wchar_t, len); 33 | if (object_name_new) { 34 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 35 | 36 | // backup original buffer 37 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 38 | const auto length_backup = ObjectAttributes->ObjectName->Length; 39 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 40 | // set new buffer 41 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 42 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 43 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 44 | const auto result = NtQueryAttributesFile_original( 45 | ObjectAttributes, 46 | FileInformation 47 | ); 48 | // restore original buffer 49 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 50 | ObjectAttributes->ObjectName->Length = length_backup; 51 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 52 | return result; 53 | } 54 | } 55 | break; 56 | 57 | default: break; 58 | } 59 | 60 | } 61 | 62 | return NtQueryAttributesFile_original( 63 | ObjectAttributes, 64 | FileInformation 65 | ); 66 | } 67 | catch (...) { 68 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryAttributesFile_hook exception occurred"); 69 | return NtQueryAttributesFile_original( 70 | ObjectAttributes, 71 | FileInformation 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtQueryDirectoryFileEx_hook/NtQueryDirectoryFileEx_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtQueryDirectoryFileEx_hook)* NtQueryDirectoryFileEx_original = nullptr; 6 | } 7 | 8 | #if !defined(SL_RESTART_SCAN) 9 | #define SL_RESTART_SCAN (0x00000001) 10 | #endif 11 | 12 | #if !defined(SL_RETURN_SINGLE_ENTRY) 13 | #define SL_RETURN_SINGLE_ENTRY (0x00000002) 14 | #endif 15 | 16 | NTSTATUS NTAPI ntfsdupe::hooks::NtQueryDirectoryFileEx_hook( 17 | __in HANDLE FileHandle, 18 | __in_opt HANDLE Event, 19 | __in_opt PIO_APC_ROUTINE ApcRoutine, 20 | __in_opt PVOID ApcContext, 21 | __out PIO_STATUS_BLOCK IoStatusBlock, 22 | __out PVOID FileInformation, 23 | __in ULONG Length, 24 | __in FILE_INFORMATION_CLASS FileInformationClass, 25 | __in ULONG QueryFlags, 26 | __in_opt PUNICODE_STRING FileName 27 | ) 28 | { 29 | try { 30 | auto result = NtQueryDirectoryFileEx_original( 31 | FileHandle, 32 | Event, 33 | ApcRoutine, 34 | ApcContext, 35 | IoStatusBlock, 36 | FileInformation, 37 | Length, 38 | FileInformationClass, 39 | QueryFlags, 40 | FileName 41 | ); 42 | if (!NT_SUCCESS(result)) return result; 43 | 44 | bool supported = false; 45 | ntfsdupe::hooks::MultiQueryOffsets_t query_info{}; 46 | switch (FileInformationClass) { 47 | case FILE_INFORMATION_CLASS_ACTUAL::FileDirectoryInformation: { 48 | auto info = (PFILE_DIRECTORY_INFORMATION)FileInformation; 49 | supported = true; 50 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 51 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 52 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 53 | } 54 | break; 55 | 56 | case FILE_INFORMATION_CLASS_ACTUAL::FileFullDirectoryInformation: { 57 | auto info = (PFILE_FULL_DIR_INFORMATION)FileInformation; 58 | supported = true; 59 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 60 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 61 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 62 | } 63 | break; 64 | 65 | case FILE_INFORMATION_CLASS_ACTUAL::FileBothDirectoryInformation: { 66 | auto info = (PFILE_BOTH_DIR_INFORMATION)FileInformation; 67 | supported = true; 68 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 69 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 70 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 71 | } 72 | break; 73 | 74 | case FILE_INFORMATION_CLASS_ACTUAL::FileNamesInformation: { 75 | auto info = (PFILE_NAMES_INFORMATION)FileInformation; 76 | supported = true; 77 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 78 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 79 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 80 | } 81 | break; 82 | 83 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdBothDirectoryInformation: { 84 | auto info = (PFILE_ID_BOTH_DIR_INFORMATION)FileInformation; 85 | supported = true; 86 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 87 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 88 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 89 | } 90 | break; 91 | 92 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdFullDirectoryInformation: { 93 | auto info = (PFILE_ID_FULL_DIR_INFORMATION)FileInformation; 94 | supported = true; 95 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 96 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 97 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 98 | } 99 | break; 100 | 101 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdGlobalTxDirectoryInformation: { 102 | auto info = (PFILE_ID_GLOBAL_TX_DIR_INFORMATION)FileInformation; 103 | supported = true; 104 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 105 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 106 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 107 | } 108 | break; 109 | 110 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdExtdDirectoryInformation: { 111 | auto info = (PFILE_ID_EXTD_DIR_INFORMATION)FileInformation; 112 | supported = true; 113 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 114 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 115 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 116 | } 117 | break; 118 | 119 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdExtdBothDirectoryInformation: { 120 | auto info = (PFILE_ID_EXTD_BOTH_DIR_INFORMATION)FileInformation; 121 | supported = true; 122 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 123 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 124 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 125 | } 126 | break; 127 | } 128 | 129 | if (!supported) return result; 130 | 131 | ULONG currentNodeBytes = *(ULONG*)((char*)FileInformation + query_info.node_next_entry_offset); 132 | 133 | // if the caller was querying a single object (via FindFirstFileExW() for example) 134 | if (!currentNodeBytes) { 135 | auto cfg = ntfsdupe::hooks::find_single_file_obj_base_handle(query_info, FileHandle, FileInformation); 136 | if (cfg) switch (cfg->mode) { 137 | case ntfsdupe::cfgs::FileType::target: 138 | case ntfsdupe::cfgs::FileType::hide: { 139 | NTFSDUPE_DBG( 140 | L"ntfsdupe::hooks::NtQueryDirectoryFileEx_hook hide/target '%s'", 141 | std::wstring( 142 | (wchar_t*)((char*)FileInformation + query_info.node_filename_offset), 143 | *(ULONG*)((char*)FileInformation + query_info.node_filename_bytes_offset) / sizeof(wchar_t) 144 | ).c_str() 145 | ); 146 | IoStatusBlock->Information = 0; 147 | return STATUS_OBJECT_NAME_NOT_FOUND; 148 | } 149 | break; 150 | 151 | case ntfsdupe::cfgs::FileType::original: { // then redirect to target 152 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryDirectoryFileEx_hook original '%s'", cfg->original.c_str()); 153 | // duplicate the handle 154 | auto handle_dup = ntfsdupe::ntapis::DuplicateHandle(FileHandle); 155 | if (handle_dup == INVALID_HANDLE_VALUE) return result; 156 | 157 | // target file info 158 | auto file_info_target = unique_ptr_stack(void, IoStatusBlock->Information); 159 | if (!file_info_target) return result; 160 | 161 | // create a copy of the target filename because we need a non const str 162 | auto len = sizeof(cfg->target_filename); 163 | auto target_filename_copy = unique_ptr_stack(wchar_t, len); 164 | if (!target_filename_copy) return result; 165 | 166 | memcpy(target_filename_copy.get(), cfg->target_filename, len); 167 | UNICODE_STRING ustr; 168 | ustr.Length = len; 169 | ustr.MaximumLength = len; 170 | ustr.Buffer = target_filename_copy.get(); 171 | 172 | // query the target file instead of the original 173 | IO_STATUS_BLOCK target_Io_status; 174 | NTSTATUS queryStatus = NtQueryDirectoryFileEx_original( 175 | handle_dup, 176 | NULL, 177 | NULL, 178 | NULL, 179 | &target_Io_status,//IoStatusBlock, 180 | file_info_target.get(),//FileInformation, 181 | (ULONG)IoStatusBlock->Information, //Length, 182 | FileInformationClass, 183 | SL_RESTART_SCAN | SL_RETURN_SINGLE_ENTRY, 184 | &ustr 185 | ); 186 | ntfsdupe::ntapis::CloseHandle(handle_dup); 187 | 188 | if (!NT_SUCCESS(queryStatus)) { 189 | memcpy(IoStatusBlock, &target_Io_status, sizeof(target_Io_status)); 190 | return queryStatus; 191 | } 192 | 193 | memcpy(FileInformation, file_info_target.get(), target_Io_status.Information); 194 | } 195 | break; 196 | 197 | default: break; 198 | } 199 | 200 | return result; 201 | } 202 | 203 | return ntfsdupe::hooks::handle_file_multi_query(query_info, FileHandle, FileInformation, IoStatusBlock, result); 204 | } 205 | catch (...) { 206 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryDirectoryFileEx_hook exception occurred"); 207 | return NtQueryDirectoryFileEx_original( 208 | FileHandle, 209 | Event, 210 | ApcRoutine, 211 | ApcContext, 212 | IoStatusBlock, 213 | FileInformation, 214 | Length, 215 | FileInformationClass, 216 | QueryFlags, 217 | FileName 218 | ); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtQueryDirectoryFile_hook/NtQueryDirectoryFile_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtQueryDirectoryFile_hook) *NtQueryDirectoryFile_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::NtQueryDirectoryFile_hook( 10 | __in HANDLE FileHandle, 11 | __in_opt HANDLE Event, 12 | __in_opt PIO_APC_ROUTINE ApcRoutine, 13 | __in_opt PVOID ApcContext, 14 | __out PIO_STATUS_BLOCK IoStatusBlock, 15 | __out PVOID FileInformation, 16 | __in ULONG Length, 17 | __in FILE_INFORMATION_CLASS FileInformationClass, 18 | __in BOOLEAN ReturnSingleEntry, 19 | __in_opt PUNICODE_STRING FileName, 20 | __in BOOLEAN RestartScan 21 | ) 22 | { 23 | try { 24 | auto result = NtQueryDirectoryFile_original( 25 | FileHandle, 26 | Event, 27 | ApcRoutine, 28 | ApcContext, 29 | IoStatusBlock, 30 | FileInformation, 31 | Length, 32 | FileInformationClass, 33 | ReturnSingleEntry, 34 | FileName, 35 | RestartScan 36 | ); 37 | if (!NT_SUCCESS(result)) return result; 38 | 39 | bool supported = false; 40 | ntfsdupe::hooks::MultiQueryOffsets_t query_info{}; 41 | switch (FileInformationClass) { 42 | case FILE_INFORMATION_CLASS_ACTUAL::FileDirectoryInformation: { 43 | auto info = (PFILE_DIRECTORY_INFORMATION)FileInformation; 44 | supported = true; 45 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 46 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 47 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 48 | } 49 | break; 50 | 51 | case FILE_INFORMATION_CLASS_ACTUAL::FileFullDirectoryInformation: { 52 | auto info = (PFILE_FULL_DIR_INFORMATION)FileInformation; 53 | supported = true; 54 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 55 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 56 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 57 | } 58 | break; 59 | 60 | case FILE_INFORMATION_CLASS_ACTUAL::FileBothDirectoryInformation: { 61 | auto info = (PFILE_BOTH_DIR_INFORMATION)FileInformation; 62 | supported = true; 63 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 64 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 65 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 66 | } 67 | break; 68 | 69 | case FILE_INFORMATION_CLASS_ACTUAL::FileNamesInformation: { 70 | auto info = (PFILE_NAMES_INFORMATION)FileInformation; 71 | supported = true; 72 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 73 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 74 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 75 | } 76 | break; 77 | 78 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdBothDirectoryInformation: { 79 | auto info = (PFILE_ID_BOTH_DIR_INFORMATION)FileInformation; 80 | supported = true; 81 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 82 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 83 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 84 | } 85 | break; 86 | 87 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdFullDirectoryInformation: { 88 | auto info = (PFILE_ID_FULL_DIR_INFORMATION)FileInformation; 89 | supported = true; 90 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 91 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 92 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 93 | } 94 | break; 95 | 96 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdGlobalTxDirectoryInformation: { 97 | auto info = (PFILE_ID_GLOBAL_TX_DIR_INFORMATION)FileInformation; 98 | supported = true; 99 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 100 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 101 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 102 | } 103 | break; 104 | 105 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdExtdDirectoryInformation: { 106 | auto info = (PFILE_ID_EXTD_DIR_INFORMATION)FileInformation; 107 | supported = true; 108 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 109 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 110 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 111 | } 112 | break; 113 | 114 | case FILE_INFORMATION_CLASS_ACTUAL::FileIdExtdBothDirectoryInformation: { 115 | auto info = (PFILE_ID_EXTD_BOTH_DIR_INFORMATION)FileInformation; 116 | supported = true; 117 | query_info.node_next_entry_offset = (unsigned long)((char*)&info->NextEntryOffset - (char*)info); 118 | query_info.node_filename_offset = (unsigned long)((char*)&info->FileName - (char*)info); 119 | query_info.node_filename_bytes_offset = (unsigned long)((char*)&info->FileNameLength - (char*)info); 120 | } 121 | break; 122 | } 123 | 124 | if (!supported) return result; 125 | 126 | ULONG currentNodeBytes = *(ULONG*)((char*)FileInformation + query_info.node_next_entry_offset); 127 | 128 | // if the caller was querying a single object 129 | if (!currentNodeBytes) { 130 | auto cfg = ntfsdupe::hooks::find_single_file_obj_base_handle(query_info, FileHandle, FileInformation); 131 | if (cfg) switch (cfg->mode) { 132 | case ntfsdupe::cfgs::FileType::target: 133 | case ntfsdupe::cfgs::FileType::hide: { 134 | NTFSDUPE_DBG( 135 | L"ntfsdupe::hooks::NtQueryDirectoryFile_hook hide/target '%s'", 136 | std::wstring( 137 | (wchar_t*)((char*)FileInformation + query_info.node_filename_offset), 138 | *(ULONG*)((char*)FileInformation + query_info.node_filename_bytes_offset) / sizeof(wchar_t) 139 | ).c_str() 140 | ); 141 | IoStatusBlock->Information = 0; 142 | return STATUS_OBJECT_NAME_NOT_FOUND; 143 | } 144 | break; 145 | 146 | case ntfsdupe::cfgs::FileType::original: { // then redirect to target 147 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryDirectoryFile_hook original '%s'", cfg->original.c_str()); 148 | // duplicate the handle 149 | auto handle_dup = ntfsdupe::ntapis::DuplicateHandle(FileHandle); 150 | if (handle_dup == INVALID_HANDLE_VALUE) return result; 151 | 152 | // target file info 153 | auto file_info_target = unique_ptr_stack(void, IoStatusBlock->Information); 154 | if (!file_info_target) return result; 155 | 156 | // create a copy of the target filename because we need a non const str 157 | auto len = sizeof(cfg->target_filename); 158 | auto target_filename_copy = unique_ptr_stack(wchar_t, len); 159 | if (!target_filename_copy) return result; 160 | 161 | memcpy(target_filename_copy.get(), cfg->target_filename, len); 162 | UNICODE_STRING ustr; 163 | ustr.Length = len; 164 | ustr.MaximumLength = len; 165 | ustr.Buffer = target_filename_copy.get(); 166 | 167 | // query the target file instead of the original 168 | IO_STATUS_BLOCK target_Io_status; 169 | NTSTATUS queryStatus = NtQueryDirectoryFile_original( 170 | handle_dup, 171 | NULL, 172 | NULL, 173 | NULL, 174 | &target_Io_status,//IoStatusBlock, 175 | file_info_target.get(),//FileInformation, 176 | (ULONG)IoStatusBlock->Information, //Length, 177 | FileInformationClass, 178 | TRUE, 179 | &ustr, 180 | TRUE 181 | ); 182 | ntfsdupe::ntapis::CloseHandle(handle_dup); 183 | 184 | if (!NT_SUCCESS(queryStatus)) { 185 | memcpy(IoStatusBlock, &target_Io_status, sizeof(target_Io_status)); 186 | return queryStatus; 187 | } 188 | 189 | memcpy(FileInformation, file_info_target.get(), target_Io_status.Information); 190 | } 191 | break; 192 | 193 | default: break; 194 | } 195 | 196 | return result; 197 | } 198 | 199 | return ntfsdupe::hooks::handle_file_multi_query(query_info, FileHandle, FileInformation, IoStatusBlock, result); 200 | } 201 | catch (...) { 202 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryDirectoryFile_hook exception occurred"); 203 | return NtQueryDirectoryFile_original( 204 | FileHandle, 205 | Event, 206 | ApcRoutine, 207 | ApcContext, 208 | IoStatusBlock, 209 | FileInformation, 210 | Length, 211 | FileInformationClass, 212 | ReturnSingleEntry, 213 | FileName, 214 | RestartScan 215 | ); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtQueryFullAttributesFile_hook/NtQueryFullAttributesFile_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtQueryFullAttributesFile_hook) *NtQueryFullAttributesFile_original = nullptr; 6 | } 7 | 8 | NTSTATUS NTAPI ntfsdupe::hooks::NtQueryFullAttributesFile_hook( 9 | __in POBJECT_ATTRIBUTES ObjectAttributes, 10 | __out PFILE_NETWORK_OPEN_INFORMATION FileInformation 11 | ) 12 | { 13 | try { 14 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 15 | if (cfg) { 16 | switch (cfg->mode) { 17 | case ntfsdupe::cfgs::FileType::hide: 18 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 19 | NTFSDUPE_DBG( 20 | L"ntfsdupe::hooks::NtQueryFullAttributesFile_hook hide/target '%s'", 21 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 22 | ); 23 | return STATUS_OBJECT_NAME_NOT_FOUND; 24 | } 25 | 26 | case ntfsdupe::cfgs::FileType::original: { 27 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryFullAttributesFile_hook original '%s'", cfg->original.c_str()); 28 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 29 | 30 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t);; 31 | auto object_name_new = unique_ptr_stack(wchar_t, len); 32 | if (object_name_new) { 33 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 34 | 35 | // backup original buffer 36 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 37 | const auto length_backup = ObjectAttributes->ObjectName->Length; 38 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 39 | // set new buffer 40 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 41 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 42 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 43 | const auto result = NtQueryFullAttributesFile_original( 44 | ObjectAttributes, 45 | FileInformation 46 | ); 47 | // restore original buffer 48 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 49 | ObjectAttributes->ObjectName->Length = length_backup; 50 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 51 | return result; 52 | } 53 | } 54 | break; 55 | 56 | default: break; 57 | } 58 | 59 | } 60 | 61 | return NtQueryFullAttributesFile_original( 62 | ObjectAttributes, 63 | FileInformation 64 | ); 65 | } 66 | catch (...) { 67 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryFullAttributesFile_hook exception occurred"); 68 | return NtQueryFullAttributesFile_original( 69 | ObjectAttributes, 70 | FileInformation 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtQueryInformationByName_hook/NtQueryInformationByName_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtQueryInformationByName_hook) *NtQueryInformationByName_original = nullptr; 6 | } 7 | 8 | 9 | NTSTATUS NTAPI ntfsdupe::hooks::NtQueryInformationByName_hook( 10 | __in POBJECT_ATTRIBUTES ObjectAttributes, 11 | __out PIO_STATUS_BLOCK IoStatusBlock, 12 | __out PVOID FileInformation, 13 | __in ULONG Length, 14 | __in FILE_INFORMATION_CLASS FileInformationClass 15 | ) 16 | { 17 | try { 18 | auto cfg = find_single_file_obj_ntpath(ObjectAttributes); 19 | if (cfg) { 20 | switch (cfg->mode) { 21 | case ntfsdupe::cfgs::FileType::hide: 22 | case ntfsdupe::cfgs::FileType::target: { // target files are invisible to the process 23 | NTFSDUPE_DBG( 24 | L"ntfsdupe::hooks::NtQueryInformationByName_hook hide/target '%s'", 25 | std::wstring(ObjectAttributes->ObjectName->Buffer, ObjectAttributes->ObjectName->Length / sizeof(wchar_t)).c_str() 26 | ); 27 | return STATUS_OBJECT_NAME_NOT_FOUND; 28 | } 29 | 30 | case ntfsdupe::cfgs::FileType::original: { 31 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryInformationByName_hook original '%s'", cfg->original.c_str()); 32 | // it would be cheaper to just manipulate the original str, but not sure if that's safe 33 | 34 | auto len = (ObjectAttributes->ObjectName->Length - cfg->filename_bytes + (cfg->target.length() * sizeof(wchar_t)) ) + sizeof(wchar_t);; 35 | auto object_name_new = unique_ptr_stack(wchar_t, len); 36 | if (object_name_new) { 37 | copy_new_file_target(object_name_new.get(), ObjectAttributes->ObjectName, cfg); 38 | 39 | // backup original buffer 40 | const auto buffer_backup = ObjectAttributes->ObjectName->Buffer; 41 | const auto length_backup = ObjectAttributes->ObjectName->Length; 42 | const auto max_length_backup = ObjectAttributes->ObjectName->MaximumLength; 43 | // set new buffer 44 | ObjectAttributes->ObjectName->Buffer = object_name_new.get(); 45 | ObjectAttributes->ObjectName->Length = static_cast(wcslen(ObjectAttributes->ObjectName->Buffer) * sizeof(wchar_t)); 46 | ObjectAttributes->ObjectName->MaximumLength = static_cast(len); 47 | const auto result = NtQueryInformationByName_original( 48 | ObjectAttributes, 49 | IoStatusBlock, 50 | FileInformation, 51 | Length, 52 | FileInformationClass 53 | ); 54 | // restore original buffer 55 | ObjectAttributes->ObjectName->Buffer = buffer_backup; 56 | ObjectAttributes->ObjectName->Length = length_backup; 57 | ObjectAttributes->ObjectName->MaximumLength = max_length_backup; 58 | return result; 59 | } 60 | } 61 | break; 62 | 63 | default: break; 64 | } 65 | 66 | } 67 | 68 | return NtQueryInformationByName_original( 69 | ObjectAttributes, 70 | IoStatusBlock, 71 | FileInformation, 72 | Length, 73 | FileInformationClass 74 | ); 75 | } 76 | catch (...) { 77 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryInformationByName_hook exception occurred"); 78 | return NtQueryInformationByName_original( 79 | ObjectAttributes, 80 | IoStatusBlock, 81 | FileInformation, 82 | Length, 83 | FileInformationClass 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /nt_file_dupe/src/Hooks/NtQueryInformationFile_hook/NtQueryInformationFile_hook.cpp: -------------------------------------------------------------------------------- 1 | #include "Hooks/Hooks.hpp" 2 | 3 | 4 | namespace ntfsdupe::hooks { 5 | decltype(NtQueryInformationFile_hook) *NtQueryInformationFile_original = nullptr; 6 | } 7 | 8 | // called from: 9 | // DeleteFileW 10 | // CreateFileInternal << CreateFileA | CreateFileW | CreateFile2 11 | // GetFileInformationByHandleEx 12 | // GetFileSize | GetFileSizeEx 13 | // RemoveDirectoryW 14 | // FindFirstFileNameW 15 | // CreateDirectoryExW 16 | NTSTATUS NTAPI ntfsdupe::hooks::NtQueryInformationFile_hook( 17 | __in HANDLE FileHandle, 18 | __out PIO_STATUS_BLOCK IoStatusBlock, 19 | __out PVOID FileInformation, 20 | __in ULONG Length, 21 | __in FILE_INFORMATION_CLASS FileInformationClass 22 | ) 23 | { 24 | try { 25 | auto result = NtQueryInformationFile_original( 26 | FileHandle, 27 | IoStatusBlock, 28 | FileInformation, 29 | Length, 30 | FileInformationClass 31 | ); 32 | if (!NT_SUCCESS(result)) return result; 33 | 34 | wchar_t* name = nullptr; 35 | ULONG nameLength = 0; 36 | const ntfsdupe::cfgs::FileCfgEntry* cfg = nullptr; 37 | switch ((FILE_INFORMATION_CLASS_ACTUAL)FileInformationClass) { 38 | case FILE_INFORMATION_CLASS_ACTUAL::FileNameInformation: 39 | case FILE_INFORMATION_CLASS_ACTUAL::FileNormalizedNameInformation: { 40 | auto info = (PFILE_NAME_INFORMATION)FileInformation; 41 | name = (wchar_t*)&info->FileName; 42 | nameLength = info->FileNameLength; 43 | cfg = ntfsdupe::hooks::find_single_file_obj_ntpath(name, nameLength); 44 | break; 45 | } 46 | 47 | case FILE_INFORMATION_CLASS_ACTUAL::FileAllInformation: 48 | case FILE_INFORMATION_CLASS_ACTUAL::FileAlternateNameInformation: { 49 | auto info = (PFILE_ALL_INFORMATION)FileInformation; 50 | name = (wchar_t*)&info->NameInformation.FileName; 51 | nameLength = info->NameInformation.FileNameLength; 52 | cfg = ntfsdupe::hooks::find_single_file_obj_ntpath(name, nameLength); 53 | break; 54 | } 55 | 56 | // FileStreamInformation is used to get alternate files streams contained inside this file, 57 | // changing their names is not really useful, is it ? 58 | 59 | // https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions#hard-links 60 | // FileHardLinkInformation returns the names of the links, not the original file 61 | // changing their names is not really useful, is it ? 62 | 63 | default: break; 64 | } 65 | 66 | // unsupported FileInformationClass 67 | if (!nameLength || !cfg) return result; 68 | 69 | // query APIs like this one has to be recrified because a process can detect it like this: 70 | // 1. call NtOpenFile() to get a file handle, which will redirect myfile.dll to myfile.rdr 71 | // 2. call NtQueryInformationFile() and specify filename as info type 72 | // in that case we have to change the name back from myfile.rdr to myfile.dll 73 | if (cfg->mode == ntfsdupe::cfgs::FileType::target) { 74 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryInformationFile_hook target '%s'", cfg->target.c_str()); 75 | const auto path_bytes = nameLength - cfg->filename_bytes; 76 | // write original file name 77 | memcpy( 78 | (char*)name + path_bytes, 79 | cfg->original_filename, 80 | cfg->filename_bytes 81 | ); 82 | } 83 | 84 | return result; 85 | } 86 | catch (...) { 87 | NTFSDUPE_DBG(L"ntfsdupe::hooks::NtQueryInformationFile_hook exception occurred"); 88 | return NtQueryInformationFile_original( 89 | FileHandle, 90 | IoStatusBlock, 91 | FileInformation, 92 | Length, 93 | FileInformationClass 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /nt_file_dupe/src/NtApis/NtApis.cpp: -------------------------------------------------------------------------------- 1 | #include "NtApis/NtApis.hpp" 2 | #include "Helpers/dbglog.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace ntfsdupe::ntapis { 9 | typedef struct _RTL_BUFFER { 10 | PUCHAR Buffer; 11 | PUCHAR StaticBuffer; 12 | SIZE_T Size; 13 | SIZE_T StaticSize; 14 | SIZE_T ReservedForAllocatedSize; // for future doubling 15 | PVOID ReservedForIMalloc; // for future pluggable growth 16 | } RTL_BUFFER, * PRTL_BUFFER; 17 | 18 | typedef struct _RTL_UNICODE_STRING_BUFFER { 19 | __in UNICODE_STRING String; 20 | __in RTL_BUFFER ByteBuffer; 21 | __in_opt UCHAR MinimumStaticBufferForTerminalNul[sizeof(WCHAR)]; 22 | } RTL_UNICODE_STRING_BUFFER, * PRTL_UNICODE_STRING_BUFFER; 23 | 24 | // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdmsec/nf-wdmsec-wdmlibrtlinitunicodestringex 25 | // https://github.com/wine-mirror/wine/blob/master/dlls/ntdll/rtlstr.c 26 | // https://github.com/mic101/windows/blob/master/WRK-v1.2/base/ntos/rtl/ntrtlp.h 27 | // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/shared/ntdef.h 28 | // https://github.com/mic101/windows/blob/6c4cf038dbb2969b1851511271e2c9d137f211a9/WRK-v1.2/public/sdk/inc/ntrtlpath.h 29 | typedef NTSTATUS(NTAPI* RtlNtPathNameToDosPathName_t)( 30 | __in ULONG Flags, 31 | __inout PRTL_UNICODE_STRING_BUFFER Path, 32 | __out_opt PULONG Disposition, 33 | __inout_opt PWSTR* FilePart 34 | ); 35 | 36 | static RtlNtPathNameToDosPathName_t RtlNtPathNameToDosPathName_original = nullptr; 37 | } 38 | 39 | namespace ntfsdupe::ntapis { 40 | typedef ULONG(NTAPI* RtlGetFullPathName_U_t)( 41 | _In_ PWSTR FileName, 42 | _In_ ULONG BufferLength, 43 | _Out_writes_bytes_(BufferLength) PWSTR Buffer, 44 | _Out_opt_ PWSTR* FilePart 45 | ); 46 | 47 | static RtlGetFullPathName_U_t RtlGetFullPathName_U_original = nullptr; 48 | } 49 | 50 | namespace ntfsdupe::ntapis { 51 | typedef NTSTATUS (NTAPI* NtDuplicateObject_t)( 52 | __in HANDLE SourceProcessHandle, 53 | __in HANDLE SourceHandle, 54 | __in_opt HANDLE TargetProcessHandle, 55 | __out_opt PHANDLE TargetHandle, 56 | __in ACCESS_MASK DesiredAccess, 57 | __in ULONG HandleAttributes, 58 | __in ULONG Options 59 | ); 60 | 61 | static NtDuplicateObject_t NtDuplicateObject_original = nullptr; 62 | } 63 | 64 | namespace ntfsdupe::ntapis { 65 | typedef NTSTATUS(NTAPI* NtClose_t)( 66 | __in HANDLE Handle 67 | ); 68 | 69 | static NtClose_t NtClose_original = nullptr; 70 | } 71 | 72 | namespace ntfsdupe::ntapis { 73 | // https://github.com/wine-mirror/wine/blob/master/dlls/ntdll/rtl.c 74 | typedef void (NTAPI* RtlAcquirePebLock_t)(void); 75 | 76 | typedef void (NTAPI* RtlReleasePebLock_t)(void); 77 | 78 | RtlAcquirePebLock_t RtlAcquirePebLock_original; 79 | RtlReleasePebLock_t RtlReleasePebLock_original; 80 | } 81 | 82 | 83 | bool ntfsdupe::ntapis::init(void) 84 | { 85 | NTFSDUPE_DBG(L"ntfsdupe::ntapis::init()"); 86 | auto NtdllHmod = GetModuleHandleW(L"ntdll.dll"); 87 | if (!NtdllHmod) { 88 | NTFSDUPE_DBG(L" failed to get ntdll.dll hmodule"); 89 | return false; 90 | } 91 | 92 | RtlNtPathNameToDosPathName_original = (decltype(RtlNtPathNameToDosPathName_original))GetProcAddress(NtdllHmod, "RtlNtPathNameToDosPathName"); 93 | if (!RtlNtPathNameToDosPathName_original) { 94 | NTFSDUPE_DBG(L" failed to get addr of RtlNtPathNameToDosPathName"); 95 | return false; 96 | } 97 | 98 | RtlGetFullPathName_U_original = (decltype(RtlGetFullPathName_U_original))GetProcAddress(NtdllHmod, "RtlGetFullPathName_U"); 99 | if (!RtlGetFullPathName_U_original) { 100 | NTFSDUPE_DBG(L" failed to get addr of RtlGetFullPathName_U"); 101 | return false; 102 | } 103 | 104 | NtDuplicateObject_original = (decltype(NtDuplicateObject_original))GetProcAddress(NtdllHmod, (std::string("Nt") + "DuplicateObject").c_str()); 105 | if (!NtDuplicateObject_original) 106 | NtDuplicateObject_original = (decltype(NtDuplicateObject_original))GetProcAddress(NtdllHmod, (std::string("Zw") + "DuplicateObject").c_str()); 107 | if (!NtDuplicateObject_original) { 108 | NTFSDUPE_DBG(L" failed to get addr of NtDuplicateObject"); 109 | return false; 110 | } 111 | 112 | NtClose_original = (decltype(NtClose_original))GetProcAddress(NtdllHmod, (std::string("Nt") + "Close").c_str()); 113 | if (!NtClose_original) 114 | NtClose_original = (decltype(NtClose_original))GetProcAddress(NtdllHmod, (std::string("Zw") + "Close").c_str()); 115 | if (!NtClose_original) { 116 | NTFSDUPE_DBG(L" failed to get addr of NtClose"); 117 | return false; 118 | } 119 | 120 | RtlAcquirePebLock_original = (decltype(RtlAcquirePebLock_original))GetProcAddress(NtdllHmod, "RtlAcquirePebLock"); 121 | if (!RtlAcquirePebLock_original) { 122 | NTFSDUPE_DBG(L" failed to get addr of RtlAcquirePebLock"); 123 | return false; 124 | } 125 | 126 | RtlReleasePebLock_original = (decltype(RtlReleasePebLock_original))GetProcAddress(NtdllHmod, "RtlReleasePebLock"); 127 | if (!RtlReleasePebLock_original) { 128 | NTFSDUPE_DBG(L" failed to get addr of RtlReleasePebLock"); 129 | return false; 130 | } 131 | 132 | return true; 133 | } 134 | 135 | 136 | // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html 137 | bool ntfsdupe::ntapis::NtPathToDosPath( 138 | PWSTR dosPath, USHORT* dosPathBytes, 139 | PCWSTR ntPath, const USHORT ntPathBytes) 140 | { 141 | if (!dosPath || !dosPathBytes || 142 | !ntPath || !*ntPath || !ntPathBytes) 143 | return false; 144 | 145 | USHORT required_bytes = ntPathBytes + sizeof(wchar_t); 146 | 147 | if (*dosPathBytes < required_bytes) 148 | return false; 149 | 150 | memcpy(dosPath, ntPath, ntPathBytes); // copy current nt path to dos path buffer 151 | dosPath[ntPathBytes / sizeof(wchar_t)] = L'\0'; 152 | 153 | ntfsdupe::ntapis::RTL_UNICODE_STRING_BUFFER rtlStr; 154 | memset(&rtlStr, 0, sizeof(rtlStr)); 155 | 156 | rtlStr.String.Buffer = (wchar_t*)dosPath; 157 | rtlStr.String.Length = ntPathBytes; 158 | rtlStr.String.MaximumLength = required_bytes; 159 | 160 | rtlStr.ByteBuffer.Buffer = (PUCHAR)dosPath; 161 | rtlStr.ByteBuffer.Size = ntPathBytes; 162 | 163 | ULONG conversion = 0; 164 | PWSTR unused = nullptr; 165 | NTSTATUS result = RtlNtPathNameToDosPathName_original(0, &rtlStr, &conversion, &unused); 166 | bool isOk = NT_SUCCESS(result) && (rtlStr.String.Length < *dosPathBytes); 167 | if (isOk) dosPath[rtlStr.String.Length / sizeof(wchar_t)] = L'\0'; 168 | 169 | // adjust to the actual amount of bytes 170 | *dosPathBytes = rtlStr.String.Length; 171 | return isOk; 172 | } 173 | 174 | 175 | // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html 176 | bool ntfsdupe::ntapis::GetFullDosPath(PWSTR fullPath, USHORT *fullPathBytes, PWSTR path) 177 | { 178 | if (!path || !fullPathBytes) return false; 179 | 180 | // when this fails it returns the required amount of bytes including null terminator 181 | // when it succeedes it returns the amount of written bytes without null terminator 182 | PWSTR filepart = nullptr; // unused 183 | ULONG writtenBytes = RtlGetFullPathName_U_original(path, *fullPathBytes, fullPath, &filepart); 184 | 185 | //_freea(pathProper); 186 | 187 | bool isOk = writtenBytes < *fullPathBytes; 188 | *fullPathBytes = (USHORT)writtenBytes; 189 | if (isOk) 190 | fullPath[writtenBytes / sizeof(wchar_t)] = L'\0'; 191 | 192 | return isOk; 193 | } 194 | 195 | 196 | // https://referencesource.microsoft.com/#mscorlib/microsoft/win32/win32native.cs,25aac1ead51b88ae 197 | HANDLE ntfsdupe::ntapis::DuplicateHandle(HANDLE original) 198 | { 199 | #define DUPLICATE_SAME_ATTRIBUTES 0x00000004 200 | 201 | HANDLE result = INVALID_HANDLE_VALUE; 202 | NTSTATUS status = NtDuplicateObject_original( 203 | GetCurrentProcess(), 204 | original, 205 | GetCurrentProcess(), 206 | &result, 207 | 0, 208 | 0, 209 | DUPLICATE_SAME_ATTRIBUTES | DUPLICATE_SAME_ACCESS 210 | ); 211 | if (!NT_SUCCESS(status)) return INVALID_HANDLE_VALUE; 212 | 213 | return result; 214 | 215 | #undef DUPLICATE_SAME_ATTRIBUTES 216 | } 217 | 218 | NTSTATUS ntfsdupe::ntapis::CloseHandle(HANDLE handle) 219 | { 220 | return NtClose_original(handle); 221 | } 222 | 223 | void ntfsdupe::ntapis::LockPeb(void) 224 | { 225 | RtlAcquirePebLock_original(); 226 | } 227 | 228 | void ntfsdupe::ntapis::ReleasePeb(void) 229 | { 230 | RtlReleasePebLock_original(); 231 | } 232 | -------------------------------------------------------------------------------- /nt_file_dupe/src/NtApis/peb_helper.cpp: -------------------------------------------------------------------------------- 1 | #include "NtApis/peb_helper.hpp" 2 | #include "NtApis/NtApis.hpp" 3 | 4 | #include // PEB stuff 5 | 6 | 7 | // worth checking: 8 | // https://learn.microsoft.com/en-us/archive/msdn-magazine/2002/march/windows-2000-loader-what-goes-on-inside-windows-2000-solving-the-mysteries-of-the-loader 9 | 10 | // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm 11 | // https://www.wikiwand.com/en/Win32_Thread_Information_Block 12 | // https://rvsec0n.wordpress.com/2019/09/13/routines-utilizing-tebs-and-pebs/ 13 | //#if defined _WIN64 14 | ////#define GetPeb() (PPEB)__readgsqword(FIELD_OFFSET(TEB, ProcessEnvironmentBlock)) 15 | //#define GetPeb() (PPEB)__readgsqword(0x60) 16 | //#else // x32 17 | ////#define GetPeb() (PPEB)__readfsdword(FIELD_OFFSET(TEB, ProcessEnvironmentBlock)) 18 | //#define GetPeb() (PPEB)__readfsdword(0x30) 19 | //#endif 20 | 21 | // Visual Studio friendly way 22 | #define GetPeb() NtCurrentTeb()->ProcessEnvironmentBlock 23 | 24 | 25 | 26 | 27 | // https://github.com/wine-mirror/wine/blob/master/include/winternl.h 28 | typedef struct 29 | { 30 | const PVOID _unused[2]; 31 | LIST_ENTRY InLoadOrderModuleList; 32 | LIST_ENTRY InMemoryOrderModuleList; 33 | LIST_ENTRY InInitializationOrderModuleList; 34 | } *PPEB_LDR_DATA_MOD_LIST; 35 | 36 | // https://github.com/wine-mirror/wine/blob/master/include/winternl.h 37 | typedef struct 38 | { 39 | LIST_ENTRY InLoadOrderLinks; 40 | LIST_ENTRY InMemoryOrderLinks; 41 | LIST_ENTRY InInitializationOrderLinks; 42 | 43 | const PVOID DllBase; 44 | const PVOID EntryPoint; 45 | ULONG SizeOfImage; 46 | UNICODE_STRING FullDllName; 47 | UNICODE_STRING BaseDllName; 48 | ULONG Flags; 49 | SHORT LoadCount; 50 | SHORT TlsIndex; 51 | 52 | LIST_ENTRY HashLinks; 53 | 54 | ULONG TimeDateStamp; 55 | HANDLE ActivationContext; 56 | const PVOID Lock; 57 | const PVOID DdagNode; 58 | 59 | LIST_ENTRY NodeModuleLink; 60 | } LDR_DATA_TABLE_ENTRY_MOD_LIST, * PLDR_DATA_TABLE_ENTRY_MOD_LIST; 61 | 62 | 63 | 64 | 65 | static inline void UnlinkLdrEntry(PLDR_DATA_TABLE_ENTRY ldrEntry) 66 | { 67 | PLIST_ENTRY ldrEntryHeader; 68 | 69 | // InMemoryOrderModuleList 70 | ldrEntryHeader = &((PLDR_DATA_TABLE_ENTRY_MOD_LIST)ldrEntry)->InMemoryOrderLinks; 71 | ldrEntryHeader->Blink->Flink = ldrEntryHeader->Flink; 72 | ldrEntryHeader->Flink->Blink = ldrEntryHeader->Blink; 73 | 74 | // InLoadOrderModuleList 75 | ldrEntryHeader = &((PLDR_DATA_TABLE_ENTRY_MOD_LIST)ldrEntry)->InLoadOrderLinks; 76 | ldrEntryHeader->Blink->Flink = ldrEntryHeader->Flink; 77 | ldrEntryHeader->Flink->Blink = ldrEntryHeader->Blink; 78 | 79 | // InInitializationOrderModuleList 80 | ldrEntryHeader = &((PLDR_DATA_TABLE_ENTRY_MOD_LIST)ldrEntry)->InInitializationOrderLinks; 81 | ldrEntryHeader->Blink->Flink = ldrEntryHeader->Flink; 82 | ldrEntryHeader->Flink->Blink = ldrEntryHeader->Blink; 83 | 84 | // HashLinks 85 | ldrEntryHeader = &((PLDR_DATA_TABLE_ENTRY_MOD_LIST)ldrEntry)->HashLinks; 86 | ldrEntryHeader->Blink->Flink = ldrEntryHeader->Flink; 87 | ldrEntryHeader->Flink->Blink = ldrEntryHeader->Blink; 88 | 89 | // NodeModuleLink 90 | ldrEntryHeader = &((PLDR_DATA_TABLE_ENTRY_MOD_LIST)ldrEntry)->NodeModuleLink; 91 | ldrEntryHeader->Blink->Flink = ldrEntryHeader->Flink; 92 | ldrEntryHeader->Flink->Blink = ldrEntryHeader->Blink; 93 | 94 | } 95 | 96 | static inline bool RemoveFromPeb_InMemoryOrder(PPEB peb, HMODULE hModule) 97 | { 98 | PLIST_ENTRY loadedModulesHead = &peb->Ldr->InMemoryOrderModuleList; 99 | for (PLIST_ENTRY ldrEntryHeader = loadedModulesHead->Flink; 100 | ldrEntryHeader != loadedModulesHead; 101 | ldrEntryHeader = ldrEntryHeader->Flink) 102 | { 103 | PLDR_DATA_TABLE_ENTRY ldrEntry = CONTAINING_RECORD(ldrEntryHeader, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 104 | if (ldrEntry->DllBase == hModule) 105 | { 106 | UnlinkLdrEntry(ldrEntry); 107 | return true; 108 | } 109 | } 110 | 111 | return false; 112 | } 113 | 114 | static inline bool RemoveFromPeb_InLoadOrder(PPEB peb, HMODULE hModule) 115 | { 116 | PLIST_ENTRY loadedModulesHead = &((PPEB_LDR_DATA_MOD_LIST)peb->Ldr)->InLoadOrderModuleList; 117 | for (PLIST_ENTRY ldrEntryHeader = loadedModulesHead->Flink; 118 | ldrEntryHeader != loadedModulesHead; 119 | ldrEntryHeader = ldrEntryHeader->Flink) 120 | { 121 | PLDR_DATA_TABLE_ENTRY ldrEntry = 122 | (PLDR_DATA_TABLE_ENTRY)CONTAINING_RECORD(ldrEntryHeader, LDR_DATA_TABLE_ENTRY_MOD_LIST, InLoadOrderLinks); 123 | if (ldrEntry->DllBase == hModule) 124 | { 125 | UnlinkLdrEntry(ldrEntry); 126 | return true; 127 | } 128 | } 129 | 130 | return false; 131 | } 132 | 133 | static inline bool RemoveFromPeb_InInitializationOrder(PPEB peb, HMODULE hModule) 134 | { 135 | PLIST_ENTRY loadedModulesHead = &((PPEB_LDR_DATA_MOD_LIST)peb->Ldr)->InInitializationOrderModuleList; 136 | for (PLIST_ENTRY ldrEntryHeader = loadedModulesHead->Flink; 137 | ldrEntryHeader != loadedModulesHead; 138 | ldrEntryHeader = ldrEntryHeader->Flink) 139 | { 140 | PLDR_DATA_TABLE_ENTRY ldrEntry = 141 | (PLDR_DATA_TABLE_ENTRY)CONTAINING_RECORD(ldrEntryHeader, LDR_DATA_TABLE_ENTRY_MOD_LIST, InInitializationOrderLinks); 142 | if (ldrEntry->DllBase == hModule) 143 | { 144 | UnlinkLdrEntry(ldrEntry); 145 | return true; 146 | } 147 | } 148 | 149 | return false; 150 | } 151 | 152 | 153 | 154 | 155 | static inline HMODULE GetMyBaseUnsafe() 156 | { 157 | PPEB peb = GetPeb(); 158 | return (HMODULE)((void**)peb)[2]; // peb->ImageBaseAddress 159 | } 160 | 161 | 162 | // https://github.com/Apxaey/Unlinking-Modules-From-The-PEB-Dll-Hiding- 163 | // https://github.com/SLAUC91/DLLHiding/blob/master/DLLHiding/Process.cpp 164 | // https://www.youtube.com/watch?v=u2jFhdrHVg0 165 | 166 | bool ntfsdupe::ntapis::peb_helper::remove_from_peb(HMODULE hModule) 167 | { 168 | ntfsdupe::ntapis::LockPeb(); 169 | 170 | PPEB peb = GetPeb(); 171 | 172 | bool isCurrentProcess = false; 173 | if (!hModule) { 174 | hModule = GetMyBaseUnsafe(); 175 | isCurrentProcess = true; 176 | } else if (hModule == GetMyBaseUnsafe()) { 177 | isCurrentProcess = true; 178 | } 179 | 180 | bool result = 181 | RemoveFromPeb_InMemoryOrder(peb, hModule) || 182 | RemoveFromPeb_InLoadOrder(peb, hModule) || 183 | RemoveFromPeb_InInitializationOrder(peb, hModule); 184 | 185 | if (isCurrentProcess) { 186 | ((void**)peb)[2] = NULL; // peb->ImageBaseAddress 187 | } 188 | 189 | ntfsdupe::ntapis::ReleasePeb(); 190 | 191 | return result; 192 | } 193 | -------------------------------------------------------------------------------- /nt_file_dupe/src/lib_main/lib_main.cpp: -------------------------------------------------------------------------------- 1 | #include "lib_main/lib_main.hpp" 2 | #include "Helpers/dbglog.hpp" 3 | #include "Configs/Configs.hpp" 4 | #include "NtApis/NtApis.hpp" 5 | #include "Hooks/Hooks.hpp" 6 | 7 | namespace ntfsdupe { 8 | struct CppRt { 9 | ~CppRt() { 10 | NTFSDUPE_DBG(L"ntfsdupe::CppRt::~CppRt()"); 11 | ntfsdupe::deinit(); 12 | } 13 | }; 14 | 15 | static CppRt cpp_rt{}; 16 | } 17 | 18 | bool ntfsdupe::init() 19 | { 20 | NTFSDUPE_DBG_INIT(); 21 | 22 | if (!ntfsdupe::cfgs::init()) return false; 23 | if (!ntfsdupe::ntapis::init()) return false; 24 | if (!ntfsdupe::hooks::init()) return false; 25 | 26 | // hide the log file 27 | #if !defined(NT_FS_DUPE_RELEASE) 28 | ntfsdupe::cfgs::add_entry(ntfsdupe::cfgs::Mode::file_hide, ntfsdupe::helpers::get_module_fullpath(nullptr) + L".NT_FS_DUPE.log"); 29 | #endif 30 | 31 | return true; 32 | } 33 | 34 | void ntfsdupe::deinit() 35 | { 36 | ntfsdupe::hooks::deinit(); 37 | ntfsdupe::cfgs::deinit(); 38 | 39 | NTFSDUPE_DBG_CLOSE(); 40 | } 41 | --------------------------------------------------------------------------------