├── .gitattributes ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── HookLib.sln ├── HookLib ├── HookLib.vcxproj ├── HookLib.vcxproj.filters └── HookLib │ ├── HookLib.c │ └── HookLib.h ├── HookLibDrvTests ├── HookLibDrvTests.vcxproj ├── HookLibDrvTests.vcxproj.filters └── Main.cpp ├── HookLibTests ├── HookLibTests.cpp ├── HookLibTests.vcxproj └── HookLibTests.vcxproj.filters ├── LICENSE ├── README.md └── props ├── HookLib-Km-x32-Debug.props ├── HookLib-Km-x32-Release.props ├── HookLib-Km-x64-Debug.props ├── HookLib-Km-x64-Release.props ├── HookLib-Um-x32-Debug.props ├── HookLib-Um-x32-Release.props ├── HookLib-Um-x64-Debug.props └── HookLib-Um-x64-Release.props /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.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 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugKernel/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleaseKernel/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | 259 | # Microsoft Fakes 260 | FakesAssemblies/ 261 | 262 | # GhostDoc plugin setting file 263 | *.GhostDoc.xml 264 | 265 | # Node.js Tools for Visual Studio 266 | .ntvs_analysis.dat 267 | node_modules/ 268 | 269 | # Visual Studio 6 build log 270 | *.plg 271 | 272 | # Visual Studio 6 workspace options file 273 | *.opt 274 | 275 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 276 | *.vbw 277 | 278 | # Visual Studio LightSwitch build output 279 | **/*.HTMLClient/GeneratedArtifacts 280 | **/*.DesktopClient/GeneratedArtifacts 281 | **/*.DesktopClient/ModelManifest.xml 282 | **/*.Server/GeneratedArtifacts 283 | **/*.Server/ModelManifest.xml 284 | _Pvt_Extensions 285 | 286 | # Paket dependency manager 287 | .paket/paket.exe 288 | paket-files/ 289 | 290 | # FAKE - F# Make 291 | .fake/ 292 | 293 | # JetBrains Rider 294 | .idea/ 295 | *.sln.iml 296 | 297 | # CodeRush personal settings 298 | .cr/personal 299 | 300 | # Python Tools for Visual Studio (PTVS) 301 | __pycache__/ 302 | *.pyc 303 | 304 | # Cake - Uncomment if you are using it 305 | # tools/** 306 | # !tools/packages.config 307 | 308 | # Tabs Studio 309 | *.tss 310 | 311 | # Telerik's JustMock configuration file 312 | *.jmconfig 313 | 314 | # BizTalk build output 315 | *.btp.cs 316 | *.btm.cs 317 | *.odx.cs 318 | *.xsd.cs 319 | 320 | # OpenCover UI analysis results 321 | OpenCover/ 322 | 323 | # Azure Stream Analytics local run output 324 | ASALocalRun/ 325 | 326 | # MSBuild Binary and Structured Log 327 | *.binlog 328 | 329 | # NVidia Nsight GPU debugger configuration file 330 | *.nvuser 331 | 332 | # MFractors (Xamarin productivity tool) working folder 333 | .mfractor/ 334 | 335 | # Local History for Visual Studio 336 | .localhistory/ 337 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "HookLib/Zydis"] 2 | path = HookLib/Zydis 3 | url = https://github.com/zyantific/zydis.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 4.0) 2 | 3 | project(HookLib LANGUAGES C) 4 | 5 | option(ZYDIS_MINIMAL_MODE ON) 6 | option(ZYDIS_FEATURE_ENCODER OFF) 7 | option(ZYDIS_FEATURE_FORMATTER OFF) 8 | option(ZYDIS_FEATURE_AVX512 OFF) 9 | option(ZYDIS_FEATURE_KNC OFF) 10 | option(ZYDIS_FEATURE_SEGMENT OFF) 11 | option(ZYDIS_BUILD_EXAMPLES OFF) 12 | option(ZYDIS_BUILD_TOOLS OFF) 13 | option(ZYDIS_BUILD_MAN OFF) 14 | option(ZYDIS_BUILD_DOXYGEN OFF) 15 | add_subdirectory(HookLib/Zydis) 16 | 17 | add_library(HookLib STATIC) 18 | 19 | target_sources(HookLib PRIVATE 20 | ./HookLib/HookLib/HookLib.c 21 | ./HookLib/HookLib/HookLib.h 22 | ) 23 | 24 | target_include_directories(HookLib PUBLIC 25 | ./HookLib/HookLib 26 | ) 27 | 28 | target_link_libraries(HookLib PUBLIC 29 | Zydis 30 | ) 31 | -------------------------------------------------------------------------------- /HookLib.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28701.123 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HookLib", "HookLib\HookLib.vcxproj", "{9379F9BC-7829-45D8-B339-90F6504FDF2B}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {88A23124-5640-35A0-B890-311D7A67A7D2} = {88A23124-5640-35A0-B890-311D7A67A7D2} 9 | EndProjectSection 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HookLibTests", "HookLibTests\HookLibTests.vcxproj", "{51822229-A0BE-4D4E-8025-F16A47ACC3EE}" 12 | ProjectSection(ProjectDependencies) = postProject 13 | {88A23124-5640-35A0-B890-311D7A67A7D2} = {88A23124-5640-35A0-B890-311D7A67A7D2} 14 | {9379F9BC-7829-45D8-B339-90F6504FDF2B} = {9379F9BC-7829-45D8-B339-90F6504FDF2B} 15 | EndProjectSection 16 | EndProject 17 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Zydis", "HookLib\Zydis\msvc\zydis\Zydis.vcxproj", "{88A23124-5640-35A0-B890-311D7A67A7D2}" 18 | EndProject 19 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HookLibDrvTests", "HookLibDrvTests\HookLibDrvTests.vcxproj", "{3E9752F7-9B84-4844-847E-08F6E2DE1D32}" 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug Kernel|x64 = Debug Kernel|x64 24 | Debug Kernel|x86 = Debug Kernel|x86 25 | Debug|x64 = Debug|x64 26 | Debug|x86 = Debug|x86 27 | Release Kernel|x64 = Release Kernel|x64 28 | Release Kernel|x86 = Release Kernel|x86 29 | Release|x64 = Release|x64 30 | Release|x86 = Release|x86 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Debug Kernel|x64.ActiveCfg = Debug Kernel|x64 34 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Debug Kernel|x64.Build.0 = Debug Kernel|x64 35 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Debug Kernel|x86.ActiveCfg = Debug Kernel|Win32 36 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Debug Kernel|x86.Build.0 = Debug Kernel|Win32 37 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Debug|x64.ActiveCfg = Debug|x64 38 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Debug|x64.Build.0 = Debug|x64 39 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Debug|x86.ActiveCfg = Debug|Win32 40 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Debug|x86.Build.0 = Debug|Win32 41 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Release Kernel|x64.ActiveCfg = Release Kernel|x64 42 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Release Kernel|x64.Build.0 = Release Kernel|x64 43 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Release Kernel|x86.ActiveCfg = Release Kernel|Win32 44 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Release Kernel|x86.Build.0 = Release Kernel|Win32 45 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Release|x64.ActiveCfg = Release|x64 46 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Release|x64.Build.0 = Release|x64 47 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Release|x86.ActiveCfg = Release|Win32 48 | {9379F9BC-7829-45D8-B339-90F6504FDF2B}.Release|x86.Build.0 = Release|Win32 49 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Debug Kernel|x64.ActiveCfg = Debug|x64 50 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Debug Kernel|x86.ActiveCfg = Debug|Win32 51 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Debug|x64.ActiveCfg = Debug|x64 52 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Debug|x64.Build.0 = Debug|x64 53 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Debug|x86.ActiveCfg = Debug|Win32 54 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Debug|x86.Build.0 = Debug|Win32 55 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Release Kernel|x64.ActiveCfg = Release|x64 56 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Release Kernel|x86.ActiveCfg = Release|Win32 57 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Release|x64.ActiveCfg = Release|x64 58 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Release|x64.Build.0 = Release|x64 59 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Release|x86.ActiveCfg = Release|Win32 60 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE}.Release|x86.Build.0 = Release|Win32 61 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Debug Kernel|x64.ActiveCfg = Debug Kernel|x64 62 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Debug Kernel|x64.Build.0 = Debug Kernel|x64 63 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Debug Kernel|x86.ActiveCfg = Debug Kernel|Win32 64 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Debug Kernel|x86.Build.0 = Debug Kernel|Win32 65 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Debug|x64.ActiveCfg = Debug MT|x64 66 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Debug|x64.Build.0 = Debug MT|x64 67 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Debug|x86.ActiveCfg = Debug MT|Win32 68 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Debug|x86.Build.0 = Debug MT|Win32 69 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Release Kernel|x64.ActiveCfg = Release Kernel|x64 70 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Release Kernel|x64.Build.0 = Release Kernel|x64 71 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Release Kernel|x86.ActiveCfg = Release Kernel|Win32 72 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Release Kernel|x86.Build.0 = Release Kernel|Win32 73 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Release|x64.ActiveCfg = Release MT|x64 74 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Release|x64.Build.0 = Release MT|x64 75 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Release|x86.ActiveCfg = Release MT|Win32 76 | {88A23124-5640-35A0-B890-311D7A67A7D2}.Release|x86.Build.0 = Release MT|Win32 77 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Debug Kernel|x64.ActiveCfg = Debug|x64 78 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Debug Kernel|x64.Build.0 = Debug|x64 79 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Debug Kernel|x86.ActiveCfg = Debug|Win32 80 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Debug Kernel|x86.Build.0 = Debug|Win32 81 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Debug|x64.ActiveCfg = Debug|x64 82 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Debug|x86.ActiveCfg = Debug|Win32 83 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Release Kernel|x64.ActiveCfg = Release|x64 84 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Release Kernel|x64.Build.0 = Release|x64 85 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Release Kernel|x86.ActiveCfg = Release|Win32 86 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Release Kernel|x86.Build.0 = Release|Win32 87 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Release|x64.ActiveCfg = Release|x64 88 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32}.Release|x86.ActiveCfg = Release|Win32 89 | EndGlobalSection 90 | GlobalSection(SolutionProperties) = preSolution 91 | HideSolutionNode = FALSE 92 | EndGlobalSection 93 | GlobalSection(ExtensibilityGlobals) = postSolution 94 | SolutionGuid = {CEB8D20F-74E6-4E73-A03A-1F423274115E} 95 | EndGlobalSection 96 | EndGlobal 97 | -------------------------------------------------------------------------------- /HookLib/HookLib.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug Kernel 6 | Win32 7 | 8 | 9 | Debug Kernel 10 | x64 11 | 12 | 13 | Debug 14 | Win32 15 | 16 | 17 | Release Kernel 18 | Win32 19 | 20 | 21 | Release Kernel 22 | x64 23 | 24 | 25 | Release 26 | Win32 27 | 28 | 29 | Debug 30 | x64 31 | 32 | 33 | Release 34 | x64 35 | 36 | 37 | 38 | 15.0 39 | {9379F9BC-7829-45D8-B339-90F6504FDF2B} 40 | Win32Proj 41 | HookLib 42 | $(LatestTargetPlatformVersion) 43 | 44 | 45 | 46 | StaticLibrary 47 | true 48 | v143 49 | Unicode 50 | 51 | 52 | StaticLibrary 53 | true 54 | WindowsKernelModeDriver10.0 55 | Unicode 56 | Windows10 57 | Desktop 58 | false 59 | WDM 60 | false 61 | 62 | 63 | StaticLibrary 64 | false 65 | v143 66 | true 67 | Unicode 68 | 69 | 70 | StaticLibrary 71 | false 72 | WindowsKernelModeDriver10.0 73 | true 74 | Unicode 75 | Windows10 76 | Desktop 77 | false 78 | WDM 79 | false 80 | 81 | 82 | StaticLibrary 83 | true 84 | v143 85 | Unicode 86 | 87 | 88 | StaticLibrary 89 | true 90 | WindowsKernelModeDriver10.0 91 | Unicode 92 | Windows10 93 | Desktop 94 | false 95 | WDM 96 | false 97 | 98 | 99 | StaticLibrary 100 | false 101 | v143 102 | true 103 | Unicode 104 | 105 | 106 | StaticLibrary 107 | false 108 | WindowsKernelModeDriver10.0 109 | true 110 | Unicode 111 | Windows10 112 | Desktop 113 | false 114 | WDM 115 | false 116 | 117 | 118 | StaticLibrary 119 | false 120 | v141 121 | true 122 | Unicode 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | true 159 | obj\$(ProjectName)-$(Platform)-$(Configuration)\ 160 | bin\Um\x32\Debug\ 161 | .\Zydis\dependencies\zycore\include;.\Zydis\include;.\Zydis\msvc;.\HookLib 162 | 163 | 164 | true 165 | obj\$(ProjectName)-$(Platform)-$(Configuration)\ 166 | bin\Km\x32\Debug\ 167 | .\Zydis\dependencies\zycore\include;.\Zydis\include;.\Zydis\msvc;.\HookLib 168 | 169 | 170 | true 171 | bin\Um\x64\Debug\ 172 | obj\$(ProjectName)-$(Platform)-$(Configuration)\ 173 | .\Zydis\dependencies\zycore\include;.\Zydis\include;.\Zydis\msvc;.\HookLib 174 | 175 | 176 | true 177 | obj\$(ProjectName)-$(Platform)-$(Configuration)\ 178 | bin\Km\x64\Debug\ 179 | .\Zydis\dependencies\zycore\include;.\Zydis\include;.\Zydis\msvc;.\HookLib 180 | 181 | 182 | false 183 | obj\$(ProjectName)-$(Platform)-$(Configuration)\ 184 | bin\Um\x32\Release\ 185 | .\Zydis\dependencies\zycore\include;.\Zydis\include;.\Zydis\msvc;.\HookLib 186 | 187 | 188 | false 189 | obj\$(ProjectName)-$(Platform)-$(Configuration)\ 190 | bin\Km\x32\Release\ 191 | .\Zydis\dependencies\zycore\include;.\Zydis\include;.\Zydis\msvc;.\HookLib 192 | 193 | 194 | false 195 | obj\$(ProjectName)-$(Platform)-$(Configuration)\ 196 | bin\Um\x64\Release\ 197 | .\Zydis\dependencies\zycore\include;.\Zydis\include;.\Zydis\msvc;.\HookLib 198 | 199 | 200 | false 201 | obj\$(ProjectName)-$(Platform)-$(Configuration)\ 202 | bin\Km\x64\Release\ 203 | .\Zydis\dependencies\zycore\include;.\Zydis\include;.\Zydis\msvc;.\HookLib 204 | 205 | 206 | false 207 | $(SolutionDir)$(Platform)\$(Configuration)\;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 208 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 209 | 210 | 211 | 212 | NotUsing 213 | Level4 214 | Disabled 215 | true 216 | WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 217 | true 218 | 219 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include 220 | Disabled 221 | Size 222 | true 223 | MultiThreadedDebug 224 | true 225 | 226 | 227 | Windows 228 | true 229 | 230 | 231 | 232 | 233 | 234 | NotUsing 235 | Level4 236 | Disabled 237 | true 238 | ZYAN_NO_LIBC;ZYDIS_NO_LIBC;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) 239 | true 240 | 241 | 242 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include;$(IntDir);%(AdditionalIncludeDirectories) 243 | Disabled 244 | false 245 | Size 246 | true 247 | 248 | 249 | Windows 250 | true 251 | 252 | 253 | 254 | 255 | 256 | NotUsing 257 | Level4 258 | Disabled 259 | true 260 | _DEBUG;_LIB;%(PreprocessorDefinitions) 261 | true 262 | 263 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include 264 | Disabled 265 | Size 266 | false 267 | true 268 | MultiThreadedDebug 269 | true 270 | 271 | 272 | Windows 273 | true 274 | 275 | 276 | 277 | 278 | 279 | NotUsing 280 | Level4 281 | Disabled 282 | true 283 | ZYAN_NO_LIBC;ZYDIS_NO_LIBC;_DEBUG;_LIB;%(PreprocessorDefinitions) 284 | true 285 | 286 | 287 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include;$(IntDir);%(AdditionalIncludeDirectories) 288 | Disabled 289 | false 290 | Size 291 | true 292 | 293 | 294 | Windows 295 | true 296 | 297 | 298 | 299 | 300 | 301 | NotUsing 302 | Level4 303 | MaxSpeed 304 | true 305 | true 306 | true 307 | WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 308 | true 309 | 310 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include 311 | AnySuitable 312 | Speed 313 | true 314 | MultiThreaded 315 | true 316 | 317 | 318 | Windows 319 | true 320 | true 321 | true 322 | 323 | 324 | 325 | 326 | 327 | NotUsing 328 | Level4 329 | MaxSpeed 330 | true 331 | true 332 | true 333 | ZYAN_NO_LIBC;ZYDIS_NO_LIBC;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) 334 | true 335 | 336 | 337 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include;$(IntDir);%(AdditionalIncludeDirectories) 338 | AnySuitable 339 | Speed 340 | true 341 | 342 | 343 | Windows 344 | true 345 | true 346 | true 347 | 348 | 349 | 350 | 351 | 352 | NotUsing 353 | Level4 354 | MaxSpeed 355 | true 356 | true 357 | true 358 | NDEBUG;_LIB;%(PreprocessorDefinitions) 359 | true 360 | 361 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include 362 | AnySuitable 363 | Speed 364 | false 365 | true 366 | MultiThreaded 367 | true 368 | 369 | 370 | Windows 371 | true 372 | true 373 | true 374 | 375 | 376 | 377 | 378 | 379 | NotUsing 380 | Level4 381 | MaxSpeed 382 | true 383 | true 384 | true 385 | ZYAN_NO_LIBC;ZYDIS_NO_LIBC;NDEBUG;_LIB;%(PreprocessorDefinitions) 386 | true 387 | 388 | 389 | .\Zydis\include;.\Zydis\msvc;.\Zydis\dependencies\zycore\include;$(IntDir);%(AdditionalIncludeDirectories) 390 | AnySuitable 391 | Speed 392 | true 393 | 394 | 395 | Windows 396 | true 397 | true 398 | true 399 | 400 | 401 | 402 | 403 | 404 | NotUsing 405 | Level3 406 | MaxSpeed 407 | true 408 | true 409 | true 410 | NDEBUG;_LIB;%(PreprocessorDefinitions) 411 | true 412 | 413 | 414 | 415 | 416 | Windows 417 | true 418 | true 419 | true 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | {88a23124-5640-35a0-b890-311d7a67a7d2} 432 | 433 | 434 | 435 | 436 | 437 | -------------------------------------------------------------------------------- /HookLib/HookLib.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {02afc4be-0ef9-42b3-ae3e-04a33fb2b6ae} 6 | 7 | 8 | 9 | 10 | HookLib 11 | 12 | 13 | 14 | 15 | HookLib 16 | 17 | 18 | -------------------------------------------------------------------------------- /HookLib/HookLib/HookLib.c: -------------------------------------------------------------------------------- 1 | #include "HookLib.h" 2 | 3 | #if _KERNEL_MODE 4 | #include 5 | #ifdef _AMD64_ 6 | #include 7 | #endif 8 | #include 9 | #else 10 | #define _USER_MODE 1 11 | 12 | #define WIN32_LEAN_AND_MEAN 13 | 14 | #define WIN32_NO_STATUS 15 | #include 16 | #undef WIN32_NO_STATUS 17 | 18 | #include 19 | 20 | #pragma comment(lib, "ntdll.lib") 21 | #endif 22 | 23 | #include 24 | 25 | #pragma warning(push) 26 | #pragma warning(disable: 4201) // Anonymous unions 27 | #define ZYDIS_STATIC_BUILD 28 | #include 29 | #pragma warning(pop) 30 | 31 | #if !defined offsetof 32 | #define offsetof(s, m) ((size_t)&(((s*)0)->m)) 33 | #endif 34 | 35 | #if _USER_MODE 36 | #define NtCurrentProcess() ((HANDLE)-1) 37 | #define NtCurrentThread() ((HANDLE)-2) 38 | 39 | #ifdef _AMD64_ 40 | #define teb() ((const void*)__readgsqword(0x30)) 41 | #define peb() ((const void*)__readgsqword(0x60)) 42 | #define pid() (*(const unsigned int*)((const unsigned char*)teb() + 0x40)) /* TEB::ClientId.UniqueProcessId */ 43 | #define tid() (*(const unsigned int*)((const unsigned char*)teb() + 0x48)) /* TEB::ClientId.UniqueThreadId */ 44 | #else 45 | #define teb() ((const void*)__readfsdword(0x18)) 46 | #define peb() ((const void*)__readfsdword(0x30)) 47 | #define pid() (*(const unsigned int*)((const unsigned char*)teb() + 0x20)) /* TEB::ClientId.UniqueProcessId */ 48 | #define tid() (*(const unsigned int*)((const unsigned char*)teb() + 0x24)) /* TEB::ClientId.UniqueThreadId */ 49 | #endif 50 | #endif 51 | 52 | #if _KERNEL_MODE 53 | #define pid() (unsigned int)(size_t)PsGetCurrentProcessId() 54 | #define tid() (unsigned int)(size_t)PsGetCurrentThreadId() 55 | #endif 56 | 57 | #ifdef _AMD64_ 58 | typedef long long ssize_t; 59 | #else 60 | typedef long ssize_t; 61 | #endif 62 | 63 | typedef unsigned char bool; 64 | #define true ((bool)1) 65 | #define false ((bool)0) 66 | 67 | #define nullptr ((void*)0) 68 | 69 | #define unused(...) __VA_ARGS__ 70 | 71 | #define k_pageSize 4096u 72 | 73 | // For debug purposes: 74 | #define k_forceLongJumps false 75 | #define k_enableIntermediateJumps true 76 | 77 | #define bitsof(type) (sizeof(type) * 8) 78 | 79 | // 'WRK' is the custom prefix to bypass these structs redeclaration error: 80 | 81 | typedef struct 82 | { 83 | union 84 | { 85 | LARGE_INTEGER KernelTime; 86 | #if _USER_MODE 87 | HANDLE hThread; // We use this member as a thread handle storage 88 | #elif _KERNEL_MODE 89 | PETHREAD thread; 90 | #endif 91 | } u0; 92 | LARGE_INTEGER UserTime; 93 | LARGE_INTEGER CreateTime; 94 | ULONG WaitTime; 95 | PVOID StartAddress; 96 | CLIENT_ID ClientId; 97 | KPRIORITY Priority; 98 | LONG BasePriority; 99 | ULONG ContextSwitches; 100 | ULONG ThreadState; 101 | ULONG WaitReason; 102 | } WRK_SYSTEM_THREAD_INFORMATION; 103 | 104 | typedef struct 105 | { 106 | ULONG NextEntryOffset; 107 | ULONG NumberOfThreads; 108 | LARGE_INTEGER SpareLi1; 109 | LARGE_INTEGER SpareLi2; 110 | LARGE_INTEGER SpareLi3; 111 | LARGE_INTEGER CreateTime; 112 | LARGE_INTEGER UserTime; 113 | LARGE_INTEGER KernelTime; 114 | UNICODE_STRING ImageName; 115 | KPRIORITY BasePriority; 116 | HANDLE UniqueProcessId; 117 | HANDLE InheritedFromUniqueProcessId; 118 | ULONG HandleCount; 119 | ULONG SessionId; 120 | ULONG_PTR PageDirectoryBase; 121 | SIZE_T PeakVirtualSize; 122 | SIZE_T VirtualSize; 123 | ULONG PageFaultCount; 124 | SIZE_T PeakWorkingSetSize; 125 | SIZE_T WorkingSetSize; 126 | SIZE_T QuotaPeakPagedPoolUsage; 127 | SIZE_T QuotaPagedPoolUsage; 128 | SIZE_T QuotaPeakNonPagedPoolUsage; 129 | SIZE_T QuotaNonPagedPoolUsage; 130 | SIZE_T PagefileUsage; 131 | SIZE_T PeakPagefileUsage; 132 | SIZE_T PrivatePageCount; 133 | LARGE_INTEGER ReadOperationCount; 134 | LARGE_INTEGER WriteOperationCount; 135 | LARGE_INTEGER OtherOperationCount; 136 | LARGE_INTEGER ReadTransferCount; 137 | LARGE_INTEGER WriteTransferCount; 138 | LARGE_INTEGER OtherTransferCount; 139 | WRK_SYSTEM_THREAD_INFORMATION Threads[1]; 140 | } WRK_SYSTEM_PROCESS_INFORMATION; 141 | 142 | #if _USER_MODE 143 | typedef enum 144 | { 145 | MemoryBasicInformation 146 | } MEMORY_INFORMATION_CLASS; 147 | #elif _KERNEL_MODE 148 | typedef enum _SYSTEM_INFORMATION_CLASS { 149 | SystemBasicInformation = 0, 150 | SystemPerformanceInformation = 2, 151 | SystemTimeOfDayInformation = 3, 152 | SystemProcessInformation = 5, 153 | SystemProcessorPerformanceInformation = 8, 154 | SystemInterruptInformation = 23, 155 | SystemExceptionInformation = 33, 156 | SystemRegistryQuotaInformation = 37, 157 | SystemLookasideInformation = 45, 158 | SystemCodeIntegrityInformation = 103, 159 | SystemPolicyInformation = 134, 160 | } SYSTEM_INFORMATION_CLASS; 161 | #endif // _KERNEL_MODE 162 | 163 | NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation( 164 | IN SYSTEM_INFORMATION_CLASS infoClass, 165 | OUT PVOID buf, 166 | IN ULONG len, 167 | OUT OPTIONAL PULONG returned 168 | ); 169 | 170 | #if _USER_MODE 171 | NTSYSAPI NTSTATUS NTAPI ZwAllocateVirtualMemory( 172 | IN HANDLE hProcess, 173 | IN OUT PVOID* baseAddress, 174 | IN ULONG zeroBits, 175 | IN OUT PSIZE_T regionSize, 176 | IN ULONG allocationType, 177 | IN ULONG protect 178 | ); 179 | 180 | NTSYSAPI NTSTATUS NTAPI ZwQueryVirtualMemory( 181 | IN HANDLE hProcess, 182 | IN PVOID baseAddress, 183 | IN MEMORY_INFORMATION_CLASS infoClass, 184 | OUT PVOID buf, 185 | IN SIZE_T length, 186 | OUT OPTIONAL PSIZE_T resultLength 187 | ); 188 | 189 | NTSYSAPI NTSTATUS NTAPI ZwFreeVirtualMemory( 190 | IN HANDLE hProcess, 191 | IN PVOID* baseAddress, 192 | IN OUT PSIZE_T regionSize, 193 | IN ULONG freeType 194 | ); 195 | 196 | NTSYSAPI NTSTATUS NTAPI ZwProtectVirtualMemory( 197 | IN HANDLE hProcess, 198 | IN OUT PVOID* baseAddress, 199 | IN OUT PSIZE_T numberOfBytes, 200 | IN ULONG newProt, 201 | OUT PULONG oldProt 202 | ); 203 | #elif _KERNEL_MODE 204 | typedef NTSTATUS (NTAPI* FnZwProtectVirtualMemory)( 205 | IN HANDLE hProcess, 206 | IN OUT PVOID* baseAddress, 207 | IN OUT PSIZE_T numberOfBytes, 208 | IN ULONG newProt, 209 | OUT PULONG oldProt 210 | ); 211 | #endif 212 | 213 | 214 | NTSYSAPI NTSTATUS NTAPI ZwOpenThread( 215 | OUT PHANDLE hThread, 216 | IN ACCESS_MASK access, 217 | IN const OBJECT_ATTRIBUTES* objAttrs, 218 | IN const CLIENT_ID* clientId 219 | ); 220 | 221 | NTSYSAPI NTSTATUS NTAPI ZwYieldExecution(); 222 | 223 | #if _USER_MODE 224 | NTSYSAPI NTSTATUS NTAPI ZwSuspendThread( 225 | IN HANDLE hThread, 226 | OUT OPTIONAL PULONG previousSuspendCount 227 | ); 228 | 229 | NTSYSAPI NTSTATUS NTAPI ZwResumeThread( 230 | IN HANDLE hThread, 231 | OUT OPTIONAL PULONG suspendCount 232 | ); 233 | 234 | NTSYSAPI NTSTATUS NTAPI ZwGetContextThread( 235 | IN HANDLE hThread, 236 | OUT PCONTEXT ctx 237 | ); 238 | 239 | NTSYSAPI NTSTATUS NTAPI ZwSetContextThread( 240 | IN HANDLE hThread, 241 | IN PCONTEXT ctx 242 | ); 243 | #endif 244 | 245 | #if _KERNEL_MODE 246 | #ifdef _AMD64_ 247 | NTSYSAPI NTSTATUS NTAPI ZwQueryInformationThread( 248 | _In_ HANDLE hThread, 249 | _In_ THREADINFOCLASS info, 250 | _In_ PVOID buf, 251 | _In_ ULONG size, 252 | _Out_opt_ PULONG returned 253 | ); 254 | 255 | NTSYSAPI const void* NTAPI PsGetProcessWow64Process(PEPROCESS process); 256 | #endif 257 | 258 | NTSYSAPI BOOLEAN NTAPI KeIsAttachedProcess(); 259 | NTSYSAPI NTSTATUS NTAPI PsSuspendProcess(PEPROCESS process); 260 | NTSYSAPI NTSTATUS NTAPI PsResumeProcess(PEPROCESS process); 261 | NTSYSAPI NTSTATUS NTAPI PsGetContextThread(_In_ PETHREAD thread, _Inout_ PCONTEXT ctx, _In_ KPROCESSOR_MODE mode); 262 | NTSYSAPI NTSTATUS NTAPI PsSetContextThread(_In_ PETHREAD thread, _Inout_ PCONTEXT ctx, _In_ KPROCESSOR_MODE mode); 263 | typedef NTSTATUS (NTAPI* FnPspGetContextThreadInternal)(_In_ PETHREAD thread, _Inout_ PCONTEXT ctx, _In_ KPROCESSOR_MODE contextDisposition, _In_ KPROCESSOR_MODE queryContextForPart, _In_ BOOLEAN unwindStack); 264 | typedef NTSTATUS (NTAPI* FnPspSetContextThreadInternal)(_In_ PETHREAD thread, _Inout_ PCONTEXT ctx, _In_ KPROCESSOR_MODE contextDisposition, _In_ KPROCESSOR_MODE setContextForPart, _In_ BOOLEAN unwindStack); 265 | #endif 266 | 267 | NTSYSAPI NTSTATUS NTAPI ZwFlushInstructionCache( 268 | IN HANDLE hProcess, 269 | IN PVOID baseAddress, 270 | IN SIZE_T numberOfBytesToFlush 271 | ); 272 | 273 | #if _USER_MODE 274 | NTSYSAPI NTSTATUS NTAPI ZwClose(HANDLE handle); 275 | 276 | NTSYSAPI NTSTATUS NTAPI LdrGetDllHandle( 277 | IN OPTIONAL PWORD path, 278 | IN OPTIONAL PVOID unused, 279 | IN PUNICODE_STRING moduleFileName, 280 | OUT PHANDLE hModule 281 | ); 282 | 283 | NTSYSAPI NTSTATUS NTAPI LdrGetProcedureAddress( 284 | IN HMODULE hModule, 285 | IN OPTIONAL PANSI_STRING funcName, 286 | IN OPTIONAL WORD ordinal, 287 | OUT PVOID* funcAddress 288 | ); 289 | 290 | void* lookupModule(const wchar_t* modName) 291 | { 292 | if (!modName) 293 | { 294 | return nullptr; 295 | } 296 | 297 | UNICODE_STRING name; 298 | RtlInitUnicodeString(&name, modName); 299 | HMODULE hModule = nullptr; 300 | const NTSTATUS status = LdrGetDllHandle(nullptr, nullptr, &name, &hModule); 301 | if (!NT_SUCCESS(status)) 302 | { 303 | return nullptr; 304 | } 305 | 306 | return hModule; 307 | } 308 | 309 | void* lookupFunction(const void* hModule, const char* funcName) 310 | { 311 | if (!hModule || !funcName) 312 | { 313 | return nullptr; 314 | } 315 | 316 | void* addr = nullptr; 317 | ANSI_STRING name; 318 | RtlInitAnsiString(&name, funcName); 319 | const NTSTATUS status = LdrGetProcedureAddress((HMODULE)hModule, &name, 0, &addr); 320 | if (!NT_SUCCESS(status)) 321 | { 322 | return nullptr; 323 | } 324 | 325 | return addr; 326 | } 327 | #endif 328 | 329 | #if _KERNEL_MODE 330 | typedef struct 331 | { 332 | unsigned long long forMagic; 333 | unsigned long long forBase; 334 | } Salt; 335 | 336 | static Salt g_salt = { .forMagic = 0, .forBase = 0 }; 337 | 338 | static const Salt* getSalt() 339 | { 340 | return &g_salt; 341 | } 342 | 343 | static void initSalt() 344 | { 345 | while (!g_salt.forMagic && !g_salt.forBase) 346 | { 347 | LARGE_INTEGER tickCount = { .QuadPart = 0 }; 348 | KeQueryTickCount(&tickCount); 349 | 350 | const unsigned long seed = tickCount.LowPart; 351 | const unsigned long k_distributedBits = 0x5956C34D; 352 | const unsigned long saltForSalt = seed ^ k_distributedBits; 353 | 354 | const unsigned long forMagicLow = RtlRandom((unsigned long*)&seed) ^ saltForSalt; 355 | const unsigned long forMagicHigh = RtlRandom((unsigned long*)&forMagicLow) ^ saltForSalt; 356 | 357 | const unsigned long forBaseLow = RtlRandom((unsigned long*)&forMagicHigh) ^ saltForSalt; 358 | const unsigned long forBaseHigh = RtlRandom((unsigned long*)&forBaseLow) ^ saltForSalt; 359 | 360 | g_salt.forMagic = ((unsigned long long)forMagicHigh << 32) | (unsigned long long)forMagicLow; 361 | g_salt.forBase = ((unsigned long long)forBaseHigh << 32) | (unsigned long long)forBaseLow; 362 | } 363 | } 364 | #endif 365 | 366 | static volatile long g_busy = 0; 367 | 368 | static void acquireGlobalLock() 369 | { 370 | while (InterlockedCompareExchange(&g_busy, true, false) == true) 371 | { 372 | _mm_pause(); 373 | } 374 | } 375 | 376 | static void releaseGlobalLock() 377 | { 378 | InterlockedExchange(&g_busy, false); 379 | } 380 | 381 | static size_t inline alignDown(size_t value, size_t factor) 382 | { 383 | return value & ~(factor - 1); 384 | } 385 | 386 | static size_t inline alignUp(size_t value, size_t factor) 387 | { 388 | return alignDown(value - 1, factor) + factor; 389 | } 390 | 391 | static bool inline aligned(size_t value, size_t factor) 392 | { 393 | return (value & (factor - 1)) == 0; 394 | } 395 | 396 | static ssize_t delta(const void* const src, const void* const dest) 397 | { 398 | return (ssize_t)(size_t)dest - (ssize_t)(size_t)src; 399 | } 400 | 401 | static size_t absDelta(const void* const src, const void* const dest) 402 | { 403 | return (src < dest) ? ((size_t)dest - (size_t)src) : ((size_t)src - (size_t)dest); 404 | } 405 | 406 | static bool relativeJumpable(const void* from, const void* to) 407 | { 408 | const size_t k_2gb = 2ul * 1024ul * 1048576ul; 409 | return absDelta(from, to) < k_2gb; 410 | } 411 | 412 | #if _KERNEL_MODE 413 | static const unsigned int k_poolTag = 'kooH'; 414 | 415 | static void* allocKernel(size_t size) 416 | { 417 | #if (NTDDI_VERSION >= NTDDI_WIN10_VB) 418 | void* const buf = ExAllocatePool2(POOL_FLAG_NON_PAGED_EXECUTE, size, k_poolTag); 419 | #else 420 | void* const buf = ExAllocatePoolWithTag(NonPagedPool, size, k_poolTag); // Always RWX 421 | #endif 422 | if (buf) 423 | { 424 | memset(buf, 0, size); 425 | } 426 | return buf; 427 | } 428 | 429 | static void freeKernel(void* const base) 430 | { 431 | if (base) 432 | { 433 | ExFreePoolWithTag(base, k_poolTag); 434 | } 435 | } 436 | 437 | 438 | static FnZwProtectVirtualMemory g_virtualProtect = nullptr; 439 | 440 | static FnZwProtectVirtualMemory findVirtualProtect() 441 | { 442 | // Windows 8.1 and above: 443 | const UNICODE_STRING name = RTL_CONSTANT_STRING(L"ZwProtectVirtualMemory"); 444 | const FnZwProtectVirtualMemory fn = (FnZwProtectVirtualMemory)MmGetSystemRoutineAddress((UNICODE_STRING*)&name); 445 | if (fn) 446 | { 447 | return fn; 448 | } 449 | 450 | RTL_OSVERSIONINFOW ver; 451 | ver.dwOSVersionInfoSize = sizeof(ver); 452 | const NTSTATUS verStatus = RtlGetVersion(&ver); 453 | if (!NT_SUCCESS(verStatus)) 454 | { 455 | return nullptr; 456 | } 457 | 458 | typedef enum 459 | { 460 | unknown, 461 | win7, // 6.1 462 | win8 // 6.2 (Windows 8.0) 463 | } WinVer; 464 | 465 | if (ver.dwMajorVersion != 6) 466 | { 467 | return nullptr; 468 | } 469 | 470 | WinVer winVer = unknown; 471 | switch (ver.dwMinorVersion) 472 | { 473 | case 1: 474 | { 475 | winVer = win7; 476 | break; 477 | } 478 | case 2: 479 | { 480 | winVer = win8; 481 | break; 482 | } 483 | default: 484 | { 485 | return nullptr; 486 | } 487 | } 488 | 489 | #pragma pack(push, 1) 490 | typedef union 491 | { 492 | unsigned char raw[32]; // With alignment 493 | struct 494 | { 495 | unsigned char movRaxRsp[3]; // 48 8B C4 | mov rax, rsp 496 | unsigned char cli; // FA | cli 497 | unsigned int subRsp10h; // 48 83 EC 10 | sub rsp, 10h 498 | unsigned char pushRax_0; // 50 | push rax 499 | unsigned char pushfq; // 9C | pushfq 500 | unsigned short push10h; // 6A 10 | push 10h 501 | unsigned char leaRaxOpcode[3]; // 48 8D 05 | -+ 502 | unsigned int KiServiceLinkage; // NN NN NN NN | -+-> lea rax, KiServiceLinkage 503 | unsigned char pushRax_1; // 50 | push rax 504 | unsigned char movEaxOpcode; // 8B | -+ 505 | unsigned int syscallNumber; // NN NN NN NN | -+-> mov eax, SyscallNumber 506 | unsigned char jmpOpcode; // E9 | -+ 507 | unsigned int KiServiceLinkageOffset; // NN NN NN NN | -+-> jmp KiServiceLinkage 508 | } layout; 509 | } ZwStubLayout64; 510 | #pragma pack(pop) 511 | 512 | #pragma pack(push, 1) 513 | typedef union 514 | { 515 | unsigned char raw[20]; 516 | struct 517 | { 518 | unsigned char movEaxOpcode; // B8 | -+ 519 | unsigned int syscallNumber; // NN NN NN NN | -+-> mov eax, SyscallNumber 520 | unsigned char pushf; // 9C | pushf 521 | unsigned short push8h; // 6A 08 | push 8 522 | unsigned char callOpcode; // E8 | -+ 523 | unsigned int KiSystemService; // NN NN NN NN | -+-> call KiSystemService 524 | unsigned char retn8; // C2 08 00 | retn 8 525 | } layout; 526 | } ZwStubLayout32; 527 | #pragma pack(pop) 528 | 529 | #ifdef _AMD64_ 530 | typedef ZwStubLayout64 ZwStubLayout; 531 | const UNICODE_STRING nearestKnownFuncName = RTL_CONSTANT_STRING(L"ZwIsProcessInJob"); // The same for Windows 7 and Windows 8.0 532 | const unsigned int k_syscallNumberWin7 = 0x4D; 533 | const unsigned int k_syscallNumberWin8 = 0x4E; 534 | #else 535 | typedef ZwStubLayout32 ZwStubLayout; 536 | const UNICODE_STRING nearestKnownFuncNameWin7 = RTL_CONSTANT_STRING(L"ZwPropagationFailed"); // Windows 7 537 | const UNICODE_STRING nearestKnownFuncNameWin8 = RTL_CONSTANT_STRING(L"ZwPulseEvent"); // Windows 8.0 538 | const UNICODE_STRING nearestKnownFuncName = (winVer == win7) 539 | ? nearestKnownFuncNameWin7 540 | : nearestKnownFuncNameWin8; 541 | const unsigned int k_syscallNumberWin7 = 0xD7; 542 | const unsigned int k_syscallNumberWin8 = 0xC3; 543 | #endif 544 | 545 | const ZwStubLayout* const nearestKnownFunc = MmGetSystemRoutineAddress((UNICODE_STRING*)&nearestKnownFuncName); 546 | if (!nearestKnownFunc) 547 | { 548 | return nullptr; 549 | } 550 | 551 | const ZwStubLayout* const candidate = (nearestKnownFunc + 1); 552 | 553 | const bool syscallMatches = (winVer == win7) 554 | ? (candidate->layout.syscallNumber == k_syscallNumberWin7) 555 | : (candidate->layout.syscallNumber == k_syscallNumberWin8); 556 | 557 | if (!syscallMatches) 558 | { 559 | return nullptr; 560 | } 561 | 562 | return (FnZwProtectVirtualMemory)candidate; 563 | } 564 | 565 | static bool initVirtualProtect() 566 | { 567 | if (g_virtualProtect) 568 | { 569 | return true; 570 | } 571 | 572 | g_virtualProtect = findVirtualProtect(); 573 | return g_virtualProtect != nullptr; 574 | } 575 | 576 | 577 | static bool isUserAddress(const void* const addr) 578 | { 579 | return (size_t)addr <= (size_t)MM_HIGHEST_USER_ADDRESS; 580 | } 581 | 582 | static bool isKernelAddress(const void* const addr) 583 | { 584 | return (size_t)addr >= (size_t)MM_SYSTEM_RANGE_START; 585 | } 586 | 587 | #ifdef _AMD64_ 588 | static bool isWow64Process(PEPROCESS process) 589 | { 590 | return PsGetProcessWow64Process(process) != nullptr; 591 | } 592 | #endif 593 | 594 | typedef struct 595 | { 596 | const PMDL mdl; 597 | void* const addr; 598 | } Mapping; 599 | 600 | static Mapping makeWriteableMapping(void* const addr, unsigned int size) 601 | { 602 | const PMDL mdl = IoAllocateMdl(addr, size, false, false, nullptr); 603 | if (!mdl) 604 | { 605 | const Mapping mapping = { .mdl = nullptr, .addr = nullptr }; 606 | return mapping; 607 | } 608 | 609 | bool locked = false; 610 | __try 611 | { 612 | MmProbeAndLockPages(mdl, KernelMode, IoReadAccess); 613 | locked = true; 614 | 615 | void* const mapped = MmMapLockedPagesSpecifyCache(mdl, KernelMode, MmCached, nullptr, false, NormalPagePriority); 616 | if (mapped) 617 | { 618 | const NTSTATUS status = MmProtectMdlSystemAddress(mdl, PAGE_READWRITE); 619 | if (!NT_SUCCESS(status)) 620 | { 621 | MmUnmapLockedPages(mapped, mdl); 622 | MmUnlockPages(mdl); 623 | IoFreeMdl(mdl); 624 | 625 | const Mapping mapping = { .mdl = nullptr, .addr = nullptr }; 626 | return mapping; 627 | } 628 | 629 | const Mapping mapping = { .mdl = mdl, .addr = mapped }; 630 | return mapping; 631 | } 632 | } 633 | __except (EXCEPTION_EXECUTE_HANDLER) 634 | { 635 | if (locked) 636 | { 637 | MmUnlockPages(mdl); 638 | } 639 | } 640 | 641 | IoFreeMdl(mdl); 642 | 643 | const Mapping mapping = { .mdl = nullptr, .addr = nullptr }; 644 | return mapping; 645 | } 646 | 647 | static bool isMappingValid(const Mapping* const mapping) 648 | { 649 | return mapping->addr != nullptr; 650 | } 651 | 652 | static void freeMapping(Mapping* const mapping) 653 | { 654 | if (mapping->addr) 655 | { 656 | MmUnmapLockedPages(mapping->addr, mapping->mdl); 657 | } 658 | 659 | if (mapping->mdl) 660 | { 661 | MmUnlockPages(mapping->mdl); 662 | IoFreeMdl(mapping->mdl); 663 | } 664 | } 665 | #endif 666 | 667 | static void* allocUser(void* base, size_t size, unsigned int protect) 668 | { 669 | const NTSTATUS status = ZwAllocateVirtualMemory( 670 | NtCurrentProcess(), 671 | &base, 672 | base ? 12 : 0, // Align by page size ((1 << 12) == 4096) 673 | (SIZE_T*)&size, 674 | MEM_RESERVE | MEM_COMMIT, 675 | protect 676 | ); 677 | 678 | if (!NT_SUCCESS(status)) 679 | { 680 | return nullptr; 681 | } 682 | 683 | memset(base, 0, size); 684 | return base; 685 | } 686 | 687 | static unsigned int protectUser(void* addr, size_t size, unsigned int protect) 688 | { 689 | unsigned long prevProtect = 0; 690 | #if _USER_MODE 691 | const NTSTATUS status = ZwProtectVirtualMemory(NtCurrentProcess(), &addr, (SIZE_T*)&size, protect, &prevProtect); 692 | #elif _KERNEL_MODE 693 | const NTSTATUS status = g_virtualProtect(NtCurrentProcess(), &addr, (SIZE_T*)&size, protect, &prevProtect); 694 | #endif 695 | return NT_SUCCESS(status) ? prevProtect : 0; 696 | } 697 | 698 | static void freeUser(void* base) 699 | { 700 | size_t regionSize = 0; 701 | ZwFreeVirtualMemory(NtCurrentProcess(), &base, (SIZE_T*)®ionSize, MEM_RELEASE); 702 | } 703 | 704 | 705 | 706 | 707 | #pragma pack(push, 1) 708 | typedef struct 709 | { 710 | unsigned char opcode; // E9 | 711 | unsigned int offset; // 44 33 22 11 | jmp rip+0x11223344 712 | } RelJump; 713 | 714 | typedef struct 715 | { 716 | unsigned short opcode; // FF 25 | 717 | unsigned int offset; // 44 33 22 11 | jmp ds:[0x11223344] on x32 and jmp [rip+0x11223344] on x64 718 | } AbsJump; 719 | 720 | typedef struct 721 | { 722 | AbsJump jmp; // FF 25 44 33 22 11 | jmp ds:[0x11223344] 723 | unsigned int address; // NN NN NN NN | <-- 0x11223344 is points to 724 | } LongJump32; 725 | 726 | typedef struct 727 | { 728 | AbsJump jmp; // FF 25 00 00 00 00 | jmp [rip+00h] 729 | unsigned long long address; // 77 66 55 44 33 22 11 00 | <-- RIP is points to 730 | } LongJump64; 731 | #pragma pack(pop) 732 | 733 | static RelJump makeRelJump(const void* from, const void* to) 734 | { 735 | const unsigned int delta = (unsigned int)((size_t)to - ((size_t)from + sizeof(RelJump))); 736 | const RelJump jump = 737 | { 738 | .opcode = 0xE9, 739 | .offset = delta 740 | }; 741 | return jump; 742 | } 743 | 744 | static LongJump32 makeLongJump32(const void* from, const void* to) 745 | { 746 | const LongJump32 jump = 747 | { 748 | .jmp = 749 | { 750 | .opcode = 0x25FF, 751 | .offset = (unsigned int)((size_t)from + sizeof(AbsJump)) 752 | }, 753 | .address = (unsigned int)((size_t)to) 754 | }; 755 | return jump; 756 | } 757 | 758 | static LongJump64 makeLongJump64(const void* dest) 759 | { 760 | const LongJump64 jump = 761 | { 762 | .jmp = 763 | { 764 | .opcode = 0x25FF, 765 | .offset = 0x00000000 766 | }, 767 | .address = ((size_t)dest) 768 | }; 769 | return jump; 770 | } 771 | 772 | 773 | typedef struct 774 | { 775 | unsigned char beginning[48]; 776 | unsigned char original[32]; 777 | void* fn; 778 | union 779 | { 780 | LongJump64 x64; 781 | LongJump32 x32; 782 | } intermediate; 783 | unsigned char affectedBytes; 784 | unsigned char indexInPage; 785 | } HookData; 786 | 787 | #if _KERNEL_MODE 788 | typedef unsigned long long Magic; 789 | static const Magic k_hookPageMagic = 0x1EE7C0DE; 790 | #endif 791 | 792 | typedef struct _HookPage 793 | { 794 | struct Header 795 | { 796 | #if _KERNEL_MODE 797 | Magic magic; 798 | unsigned long long pageBase; 799 | #endif 800 | unsigned long long freeBitmap; // Each setted bit is a free cell 801 | struct _HookPage* prev; 802 | struct _HookPage* next; 803 | } header; 804 | HookData cells[(k_pageSize - sizeof(struct Header)) / sizeof(HookData)]; 805 | } HookPage; 806 | 807 | static HookPage* g_pages = nullptr; 808 | 809 | static HookPage* getHookPagesList() 810 | { 811 | return g_pages; 812 | } 813 | 814 | static void setHookPagesList(const HookPage* const firstPage) 815 | { 816 | g_pages = (HookPage*)firstPage; 817 | } 818 | 819 | #if _KERNEL_MODE 820 | static bool isHookPage(const void* const page) 821 | { 822 | const unsigned int k_allocationGranularity = 64 * 1024; 823 | if (!aligned((size_t)page, k_allocationGranularity)) 824 | { 825 | return false; 826 | } 827 | 828 | const HookPage* const candidate = (const HookPage*)page; 829 | const PEPROCESS currentProcess = PsGetCurrentProcess(); 830 | const Salt* const salt = getSalt(); 831 | return ((candidate->header.magic ^ ((size_t)currentProcess) ^ salt->forMagic) == k_hookPageMagic) 832 | && ((candidate->header.pageBase ^ ((size_t)currentProcess) ^ salt->forBase) == (size_t)page); 833 | } 834 | 835 | static HookPage* lookupHookPagesList() 836 | { 837 | void* baseAddress = nullptr; 838 | MEMORY_BASIC_INFORMATION info; 839 | SIZE_T returned = 0; 840 | while (NT_SUCCESS(ZwQueryVirtualMemory(NtCurrentProcess(), baseAddress, MemoryBasicInformation, &info, sizeof(info), &returned))) 841 | { 842 | const bool isThisHookPage = (info.Protect == PAGE_EXECUTE_READWRITE) 843 | && (info.RegionSize == k_pageSize) 844 | && (info.Type == MEM_PRIVATE) 845 | && ((info.State & MEM_COMMIT) == MEM_COMMIT) 846 | && isHookPage(info.BaseAddress); 847 | 848 | if (!isThisHookPage) 849 | { 850 | baseAddress = (void*)((size_t)info.BaseAddress + info.RegionSize); 851 | continue; 852 | } 853 | 854 | HookPage* hookPage = (HookPage*)info.BaseAddress; 855 | while (hookPage->header.prev) 856 | { 857 | hookPage = hookPage->header.prev; 858 | } 859 | 860 | return hookPage; 861 | } 862 | 863 | return nullptr; 864 | } 865 | 866 | static void resetHookPagesList() 867 | { 868 | g_pages = nullptr; 869 | } 870 | #endif 871 | 872 | static HookPage* allocHookPage(void* const preferred) 873 | { 874 | HookPage* const page = (HookPage*)allocUser(preferred, sizeof(HookPage), PAGE_EXECUTE_READWRITE); 875 | if (!page) 876 | { 877 | return nullptr; 878 | } 879 | 880 | #if _KERNEL_MODE 881 | const PEPROCESS currentProcess = PsGetCurrentProcess(); 882 | const Salt* const salt = getSalt(); 883 | page->header.magic = k_hookPageMagic ^ ((size_t)currentProcess) ^ salt->forMagic; 884 | page->header.pageBase = (size_t)page ^ ((size_t)currentProcess) ^ salt->forBase; 885 | #endif 886 | 887 | const unsigned char k_cellsCount = sizeof(((const HookPage*)nullptr)->cells) / sizeof(*(((const HookPage*)nullptr)->cells)); 888 | page->header.freeBitmap = (1ull << k_cellsCount) - 1ull; 889 | return page; 890 | } 891 | 892 | static void unlinkHookPage(HookPage* page); 893 | 894 | static void freeHookPage(HookPage* const page) 895 | { 896 | if (getHookPagesList() == page) 897 | { 898 | const HookPage* const next = page->header.next; 899 | setHookPagesList(next); 900 | } 901 | 902 | unlinkHookPage(page); 903 | 904 | freeUser(page); 905 | } 906 | 907 | static bool isHookPageEmpty(const HookPage* const page) 908 | { 909 | const unsigned char k_cellsCount = sizeof(((const HookPage*)nullptr)->cells) / sizeof(*(((const HookPage*)nullptr)->cells)); 910 | return page->header.freeBitmap == ((1ull << k_cellsCount) - 1ull); 911 | } 912 | 913 | static bool isHookPageFilled(const HookPage* const page) 914 | { 915 | return page->header.freeBitmap == 0; 916 | } 917 | 918 | static bool isHookPageHasFreeCells(const HookPage* const page) 919 | { 920 | return page->header.freeBitmap != 0; 921 | } 922 | 923 | static void insertHookPage(HookPage* page) 924 | { 925 | HookPage* const pages = getHookPagesList(); 926 | 927 | if (!pages) 928 | { 929 | page->header.prev = nullptr; 930 | page->header.next = nullptr; 931 | setHookPagesList(page); 932 | return; 933 | } 934 | 935 | for (HookPage* entry = pages; entry != nullptr; entry = entry->header.next) 936 | { 937 | HookPage* const next = entry->header.next; 938 | if ((page > entry) && (!next || (page < next))) 939 | { 940 | entry->header.next = page; 941 | page->header.prev = entry; 942 | page->header.next = next; 943 | if (next) 944 | { 945 | next->header.prev = page; 946 | } 947 | break; 948 | } 949 | } 950 | } 951 | 952 | static void unlinkHookPage(HookPage* page) 953 | { 954 | HookPage* const prev = page->header.prev; 955 | HookPage* const next = page->header.next; 956 | if (prev) 957 | { 958 | prev->header.next = next; 959 | } 960 | 961 | if (next) 962 | { 963 | next->header.prev = prev; 964 | } 965 | 966 | page->header.prev = nullptr; 967 | page->header.next = nullptr; 968 | } 969 | 970 | static HookPage* findHookPage(void* addr) 971 | { 972 | HookPage* const pages = getHookPagesList(); 973 | for (HookPage* entry = pages; entry != nullptr; entry = entry->header.next) 974 | { 975 | if (!relativeJumpable(entry, addr)) 976 | { 977 | if ((void*)entry < addr) 978 | { 979 | continue; 980 | } 981 | 982 | if (addr < (void*)entry) 983 | { 984 | return nullptr; 985 | } 986 | } 987 | 988 | const bool hasFreeCells = !isHookPageFilled(entry); 989 | if (hasFreeCells) 990 | { 991 | return entry; 992 | } 993 | } 994 | 995 | return nullptr; 996 | } 997 | 998 | static HookData* claimHookCell(HookPage* page) 999 | { 1000 | unsigned long firstFree = 0; 1001 | const bool hasFreeCell = BitScanForward64(&firstFree, page->header.freeBitmap); 1002 | if (!hasFreeCell) 1003 | { 1004 | return nullptr; 1005 | } 1006 | 1007 | page->header.freeBitmap &= ~(1ull << firstFree); 1008 | 1009 | HookData* const cell = &page->cells[firstFree]; 1010 | cell->indexInPage = (unsigned char)firstFree; 1011 | 1012 | return cell; 1013 | } 1014 | 1015 | static HookPage* releaseHookCell(HookData* data) 1016 | { 1017 | HookPage* page = (HookPage*)((unsigned char*)data - offsetof(HookPage, cells[data->indexInPage])); 1018 | page->header.freeBitmap |= (1ull << data->indexInPage); 1019 | memset(data, 0, sizeof(*data)); 1020 | return page; 1021 | } 1022 | 1023 | 1024 | 1025 | 1026 | typedef WRK_SYSTEM_PROCESS_INFORMATION ProcInfo; 1027 | typedef WRK_SYSTEM_THREAD_INFORMATION ThreadInfo; 1028 | 1029 | 1030 | static ProcInfo* makeProcSnapshot() 1031 | { 1032 | unsigned long len = 0; 1033 | const NTSTATUS lengthStatus = ZwQuerySystemInformation(SystemProcessInformation, nullptr, 0, &len); 1034 | if (lengthStatus != STATUS_INFO_LENGTH_MISMATCH) 1035 | { 1036 | return nullptr; 1037 | } 1038 | 1039 | ProcInfo* info = nullptr; 1040 | 1041 | while (1) 1042 | { 1043 | const unsigned int k_additionalSize = 4096 * 5; 1044 | len += k_additionalSize; 1045 | info = allocUser(nullptr, len, PAGE_READWRITE); 1046 | if (!info) 1047 | { 1048 | return nullptr; 1049 | } 1050 | 1051 | const NTSTATUS status = ZwQuerySystemInformation(SystemProcessInformation, info, len, &len); 1052 | if (NT_SUCCESS(status)) 1053 | { 1054 | break; 1055 | } 1056 | 1057 | freeUser(info); 1058 | 1059 | if (status != STATUS_INFO_LENGTH_MISMATCH) 1060 | { 1061 | return nullptr; 1062 | } 1063 | } 1064 | 1065 | return info; 1066 | } 1067 | 1068 | static void freeProcSnapshot(ProcInfo* snapshot) 1069 | { 1070 | if (snapshot) 1071 | { 1072 | freeUser(snapshot); 1073 | } 1074 | } 1075 | 1076 | 1077 | 1078 | #if _KERNEL_MODE 1079 | static bool isCurrentThreadBelongsToCurrentProcess() 1080 | { 1081 | const PETHREAD thread = PsGetCurrentThread(); 1082 | if (IoIsSystemThread(thread)) 1083 | { 1084 | return false; 1085 | } 1086 | 1087 | const bool attached = KeIsAttachedProcess(); 1088 | 1089 | return !attached; 1090 | } 1091 | #endif 1092 | 1093 | 1094 | 1095 | typedef enum 1096 | { 1097 | stop, 1098 | next 1099 | } EnumAction; 1100 | 1101 | typedef enum 1102 | { 1103 | failed, 1104 | completed, 1105 | stopped 1106 | } EnumStatus; 1107 | 1108 | static EnumStatus forEachProcess(const ProcInfo* const snapshot, EnumAction(*const cb)(const ProcInfo* proc, void* arg), void* const arg) 1109 | { 1110 | if (!snapshot) 1111 | { 1112 | return failed; 1113 | } 1114 | 1115 | const ProcInfo* info = snapshot; 1116 | 1117 | EnumStatus enumStatus = completed; 1118 | bool needToContinue = true; 1119 | do { 1120 | const EnumAction action = cb(info, arg); 1121 | if (action == stop) 1122 | { 1123 | enumStatus = stopped; 1124 | break; 1125 | } 1126 | 1127 | needToContinue = info->NextEntryOffset != 0; 1128 | if (needToContinue) 1129 | { 1130 | info = (const ProcInfo*)((const unsigned char*)info + info->NextEntryOffset); 1131 | } 1132 | } while (needToContinue); 1133 | 1134 | return enumStatus; 1135 | } 1136 | 1137 | static EnumStatus forEachThread(const ProcInfo* const proc, EnumAction(*const cb)(const ProcInfo* proc, const ThreadInfo* thread, void* arg), void* const arg) 1138 | { 1139 | if (!proc) 1140 | { 1141 | return failed; 1142 | } 1143 | 1144 | EnumStatus enumStatus = completed; 1145 | 1146 | for (unsigned int i = 0; i < proc->NumberOfThreads; ++i) 1147 | { 1148 | const EnumAction action = cb(proc, &proc->Threads[i], arg); 1149 | if (action == stop) 1150 | { 1151 | enumStatus = stopped; 1152 | break; 1153 | } 1154 | } 1155 | 1156 | return enumStatus; 1157 | } 1158 | 1159 | 1160 | static EnumAction findCurrentProcessCallback(const ProcInfo* const proc, void* const arg) 1161 | { 1162 | if (((size_t)proc->UniqueProcessId) == pid()) 1163 | { 1164 | const ProcInfo** const currentProcess = (const ProcInfo**)arg; 1165 | *currentProcess = proc; 1166 | return stop; 1167 | } 1168 | 1169 | return next; 1170 | } 1171 | 1172 | static const ProcInfo* findCurrentProcess(const ProcInfo* const snapshot) 1173 | { 1174 | ProcInfo* currentProcess = nullptr; 1175 | const EnumStatus enumStatus = forEachProcess(snapshot, findCurrentProcessCallback, ¤tProcess); 1176 | if (enumStatus != stopped) 1177 | { 1178 | return nullptr; 1179 | } 1180 | 1181 | return currentProcess; 1182 | } 1183 | 1184 | 1185 | #if _USER_MODE 1186 | static EnumAction beginHookSessionCallback(const ProcInfo* proc, const ThreadInfo* thread, void* arg) 1187 | { 1188 | unused(proc, arg); 1189 | 1190 | ThreadInfo* const mutableThread = (ThreadInfo*)thread; 1191 | mutableThread->u0.hThread = 0; 1192 | 1193 | if (((size_t)thread->ClientId.UniqueThread) == tid()) 1194 | { 1195 | return next; 1196 | } 1197 | 1198 | OBJECT_ATTRIBUTES attrs; 1199 | InitializeObjectAttributes(&attrs, nullptr, 0, nullptr, nullptr); 1200 | 1201 | HANDLE hThread = nullptr; 1202 | const NTSTATUS openStatus = ZwOpenThread(&hThread, THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, &attrs, &thread->ClientId); 1203 | if (!NT_SUCCESS(openStatus) || !hThread) 1204 | { 1205 | return next; 1206 | } 1207 | 1208 | unsigned long prevSuspendCount = 0; 1209 | const NTSTATUS suspendStatus = ZwSuspendThread(hThread, &prevSuspendCount); 1210 | if (!NT_SUCCESS(suspendStatus)) 1211 | { 1212 | ZwClose(hThread); 1213 | return next; 1214 | } 1215 | 1216 | mutableThread->u0.hThread = hThread; 1217 | return next; 1218 | } 1219 | #elif _KERNEL_MODE 1220 | typedef struct 1221 | { 1222 | FnPspGetContextThreadInternal get; 1223 | FnPspSetContextThreadInternal set; 1224 | } ContextFunctions; 1225 | 1226 | static ContextFunctions lookupContextFunctions() 1227 | { 1228 | #ifdef _AMD64_ 1229 | /* 1230 | Ps[Get/Set]ContextThread(PETHREAD:rcx, CONTEXT*:rdx, KPROCESSOR_MODE:r8): 1231 | +00: sub rsp, 38h | 48 83 EC 38 1232 | +04: mov r9b, r8b | 45 8A C8 1233 | +07: mov[b/l] [rsp+38h+arg5], 1 | C6 44 24 20 01 or C7 44 24 20 01 00 00 00 1234 | +12/15: call Psp[Get/Set]ContextThreadInternal | E8 NN NN NN NN 1235 | +17/20: add rsp, 38h | 48 83 C4 38 1236 | +21/24: retn | C3 1237 | */ 1238 | 1239 | #pragma pack(push, 1) 1240 | typedef struct 1241 | { 1242 | unsigned int subRsp38h; 1243 | unsigned char movR9bR8b[3]; 1244 | unsigned char movOpcode; 1245 | } LayoutGeneric; 1246 | #pragma pack(pop) 1247 | 1248 | #pragma pack(push, 1) 1249 | typedef struct 1250 | { 1251 | unsigned int subRsp38h; 1252 | unsigned char movR9bR8b[3]; 1253 | unsigned char movbArg5[5]; // C6 44 24 20 01 | movb [rsp+38h+arg5], 1 1254 | unsigned char callOpcode; 1255 | int calleeOffset; 1256 | unsigned int addRsp38h; 1257 | unsigned char retn; 1258 | } LayoutMovb; 1259 | #pragma pack(pop) 1260 | 1261 | #pragma pack(push, 1) 1262 | typedef struct 1263 | { 1264 | unsigned int subRsp38h; 1265 | unsigned char movR9bR8b[3]; 1266 | unsigned char movlArg5[8]; // C7 44 24 20 01 00 00 00 | movl [rsp+38h+arg5], 1 1267 | unsigned char callOpcode; 1268 | int calleeOffset; 1269 | unsigned int addRsp38h; 1270 | unsigned char retn; 1271 | } LayoutMovl; 1272 | #pragma pack(pop) 1273 | #else 1274 | /* 1275 | Ps[Get/Set]ContextThread(PETHREAD:[esp+4], CONTEXT*:[esp+8], KPROCESSOR_MODE:[esp+12]): 1276 | +00: mov edi, edi | 8B FF 1277 | +02: push ebp | 55 1278 | +03: mov ebp, esp | 8B EC 1279 | +05: push 1 | 6A 01 1280 | +07: push [ebp+arg2] | FF 75 10 1281 | +10: push [ebp+arg2] | FF 75 10 1282 | +13: push [ebp+arg1] | FF 75 0C 1283 | +16: push [ebp+arg0] | FF 75 08 1284 | +19: call Psp[Get/Set]ContextThreadInternal | E8 NN NN NN NN 1285 | +24: pop ebp | 5D 1286 | +25: retn 0Ch | C2 0C 00 1287 | */ 1288 | 1289 | #pragma pack(push, 1) 1290 | typedef struct 1291 | { 1292 | unsigned short movEdiEdi; 1293 | unsigned char pushEbp; 1294 | unsigned short movEbpEsp; 1295 | unsigned short push1; // Arg4 1296 | unsigned char pushArg3[3]; 1297 | unsigned char pushArg2[3]; 1298 | unsigned char pushArg1[3]; 1299 | unsigned char pushArg0[3]; 1300 | unsigned char callOpcode; 1301 | int calleeOffset; 1302 | unsigned char popEbp; 1303 | unsigned char retn; 1304 | unsigned short argsSize; 1305 | } Layout; 1306 | #pragma pack(pop) 1307 | #endif 1308 | 1309 | const unsigned char k_callOpcode = 0xE8; 1310 | 1311 | #ifdef _AMD64_ 1312 | const LayoutGeneric* const getLayoutGeneric = (const LayoutGeneric*)PsGetContextThread; 1313 | const LayoutGeneric* const setLayoutGeneric = (const LayoutGeneric*)PsSetContextThread; 1314 | 1315 | const unsigned char k_movbOpcode = 0xC6; 1316 | const unsigned char k_movlOpcode = 0xC7; 1317 | 1318 | const __unaligned int* getCalleeOffsetAddress = nullptr; 1319 | const __unaligned int* setCalleeOffsetAddress = nullptr; 1320 | 1321 | if ((getLayoutGeneric->movOpcode == k_movlOpcode) && (setLayoutGeneric->movOpcode == k_movlOpcode)) 1322 | { 1323 | const LayoutMovl* const getLayout = (const LayoutMovl*)PsGetContextThread; 1324 | const LayoutMovl* const setLayout = (const LayoutMovl*)PsSetContextThread; 1325 | if ((getLayout->callOpcode != k_callOpcode) || (setLayout->callOpcode != k_callOpcode)) 1326 | { 1327 | const ContextFunctions functions = { .get = nullptr, .set = nullptr }; 1328 | return functions; 1329 | } 1330 | 1331 | getCalleeOffsetAddress = &getLayout->calleeOffset; 1332 | setCalleeOffsetAddress = &setLayout->calleeOffset; 1333 | } 1334 | else if ((getLayoutGeneric->movOpcode == k_movbOpcode) && (setLayoutGeneric->movOpcode == k_movbOpcode)) 1335 | { 1336 | const LayoutMovb* const getLayout = (const LayoutMovb*)PsGetContextThread; 1337 | const LayoutMovb* const setLayout = (const LayoutMovb*)PsSetContextThread; 1338 | if ((getLayout->callOpcode != k_callOpcode) || (setLayout->callOpcode != k_callOpcode)) 1339 | { 1340 | const ContextFunctions functions = { .get = nullptr, .set = nullptr }; 1341 | return functions; 1342 | } 1343 | 1344 | getCalleeOffsetAddress = &getLayout->calleeOffset; 1345 | setCalleeOffsetAddress = &setLayout->calleeOffset; 1346 | } 1347 | else 1348 | { 1349 | const ContextFunctions functions = { .get = nullptr, .set = nullptr }; 1350 | return functions; 1351 | } 1352 | 1353 | #else 1354 | const Layout* const getLayout = (const Layout*)PsGetContextThread; 1355 | const Layout* const setLayout = (const Layout*)PsSetContextThread; 1356 | if ((getLayout->callOpcode != k_callOpcode) || (setLayout->callOpcode != k_callOpcode)) 1357 | { 1358 | const ContextFunctions functions = { .get = nullptr, .set = nullptr }; 1359 | return functions; 1360 | } 1361 | 1362 | const int* const getCalleeOffsetAddress = &getLayout->calleeOffset; 1363 | const int* const setCalleeOffsetAddress = &setLayout->calleeOffset; 1364 | #endif 1365 | 1366 | const FnPspGetContextThreadInternal get = (FnPspGetContextThreadInternal)((const unsigned char*)(getCalleeOffsetAddress + 1) + *getCalleeOffsetAddress); 1367 | const FnPspSetContextThreadInternal set = (FnPspSetContextThreadInternal)((const unsigned char*)(setCalleeOffsetAddress + 1) + *setCalleeOffsetAddress); 1368 | 1369 | const ContextFunctions functions = { .get = get, .set = set }; 1370 | 1371 | return functions; 1372 | } 1373 | 1374 | 1375 | typedef unsigned int CrossThreadFlags; 1376 | 1377 | typedef struct 1378 | { 1379 | unsigned int offset; 1380 | unsigned int flag; 1381 | } TerminatingFlag; 1382 | 1383 | static TerminatingFlag parseThreadTerminatingFlag() 1384 | { 1385 | #ifdef _AMD64_ 1386 | /* 1387 | PsIsThreadTerminating(PETHREAD:rcx): 1388 | +00: mov eax, [rcx+offset] | 8B 81 NN NN NN NN 1389 | +06: and al, 1 | 24 01 1390 | +08: retn | C3 1391 | */ 1392 | 1393 | #pragma pack(push, 1) 1394 | typedef struct 1395 | { 1396 | unsigned short movEaxOpcode; 1397 | unsigned int offset; 1398 | unsigned char andAlOpcode; 1399 | unsigned char value; 1400 | unsigned char retn; 1401 | } Layout; 1402 | #pragma pack(pop) 1403 | 1404 | const unsigned short k_movEaxOffsetOpcode = 0x818B; 1405 | const unsigned char k_retnOpcode = 0xC3; 1406 | 1407 | #else 1408 | /* 1409 | PsIsThreadTerminating(PETHREAD:[esp+4]): 1410 | +00: mov edi, edi | 8B FF 1411 | +02: push ebp | 55 1412 | +03: mov ebp, esp | 8B EC 1413 | +05: mov eax, [ebp+PETHREAD] | 8B 45 08 1414 | +08: mov eax, [eax+offset] | 8B 80 NN NN NN NN 1415 | +14: and al, 1 | 24 01 1416 | +16: pop ebp | 5D 1417 | +17: retn 4 | C2 04 00 1418 | */ 1419 | 1420 | #pragma pack(push, 1) 1421 | typedef struct 1422 | { 1423 | unsigned char beginning[8]; 1424 | unsigned short movEaxOpcode; 1425 | unsigned int offset; 1426 | unsigned char andAlOpcode; 1427 | unsigned char value; 1428 | unsigned char popEbp; 1429 | unsigned char retn; 1430 | unsigned short argsSize; 1431 | } Layout; 1432 | #pragma pack(pop) 1433 | 1434 | const unsigned short k_movEaxOffsetOpcode = 0x808B; 1435 | const unsigned char k_retnOpcode = 0xC2; 1436 | 1437 | #endif 1438 | 1439 | const unsigned char k_andAlOpcode = 0x24; 1440 | 1441 | const Layout* const layout = (const Layout*)PsIsThreadTerminating; 1442 | if ((layout->movEaxOpcode != k_movEaxOffsetOpcode) || (layout->andAlOpcode != k_andAlOpcode) || (layout->retn != k_retnOpcode)) 1443 | { 1444 | const TerminatingFlag flag = { .offset = 0, .flag = 0 }; 1445 | return flag; 1446 | } 1447 | 1448 | const TerminatingFlag flag = { .offset = layout->offset, .flag = layout->value }; 1449 | return flag; 1450 | } 1451 | 1452 | static void setThreadIsTerminating(PETHREAD thread, const TerminatingFlag* flag) 1453 | { 1454 | *(CrossThreadFlags*)((unsigned char*)thread + flag->offset) |= (CrossThreadFlags)flag->flag; 1455 | } 1456 | 1457 | static void resetThreadIsTerminating(PETHREAD thread, const TerminatingFlag* flag) 1458 | { 1459 | *(CrossThreadFlags*)((unsigned char*)thread + flag->offset) &= ~(CrossThreadFlags)flag->flag; 1460 | } 1461 | 1462 | static bool suspendCurrentProcess() 1463 | { 1464 | const PEPROCESS currentProcess = PsGetCurrentProcess(); 1465 | 1466 | if (!isCurrentThreadBelongsToCurrentProcess()) 1467 | { 1468 | const NTSTATUS status = PsSuspendProcess(currentProcess); 1469 | return NT_SUCCESS(status); 1470 | } 1471 | 1472 | const TerminatingFlag flag = parseThreadTerminatingFlag(); 1473 | if (!flag.offset) 1474 | { 1475 | return false; 1476 | } 1477 | 1478 | const PETHREAD currentThread = PsGetCurrentThread(); 1479 | setThreadIsTerminating(currentThread, &flag); 1480 | const NTSTATUS status = PsSuspendProcess(currentProcess); // Exclude the current thread from suspension as all terminating threads will be skipped 1481 | resetThreadIsTerminating(currentThread, &flag); 1482 | 1483 | return NT_SUCCESS(status); 1484 | } 1485 | 1486 | static void resumeCurrentProcess() 1487 | { 1488 | PsResumeProcess(PsGetCurrentProcess()); 1489 | } 1490 | 1491 | static EnumAction beginHookSessionCallback(const ProcInfo* proc, const ThreadInfo* thread, void* arg) 1492 | { 1493 | unused(proc, arg); 1494 | 1495 | ThreadInfo* const mutableThread = (ThreadInfo*)thread; 1496 | mutableThread->u0.thread = 0; 1497 | 1498 | if (((size_t)thread->ClientId.UniqueThread) == tid()) 1499 | { 1500 | return next; 1501 | } 1502 | 1503 | const NTSTATUS lookupStatus = PsLookupThreadByThreadId(thread->ClientId.UniqueThread, &mutableThread->u0.thread); 1504 | if (!NT_SUCCESS(lookupStatus)) 1505 | { 1506 | return next; 1507 | } 1508 | 1509 | return next; 1510 | } 1511 | #endif 1512 | 1513 | 1514 | static bool beginHookSession(ProcInfo* process) 1515 | { 1516 | acquireGlobalLock(); 1517 | 1518 | #if _KERNEL_MODE 1519 | initSalt(); 1520 | 1521 | const bool suspendStatus = suspendCurrentProcess(); 1522 | if (!suspendStatus) 1523 | { 1524 | return false; 1525 | } 1526 | 1527 | HookPage* const firstPage = lookupHookPagesList(); 1528 | setHookPagesList(firstPage); 1529 | #endif 1530 | 1531 | const EnumStatus enumStatus = forEachThread(process, beginHookSessionCallback, nullptr); 1532 | return enumStatus != failed; 1533 | } 1534 | 1535 | #if _KERNEL_MODE 1536 | static void beginKernelHookSession() 1537 | { 1538 | initSalt(); 1539 | acquireGlobalLock(); 1540 | } 1541 | #endif 1542 | 1543 | static EnumAction endHookSessionCallback(const ProcInfo* proc, const ThreadInfo* thread, void* arg) 1544 | { 1545 | unused(proc, arg); 1546 | 1547 | ThreadInfo* const mutableThread = (ThreadInfo*)thread; 1548 | 1549 | #if _USER_MODE 1550 | if (!mutableThread->u0.hThread) 1551 | { 1552 | return next; 1553 | } 1554 | 1555 | unsigned long suspendCount = 0; 1556 | ZwResumeThread(mutableThread->u0.hThread, &suspendCount); 1557 | ZwClose(mutableThread->u0.hThread); 1558 | mutableThread->u0.hThread = 0; 1559 | #elif _KERNEL_MODE 1560 | if (!mutableThread->u0.thread) 1561 | { 1562 | return next; 1563 | } 1564 | 1565 | ObDereferenceObject(mutableThread->u0.thread); 1566 | mutableThread->u0.thread = nullptr; 1567 | #endif 1568 | 1569 | return next; 1570 | } 1571 | 1572 | static bool endHookSession(ProcInfo* process) 1573 | { 1574 | ZwFlushInstructionCache(NtCurrentProcess(), 0, 0); 1575 | const EnumStatus enumStatus = forEachThread(process, endHookSessionCallback, nullptr); 1576 | 1577 | #if _KERNEL_MODE 1578 | resumeCurrentProcess(); 1579 | resetHookPagesList(); 1580 | #endif 1581 | 1582 | releaseGlobalLock(); 1583 | return enumStatus != failed; 1584 | } 1585 | 1586 | #if _KERNEL_MODE 1587 | static void endKernelHookSession() 1588 | { 1589 | ZwFlushInstructionCache(NtCurrentProcess(), 0, 0); 1590 | releaseGlobalLock(); 1591 | } 1592 | #endif 1593 | 1594 | 1595 | 1596 | static void* findPageForRelativeJump(const void* addr) 1597 | { 1598 | const unsigned int k_granularity = 64 * 1024; 1599 | 1600 | unsigned char* base = (unsigned char*)alignUp((size_t)addr, k_granularity); 1601 | 1602 | MEMORY_BASIC_INFORMATION info; 1603 | SIZE_T resLen = 0; 1604 | 1605 | // Forward: 1606 | while (NT_SUCCESS(ZwQueryVirtualMemory(NtCurrentProcess(), (void*)base, MemoryBasicInformation, &info, sizeof(info), &resLen))) 1607 | { 1608 | if (info.State == MEM_FREE) 1609 | { 1610 | return base; 1611 | } 1612 | 1613 | base += info.RegionSize; 1614 | base = (unsigned char*)alignUp((size_t)base, k_granularity); 1615 | 1616 | if (!relativeJumpable(base, addr)) 1617 | { 1618 | break; 1619 | } 1620 | } 1621 | 1622 | base = (unsigned char*)(alignDown((size_t)addr, k_granularity) - 1); 1623 | 1624 | // Backward: 1625 | while (NT_SUCCESS(ZwQueryVirtualMemory(NtCurrentProcess(), (void*)base, MemoryBasicInformation, &info, sizeof(info), &resLen))) 1626 | { 1627 | if (info.State == MEM_FREE) 1628 | { 1629 | return base; 1630 | } 1631 | 1632 | base = ((unsigned char*)info.BaseAddress) - 1; 1633 | 1634 | if (!relativeJumpable(base, addr)) 1635 | { 1636 | break; 1637 | } 1638 | } 1639 | 1640 | return nullptr; // Not found 1641 | } 1642 | 1643 | 1644 | 1645 | 1646 | 1647 | static void relocate(void* const addr, ssize_t relocationDelta, unsigned char patchSizeInBits) 1648 | { 1649 | switch (patchSizeInBits) 1650 | { 1651 | case bitsof(char): 1652 | { 1653 | *(char*)(addr) -= (char)relocationDelta; 1654 | break; 1655 | } 1656 | case bitsof(short): 1657 | { 1658 | *(short*)(addr) -= (short)relocationDelta; 1659 | break; 1660 | } 1661 | case bitsof(int): 1662 | { 1663 | *(int*)(addr) -= (int)relocationDelta; 1664 | break; 1665 | } 1666 | case bitsof(long long): 1667 | { 1668 | *(long long*)(addr) -= (long long)relocationDelta; 1669 | break; 1670 | } 1671 | } 1672 | } 1673 | 1674 | static bool relocatable(unsigned char relocatableBits, size_t length) 1675 | { 1676 | const size_t availableRange = (size_t)(((1ull << relocatableBits) - 1ul) / 2); 1677 | return length < availableRange; 1678 | } 1679 | 1680 | typedef enum 1681 | { 1682 | x32, 1683 | x64, 1684 | native = (sizeof(size_t) == sizeof(unsigned int)) ? x32 : x64 1685 | } Arch; 1686 | 1687 | static unsigned char relocateBeginning(Arch arch, const void* from, void* to, unsigned int bytesToRelocate) 1688 | { 1689 | ZydisDecoder decoder; 1690 | if (arch == x64) 1691 | { 1692 | ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64); 1693 | } 1694 | else 1695 | { 1696 | ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LEGACY_32, ZYDIS_STACK_WIDTH_32); 1697 | } 1698 | 1699 | unsigned char relocatedBytes = 0; 1700 | 1701 | const unsigned char* srcInstr = (const unsigned char*)from; 1702 | ZydisDecodedInstruction instr; 1703 | while (ZYAN_SUCCESS(ZydisDecoderDecodeInstruction(&decoder, ZYAN_NULL, srcInstr, 16, &instr))) 1704 | { 1705 | unsigned char* const destInstr = (unsigned char*)to + (srcInstr - (const unsigned char*)from); 1706 | memcpy(destInstr, srcInstr, instr.length); 1707 | 1708 | if (instr.attributes & ZYDIS_ATTRIB_IS_RELATIVE) 1709 | { 1710 | const ssize_t direction = delta(srcInstr, destInstr); 1711 | const size_t length = direction >= 0 ? ((size_t)direction) : ((size_t)-direction); 1712 | 1713 | if (instr.raw.disp.offset) 1714 | { 1715 | if (!relocatable(instr.raw.disp.size, length)) 1716 | { 1717 | return 0; // It is impossible to relocate this instruction 1718 | } 1719 | 1720 | relocate(destInstr + instr.raw.disp.offset, direction, instr.raw.disp.size); 1721 | } 1722 | 1723 | for (unsigned char i = 0; i < 2; ++i) 1724 | { 1725 | if (instr.raw.imm[i].offset && instr.raw.imm[i].is_relative) 1726 | { 1727 | if (!relocatable(instr.raw.imm[i].size, length)) 1728 | { 1729 | return 0; // It is impossible to relocate this instruction 1730 | } 1731 | 1732 | relocate(destInstr + instr.raw.imm[i].offset, direction, instr.raw.imm[i].size); 1733 | } 1734 | } 1735 | } 1736 | 1737 | srcInstr += instr.length; 1738 | relocatedBytes += instr.length; 1739 | if (relocatedBytes >= bytesToRelocate) 1740 | { 1741 | break; 1742 | } 1743 | } 1744 | 1745 | return relocatedBytes; 1746 | } 1747 | 1748 | 1749 | static bool writeToUser(void* const dest, const void* const src, unsigned int size) 1750 | { 1751 | const unsigned int prevProtect = protectUser(dest, size, PAGE_EXECUTE_READWRITE); 1752 | if (!prevProtect) 1753 | { 1754 | return false; 1755 | } 1756 | 1757 | memcpy(dest, src, size); 1758 | 1759 | protectUser(dest, size, prevProtect); 1760 | return true; 1761 | } 1762 | 1763 | #if _KERNEL_MODE 1764 | static bool writeToKernel(void* const dest, const void* const src, const unsigned int size) 1765 | { 1766 | Mapping mapping = makeWriteableMapping(dest, size); 1767 | if (!isMappingValid(&mapping)) 1768 | { 1769 | return false; 1770 | } 1771 | 1772 | memcpy(mapping.addr, src, size); 1773 | 1774 | freeMapping(&mapping); 1775 | return true; 1776 | } 1777 | #endif 1778 | 1779 | static bool writeToReadonly(void* const dest, const void* const src, const unsigned int size) 1780 | { 1781 | #if _USER_MODE 1782 | const bool status = writeToUser(dest, src, size); 1783 | #elif _KERNEL_MODE 1784 | const bool status = isKernelAddress(dest) 1785 | ? writeToKernel(dest, src, size) 1786 | : writeToUser(dest, src, size); 1787 | #endif 1788 | 1789 | return status; 1790 | } 1791 | 1792 | 1793 | static void writeJumpToContinuation(const Arch arch, void* const from, const void* const to) 1794 | { 1795 | if (relativeJumpable(from, to)) 1796 | { 1797 | *(RelJump*)(from) = makeRelJump(from, to); 1798 | } 1799 | else 1800 | { 1801 | if (arch == x64) 1802 | { 1803 | *(LongJump64*)(from) = makeLongJump64(to); 1804 | } 1805 | else 1806 | { 1807 | *(LongJump32*)(from) = makeLongJump32(from, to); 1808 | } 1809 | } 1810 | } 1811 | 1812 | static bool applyHook(const Arch arch, HookData* const hook, void* const fn, const void* const handler, void** original) 1813 | { 1814 | if (!hook || !fn || !original) 1815 | { 1816 | return false; 1817 | } 1818 | 1819 | hook->fn = fn; 1820 | 1821 | const bool needLongJump = k_forceLongJumps || !relativeJumpable(fn, handler); 1822 | const bool intermediateJumpAppliable = k_enableIntermediateJumps && needLongJump && relativeJumpable(fn, &hook->intermediate); 1823 | 1824 | if (needLongJump && !intermediateJumpAppliable) 1825 | { 1826 | // Absolute jump: 1827 | if (arch == x64) 1828 | { 1829 | const unsigned char relocatedBytes = relocateBeginning(arch, fn, hook->beginning, sizeof(LongJump64)); 1830 | if (!relocatedBytes) 1831 | { 1832 | return false; 1833 | } 1834 | 1835 | memcpy(hook->original, fn, relocatedBytes); 1836 | 1837 | const void* const beginningContinuation = (const unsigned char*)fn + relocatedBytes; 1838 | writeJumpToContinuation(x64, &hook->beginning[relocatedBytes], beginningContinuation); 1839 | 1840 | hook->affectedBytes = relocatedBytes; 1841 | 1842 | *original = hook->beginning; 1843 | _mm_sfence(); 1844 | 1845 | const LongJump64 jump = makeLongJump64(handler); 1846 | const bool status = writeToReadonly(fn, &jump, sizeof(jump)); 1847 | if (!status) 1848 | { 1849 | *original = nullptr; 1850 | return false; 1851 | } 1852 | } 1853 | else 1854 | { 1855 | const unsigned char relocatedBytes = relocateBeginning(arch, fn, hook->beginning, sizeof(LongJump32)); 1856 | if (!relocatedBytes) 1857 | { 1858 | return false; 1859 | } 1860 | 1861 | memcpy(hook->original, fn, relocatedBytes); 1862 | 1863 | const void* const beginningContinuation = (const unsigned char*)fn + relocatedBytes; 1864 | writeJumpToContinuation(x32, &hook->beginning[relocatedBytes], beginningContinuation); 1865 | 1866 | hook->affectedBytes = relocatedBytes; 1867 | 1868 | *original = hook->beginning; 1869 | _mm_sfence(); 1870 | 1871 | const LongJump32 jump = makeLongJump32(fn, handler); 1872 | const bool status = writeToReadonly(fn, &jump, sizeof(jump)); 1873 | if (!status) 1874 | { 1875 | *original = nullptr; 1876 | return false; 1877 | } 1878 | } 1879 | } 1880 | else 1881 | { 1882 | // Relative jump: 1883 | if (intermediateJumpAppliable) 1884 | { 1885 | const unsigned char relocatedBytes = relocateBeginning(arch, fn, hook->beginning, sizeof(RelJump)); 1886 | if (!relocatedBytes) 1887 | { 1888 | return false; 1889 | } 1890 | 1891 | if (arch == x64) 1892 | { 1893 | hook->intermediate.x64 = makeLongJump64(handler); 1894 | } 1895 | else 1896 | { 1897 | hook->intermediate.x32 = makeLongJump32(fn, handler); 1898 | } 1899 | 1900 | memcpy(hook->original, fn, relocatedBytes); 1901 | 1902 | const void* const beginningContinuation = (const unsigned char*)fn + relocatedBytes; 1903 | writeJumpToContinuation(arch, &hook->beginning[relocatedBytes], beginningContinuation); 1904 | 1905 | hook->affectedBytes = relocatedBytes; 1906 | 1907 | *original = hook->beginning; 1908 | _mm_sfence(); 1909 | 1910 | const RelJump jump = makeRelJump(fn, &hook->intermediate); 1911 | const bool status = writeToReadonly(fn, &jump, sizeof(jump)); 1912 | if (!status) 1913 | { 1914 | *original = nullptr; 1915 | return false; 1916 | } 1917 | } 1918 | else 1919 | { 1920 | const unsigned char relocatedBytes = relocateBeginning(arch, fn, hook->beginning, sizeof(RelJump)); 1921 | if (!relocatedBytes) 1922 | { 1923 | return false; 1924 | } 1925 | 1926 | memcpy(hook->original, fn, relocatedBytes); 1927 | 1928 | const void* const beginningContinuation = (const unsigned char*)fn + relocatedBytes; 1929 | writeJumpToContinuation(arch, &hook->beginning[relocatedBytes], beginningContinuation); 1930 | 1931 | hook->affectedBytes = relocatedBytes; 1932 | 1933 | *original = hook->beginning; 1934 | _mm_sfence(); 1935 | 1936 | const RelJump jump = makeRelJump(fn, handler); 1937 | const bool status = writeToReadonly(fn, &jump, sizeof(jump)); 1938 | if (!status) 1939 | { 1940 | return false; 1941 | } 1942 | } 1943 | } 1944 | 1945 | return true; 1946 | } 1947 | 1948 | 1949 | #if _USER_MODE 1950 | static void setHook(Arch arch, void* fn, const void* handler, void** original) 1951 | { 1952 | HookPage* const existingPage = findHookPage(fn); 1953 | if (existingPage) 1954 | { 1955 | HookData* const freeHookCell = claimHookCell(existingPage); 1956 | const bool hookStatus = applyHook(arch, freeHookCell, fn, handler, original); 1957 | if (hookStatus) 1958 | { 1959 | return; 1960 | } 1961 | 1962 | releaseHookCell(freeHookCell); 1963 | } 1964 | 1965 | void* const nearestFreePage = findPageForRelativeJump(fn); // Nullable 1966 | HookPage* const newPage = allocHookPage(nearestFreePage); 1967 | if (!newPage) 1968 | { 1969 | return; 1970 | } 1971 | 1972 | HookData* const freeHookCell = claimHookCell(newPage); 1973 | const bool hookStatus = applyHook(arch, freeHookCell, fn, handler, original); 1974 | if (!hookStatus) 1975 | { 1976 | releaseHookCell(freeHookCell); 1977 | freeHookPage(newPage); 1978 | return; 1979 | } 1980 | 1981 | insertHookPage(newPage); 1982 | } 1983 | #else 1984 | static void setHook(Arch arch, void* fn, const void* handler, void** original) 1985 | { 1986 | if (isUserAddress(fn)) 1987 | { 1988 | HookPage* const existingPage = findHookPage(fn); 1989 | if (existingPage) 1990 | { 1991 | HookData* const freeHookCell = claimHookCell(existingPage); 1992 | const bool hookStatus = applyHook(arch, freeHookCell, fn, handler, original); 1993 | if (hookStatus) 1994 | { 1995 | return; 1996 | } 1997 | 1998 | releaseHookCell(freeHookCell); 1999 | } 2000 | 2001 | void* const nearestFreePage = findPageForRelativeJump(fn); // Nullable 2002 | HookPage* const newPage = allocHookPage(nearestFreePage); 2003 | if (!newPage) 2004 | { 2005 | return; 2006 | } 2007 | 2008 | HookData* const freeHookCell = claimHookCell(newPage); 2009 | const bool hookStatus = applyHook(arch, freeHookCell, fn, handler, original); 2010 | if (!hookStatus) 2011 | { 2012 | releaseHookCell(freeHookCell); 2013 | freeHookPage(newPage); 2014 | return; 2015 | } 2016 | 2017 | insertHookPage(newPage); 2018 | } 2019 | else 2020 | { 2021 | HookData* const hookData = (HookData*)allocKernel(sizeof(HookData)); 2022 | const bool hookStatus = applyHook(arch, hookData, fn, handler, original); 2023 | if (!hookStatus) 2024 | { 2025 | freeKernel(hookData); 2026 | } 2027 | } 2028 | } 2029 | #endif 2030 | 2031 | typedef enum 2032 | { 2033 | fixForHook, 2034 | fixForUnhook 2035 | } FixupType; 2036 | 2037 | typedef union 2038 | { 2039 | const Hook* hooks; 2040 | Unhook* unhooks; 2041 | } HooksUnhooks; 2042 | 2043 | static size_t calcNewInstructionPointer(const HooksUnhooks hooksUnhooks, const size_t count, size_t ip, const FixupType type) 2044 | { 2045 | bool needToFix = false; 2046 | switch (type) 2047 | { 2048 | case fixForHook: 2049 | { 2050 | for (const Hook* hook = hooksUnhooks.hooks; hook != &hooksUnhooks.hooks[count]; ++hook) 2051 | { 2052 | if (!hook->original) 2053 | { 2054 | continue; 2055 | } 2056 | 2057 | const HookData* const hookData = (HookData*)((unsigned char*)*hook->original - offsetof(HookData, beginning)); 2058 | if ((ip >= (size_t)hookData->fn) && (ip < ((size_t)hookData->fn + hookData->affectedBytes))) 2059 | { 2060 | needToFix = true; 2061 | const size_t offset = (size_t)ip - (size_t)hookData->fn; 2062 | ip = (size_t)&hookData->beginning[offset]; 2063 | } 2064 | } 2065 | break; 2066 | } 2067 | case fixForUnhook: 2068 | { 2069 | for (const Unhook* unhook = hooksUnhooks.unhooks; unhook != &hooksUnhooks.unhooks[count]; ++unhook) 2070 | { 2071 | if (!unhook->original) 2072 | { 2073 | continue; 2074 | } 2075 | 2076 | const HookData* const hookData = (HookData*)((unsigned char*)unhook->original - offsetof(HookData, beginning)); 2077 | if ((ip >= (size_t)hookData->beginning) && (ip < ((size_t)hookData->beginning + hookData->affectedBytes))) 2078 | { 2079 | needToFix = true; 2080 | const size_t offset = (size_t)ip - (size_t)hookData->beginning; 2081 | ip = (size_t)hookData->fn + offset; 2082 | } 2083 | } 2084 | break; 2085 | } 2086 | } 2087 | 2088 | return ip; 2089 | } 2090 | 2091 | 2092 | 2093 | #if _USER_MODE 2094 | static void fixupContexts(const ProcInfo* const proc, const HooksUnhooks hooksUnhooks, const size_t count, const FixupType type) 2095 | { 2096 | const unsigned int currentTid = tid(); 2097 | 2098 | CONTEXT ctx; 2099 | ctx.ContextFlags = CONTEXT_CONTROL; 2100 | 2101 | for (const ThreadInfo* thread = proc->Threads; thread != &proc->Threads[proc->NumberOfThreads]; ++thread) 2102 | { 2103 | if ((size_t)thread->ClientId.UniqueThread == currentTid) 2104 | { 2105 | continue; 2106 | } 2107 | 2108 | #ifdef _AMD64_ 2109 | size_t* const currentIp = (size_t*)&ctx.Rip; 2110 | #else 2111 | size_t* const currentIp = (size_t*)&ctx.Eip; 2112 | #endif 2113 | 2114 | *currentIp = 0; 2115 | 2116 | const NTSTATUS getCtxStatus = ZwGetContextThread(thread->u0.hThread, &ctx); 2117 | if (!NT_SUCCESS(getCtxStatus)) 2118 | { 2119 | continue; 2120 | } 2121 | 2122 | const size_t newIp = calcNewInstructionPointer(hooksUnhooks, count, *currentIp, type); 2123 | const bool needToFix = (newIp != *currentIp); 2124 | 2125 | if (needToFix) 2126 | { 2127 | *currentIp = newIp; 2128 | ZwSetContextThread(thread->u0.hThread, &ctx); 2129 | } 2130 | } 2131 | } 2132 | #elif _KERNEL_MODE 2133 | #ifdef _AMD64_ 2134 | 2135 | /* These structs and defines are from winnt.h which we can't include as it causes redeclaration conflicts with ntifs.h */ 2136 | 2137 | #define WOW64_SIZE_OF_80387_REGISTERS 80 2138 | 2139 | #define WOW64_MAXIMUM_SUPPORTED_EXTENSION 512 2140 | 2141 | typedef struct _WOW64_FLOATING_SAVE_AREA { 2142 | DWORD ControlWord; 2143 | DWORD StatusWord; 2144 | DWORD TagWord; 2145 | DWORD ErrorOffset; 2146 | DWORD ErrorSelector; 2147 | DWORD DataOffset; 2148 | DWORD DataSelector; 2149 | BYTE RegisterArea[WOW64_SIZE_OF_80387_REGISTERS]; 2150 | DWORD Cr0NpxState; 2151 | } WOW64_FLOATING_SAVE_AREA; 2152 | 2153 | typedef WOW64_FLOATING_SAVE_AREA* PWOW64_FLOATING_SAVE_AREA; 2154 | 2155 | #include "pshpack4.h" 2156 | 2157 | // 2158 | // Context Frame 2159 | // 2160 | // This frame has a several purposes: 1) it is used as an argument to 2161 | // NtContinue, 2) is is used to constuct a call frame for APC delivery, 2162 | // and 3) it is used in the user level thread creation routines. 2163 | // 2164 | // The layout of the record conforms to a standard call frame. 2165 | // 2166 | 2167 | typedef struct _WOW64_CONTEXT { 2168 | 2169 | // 2170 | // The flags values within this flag control the contents of 2171 | // a CONTEXT record. 2172 | // 2173 | // If the context record is used as an input parameter, then 2174 | // for each portion of the context record controlled by a flag 2175 | // whose value is set, it is assumed that that portion of the 2176 | // context record contains valid context. If the context record 2177 | // is being used to modify a threads context, then only that 2178 | // portion of the threads context will be modified. 2179 | // 2180 | // If the context record is used as an IN OUT parameter to capture 2181 | // the context of a thread, then only those portions of the thread's 2182 | // context corresponding to set flags will be returned. 2183 | // 2184 | // The context record is never used as an OUT only parameter. 2185 | // 2186 | 2187 | DWORD ContextFlags; 2188 | 2189 | // 2190 | // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is 2191 | // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT 2192 | // included in CONTEXT_FULL. 2193 | // 2194 | 2195 | DWORD Dr0; 2196 | DWORD Dr1; 2197 | DWORD Dr2; 2198 | DWORD Dr3; 2199 | DWORD Dr6; 2200 | DWORD Dr7; 2201 | 2202 | // 2203 | // This section is specified/returned if the 2204 | // ContextFlags word contians the flag CONTEXT_FLOATING_POINT. 2205 | // 2206 | 2207 | WOW64_FLOATING_SAVE_AREA FloatSave; 2208 | 2209 | // 2210 | // This section is specified/returned if the 2211 | // ContextFlags word contians the flag CONTEXT_SEGMENTS. 2212 | // 2213 | 2214 | DWORD SegGs; 2215 | DWORD SegFs; 2216 | DWORD SegEs; 2217 | DWORD SegDs; 2218 | 2219 | // 2220 | // This section is specified/returned if the 2221 | // ContextFlags word contians the flag CONTEXT_INTEGER. 2222 | // 2223 | 2224 | DWORD Edi; 2225 | DWORD Esi; 2226 | DWORD Ebx; 2227 | DWORD Edx; 2228 | DWORD Ecx; 2229 | DWORD Eax; 2230 | 2231 | // 2232 | // This section is specified/returned if the 2233 | // ContextFlags word contians the flag CONTEXT_CONTROL. 2234 | // 2235 | 2236 | DWORD Ebp; 2237 | DWORD Eip; 2238 | DWORD SegCs; // MUST BE SANITIZED 2239 | DWORD EFlags; // MUST BE SANITIZED 2240 | DWORD Esp; 2241 | DWORD SegSs; 2242 | 2243 | // 2244 | // This section is specified/returned if the ContextFlags word 2245 | // contains the flag CONTEXT_EXTENDED_REGISTERS. 2246 | // The format and contexts are processor specific 2247 | // 2248 | 2249 | BYTE ExtendedRegisters[WOW64_MAXIMUM_SUPPORTED_EXTENSION]; 2250 | 2251 | } WOW64_CONTEXT; 2252 | 2253 | typedef WOW64_CONTEXT* PWOW64_CONTEXT; 2254 | 2255 | #include "poppack.h" 2256 | 2257 | /* End of types from winnt.h */ 2258 | 2259 | static void fixupWow64Contexts(const ProcInfo* const proc, const HooksUnhooks hooksUnhooks, const size_t hooksCount, const FixupType type) 2260 | { 2261 | WOW64_CONTEXT* const ctx = (WOW64_CONTEXT*)allocUser(nullptr, sizeof(WOW64_CONTEXT), PAGE_READWRITE); 2262 | if (!ctx) 2263 | { 2264 | return; 2265 | } 2266 | 2267 | // These are from winnt.h (WOW64_CONTEXT_i386, WOW64_CONTEXT_CONTROL): 2268 | const unsigned int k_wow64Context386 = 0x00010000; 2269 | const unsigned int k_wow64ContextControl = (k_wow64Context386 | 0x00000001L); // SS:SP, CS:IP, FLAGS, BP 2270 | 2271 | ctx->ContextFlags = k_wow64ContextControl; 2272 | 2273 | const unsigned int currentTid = tid(); 2274 | 2275 | for (const ThreadInfo* thread = proc->Threads; thread != &proc->Threads[proc->NumberOfThreads]; ++thread) 2276 | { 2277 | if ((size_t)thread->ClientId.UniqueThread == currentTid) 2278 | { 2279 | continue; 2280 | } 2281 | 2282 | HANDLE hThread = nullptr; 2283 | const NTSTATUS openStatus = ObOpenObjectByPointer(thread->u0.thread, OBJ_KERNEL_HANDLE, nullptr, THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, *PsThreadType, KernelMode, &hThread); 2284 | if (!NT_SUCCESS(openStatus)) 2285 | { 2286 | continue; 2287 | } 2288 | 2289 | unsigned long returned = 0; 2290 | const NTSTATUS getCtxStatus = ZwQueryInformationThread(hThread, ThreadWow64Context, ctx, sizeof(*ctx), &returned); 2291 | if (!NT_SUCCESS(getCtxStatus)) 2292 | { 2293 | ZwClose(hThread); 2294 | continue; 2295 | } 2296 | 2297 | const size_t newIp = calcNewInstructionPointer(hooksUnhooks, hooksCount, ctx->Eip, type); 2298 | const bool needToFix = (newIp != ctx->Eip); 2299 | 2300 | if (needToFix) 2301 | { 2302 | ctx->Eip = (unsigned int)newIp; 2303 | ZwSetInformationThread(hThread, ThreadWow64Context, ctx, sizeof(*ctx)); 2304 | } 2305 | 2306 | ZwClose(hThread); 2307 | } 2308 | 2309 | freeUser(ctx); 2310 | } 2311 | #endif // _AMD64_ 2312 | 2313 | static void fixupNativeContexts(const ProcInfo* const proc, const HooksUnhooks hooksUnhooks, const size_t hooksCount, const FixupType type) 2314 | { 2315 | const ContextFunctions fnCtx = lookupContextFunctions(); 2316 | if (!fnCtx.get || !fnCtx.set) 2317 | { 2318 | return; 2319 | } 2320 | 2321 | const unsigned int currentTid = tid(); 2322 | 2323 | CONTEXT ctx; 2324 | ctx.ContextFlags = CONTEXT_CONTROL; 2325 | 2326 | for (const ThreadInfo* thread = proc->Threads; thread != &proc->Threads[proc->NumberOfThreads]; ++thread) 2327 | { 2328 | if ((size_t)thread->ClientId.UniqueThread == currentTid) 2329 | { 2330 | continue; 2331 | } 2332 | 2333 | #ifdef _AMD64_ 2334 | size_t* const currentIp = (size_t*)&ctx.Rip; 2335 | #else 2336 | size_t* const currentIp = (size_t*)&ctx.Eip; 2337 | #endif 2338 | 2339 | *currentIp = 0; 2340 | 2341 | const NTSTATUS getCtxStatus = fnCtx.get(thread->u0.thread, &ctx, KernelMode, UserMode, false); 2342 | if (!NT_SUCCESS(getCtxStatus)) 2343 | { 2344 | continue; 2345 | } 2346 | 2347 | const size_t newIp = calcNewInstructionPointer(hooksUnhooks, hooksCount, *currentIp, type); 2348 | const bool needToFix = (newIp != *currentIp); 2349 | 2350 | if (needToFix) 2351 | { 2352 | *currentIp = newIp; 2353 | fnCtx.set(thread->u0.thread, &ctx, KernelMode, UserMode, false); 2354 | } 2355 | } 2356 | } 2357 | 2358 | static void fixupContexts(const ProcInfo* const proc, const HooksUnhooks hooksUnhooks, const size_t hooksCount, const FixupType type) 2359 | { 2360 | #ifdef _AMD64_ 2361 | if (isWow64Process(PsGetCurrentProcess())) 2362 | { 2363 | fixupWow64Contexts(proc, hooksUnhooks, hooksCount, type); 2364 | } 2365 | else 2366 | { 2367 | fixupNativeContexts(proc, hooksUnhooks, hooksCount, type); 2368 | } 2369 | #else 2370 | fixupNativeContexts(proc, hooksUnhooks, hooksCount, type); 2371 | #endif 2372 | 2373 | ZwYieldExecution(); 2374 | } 2375 | #endif // _KERNEL_MODE 2376 | 2377 | 2378 | 2379 | static size_t applyHooks(Arch arch, const Hook* hooks, size_t count) 2380 | { 2381 | size_t hookedCount = 0; 2382 | 2383 | for (size_t i = 0; i < count; ++i) 2384 | { 2385 | const Hook* const hook = &hooks[i]; 2386 | setHook(arch, hook->fn, hook->handler, hook->original); 2387 | if (*hook->original) 2388 | { 2389 | #if _KERNEL_MODE 2390 | _mm_clflush(hook->fn); 2391 | #endif 2392 | ++hookedCount; 2393 | } 2394 | } 2395 | 2396 | _mm_sfence(); 2397 | 2398 | return hookedCount; 2399 | } 2400 | 2401 | #if _USER_MODE 2402 | size_t multihook(const Hook* hooks, size_t count) 2403 | { 2404 | if (!hooks || !count) 2405 | { 2406 | return 0; 2407 | } 2408 | 2409 | for (size_t i = 0; i < count; ++i) 2410 | { 2411 | *hooks[i].original = nullptr; 2412 | } 2413 | 2414 | ProcInfo* const snapshot = makeProcSnapshot(); 2415 | if (!snapshot) 2416 | { 2417 | return 0; 2418 | } 2419 | 2420 | ProcInfo* const currentProcess = (ProcInfo*)findCurrentProcess(snapshot); 2421 | if (!currentProcess) 2422 | { 2423 | freeProcSnapshot(snapshot); 2424 | return 0; 2425 | } 2426 | 2427 | const bool beginStatus = beginHookSession(currentProcess); 2428 | if (!beginStatus) 2429 | { 2430 | freeProcSnapshot(snapshot); 2431 | return 0; 2432 | } 2433 | 2434 | const size_t hookedCount = applyHooks(native, hooks, count); 2435 | 2436 | const HooksUnhooks hooksUnhooks = { .hooks = hooks }; 2437 | fixupContexts(currentProcess, hooksUnhooks, count, fixForHook); 2438 | 2439 | endHookSession(currentProcess); 2440 | 2441 | freeProcSnapshot(snapshot); 2442 | return hookedCount; 2443 | } 2444 | #else 2445 | size_t multihook(const Hook* hooks, size_t count) 2446 | { 2447 | if (!hooks || !count) 2448 | { 2449 | return 0; 2450 | } 2451 | 2452 | bool hasUserHooks = false; 2453 | for (size_t i = 0; i < count; ++i) 2454 | { 2455 | hasUserHooks |= isUserAddress(hooks[i].fn); 2456 | *hooks[i].original = nullptr; 2457 | } 2458 | 2459 | if (hasUserHooks) 2460 | { 2461 | const bool virtualProtectStatus = initVirtualProtect(); 2462 | if (!virtualProtectStatus) 2463 | { 2464 | return 0; 2465 | } 2466 | 2467 | ProcInfo* const snapshot = makeProcSnapshot(); 2468 | if (!snapshot) 2469 | { 2470 | return 0; 2471 | } 2472 | 2473 | ProcInfo* const currentProcess = (ProcInfo*)findCurrentProcess(snapshot); 2474 | if (!currentProcess) 2475 | { 2476 | freeProcSnapshot(snapshot); 2477 | return 0; 2478 | } 2479 | 2480 | const bool beginStatus = beginHookSession(currentProcess); 2481 | if (!beginStatus) 2482 | { 2483 | freeProcSnapshot(snapshot); 2484 | return 0; 2485 | } 2486 | 2487 | #ifdef _AMD64_ 2488 | const Arch k_arch = isWow64Process(PsGetCurrentProcess()) ? x32 : x64; 2489 | #else 2490 | const Arch k_arch = x32; 2491 | #endif 2492 | 2493 | const size_t hookedCount = applyHooks(k_arch, hooks, count); 2494 | 2495 | const HooksUnhooks hooksUnhooks = { .hooks = hooks }; 2496 | fixupContexts(currentProcess, hooksUnhooks, count, fixForHook); 2497 | 2498 | endHookSession(currentProcess); 2499 | 2500 | freeProcSnapshot(snapshot); 2501 | return hookedCount; 2502 | } 2503 | else 2504 | { 2505 | beginKernelHookSession(); 2506 | const size_t hookedCount = applyHooks(native, hooks, count); 2507 | endKernelHookSession(); 2508 | 2509 | return hookedCount; 2510 | } 2511 | } 2512 | #endif 2513 | 2514 | void hook(void* fn, const void* handler, void** original) 2515 | { 2516 | if (!fn || !original) 2517 | { 2518 | return; 2519 | } 2520 | 2521 | const Hook hook = 2522 | { 2523 | .fn = fn, 2524 | .handler = handler, 2525 | .original = original 2526 | }; 2527 | 2528 | multihook(&hook, 1); 2529 | } 2530 | 2531 | static bool performUnhook(HookData* hook) 2532 | { 2533 | if (!hook) 2534 | { 2535 | return false; 2536 | } 2537 | 2538 | const bool writeStatus = writeToReadonly(hook->fn, hook->original, hook->affectedBytes); 2539 | if (!writeStatus) 2540 | { 2541 | return false; 2542 | } 2543 | 2544 | #if _USER_MODE 2545 | HookPage* const page = releaseHookCell(hook); 2546 | if (isHookPageEmpty(page)) 2547 | { 2548 | freeHookPage(page); 2549 | } 2550 | #elif _KERNEL_MODE 2551 | if (isKernelAddress(hook)) 2552 | { 2553 | freeKernel(hook); 2554 | } 2555 | else 2556 | { 2557 | HookPage* const page = releaseHookCell(hook); 2558 | if (isHookPageEmpty(page)) 2559 | { 2560 | freeHookPage(page); 2561 | } 2562 | } 2563 | #endif 2564 | 2565 | return true; 2566 | } 2567 | 2568 | #if _USER_MODE 2569 | size_t multiunhook(Unhook* originals, size_t count) 2570 | { 2571 | if (!originals || !count) 2572 | { 2573 | return 0; 2574 | } 2575 | 2576 | ProcInfo* const snapshot = makeProcSnapshot(); 2577 | if (!snapshot) 2578 | { 2579 | return 0; 2580 | } 2581 | 2582 | ProcInfo* const currentProcess = (ProcInfo*)findCurrentProcess(snapshot); 2583 | if (!currentProcess) 2584 | { 2585 | freeProcSnapshot(snapshot); 2586 | return 0; 2587 | } 2588 | 2589 | const bool beginStatus = beginHookSession(currentProcess); 2590 | if (!beginStatus) 2591 | { 2592 | freeProcSnapshot(snapshot); 2593 | return 0; 2594 | } 2595 | 2596 | size_t unhookedCount = 0; 2597 | 2598 | const HooksUnhooks hooksUnhooks = { .unhooks = originals }; 2599 | fixupContexts(currentProcess, hooksUnhooks, count, fixForUnhook); 2600 | 2601 | for (size_t i = 0; i < count; ++i) 2602 | { 2603 | if (!originals[i].original) 2604 | { 2605 | continue; 2606 | } 2607 | 2608 | HookData* const hook = (HookData*)((unsigned char*)originals[i].original - offsetof(HookData, beginning)); 2609 | 2610 | const bool status = performUnhook(hook); 2611 | if (status) 2612 | { 2613 | originals[i].original = nullptr; 2614 | ++unhookedCount; 2615 | } 2616 | } 2617 | 2618 | _mm_sfence(); 2619 | 2620 | endHookSession(currentProcess); 2621 | 2622 | freeProcSnapshot(snapshot); 2623 | 2624 | return unhookedCount; 2625 | } 2626 | #elif _KERNEL_MODE 2627 | size_t multiunhook(Unhook* originals, size_t count) 2628 | { 2629 | if (!originals || !count) 2630 | { 2631 | return 0; 2632 | } 2633 | 2634 | bool hasUserHooks = false; 2635 | for (size_t i = 0; i < count; ++i) 2636 | { 2637 | hasUserHooks |= isUserAddress(originals[i].original); 2638 | } 2639 | 2640 | size_t unhookedCount = 0; 2641 | 2642 | if (hasUserHooks) 2643 | { 2644 | const bool virtualProtectStatus = initVirtualProtect(); 2645 | if (!virtualProtectStatus) 2646 | { 2647 | return 0; 2648 | } 2649 | 2650 | ProcInfo* const snapshot = makeProcSnapshot(); 2651 | if (!snapshot) 2652 | { 2653 | return 0; 2654 | } 2655 | 2656 | ProcInfo* const currentProcess = (ProcInfo*)findCurrentProcess(snapshot); 2657 | if (!currentProcess) 2658 | { 2659 | freeProcSnapshot(snapshot); 2660 | return 0; 2661 | } 2662 | 2663 | const bool beginStatus = beginHookSession(currentProcess); 2664 | if (!beginStatus) 2665 | { 2666 | freeProcSnapshot(snapshot); 2667 | return 0; 2668 | } 2669 | 2670 | const HooksUnhooks hooksUnhooks = { .unhooks = originals }; 2671 | fixupContexts(currentProcess, hooksUnhooks, count, fixForUnhook); 2672 | 2673 | for (size_t i = 0; i < count; ++i) 2674 | { 2675 | if (!originals[i].original) 2676 | { 2677 | continue; 2678 | } 2679 | 2680 | HookData* const hook = (HookData*)((unsigned char*)originals[i].original - offsetof(HookData, beginning)); 2681 | const void* const fn = hook->fn; 2682 | 2683 | const bool status = performUnhook(hook); 2684 | if (status) 2685 | { 2686 | _mm_clflush(fn); 2687 | originals[i].original = nullptr; 2688 | ++unhookedCount; 2689 | } 2690 | } 2691 | 2692 | _mm_sfence(); 2693 | 2694 | endHookSession(currentProcess); 2695 | 2696 | freeProcSnapshot(snapshot); 2697 | } 2698 | else 2699 | { 2700 | beginKernelHookSession(); 2701 | 2702 | for (size_t i = 0; i < count; ++i) 2703 | { 2704 | if (!originals[i].original) 2705 | { 2706 | continue; 2707 | } 2708 | 2709 | HookData* const hook = (HookData*)((unsigned char*)originals[i].original - offsetof(HookData, beginning)); 2710 | const void* const fn = hook->fn; 2711 | 2712 | const bool status = performUnhook(hook); 2713 | if (status) 2714 | { 2715 | _mm_clflush(fn); 2716 | originals[i].original = nullptr; 2717 | ++unhookedCount; 2718 | } 2719 | } 2720 | 2721 | _mm_sfence(); 2722 | 2723 | endKernelHookSession(); 2724 | } 2725 | 2726 | return unhookedCount; 2727 | } 2728 | #endif 2729 | 2730 | size_t unhook(void* original) 2731 | { 2732 | Unhook fn = { .original = original }; 2733 | return multiunhook(&fn, 1); 2734 | } 2735 | -------------------------------------------------------------------------------- /HookLib/HookLib/HookLib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | #include 5 | #define hooklib_export extern "C" 6 | #else 7 | #include 8 | #define hooklib_export 9 | #endif 10 | 11 | typedef struct 12 | { 13 | void* fn; 14 | const void* handler; 15 | void** original; // hook() makes it valid callable pointer after successful hook and sets as nullptr otherwise 16 | } Hook; 17 | 18 | typedef struct 19 | { 20 | void* original; // unhook() makes it nullptr after successful unhook and keeps unchanged otherwise 21 | } Unhook; 22 | 23 | hooklib_export void hook(void* fn, const void* handler, void** original); 24 | hooklib_export size_t multihook(const Hook* hooks, size_t count); 25 | 26 | hooklib_export size_t multiunhook(Unhook* originals, size_t count); 27 | hooklib_export size_t unhook(void* original); 28 | 29 | #ifndef _KERNEL_MODE 30 | hooklib_export void* lookupModule(const wchar_t* modName); // LdrGetDllHandle 31 | hooklib_export void* lookupFunction(const void* hModule, const char* funcName); // LdrGetProcedureAddress 32 | #endif 33 | 34 | #ifdef __cplusplus 35 | template 36 | class HookHolder 37 | { 38 | private: 39 | struct tr 40 | { 41 | template 42 | static constexpr Type exchange(Type& val, Other&& newVal) 43 | { 44 | const Type oldVal = static_cast(val); 45 | val = static_cast(newVal); 46 | return oldVal; 47 | } 48 | }; 49 | 50 | protected: 51 | Fn m_orig; 52 | Fn m_fn; 53 | Fn m_handler; 54 | 55 | public: 56 | HookHolder() = default; 57 | 58 | HookHolder(Fn fn, Fn handler) noexcept : m_orig(nullptr), m_fn(fn), m_handler(handler) 59 | { 60 | } 61 | 62 | HookHolder(const HookHolder&) = delete; 63 | 64 | HookHolder(HookHolder&& holder) noexcept 65 | : m_orig(tr::exchange(holder.m_orig, nullptr)) 66 | , m_fn(tr::exchange(holder.m_fn, nullptr)) 67 | , m_handler(tr::exchange(holder.m_handler, nullptr)) 68 | { 69 | } 70 | 71 | HookHolder& operator = (const HookHolder&) = delete; 72 | 73 | HookHolder& operator = (HookHolder&& holder) noexcept 74 | { 75 | if (&holder == this) 76 | { 77 | return *this; 78 | } 79 | 80 | disable(); 81 | 82 | m_orig = tr::exchange(holder.m_orig, nullptr); 83 | m_fn = tr::exchange(holder.m_fn, nullptr); 84 | m_handler = tr::exchange(holder.m_handler, nullptr); 85 | 86 | return *this; 87 | } 88 | 89 | ~HookHolder() noexcept 90 | { 91 | if (active()) 92 | { 93 | disable(); 94 | } 95 | } 96 | 97 | bool valid() const noexcept 98 | { 99 | return m_fn && m_handler; 100 | } 101 | 102 | bool active() const noexcept 103 | { 104 | return m_orig != nullptr; 105 | } 106 | 107 | bool enable() noexcept 108 | { 109 | if (!valid()) 110 | { 111 | return false; 112 | } 113 | 114 | if (active()) 115 | { 116 | return true; 117 | } 118 | 119 | hook(m_fn, m_handler, reinterpret_cast(&m_orig)); 120 | 121 | return m_orig != nullptr; 122 | } 123 | 124 | bool disable() noexcept 125 | { 126 | if (!valid()) 127 | { 128 | return false; 129 | } 130 | 131 | if (!active()) 132 | { 133 | return true; 134 | } 135 | 136 | const bool unhookStatus = (unhook(m_orig) == 1); 137 | if (unhookStatus) 138 | { 139 | m_orig = nullptr; 140 | } 141 | 142 | return unhookStatus; 143 | } 144 | 145 | Fn detach() noexcept 146 | { 147 | return tr::exchange(m_orig, nullptr); 148 | } 149 | 150 | Fn original() const noexcept 151 | { 152 | return m_orig; 153 | } 154 | 155 | Fn fn() const noexcept 156 | { 157 | return m_fn; 158 | } 159 | 160 | Fn handler() const noexcept 161 | { 162 | return m_handler; 163 | } 164 | 165 | #ifdef _MSC_VER 166 | __declspec(property(get = original)) Fn call; 167 | #endif 168 | }; 169 | 170 | struct HookFactory 171 | { 172 | template 173 | [[nodiscard]] static HookHolder install(Fn fn, Fn handler) noexcept 174 | { 175 | HookHolder hook(fn, handler); 176 | hook.enable(); 177 | return hook; 178 | } 179 | 180 | template 181 | [[nodiscard]] static HookHolder install(void* fn, Fn handler) noexcept 182 | { 183 | return install(static_cast(fn), handler); 184 | } 185 | 186 | #ifndef _KERNEL_MODE 187 | template 188 | [[nodiscard]] static HookHolder install(void* mod, const char* const funcName, Fn handler) noexcept 189 | { 190 | if (!mod) 191 | { 192 | return HookHolder(nullptr, handler); 193 | } 194 | 195 | void* const fn = lookupFunction(mod, funcName); 196 | if (!fn) 197 | { 198 | return HookHolder(nullptr, handler); 199 | } 200 | 201 | return install(static_cast(fn), handler); 202 | } 203 | 204 | template 205 | [[nodiscard]] static HookHolder install(const wchar_t* const modName, const char* const funcName, Fn handler) noexcept 206 | { 207 | const void* const mod = lookupModule(modName); 208 | return install(mod, funcName, handler); 209 | } 210 | #endif 211 | }; 212 | #endif -------------------------------------------------------------------------------- /HookLibDrvTests/HookLibDrvTests.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {3E9752F7-9B84-4844-847E-08F6E2DE1D32} 23 | {dd38f7fc-d7bd-488b-9242-7d8754cde80d} 24 | v4.5 25 | 12.0 26 | Debug 27 | Win32 28 | HookLibDrvTests 29 | $(LatestTargetPlatformVersion) 30 | 31 | 32 | 33 | Windows10 34 | true 35 | WindowsKernelModeDriver10.0 36 | Driver 37 | WDM 38 | false 39 | 40 | 41 | Windows10 42 | false 43 | WindowsKernelModeDriver10.0 44 | Driver 45 | WDM 46 | false 47 | 48 | 49 | Windows10 50 | true 51 | WindowsKernelModeDriver10.0 52 | Driver 53 | WDM 54 | false 55 | 56 | 57 | Windows10 58 | false 59 | WindowsKernelModeDriver10.0 60 | Driver 61 | WDM 62 | false 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | DbgengKernelDebugger 74 | false 75 | 76 | 77 | DbgengKernelDebugger 78 | false 79 | 80 | 81 | DbgengKernelDebugger 82 | false 83 | 84 | 85 | DbgengKernelDebugger 86 | false 87 | 88 | 89 | 90 | sha256 91 | 92 | 93 | true 94 | 95 | 96 | true 97 | true 98 | true 99 | stdcpplatest 100 | stdc17 101 | 102 | 103 | 104 | 105 | sha256 106 | 107 | 108 | true 109 | 110 | 111 | true 112 | true 113 | true 114 | stdcpplatest 115 | stdc17 116 | MaxSpeed 117 | true 118 | Speed 119 | true 120 | /Ob3 %(AdditionalOptions) 121 | 122 | 123 | UseLinkTimeCodeGeneration 124 | 125 | 126 | 127 | 128 | sha256 129 | 130 | 131 | true 132 | 133 | 134 | true 135 | true 136 | true 137 | stdcpplatest 138 | stdc17 139 | 140 | 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | sha256 148 | 149 | 150 | true 151 | 152 | 153 | true 154 | true 155 | true 156 | stdcpplatest 157 | stdc17 158 | MaxSpeed 159 | true 160 | Speed 161 | true 162 | /Ob3 %(AdditionalOptions) 163 | 164 | 165 | UseLinkTimeCodeGeneration 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | {9379f9bc-7829-45d8-b339-90f6504fdf2b} 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /HookLibDrvTests/HookLibDrvTests.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {8E41214B-6785-4CFE-B992-037D68949A14} 14 | inf;inv;inx;mof;mc; 15 | 16 | 17 | {8226bbda-85fe-4a63-9093-f214a2a521b1} 18 | 19 | 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | HookLib 28 | 29 | 30 | -------------------------------------------------------------------------------- /HookLibDrvTests/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define hk_assert(cond) if (!(cond)) { __int2c(); } __assume((cond)) 5 | 6 | extern "C" NTSTATUS NTAPI ZwYieldExecution(); 7 | 8 | namespace 9 | { 10 | template 11 | constexpr inline unsigned int validFunc(int a, int b) 12 | { 13 | return 0x1ee7c0de * (a + b + index); 14 | } 15 | 16 | template 17 | constexpr inline unsigned int validHandler(int a, int b) 18 | { 19 | return 0xc0ffee * (a + b + index); 20 | } 21 | 22 | template 23 | __declspec(noinline) __declspec(dllexport) unsigned int func(int a, int b) 24 | { 25 | ZwYieldExecution(); 26 | return validFunc(a, b); 27 | } 28 | 29 | template 30 | __declspec(noinline) __declspec(dllexport) unsigned int handler(int a, int b) 31 | { 32 | ZwYieldExecution(); 33 | return validHandler(a, b); 34 | } 35 | 36 | void testHookOnce() 37 | { 38 | decltype(func<0>)* original = nullptr; 39 | hook(func<0>, handler<0>, reinterpret_cast(&original)); 40 | hk_assert(func<0>(11, 22) == validHandler<0>(11, 22)); 41 | hk_assert(original(11, 22) == validFunc<0>(11, 22)); 42 | 43 | unhook(original); 44 | 45 | hk_assert(func<0>(11, 22) == validFunc<0>(11, 22)); 46 | } 47 | 48 | void testMultihook() 49 | { 50 | void* originals[2]{}; 51 | Hook hooks[] 52 | { 53 | { 54 | .fn = func<1>, 55 | .handler = handler<1>, 56 | .original = &originals[0] 57 | }, 58 | { 59 | .fn = func<2>, 60 | .handler = handler<2>, 61 | .original = &originals[1] 62 | } 63 | }; 64 | 65 | const auto hookedCount = multihook(hooks, 2); 66 | hk_assert(hookedCount == 2); 67 | 68 | hk_assert(originals[0] && originals[1]); 69 | 70 | using Fn1 = decltype(func<1>)*; 71 | using Fn2 = decltype(func<2>)*; 72 | 73 | const Fn1 orig1 = static_cast(originals[0]); 74 | const Fn2 orig2 = static_cast(originals[1]); 75 | 76 | hk_assert(func<1>(11, 22) == validHandler<1>(11, 22)); 77 | hk_assert(func<2>(11, 22) == validHandler<2>(11, 22)); 78 | hk_assert(orig1(11, 22) == validFunc<1>(11, 22)); 79 | hk_assert(orig2(11, 22) == validFunc<2>(11, 22)); 80 | 81 | Unhook unhooks[] 82 | { 83 | { .original = orig1 }, 84 | { .original = orig2 } 85 | }; 86 | const auto unhookedCount = multiunhook(unhooks, 2); 87 | hk_assert(unhookedCount == 2); 88 | 89 | hk_assert(func<1>(11, 22) == validFunc<1>(11, 22)); 90 | hk_assert(func<2>(11, 22) == validFunc<2>(11, 22)); 91 | } 92 | 93 | void runTests() 94 | { 95 | testHookOnce(); 96 | testMultihook(); 97 | } 98 | } // namespace 99 | 100 | 101 | namespace 102 | { 103 | const UNICODE_STRING k_devName = RTL_CONSTANT_STRING(L"\\Device\\HookLibTestDrv"); 104 | const UNICODE_STRING k_symLink = RTL_CONSTANT_STRING(L"\\??\\HookLibTestDrv"); 105 | PDEVICE_OBJECT g_devObj = nullptr; 106 | 107 | _Function_class_(DRIVER_DISPATCH) 108 | _IRQL_requires_max_(DISPATCH_LEVEL) 109 | _IRQL_requires_same_ 110 | NTSTATUS NTAPI driverStub(PDEVICE_OBJECT, PIRP irp) 111 | { 112 | irp->IoStatus.Information = 0; 113 | irp->IoStatus.Status = STATUS_SUCCESS; 114 | 115 | IoCompleteRequest(irp, IO_NO_INCREMENT); 116 | 117 | return STATUS_SUCCESS; 118 | } 119 | 120 | _Function_class_(DRIVER_DISPATCH) 121 | _IRQL_requires_max_(DISPATCH_LEVEL) 122 | _IRQL_requires_same_ 123 | NTSTATUS NTAPI ioctlHandler(PDEVICE_OBJECT, PIRP irp) 124 | { 125 | const PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp); 126 | 127 | const auto ctl = irpStack->Parameters.DeviceIoControl.IoControlCode; 128 | 129 | switch (ctl) 130 | { 131 | case CTL_CODE(0x8000, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS): 132 | { 133 | runTests(); 134 | irp->IoStatus.Information = 0; 135 | irp->IoStatus.Status = STATUS_SUCCESS; 136 | break; 137 | } 138 | case CTL_CODE(0x8000, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS): 139 | { 140 | struct HookRequest 141 | { 142 | struct Input 143 | { 144 | unsigned long long fn; 145 | unsigned long long handler; 146 | }; 147 | 148 | struct Output 149 | { 150 | unsigned long long original; 151 | }; 152 | }; 153 | 154 | const auto* const in = static_cast(irpStack->Parameters.DeviceIoControl.Type3InputBuffer); 155 | auto* const out = static_cast(irp->UserBuffer); 156 | 157 | hook(reinterpret_cast(in->fn), reinterpret_cast(in->handler), reinterpret_cast(&out->original)); 158 | 159 | irp->IoStatus.Status = out->original ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; 160 | irp->IoStatus.Information = sizeof(HookRequest::Output); 161 | break; 162 | } 163 | case CTL_CODE(0x8000, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS): 164 | { 165 | struct UnhookRequest 166 | { 167 | struct Input 168 | { 169 | unsigned long long original; 170 | }; 171 | 172 | struct Output 173 | { 174 | bool status; 175 | }; 176 | }; 177 | 178 | const auto* const in = static_cast(irpStack->Parameters.DeviceIoControl.Type3InputBuffer); 179 | auto* const out = static_cast(irp->UserBuffer); 180 | 181 | out->status = (1 == unhook(reinterpret_cast(in->original))); 182 | 183 | irp->IoStatus.Status = out->status ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; 184 | irp->IoStatus.Information = sizeof(UnhookRequest::Output); 185 | break; 186 | } 187 | default: 188 | { 189 | irp->IoStatus.Information = 0; 190 | irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; 191 | IoCompleteRequest(irp, IO_NO_INCREMENT); 192 | return STATUS_INVALID_DEVICE_REQUEST; 193 | } 194 | } 195 | 196 | const NTSTATUS status = irp->IoStatus.Status; 197 | 198 | IoCompleteRequest(irp, IO_NO_INCREMENT); 199 | return status; 200 | } 201 | } // namespace 202 | 203 | 204 | extern "C" NTSTATUS NTAPI DriverEntry(PDRIVER_OBJECT drv, PUNICODE_STRING) 205 | { 206 | drv->DriverUnload = [](PDRIVER_OBJECT drv) 207 | { 208 | if (drv->DeviceObject) 209 | { 210 | IoDeleteSymbolicLink(const_cast(&k_symLink)); 211 | IoDeleteDevice(drv->DeviceObject); 212 | } 213 | }; 214 | 215 | const NTSTATUS devStatus = IoCreateDevice(drv, 0, const_cast(&k_devName), FILE_DEVICE_UNKNOWN, 0, false, &g_devObj); 216 | if (!NT_SUCCESS(devStatus)) 217 | { 218 | return devStatus; 219 | } 220 | 221 | const NTSTATUS symlinkStatus = IoCreateSymbolicLink(const_cast(&k_symLink), const_cast(&k_devName)); 222 | if (!NT_SUCCESS(symlinkStatus)) 223 | { 224 | IoDeleteDevice(g_devObj); 225 | return symlinkStatus; 226 | } 227 | 228 | drv->MajorFunction[IRP_MJ_CREATE] = driverStub; 229 | drv->MajorFunction[IRP_MJ_CLEANUP] = driverStub; 230 | drv->MajorFunction[IRP_MJ_CLOSE] = driverStub; 231 | drv->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ioctlHandler; 232 | 233 | return STATUS_SUCCESS; 234 | } -------------------------------------------------------------------------------- /HookLibTests/HookLibTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | 8 | constexpr bool k_testKernelMode = false; 9 | 10 | namespace 11 | { 12 | 13 | template 14 | __declspec(noinline) int func(int arg1, float arg2) 15 | { 16 | printf("Orig: %u %i %f\n", num, arg1, arg2); 17 | return 0x1ee7c0de; 18 | } 19 | 20 | template 21 | __declspec(noinline) int handler(int arg1, float arg2) 22 | { 23 | printf("Hook: %u %i %f\n", num, arg1, arg2); 24 | return 1337; 25 | } 26 | 27 | template 28 | void hookFunc(Func fn, Func handler, Func* original) 29 | { 30 | hook(fn, handler, reinterpret_cast(original)); 31 | } 32 | 33 | #define begin_test printf("%s:\n", __FUNCTION__) 34 | #define end_test printf("\n") 35 | #define hk_assert(cond) if (!(cond)) { printf("[X] %s\n", #cond); __int2c(); } __assume((cond)) 36 | #define log(fmt, ...) printf("[" __FILE__ ":%u]: " fmt "\n", __LINE__, __VA_ARGS__) 37 | 38 | void testHookOnce() 39 | { 40 | begin_test; 41 | 42 | func<0>(0, 0.123f); 43 | 44 | decltype(func<0>)* orig0 = nullptr; 45 | hookFunc(func<0>, handler<0>, &orig0); 46 | 47 | hk_assert(orig0 != nullptr); 48 | 49 | func<0>(0, 0.0f); 50 | orig0(0, 0.0f); 51 | 52 | unhook(orig0); 53 | func<0>(0, 0.0f); 54 | 55 | end_test; 56 | } 57 | 58 | void testSerialHooks() 59 | { 60 | begin_test; 61 | 62 | decltype(func<1>)* orig1 = nullptr; 63 | decltype(func<2>)* orig2 = nullptr; 64 | hookFunc(func<1>, handler<1>, &orig1); 65 | hookFunc(func<2>, handler<2>, &orig2); 66 | 67 | func<1>(1, 0.1f); 68 | orig1(1, 0.1f); 69 | 70 | func<2>(2, 0.2f); 71 | orig2(2, 0.2f); 72 | 73 | unhook(orig2); 74 | func<2>(2, 0.2f); 75 | 76 | unhook(orig1); 77 | func<1>(1, 0.1f); 78 | 79 | end_test; 80 | } 81 | 82 | void testSerialHooksMultiunhook() 83 | { 84 | begin_test; 85 | 86 | decltype(func<1>)* orig1 = nullptr; 87 | decltype(func<2>)* orig2 = nullptr; 88 | hookFunc(func<1>, handler<1>, &orig1); 89 | hookFunc(func<2>, handler<2>, &orig2); 90 | 91 | func<1>(1, 0.1f); 92 | orig1(1, 0.1f); 93 | 94 | func<2>(2, 0.2f); 95 | orig2(2, 0.2f); 96 | 97 | Unhook fns[2]{ orig1, orig2 }; 98 | multiunhook(fns, 2); 99 | 100 | func<1>(1, 0.1f); 101 | func<2>(2, 0.2f); 102 | 103 | end_test; 104 | } 105 | 106 | void testMultihook() 107 | { 108 | begin_test; 109 | 110 | void* originals[2]{}; 111 | 112 | Hook hooks[2] 113 | { 114 | { 115 | .fn = func<1>, 116 | .handler = handler<1>, 117 | .original = &originals[0] 118 | }, 119 | { 120 | .fn = func<2>, 121 | .handler = handler<2>, 122 | .original = &originals[1] 123 | } 124 | }; 125 | 126 | const auto hooked = multihook(hooks, 2); 127 | hk_assert(hooked == 2); 128 | 129 | using Fn1 = decltype(func<1>)*; 130 | using Fn2 = decltype(func<2>)*; 131 | 132 | hk_assert(originals[0] != nullptr); 133 | hk_assert(originals[1] != nullptr); 134 | 135 | func<1>(1, 0.1f); 136 | static_cast(originals[0])(1, 0.1f); 137 | 138 | func<2>(2, 0.2f); 139 | static_cast(originals[1])(2, 0.2f); 140 | 141 | Unhook fns[2]{ originals[0], originals[1] }; 142 | multiunhook(fns, 2); 143 | 144 | func<1>(1, 0.1f); 145 | func<2>(2, 0.2f); 146 | 147 | end_test; 148 | } 149 | 150 | void testContextsFixup() 151 | { 152 | begin_test; 153 | 154 | const HANDLE hThread = CreateThread(nullptr, 0, [](void* arg) -> unsigned long 155 | { 156 | return 0; 157 | }, nullptr, CREATE_SUSPENDED, nullptr); 158 | 159 | hk_assert(hThread != nullptr); 160 | 161 | CONTEXT ctx{}; 162 | ctx.ContextFlags = CONTEXT_CONTROL; 163 | const bool getStatus = !!GetThreadContext(hThread, &ctx); 164 | hk_assert(getStatus); 165 | 166 | #ifdef _AMD64_ 167 | const size_t origIp = ctx.Rip; 168 | ctx.Rip = reinterpret_cast(func<0>); 169 | #else 170 | const size_t origIp = ctx.Eip; 171 | ctx.Eip = reinterpret_cast(func<0>); 172 | #endif 173 | 174 | const bool setStatus = !!SetThreadContext(hThread, &ctx); 175 | hk_assert(setStatus); 176 | 177 | SwitchToThread(); 178 | const bool ensureStatus = !!GetThreadContext(hThread, &ctx); // Ensure that the context was setted 179 | hk_assert(ensureStatus); 180 | #ifdef _AMD64_ 181 | hk_assert(ctx.Rip == reinterpret_cast(func<0>)); 182 | #else 183 | hk_assert(ctx.Eip == reinterpret_cast(func<0>)); 184 | #endif 185 | 186 | decltype(func<0>)* orig = nullptr; 187 | hookFunc(func<0>, handler<0>, &orig); 188 | 189 | const bool secondGetStatus = !!GetThreadContext(hThread, &ctx); 190 | hk_assert(secondGetStatus); 191 | 192 | #ifdef _AMD64_ 193 | hk_assert(ctx.Rip == reinterpret_cast(orig)); 194 | #else 195 | hk_assert(ctx.Eip == reinterpret_cast(orig)); 196 | #endif 197 | 198 | unhook(orig); 199 | 200 | const bool thirdGetStatus = !!GetThreadContext(hThread, &ctx); 201 | hk_assert(thirdGetStatus); 202 | 203 | #ifdef _AMD64_ 204 | hk_assert(ctx.Rip == reinterpret_cast(func<0>)); 205 | ctx.Rip = origIp; 206 | #else 207 | hk_assert(ctx.Eip == reinterpret_cast(func<0>)); 208 | ctx.Eip = origIp; 209 | #endif 210 | 211 | const bool restoreOrigIpStatus = !!SetThreadContext(hThread, &ctx); 212 | hk_assert(restoreOrigIpStatus); 213 | 214 | SwitchToThread(); 215 | 216 | ResumeThread(hThread); 217 | WaitForSingleObject(hThread, INFINITE); 218 | 219 | CloseHandle(hThread); 220 | 221 | end_test; 222 | } 223 | 224 | void driverTestKernelHooks(HANDLE hDev) 225 | { 226 | begin_test; 227 | unsigned long returned = 0; 228 | const bool testStatus = !!DeviceIoControl(hDev, CTL_CODE(0x8000, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS), nullptr, 0, nullptr, 0, &returned, nullptr); 229 | hk_assert(testStatus); 230 | end_test; 231 | } 232 | 233 | void driverTestUserHooks(HANDLE hDev) 234 | { 235 | begin_test; 236 | 237 | const HANDLE hThread = CreateThread(nullptr, 0, [](void* arg) -> unsigned long 238 | { 239 | return 0; 240 | }, nullptr, CREATE_SUSPENDED, nullptr); 241 | 242 | hk_assert(hThread != nullptr); 243 | 244 | CONTEXT ctx{}; 245 | ctx.ContextFlags = CONTEXT_CONTROL; 246 | const bool getStatus = !!GetThreadContext(hThread, &ctx); 247 | hk_assert(getStatus); 248 | 249 | #ifdef _AMD64_ 250 | const size_t origIp = ctx.Rip; 251 | ctx.Rip = reinterpret_cast(func<0>); 252 | #else 253 | const size_t origIp = ctx.Eip; 254 | ctx.Eip = reinterpret_cast(func<0>); 255 | #endif 256 | 257 | const bool setStatus = !!SetThreadContext(hThread, &ctx); 258 | hk_assert(setStatus); 259 | 260 | SwitchToThread(); 261 | 262 | struct HookRequest 263 | { 264 | struct Input 265 | { 266 | unsigned long long fn; 267 | unsigned long long handler; 268 | }; 269 | 270 | struct Output 271 | { 272 | unsigned long long original; 273 | }; 274 | }; 275 | const HookRequest::Input hookIn{ .fn = reinterpret_cast(func<0>), .handler = reinterpret_cast(handler<0>) }; 276 | HookRequest::Output hookOut{}; 277 | unsigned long returned = 0; 278 | const bool hookStatus = !!DeviceIoControl(hDev, CTL_CODE(0x8000, 0x801, METHOD_NEITHER, FILE_ANY_ACCESS), const_cast(&hookIn), sizeof(hookIn), &hookOut, sizeof(hookOut), &returned, nullptr); 279 | hk_assert(hookStatus); 280 | 281 | const auto orig = reinterpret_cast)*>(hookOut.original); 282 | 283 | const bool secondGetStatus = !!GetThreadContext(hThread, &ctx); 284 | hk_assert(secondGetStatus); 285 | 286 | #ifdef _AMD64_ 287 | hk_assert(ctx.Rip == reinterpret_cast(orig)); 288 | #else 289 | hk_assert(ctx.Eip == reinterpret_cast(orig)); 290 | #endif 291 | 292 | struct UnhookRequest 293 | { 294 | struct Input 295 | { 296 | unsigned long long original; 297 | }; 298 | 299 | struct Output 300 | { 301 | bool status; 302 | }; 303 | }; 304 | const UnhookRequest::Input unhookIn{ .original = hookOut.original }; 305 | UnhookRequest::Output unhookOut{}; 306 | const bool unhookStatus = !!DeviceIoControl(hDev, CTL_CODE(0x8000, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS), const_cast(&unhookIn), sizeof(unhookIn), &unhookOut, sizeof(unhookOut), &returned, nullptr); 307 | hk_assert(unhookStatus); 308 | hk_assert(unhookOut.status); 309 | 310 | const bool thirdGetStatus = !!GetThreadContext(hThread, &ctx); 311 | hk_assert(thirdGetStatus); 312 | 313 | #ifdef _AMD64_ 314 | hk_assert(ctx.Rip == reinterpret_cast(func<0>)); 315 | ctx.Rip = origIp; 316 | #else 317 | hk_assert(ctx.Eip == reinterpret_cast(func<0>)); 318 | ctx.Eip = origIp; 319 | #endif 320 | 321 | const bool restoreOrigIpStatus = !!SetThreadContext(hThread, &ctx); 322 | hk_assert(restoreOrigIpStatus); 323 | 324 | SwitchToThread(); 325 | 326 | ResumeThread(hThread); 327 | WaitForSingleObject(hThread, INFINITE); 328 | 329 | CloseHandle(hThread); 330 | 331 | end_test; 332 | } 333 | 334 | void testCppHelpers() 335 | { 336 | begin_test; 337 | 338 | auto holder = HookFactory::install(func<0>, handler<0>); 339 | func<0>(111, 0.222f); 340 | holder.call(111, 0.222f); 341 | 342 | holder.disable(); 343 | 344 | func<0>(111, 0.222f); 345 | 346 | end_test; 347 | } 348 | 349 | void runDriverTests() 350 | { 351 | const auto getExeFolder = []() -> std::wstring 352 | { 353 | std::wstring path(MAX_PATH, L'\0'); 354 | const auto len = GetModuleFileNameW(nullptr, path.data(), static_cast(path.size())); 355 | if (!len) 356 | { 357 | return {}; 358 | } 359 | 360 | const auto lastDelim = path.rfind(L'\\'); 361 | if (lastDelim == std::wstring::npos) 362 | { 363 | return {}; 364 | } 365 | 366 | path.resize(lastDelim); 367 | return path; 368 | }; 369 | 370 | const auto exeFolder = getExeFolder(); 371 | if (exeFolder.empty()) 372 | { 373 | log("Unable to retrieve the current exe folder"); 374 | return; 375 | } 376 | 377 | const auto driverPath = exeFolder + L"\\HookLibDrvTests.sys"; 378 | 379 | const SC_HANDLE hScm = OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS); 380 | if (!hScm) 381 | { 382 | const auto lastError = GetLastError(); 383 | log("Unable to open the SC manager: 0x%X", lastError); 384 | return; 385 | } 386 | 387 | const SC_HANDLE hSvc = CreateServiceW(hScm, L"HookLibTestDrv", L"HookLibTestDrv", SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, driverPath.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr); 388 | if (!hSvc) 389 | { 390 | const auto lastError = GetLastError(); 391 | log("Unable to create the service: 0x%X", lastError); 392 | CloseServiceHandle(hScm); 393 | return; 394 | } 395 | 396 | const bool startStatus = !!StartServiceW(hSvc, 0, nullptr); 397 | if (!startStatus) 398 | { 399 | const auto lastError = GetLastError(); 400 | log("Unable to start the service: 0x%X", lastError); 401 | DeleteService(hSvc); 402 | CloseServiceHandle(hSvc); 403 | CloseServiceHandle(hScm); 404 | return; 405 | } 406 | 407 | const HANDLE hDev = CreateFileW(L"\\\\.\\HookLibTestDrv", FILE_ALL_ACCESS, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 408 | if (hDev == INVALID_HANDLE_VALUE) 409 | { 410 | const auto lastError = GetLastError(); 411 | log("Unable to open the HookLibDrv device: 0x%X", lastError); 412 | SERVICE_STATUS svcStatus{}; 413 | ControlService(hSvc, SERVICE_CONTROL_STOP, &svcStatus); 414 | DeleteService(hSvc); 415 | CloseServiceHandle(hSvc); 416 | CloseServiceHandle(hScm); 417 | return; 418 | } 419 | 420 | driverTestKernelHooks(hDev); 421 | driverTestUserHooks(hDev); 422 | 423 | CloseHandle(hDev); 424 | 425 | SERVICE_STATUS svcStatus{}; 426 | ControlService(hSvc, SERVICE_CONTROL_STOP, &svcStatus); 427 | DeleteService(hSvc); 428 | CloseServiceHandle(hSvc); 429 | CloseServiceHandle(hScm); 430 | } 431 | 432 | void runTests() 433 | { 434 | testHookOnce(); 435 | testSerialHooks(); 436 | testSerialHooksMultiunhook(); 437 | testMultihook(); 438 | testContextsFixup(); 439 | testCppHelpers(); 440 | 441 | if constexpr (k_testKernelMode) 442 | { 443 | runDriverTests(); 444 | } 445 | } 446 | 447 | } // namespace 448 | 449 | auto g_holder = HookFactory::install(func<0>, handler<0>); 450 | 451 | int main() 452 | { 453 | runTests(); 454 | log("Tests are finished"); 455 | return 0; 456 | } -------------------------------------------------------------------------------- /HookLibTests/HookLibTests.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {51822229-A0BE-4D4E-8025-F16A47ACC3EE} 24 | Win32Proj 25 | HookLibTests 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | true 79 | 80 | 81 | true 82 | 83 | 84 | false 85 | 86 | 87 | false 88 | 89 | 90 | 91 | NotUsing 92 | Level3 93 | Disabled 94 | true 95 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | true 97 | pch.h 98 | stdcpplatest 99 | 100 | 101 | MultiThreadedDebug 102 | 103 | 104 | Console 105 | true 106 | 107 | 108 | RequireAdministrator 109 | 110 | 111 | 112 | 113 | NotUsing 114 | Level3 115 | Disabled 116 | true 117 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | pch.h 120 | stdcpplatest 121 | 122 | 123 | MultiThreadedDebug 124 | 125 | 126 | Console 127 | true 128 | 129 | 130 | RequireAdministrator 131 | 132 | 133 | 134 | 135 | NotUsing 136 | Level3 137 | MaxSpeed 138 | true 139 | true 140 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 141 | true 142 | pch.h 143 | stdcpplatest 144 | 145 | 146 | AnySuitable 147 | Speed 148 | MultiThreaded 149 | 150 | 151 | Console 152 | true 153 | true 154 | true 155 | 156 | 157 | RequireAdministrator 158 | 159 | 160 | 161 | 162 | NotUsing 163 | Level3 164 | MaxSpeed 165 | true 166 | true 167 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 168 | true 169 | pch.h 170 | stdcpplatest 171 | 172 | 173 | AnySuitable 174 | Speed 175 | false 176 | MultiThreaded 177 | 178 | 179 | Console 180 | true 181 | true 182 | true 183 | 184 | 185 | RequireAdministrator 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | {9379f9bc-7829-45d8-b339-90f6504fdf2b} 194 | true 195 | true 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /HookLibTests/HookLibTests.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {f1db84d6-39b6-40b8-b1f7-4eeec0967e7c} 18 | 19 | 20 | 21 | 22 | Исходные файлы 23 | 24 | 25 | 26 | 27 | HookLib 28 | 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 HoShiMin 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HookLib² 2 | ## The Win32 lightweight functions interception library 3 | ### ✔ Advantages: 4 | * Written on pure C 5 | * Extremely lightweight 6 | * Based on the fastest and lightweight [Zydis](https://github.com/zyantific/zydis) disassembler 7 | * Uses only NativeAPI functions 8 | * Has no other dependencies 9 | * Kernelmode support 10 | * Supports instructions relocation and thread's contexts fixup 11 | 12 | ### 📰 What's new in the 2nd Gen: 13 | * The HookLib was completely rewritten 14 | * Extremely reduced allocations, processes/threads enumerations and handles manipulations count 15 | * Multihook/multiunhook support that hooks/unhooks multiple functions in one session 16 | * Extremely reduced memory consumption for usermode hooks: one hook page (4Kb) can hold 39 cells for nearest hooks that removes the need to allocate one page per each hook 17 | * Support for KM->UM hooks (even with support for contexts fixup directly from kernelmode): 18 | * KM:Amd64 -> UM:Amd64 19 | * KM:Amd64 -> UM:Wow64 20 | * KM:i386 -> UM:i386 21 | 22 | ### 🔬 How it works: 23 | ``` 24 | TargetFunction(): ^ ; return 25 | -> jmp Interceptor ------> Interceptor(): | 26 | ??? ; Broken bytes ... Handler code ... | 27 | ... ; Continuation <--+ CallOriginal() ------|--> OriginalBeginning(): 28 | ... +---------|-> ... | ... Original beginning ... 29 | ret --------+ | ret -----------------+ ... of TargetFunction ... 30 | +------------------------------ jmp Continuation 31 | ``` 32 | ### 🧵 Trampolines: 33 | Supported trampolines: 34 | ```assembly 35 | Jump to a relative offset: 36 | E9 44 33 22 11 | jmp rip+0x11223344 ; Relative jump to ±2Gb only 37 | 38 | Jump to an absolute address (x32): 39 | FF 25 44 33 22 11 | jmp ds:[0x11223344] 40 | NN NN NN NN | <- 0x11223344 is points to 41 | 42 | Jump to an absolute address (x64): 43 | FF 25 00 00 00 00 | jmp [rip+00h] 44 | 88 77 66 55 44 33 22 11 | <- RIP is points to 45 | ``` 46 | Trampolines selection logic: 47 | ```cpp 48 | if (relative_jumpable(fn, handler)) 49 | { 50 | set_relative_jump(fn, handler); 51 | } 52 | else 53 | { 54 | /* 55 | 'Intermediate' is an intermediate buffer that allocates 56 | in the same block with the function beginning: 57 | */ 58 | if (relative_jumpable(fn, intermediate)) 59 | { 60 | set_relative_jump(fn, intermediate); 61 | set_absolute_jump(intermediate, handler); 62 | } 63 | else 64 | { 65 | set_absolute_jump(fn, handler); 66 | } 67 | } 68 | ``` 69 | ### 🪡 Usage: 70 | Add the **HookLib.vcxproj** to your **.sln** and add the reference to the HookLib project into your project references list as described [here](https://docs.microsoft.com/en-us/troubleshoot/cpp/add-references-managed): select project, open the project menu, click **Add -> Reference** and select the HookLib. 71 | Then add **./HookLib/HookLib/** folder to your header folders list and you're good to go. 72 | ```cpp 73 | #include 74 | 75 | int func(int a, int b) 76 | { 77 | return a + b; 78 | } 79 | 80 | int handler(int a, int b) 81 | { 82 | return a * b; 83 | } 84 | 85 | template 86 | Fn hookFunc(Fn fn, Fn handler) 87 | { 88 | return static_cast(hook(fn, handler)); 89 | } 90 | 91 | void testSimpleHook() 92 | { 93 | const auto orig = hookFunc(func, handler); 94 | 95 | assert(func(2, 3) == 6); // Hooked, the 'handler' will be called instead 96 | assert(orig(2, 3) == 5); 97 | 98 | unhook(orig); 99 | 100 | assert(func(2, 3) == 5); 101 | } 102 | 103 | void testCppHelpers() 104 | { 105 | const auto holder = HookFactory::install(func, handler); 106 | assert(func(2, 3) == 6); 107 | assert(holder.call(2, 3) == 5); 108 | } 109 | 110 | int main() 111 | { 112 | testSimpleHook(); 113 | testCppHelpers(); 114 | 115 | return 0; 116 | } 117 | ``` 118 | -------------------------------------------------------------------------------- /props/HookLib-Km-x32-Debug.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)')) 6 | 7 | 8 | <_PropertySheetDisplayName>HookLib-Km-Debug-x32 9 | 10 | 11 | 12 | $(PropSheetPath)..\HookLib\HookLib;$(PropSheetPath)..\HookLib\Zydis\include 13 | 14 | 15 | ..\HookLib\Zydis\msvc\bin\DebugX86Kernel;..\HookLib\bin\Km\x32\Debug 16 | 17 | 18 | 19 | 20 | $(PropSheetPath) 21 | 22 | 23 | -------------------------------------------------------------------------------- /props/HookLib-Km-x32-Release.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)')) 6 | 7 | 8 | <_PropertySheetDisplayName>HookLib-Km-Release-x32 9 | 10 | 11 | 12 | $(PropSheetPath)..\HookLib\HookLib;$(PropSheetPath)..\HookLib\Zydis\include 13 | 14 | 15 | ..\HookLib\Zydis\msvc\bin\ReleaseX86Kernel;..\HookLib\bin\Km\x32\Release 16 | 17 | 18 | 19 | 20 | $(PropSheetPath) 21 | 22 | 23 | -------------------------------------------------------------------------------- /props/HookLib-Km-x64-Debug.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)')) 6 | 7 | 8 | <_PropertySheetDisplayName>HookLib-Km-Debug-x64 9 | 10 | 11 | 12 | $(PropSheetPath)..\HookLib\HookLib;$(PropSheetPath)..\HookLib\Zydis\include 13 | 14 | 15 | ..\HookLib\Zydis\msvc\bin\DebugX64Kernel;..\HookLib\bin\Km\x64\Debug 16 | 17 | 18 | 19 | 20 | $(PropSheetPath) 21 | 22 | 23 | -------------------------------------------------------------------------------- /props/HookLib-Km-x64-Release.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)')) 6 | 7 | 8 | <_PropertySheetDisplayName>HookLib-Km-Release-x64 9 | 10 | 11 | 12 | $(PropSheetPath)..\HookLib\HookLib;$(PropSheetPath)..\HookLib\Zydis\include 13 | 14 | 15 | ..\HookLib\Zydis\msvc\bin\ReleaseX64Kernel;..\HookLib\bin\Km\x64\Release 16 | 17 | 18 | 19 | 20 | $(PropSheetPath) 21 | 22 | 23 | -------------------------------------------------------------------------------- /props/HookLib-Um-x32-Debug.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)')) 6 | 7 | 8 | <_PropertySheetDisplayName>HookLib-Um-Debug-x32 9 | 10 | 11 | 12 | $(PropSheetPath)..\HookLib\HookLib;$(PropSheetPath)..\HookLib\Zydis\include 13 | 14 | 15 | ..\HookLib\Zydis\msvc\bin\DebugX86;..\HookLib\bin\Um\x32\Debug 16 | 17 | 18 | 19 | 20 | $(PropSheetPath) 21 | 22 | 23 | -------------------------------------------------------------------------------- /props/HookLib-Um-x32-Release.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)')) 6 | 7 | 8 | <_PropertySheetDisplayName>HookLib-Um-Release-x32 9 | 10 | 11 | 12 | $(PropSheetPath)..\HookLib\HookLib;$(PropSheetPath)..\HookLib\Zydis\include 13 | 14 | 15 | ..\HookLib\Zydis\msvc\bin\ReleaseX86;..\HookLib\bin\Um\x32\Release 16 | 17 | 18 | 19 | 20 | $(PropSheetPath) 21 | 22 | 23 | -------------------------------------------------------------------------------- /props/HookLib-Um-x64-Debug.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)')) 6 | 7 | 8 | <_PropertySheetDisplayName>HookLib-Um-Debug-x64 9 | 10 | 11 | 12 | $(PropSheetPath)..\HookLib\HookLib;$(PropSheetPath)..\HookLib\Zydis\include 13 | 14 | 15 | ..\HookLib\Zydis\msvc\bin\DebugX64;..\HookLib\bin\Um\x64\Debug 16 | 17 | 18 | 19 | 20 | $(PropSheetPath) 21 | 22 | 23 | -------------------------------------------------------------------------------- /props/HookLib-Um-x64-Release.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)')) 6 | 7 | 8 | <_PropertySheetDisplayName>HookLib-Um-Release-x64 9 | 10 | 11 | 12 | $(PropSheetPath)..\HookLib\HookLib;$(PropSheetPath)..\HookLib\Zydis\include 13 | 14 | 15 | ..\HookLib\Zydis\msvc\bin\ReleaseX64;..\HookLib\bin\Um\x64\Release 16 | 17 | 18 | 19 | 20 | $(PropSheetPath) 21 | 22 | 23 | --------------------------------------------------------------------------------