├── .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 |
--------------------------------------------------------------------------------