├── .gitignore ├── 0-whats-new.md ├── 1-more-about.md ├── 2-building.md ├── 3-installing.md ├── 4-using.md ├── 5-resources.md ├── 6-examples.md ├── Examples ├── bugzilla1201401 │ ├── hook-library │ │ ├── Makefile │ │ ├── hook.mm │ │ └── hook.mm.diff │ └── output │ │ ├── Hook-library-crash-output-Firefox-Nightly.txt │ │ ├── Hook-library-output-Firefox-Nightly.txt │ │ ├── Hook-library-output-Google-Chrome.txt │ │ ├── Hook-library-output-Safari.txt │ │ └── Hook-library-workaround-output-Firefox-Nightly.txt ├── dynamic-hooking │ ├── Makefile │ └── hook.mm ├── events │ ├── Makefile │ └── hook.mm ├── kernel-logging │ ├── KernelLogging │ │ ├── KernelLogging.xcodeproj │ │ │ └── project.pbxproj │ │ └── KernelLogging │ │ │ ├── Info.plist │ │ │ └── KernelLogging.c │ ├── Makefile │ ├── hook.mm │ └── runtest.sh ├── secinit │ ├── Makefile │ ├── hook.mm │ └── runtest.sh ├── watchpoints │ ├── Makefile │ └── hook.mm └── xpcproxy │ ├── Makefile │ └── hook.mm ├── HookCase ├── HookCase.xcodeproj │ └── project.pbxproj └── HookCase │ ├── HookCase.cpp │ ├── HookCase.h │ ├── HookCase.s │ └── Info.plist ├── HookLibraryTemplate ├── Makefile ├── Makefile.alt └── hook.mm ├── README.md ├── call_orig └── call_orig.s ├── examples-dynamic-hooking.md ├── examples-events.md ├── examples-kernel-logging.md ├── examples-secinit.md ├── examples-watchpoints.md ├── examples-xpcproxy.md └── genassym ├── Makefile └── genassym.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | ._* 3 | *.DS_Store 4 | **/build/ 5 | **/project.xcworkspace/ 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /0-whats-new.md: -------------------------------------------------------------------------------- 1 | # What's New in Version 9.0.2 2 | 3 | macOS 15.4 broke HookCase by making several internal changes of the 4 | kind that normally only happen in a major release. HookCase 9.0.2 5 | works around them. This resolves 6 | [Issue #52](https://github.com/steven-michaud/HookCase/issues/52). 7 | 8 | # What's New in Version 9.0.1 9 | 10 | macOS 15.2 broke HookCase by hiding the symbol for an internal 11 | function that HookCase uses. HookCase 9.0.1 works around the 12 | problem. This resolves 13 | [Issue #51](https://github.com/steven-michaud/HookCase/issues/51). 14 | 15 | # What's New in Version 9.0.0 16 | 17 | HookCase 9.0 now supports macOS 15 (Sequoia). 18 | 19 | # What's New in Version 8.0.2 20 | 21 | In HookCase 8.0.0 I added code to parse build ids, to deal with 22 | breakage caused by unexpected kernel changes in macOS 12.7.1 and 23 | 13.6.1. But my implementation was too simple, and ended up passing the 24 | wrong version information when run on macOS 13.7. HookCase 8.0.2 fixes 25 | this problem, and resolves 26 | [Issue #49](https://github.com/steven-michaud/HookCase/issues/49). 27 | 28 | # What's New in Version 8.0.1 29 | 30 | macOS 14.4 broke HookCase by making changes to an internal structure 31 | (`struct proc`) and to two internal function calls. These are the 32 | kinds of changes that normally only happen in a major 33 | release. HookCase 8.0.1 works around them. This resolves 34 | [Issue #48](https://github.com/steven-michaud/HookCase/issues/48). 35 | 36 | # What's New in Version 8.0 37 | 38 | HookCase 8.0 now supports macOS 14 (Sonoma). 39 | 40 | It also fixes breakage caused by the macOS 13.6.1 and 12.7.1 updates. 41 | 42 | # What's New in Version 7.3 43 | 44 | HookCase 7.3 now works with the 45 | [OpenCore Legacy Patcher](https://github.com/dortania/OpenCore-Legacy-Patcher). 46 | This environment is something of a torture test for HookCase. It 47 | needed several new sanity checks. But it also needs a *much* larger 48 | kernel stack size. For more information see 49 | [Using the OpenCore Legacy Patcher](3-installing.md#using-the-opencore-legacy-patcher) 50 | 51 | # What's New in Version 7.2.1 52 | 53 | HookCase 7.2.1 has further improvements in watchpoint support. There 54 | are now three different kinds, including two that can catch both read 55 | and write accesses. 56 | 57 | # What's New in Version 7.2 58 | 59 | HookCase 7.2 has improved support for watchpoints. Its documentation 60 | describes their use and limitations more precisely. 61 | The [watchpoints example](examples-watchpoints.md) has been expanded. 62 | 63 | # What's New in Version 7.1.3 64 | 65 | macOS 13.3 broke HookCase by making changes to an internal structure 66 | (`struct proc`) that normally only happen in a major release. HookCase 67 | 7.1.3 works around these changes. 68 | 69 | # What's New in Version 7.1.2 70 | 71 | As of macOS 12 (Monterey) Apple supports two new kinds of sandboxing 72 | -- system call filtering and message filtering. These can interfere 73 | with hook library logging and the production of stack traces. So 74 | HookCase 7.1.2 disables them for processes that contain a hook 75 | library. 76 | 77 | macOS Monterey changed how `dyld` calls C++ and Objective-C 78 | initializers, such that initializers for the hook library and its 79 | dependents are no longer called automatically. HookCase 6.0 introduced 80 | a workaround -- call the hook library's initializers explicitly. But 81 | doing this doesn't also trigger calls to all of its dependent 82 | frameworks' initializers. HookCase 7.1.2 introduces a different 83 | workaround, which does ensure that all the relevant initializers are 84 | called. This resolves 85 | [Issue #44](https://github.com/steven-michaud/HookCase/issues/44). 86 | 87 | HookCase 7.1.2 fixes issues with `execv()` and `fork()`. This resolves 88 | [Issue #45](https://github.com/steven-michaud/HookCase/issues/45). 89 | 90 | As best I can tell system call filtering and message filtering aren't 91 | (yet) documented. But the `*.sb` file syntax can be seen in a 92 | [WebKit file](https://opensource.apple.com/source/WebKit2/WebKit2-7611.3.10.1.3/WebProcess/com.apple.WebProcess.sb.in.auto.html) 93 | Search on "syscall-unix", "syscall-mach" and "apply-message-filter". 94 | 95 | # What's New in Version 7.1.1 96 | 97 | macOS 13 (Ventura) made changes to some system dylibs and frameworks 98 | that break HookCase's support for setting interpose hooks on methods 99 | called from them. I didn't notice this at first, since interpose hooks 100 | still worked in some system modules. But the bug was reported at 101 | [Issue #40](https://github.com/steven-michaud/HookCase/issues/40), 102 | and I've now implemented a workaround in HookCase 7.1.1. 103 | 104 | As of macOS 12 (Monterey), HookCase uses a mach-o module's "__got" 105 | section to implement interpose hooks. "GOT" stands for "global offset 106 | table". It's also called the lazy pointer table. It contains the 107 | addresses of functions in other modules. It's used to resolve calls to 108 | these functions. Interpose hooks are set by overwriting entries in the 109 | lazy pointer table. 110 | 111 | As of macOS 13 (Ventura), some modules' "__got" sections have been 112 | "optimized" away, and no longer contain usable information. The lazy 113 | pointer tables haven't disappeared: They've just been moved to new 114 | locations, where they can be shared by more than one module. But 115 | they're now only (indirectly) accessible via something called the 116 | stubs table. HookCase 7.1.1 knows how to use the stubs table to 117 | implement interpose hooks. 118 | 119 | # What's New in Version 7.1 120 | 121 | HookCase 7.1 now supports a powerful new feature -- the `HC_ADDKIDS` 122 | enviroment variable. Sometimes you're trying to debug the interaction 123 | between an application and some kind of server or daemon process. The 124 | daemon isn't a child of the application. So it would have been 125 | difficult or impossible to use previous versions of HookCase to load a 126 | hook library into it. But if you can arrange for the daemon to restart 127 | while the application is running, you can use `HC_ADDKIDS` to get 128 | `HookCase.kext` to count it as one of the application's children, and 129 | load into the daemon the same hook library as was loaded into the 130 | application. 131 | 132 | I've rewritten two of HookCase's examples to take advantage of this 133 | new feature -- the [secinitd subsystem example](examples-secinit.md) 134 | and the [kernel logging example](examples-kernel-logging.md). The 135 | previous versions of both examples had stopped working as of macOS 11. 136 | 137 | HookCase 7.1 is also significantly better at working with dynamic 138 | patch hooks and virtual serial ports (as provided by 139 | [PySerialPortLogger](https://github.com/steven-michaud/PySerialPortLogger)). 140 | 141 | # What's New in Version 7.0 142 | 143 | HookCase 7.0 now supports macOS 13 (Ventura). 144 | 145 | It also makes some changes to better support 146 | [PySerialPortLogger](https://github.com/steven-michaud/PySerialPortLogger). 147 | Specifically, it stops hook libraries from triggering sandbox errors 148 | when opening virtual serial ports created by this utility. 149 | 150 | # What's New in Version 6.0.5 151 | 152 | HookCase 6.0.5 introduces a new way to "catch" logging output from 153 | hook libraries: As an option, you can now redirect it to a virtual 154 | serial port created by 155 | [PySerialPortLogger](https://github.com/steven-michaud/PySerialPortLogger). 156 | This helps get around inherent problems logging from a hook library, 157 | and Apple's increasing efforts to block even system logging in such an 158 | environment. This change resolves 159 | [Issue #39](https://github.com/steven-michaud/HookCase/issues/39). 160 | 161 | Version 6.0.5 also makes minor changes to the code `HookCase.kext` 162 | uses to track the parentage of potentially "hookable" processes, to 163 | make it more resilient. 164 | 165 | Here's [more information on how to use PySerialPortLogger](HookLibraryTemplate/hook.mm#L334). 166 | 167 | # What's New in Version 6.0.4 168 | 169 | macOS 12.5 and macOS 10.15.7 build 19H2026 both broke HookCase. The 170 | Catalina breakage was minor and easily fixed. The Monterey breakage 171 | was larger, and once again resulted from a change of the kind that 172 | normally only happens in major releases. This time none of the 173 | internal kernel structures used by HookCase were altered. But the 174 | interpretation of `vm_map_entry.vme_object` did change -- from a 175 | union of simple pointers to one of "packed" pointers. 176 | 177 | For more information see 178 | [Issue #35](https://github.com/steven-michaud/HookCase/issues/35) 179 | and [Issue #36](https://github.com/steven-michaud/HookCase/issues/36). 180 | 181 | # What's New in Version 6.0.3 182 | 183 | macOS 12.4 once again broke HookCase, by making changes that normally 184 | only happen in major releases. This time none of the breakage was 185 | caused by changes to internal kernel structures (though some of those 186 | used by HookCase did change). Instead it was caused by two changes in 187 | behavior. HookCase 6.0.3 works around them. 188 | 189 | For more information see 190 | [Issue #34](https://github.com/steven-michaud/HookCase/issues/34). 191 | 192 | # What's New in Version 6.0.2 193 | 194 | macOS 12.3 once again broke HookCase, by making changes that normally 195 | only happen in major release. These included several changes to 196 | internal kernel structures. HookCase 6.0.2 works around these changes. 197 | 198 | For more information see 199 | [Issue #33](https://github.com/steven-michaud/HookCase/issues/33). 200 | 201 | # What's New in Version 6.0.1 202 | 203 | macOS 12.1 broke HookCase, by making lots of changes to internal 204 | kernel structures, of the kind that normally only happen in a major 205 | release. HookCase 6.0.1 alters its copies of these structures to 206 | reflect the changes. 207 | 208 | It also fixes incorrect entries in two of the kernel structures used 209 | on macOS 12.0.1. This flaw didn't effect the behavior of HookCase 6.0 210 | on macOS 12.0.1: Not realizing the flaw was my own, I added code to 211 | HookCase 6.0 to work around it. These workarounds are no longer 212 | necessary, and have been removed. 213 | 214 | For more information see 215 | [Issue #31](https://github.com/steven-michaud/HookCase/issues/31). 216 | 217 | # What's New in Version 6.0 218 | 219 | HookCase now supports macOS 12 (Monterey). 220 | 221 | Note that, on macOS 12, as on macOS 11, HookCase now requires the 222 | `keepsyms=1` boot arg. To set this you'll need to turn off SIP at least 223 | temporarily. 224 | 225 | `sudo nvram boot-args="keepsyms=1"` 226 | 227 | # What's New in Version 5.0.5 228 | 229 | macOS 11.4 broke HookCase, just like macOS 11.3 did. macOS 11.4 made 230 | further changes to `struct thread`, of a kind that normally only takes 231 | place in a new major release. These changes caused a kernel panic 232 | every time you tried to load a hook library into an application. The 233 | problem is fixed by HookCase 5.0.5. `struct thread` is one of several 234 | kernel structures that HookCase needs to access directly. For more 235 | information see 236 | [Issue #28](https://github.com/steven-michaud/HookCase/issues/28). 237 | 238 | # What's New in Version 5.0.4 239 | 240 | This version of HookCase fixes a bug that caused intermittent 241 | instability, though not kernel panics. I fixed it by tweaking the 242 | [code at the heart of HookCase's watchpoint support](HookCase/HookCase/HookCase.cpp#L15973). 243 | See [Issue #26](https://github.com/steven-michaud/HookCase/issues/26) 244 | for more information. 245 | 246 | HookCase's watchpoint code is quite complex. So if you see any sort of 247 | instability short of kernel panics, especially if it resembles what's 248 | reported at Issue #26, you should try 249 | [disabling watchpoint support](HookCase/HookCase/HookCase.cpp#L16923). 250 | 251 | # What's New in Version 5.0.3 252 | 253 | This release deals with changes in macOS 11.3 that broke HookCase. The 254 | 11.3 update changed two kernel structures whose fields HookCase needs 255 | to access directly. Major changes were made to `struct task`, and 256 | `struct thread` seems to have been completely redesigned. This kind of 257 | change normally only takes place in a new major release, so HookCase 258 | wasn't "expecting" it. HookCase now does separate version checks for 259 | macOS 11 and macOS 11.3. This fixes 260 | [Issue #27](https://github.com/steven-michaud/HookCase/issues/27). 261 | 262 | # What's New in Version 5.0.2 263 | 264 | This version of HookCase fixes a bug that caused some interpose hooks 265 | to be skipped on macOS 11 (Big Sur) 266 | ([Issue #24](https://github.com/steven-michaud/HookCase/issues/24)). 267 | HookCase uses a structure called the lazy pointer table to implement 268 | interpose hooks. In the past it was always located in the `__DATA` 269 | segment. But in Big Sur it's sometimes located in the `__DATA_CONST` 270 | segment. HookCase now looks for it in both places. 271 | 272 | # What's New in Version 5.0.1 273 | 274 | This version of HookCase fixes a bug that caused intermittent kernel 275 | panics in `set_interpose_hooks_for_module()` 276 | ([Issue #22](https://github.com/steven-michaud/HookCase/issues/22)). 277 | They seem to have been particularly likely to occur with hook 278 | libraries containing lots of interpose hooks, particularly ones that 279 | are invoked both before and after the CoreFoundation framework is 280 | initialized. 281 | 282 | # What's New in Version 5.0 283 | 284 | HookCase now supports macOS 11 (Big Sur). 285 | 286 | Note that, on macOS 11, HookCase now requires the `keepsyms=1` boot 287 | arg. To set this you'll need to turn off SIP at least temporarily. 288 | 289 | `sudo nvram boot-args="keepsyms=1"` 290 | 291 | # What's New in Version 4.1.1 292 | 293 | This version of HookCase contains several tweaks to its watchpoint 294 | support. Together they merit a bump in the version number. The most 295 | significant of them are: 296 | 297 | [Remove all watchers on process exit](https://github.com/steven-michaud/HookCase/commit/d2d82e3020f6cefb2a475589fe3cfc44107a3be3) 298 | 299 | [Make watchpoint example work with multiple graphics cards at once](https://github.com/steven-michaud/HookCase/commit/a6001614115270219860ffd811555a55fa36091b) 300 | 301 | [Check if a watchpoint has already been set or unset](https://github.com/steven-michaud/HookCase/commit/4d205078f97e5277294c799811a66b3adba13e88) 302 | 303 | # What's New in Version 4.1 304 | 305 | This version of HookCase supports watchpoints. You can now set a 306 | watchpoint on a location in memory and gather information (including a 307 | stack trace) about the code that writes to that location. For more 308 | information see 309 | [config_watcher() in the hook library template](HookLibraryTemplate/hook.mm#L1130), 310 | [Hooked_watcher_example() in the hook library template](HookLibraryTemplate/hook.mm#L1313) 311 | and [the watchpoints example](examples-watchpoints.md). 312 | 313 | # What's New in Version 4.0.5 314 | 315 | This version of HookCase fixes a bug which caused some patch hooks not 316 | to work properly. If the original function didn't have a standard 317 | prologue, the hook's call to `reset_hook()` would fail and the hook 318 | would only be called once. For more information see 319 | [Issue #17](https://github.com/steven-michaud/HookCase/issues/17) 320 | 321 | # What's New in Version 4.0.4 322 | 323 | My version 4.0.3 patch didn't fix that kernel panic, either (the one 324 | in `vn_authorize_open_existing()`). Now I really think I've found the 325 | problem -- one that dates back to the earliest HookCase release. It's 326 | only by chance that it didn't become visible earlier. I've done a week 327 | of hard testing without seeing any more kernel panics. For more 328 | information see 329 | [Really really fix kernel panic reported at issue #14](https://github.com/steven-michaud/HookCase/commit/8cf8a444aacea7c1cd752f09407224458cf190b6) 330 | and 331 | [Issue #14](https://github.com/steven-michaud/HookCase/issues/14). 332 | 333 | # What's New in Version 4.0.3 334 | 335 | It turns out my version 4.0.2 patch didn't fix one of the kernel 336 | panics it was supposed to. This version's patch really does fix it, as 337 | best I can tell after several days of testing. See 338 | [Issue #14](https://github.com/steven-michaud/HookCase/issues/14). 339 | 340 | # What's New in Version 4.0.2 341 | 342 | This version of HookCase fixes two intermittent kernel panics. For 343 | more information see [Issue #14](https://github.com/steven-michaud/HookCase/issues/14). 344 | 345 | # What's New in Version 4.0.1 346 | 347 | This version of HookCase documents how to use `sudo mount -uw /` to 348 | temporarily make system files and directories writable on macOS 349 | Catalina (10.15). It also updates 350 | [one of the examples](Examples/secinit/) for Catalina. 351 | 352 | # What's New in Version 4.0 353 | 354 | HookCase now supports macOS Catalina (10.15). 355 | 356 | # What's New in Version 3.3.1 357 | 358 | This version of HookCase tightens up the code that supports 359 | dynamically adding patch hooks. Among other things, it completely 360 | fixes a race condition that effected interactions between a hook 361 | library and the HookCase kernel extension, particularly in the 362 | get_dynamic_caller() methods. (Previous code just minimized its 363 | effect.) For more information see the following commit: 364 | 365 | [Actual fix for race condition in get_dynamic_caller() and elsewhere](https://github.com/steven-michaud/HookCase/commit/7d6b56ac070eaab758c13a75b8cd8f6ada1b5978) 366 | 367 | # What's New in Version 3.3 368 | 369 | HookCase now supports dynamically adding patch hooks for raw function 370 | pointers. This is useful in hooks for methods that use callbacks -- 371 | for example CFMachPortCreate() and CFRunLoopObserverCreate(). For more 372 | information see 373 | [dynamic_patch_example() in the hook library template](HookLibraryTemplate/hook.mm#L1257) 374 | and [the dynamic patch hooks example](examples-dynamic-hooking.md). 375 | 376 | # What's New in Version 3.2.1 377 | 378 | Version 3.2.1 fixes some bugs, and restores support for the debug 379 | kernel on macOS 10.14. For more information see 380 | [Issue #11](https://github.com/steven-michaud/HookCase/issues/11) 381 | and the following commit: 382 | 383 | [Resume resetting iotier_override, and restore support for debug kernel on 10.14](https://github.com/steven-michaud/HookCase/commit/30dd592df4f4792e5487d6e53d72eb585fd10028) 384 | 385 | # What's New in Version 3.2 386 | 387 | Version 3.2 works around changes in macOS 10.14.5 that broke 388 | HookCase. These were part of Apple's workaround for Intel's MDS bug. 389 | For more information see 390 | [Issue #9](https://github.com/steven-michaud/HookCase/issues/9). 391 | 392 | # What's New in Version 3.1 393 | 394 | HookCase now supports enabling all parts of "system integrity 395 | protection" (SIP) but the protection against loading unsigned kernel 396 | extensions. 397 | 398 | Though Apple has never documented it, since OS X 10.11 (El Capitan) 399 | it's been possible to 400 | [enable parts of SIP](https://forums.developer.apple.com/thread/17452). 401 | So, for example, you can use the following command (when booted from 402 | the recovery partition) to enable everything but "kernel extension 403 | protection": 404 | 405 | csrutil enable --without kext 406 | 407 | I only became aware of this recently, thanks to the reporter of 408 | [Issue #7](https://github.com/steven-michaud/HookCase/issues/7). 409 | That bug report also revealed problems using HookCase on macOS 10.14 410 | (Mojave) with this configuration. Version 3.1 fixes these problems. 411 | 412 | Note that, for HookCase to work properly on macOS 10.14 with this 413 | configuration, you will need to codesign your hook libraries (using 414 | something like `codesign -s "Your Name" hook.dylib`). For this you'll 415 | need to get a Mac Developer codesigning certificate from Apple, 416 | presumably by joining their 417 | [Apple Developer Program](https://developer.apple.com/programs/). 418 | 419 | # What's New in Version 3.0 420 | 421 | HookCase now supports macOS Mojave (10.14). 422 | 423 | But Mojave's Debug kernel is currently very flaky -- lots of panics, 424 | with and without HookCase. So support for the Debug kernel 425 | [has been disabled](HookCase/HookCase/HookCase.cpp#L794), at least 426 | temporarily. 427 | 428 | # What's New in Version 2.1 429 | 430 | HookCase now works properly with VMware Fusion running as a host. This 431 | required changing the range of software interrupts used internally by 432 | HookCase (from `0x20-0x23` to `0x30-0x33`). See 433 | [Issue #5](https://github.com/steven-michaud/HookCase/issues/5) for 434 | more information. 435 | 436 | To make existing hook libraries fully compatible with version 2.1, 437 | their reset_hook() methods will need to be changed to use `int 0x32` 438 | instead of `int 0x22`, as follows: 439 | 440 | void reset_hook(void *hook) 441 | { 442 | __asm__ ("int %0" :: "N" (0x32)); 443 | } 444 | 445 | # What's New in Version 2.0 446 | 447 | * HookCase now supports macOS High Sierra (10.13). 448 | 449 | * HookCase now supports creating a patch hook for an (un-named) method 450 | at a particular address in a given module. This means that HookCase 451 | can now hook methods that aren't in their module's symbol table. For 452 | more information see 453 | [Hooked_sub_123abc() in the hook library template](HookLibraryTemplate/hook.mm#L1296). 454 | 455 | * Version 2.0 [fixes a bug](HookCase/HookCase/HookCase.cpp#L13620) that 456 | prevented interpose hooks from working outside the shared cache of 457 | system modules. 458 | 459 | * Version 2.0 460 | [fixes a previously undiscovered edge case](HookCase/HookCase/HookCase.cpp#L15415) 461 | of an Apple kernel panic bug that was partially fixed in version 1. 462 | 463 | * Version 2.0 464 | [fixes a premature-release bug](Examples/events/hook.mm#L1630) 465 | in the "System Events" example's hook library. 466 | -------------------------------------------------------------------------------- /1-more-about.md: -------------------------------------------------------------------------------- 1 | # More About HookCase 2 | 3 | Apple's `DYLD_INSERT_LIBRARIES` environment variable is used to load 4 | an "interpose library" into a process before any of its other modules. 5 | The interpose library contains "hooks" for methods implemented 6 | elsewhere. Once the process is up and running, each hook is called 7 | instead of the original method that it hooks. Then the hook can call 8 | the original method (if that's how it's been written). Typically a 9 | hook logs calls to its original method (including its parameters and 10 | return value, plus maybe a stack trace). It can also be used to alter 11 | the behavior of the original method, or to replace it entirely. 12 | 13 | `DYLD_INSERT_LIBRARIES`'s hooks are what we call "interpose hooks". 14 | They're implemented by changing pointers in tables used to dynamically 15 | link methods called from other modules. So they can only be used to 16 | hook "cross-module" calls to exported methods. 17 | 18 | HookCase supports interpose hooks. But it also supports another, more 19 | powerful kind of hook that we call "patch hooks". These can hook 20 | calls to a method named in its module's symbol table, including ones 21 | that come from the same module. They can also hook calls to an 22 | unnamed method (one that isn't in its module's symbol table), by 23 | specifying the method's address in its module. So they can be used 24 | with non-exported (aka private) methods (named and unnamed) -- ones 25 | not intended for use by external modules. 26 | 27 | Patch hooks are so-called because we set them up by "patching" the 28 | beginning of an original method with a software interrupt instruction 29 | (`int 0x30`). HookCase's kernel extension handles the interrupt to 30 | implement the hook. This is analogous to what a debugger does when it 31 | sets a breakpoint (though it uses `int 3` instead of `int 0x30`). 32 | Software interrupts are mostly not used on BSD-style operating systems 33 | like macOS and OS X, so we have plenty to choose among. For now we're 34 | using those in the range `0x30-0x35`. 35 | 36 | Whatever their disadvantages, interpose hooks are very performant. 37 | They're implemented by changing a pointer, so they impose no 38 | performance penalty whatsoever (aside from the cost of whatever 39 | additional code runs inside the hook). Patch hooks can be 40 | substantially less performant -- if we have to unset the breakpoint on 41 | every call to the hook, then reset it afterwards (and protect these 42 | operations from race conditions). But this isn't needed for methods 43 | that start with a standard C/C++ prologue in machine code (which is 44 | most of them). So most patch hooks run with only a very small 45 | performance penalty (that of a single software interrupt). 46 | 47 | HookCase is compatible with `DYLD_INSERT_LIBRARIES`, and doesn't stomp 48 | on any of the changes it may have been used to make. So a 49 | `DYLD_INSERT_LIBRARIES` hook will always override the "same" HookCase 50 | interpose hook. This is because Apple often uses 51 | `DYLD_INSERT_LIBRARIES` internally, in ways it doesn't document. 52 | HookCase would likely break Apple functionality if it could override 53 | Apple's hooks. But this doesn't apply to patch hooks. Since Apple 54 | doesn't use them, we don't need to worry about overriding any that 55 | Apple may have set. If an interpose hook doesn't seem to work, try a 56 | patch hook instead. (Unless you write them to do so, neither 57 | interpose hooks nor patch hooks inherently change the behavior of the 58 | methods they hook.) 59 | 60 | HookCase is compatible with `lldb` and `gdb`: Any process with 61 | HookCase's interpose or patch hooks can run inside these debuggers. 62 | But you may encounter trouble if you set a breakpoint and a patch hook 63 | on the same method, or try to step through code that contains a patch 64 | hook. 65 | 66 | Apple's support for `DYLD_INSERT_LIBRARIES` is implemented in 67 | [`/usr/lib/dyld`](https://opensource.apple.com/source/dyld/dyld-750.6/). 68 | A (shared) copy of this module gets loaded into the image of every new 69 | process before it starts running. `dyld`'s `man` page calls it the 70 | "dynamic link editor", and it's what runs first (starting from 71 | `_dyld_start` in `dyld`'s 72 | [`src/dyldStartup.s`](https://opensource.apple.com/source/dyld/dyld-655.1.1/src/dyldStartup.s.auto.html)) 73 | as a new process starts up. Once `dyld` has finished its work, it 74 | jumps to the new process's `main()` method. 75 | 76 | HookCase is a kernel extension (`HookCase.kext`), which can't call 77 | user-mode code directly. So we can't just replace `dyld` with code in 78 | HookCase. But we can read and alter the memory image of any running 79 | process. And we can "set up" user-mode calls to take place after 80 | we've returned to user mode from kernel mode. So we can hook methods 81 | in `dyld`, and "call" code in it indirectly. The best time to 82 | intervene is after `dyld` has finished most of its initialization, but 83 | before any of the new process's "own" code has run (including its C++ 84 | initializers). This is when `dyld`'s 85 | `dyld::initializeMainExecutable()` method runs (or 86 | `dyld4::APIs::runAllInitializersForMain()` on macOS 12 and up). So we 87 | hook that method as a process starts up, perform our own 88 | initialization, then allow the original method to run. 89 | 90 | For more information, the best place to start is the 91 | [long series of comments](HookCase/HookCase/HookCase.cpp#L9879) 92 | in `HookCase.cpp` before the definition of `C_64_REDZONE_LEN`. 93 | -------------------------------------------------------------------------------- /2-building.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | HookCase requires a compatible version of OS X -- OS X 10.9 4 | (Mavericks) through macOS 13 (Ventura). Building it also requires a 5 | relatively recent version of XCode. I recommend building on the 6 | version of OS X where you'll be using HookCase, and using the most 7 | recent version of XCode available for that version. But the version 8 | of XCode you use must contain an SDK matching the version of macOS/OS 9 | X on which you're building. Check in the 10 | `Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs` 11 | directory of your XCode.app package. 12 | 13 | Building `HookCase.kext` should be straightforward. I ususally just 14 | run `xcodebuild` from the command line. This drops a release build 15 | into the project's `build/Release/` subdirectory. 16 | 17 | On macOS 10.13 (HighSierra) and below, building some hook libraries 18 | requires additional tools, like `llvm-as` and `llc`, which aren't 19 | provided with XCode. But they do come with third party distros like 20 | [the LLVM 3.9.0 Clang download](http://releases.llvm.org/3.9.0/clang+llvm-3.9.0-x86_64-apple-darwin.tar.xz) or 21 | [the LLVM 4.0.0 Clang download](http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-apple-darwin.tar.xz). 22 | -------------------------------------------------------------------------------- /3-installing.md: -------------------------------------------------------------------------------- 1 | # Installing 2 | 3 | On OS X 10.10 (Yosemite) and up, to load `HookCase.kext` you'll need 4 | to turn off Apple's protection against loading unsigned or 5 | "inappropriately" signed kernel extensions. (From Apple's point of 6 | view, the only "appropriately" signed kernel extensions are those 7 | signed with a special kernel extension signing certificate -- in 8 | practice almost exclusively Apple's own kernel extensions.) On OS X 9 | 10.11 (El Capitan) and up, to do this you'll need to at least partly 10 | turn off "system integrity protection" (SIP, also known as "rootless 11 | mode"). 12 | 13 | Apple documents turning SIP completely off or completely on. But it's 14 | also possible 15 | [to only enable parts of it](https://forums.developer.apple.com/thread/17452). 16 | For example, it's possible to turn on everything but the protection 17 | against loading unsigned kernel extensions. This is the most secure 18 | configuation that's still compatible with HookCase. But using it on 19 | macOS 10.14 (Mojave) and above will require you to codesign your hook 20 | libraries. 21 | 22 | ## On OS X 10.10: 23 | 24 | 1. From the command line run `nvram boot-args` to see if you already 25 | have some boot-args. Then run the following command: 26 | 27 | sudo nvram boot-args=" kext-dev-mode=1" 28 | 29 | 2. Reboot your computer. 30 | 31 | ## On OS X 10.11 and up: 32 | 33 | 1. Boot into your Recovery partition by restarting your computer and 34 | pressing `Command-R` immediately after you hear the Mac startup 35 | sound. Release these keys when you see the Apple logo. 36 | 37 | 2. Choose Utilties : Terminal, then run one of the the following at 38 | the command line. The first command disables SIP completely. The 39 | second enables everything but protection against loading unsigned 40 | kernel extensions. The third also disables file-system protection. 41 | 42 | csrutil disable 43 | 44 | csrutil enable --without kext 45 | 46 | csrutil enable --without kext --without fs 47 | 48 | 3. Quit Terminal and reboot your computer. 49 | 50 | Now copy `HookCase.kext` to the `/usr/local/sbin/` directory. You may 51 | need to create this directory. Make sure it's owned by `root:wheel`. 52 | 53 | `sudo cp -R HookCase.kext /usr/local/sbin` 54 | 55 | ## On macOS 10.15 and below: 56 | 57 | On macOS 10.15 (Catalina) and below, you need only a single command to 58 | load `HookCase.kext` into the kernel: 59 | 60 | `sudo kextutil /usr/local/sbin/HookCase.kext` 61 | 62 | Because it won't have been signed using a kernel extension signing 63 | certificate, you'll see the following error (or something like it): 64 | 65 | Diagnostics for HookCase.kext: 66 | Code Signing Failure: code signature is invalid 67 | kext-dev-mode allowing invalid signature -67050 0xFFFFFFFFFFFEFA16 68 | for kext "HookCase.kext" 69 | kext signature failure override allowing invalid signature -67050 70 | 0xFFFFFFFFFFFEFA16 for kext "/usr/local/sbin/HookCase.kext" 71 | 72 | Run `kextstat` to see that it did load. 73 | 74 | To unload `HookCase.kext` from the kernel, run the following command: 75 | 76 | `sudo kextunload -b org.smichaud.HookCase` 77 | 78 | ## On macOS 11 and above: 79 | 80 | Things are more complicated on macOS 11 (Big Sur) and above. As of 81 | this version of macOS, HookCase requires the `keepsyms=1` boot arg. 82 | And third party kernel extensions must be loaded into the "auxiliary 83 | kext collection" before they can be loaded into the kernel. Along the 84 | way you'll need to explicitly give permission for `HookCase.kext` to 85 | be loaded, then reboot your computer. macOS 11 also uses a new set of 86 | command line utilties to load and unload kernel extensions. 87 | 88 | 1. Change your boot args as follows. To do this, you will need to 89 | disable SIP at least temporarily. Reboot your computer to make the 90 | change take effect. 91 | 92 | `sudo nvram boot-args="keepsyms=1"` 93 | 94 | 2. Run the following command at a Terminal prompt: 95 | 96 | `sudo kmutil load -p /usr/local/sbin/HookCase.kext` 97 | 98 | 3. After a few seconds, a "System Extension Updated" dialog will 99 | appear telling you that the HookCase system extension has been 100 | updated. Click on the "Open Security Preferences" button. 101 | 102 | 4. In the "Security & Privacy" preference panel, first "click the lock 103 | to make changes", then click on the "Allow" button next to HookCase. 104 | Another dialog will appear telling you that "a restart is required 105 | before new system extensions can be used". The default choice is "Not 106 | Now", and it's best to choose that. Wierdness can happen if you 107 | restart immediately. I usually close all open applications and then 108 | restart. 109 | 110 | 5. After your computer has restarted, open a Terminal prompt and once 111 | again enter the following command. It should immediately load 112 | `HookCase.kext` into the kernel. 113 | 114 | `sudo kmutil load -p /usr/local/sbin/HookCase.kext` 115 | 116 | Run `kextstat` to see that it did load. 117 | 118 | Run one of the following commands to unload `HookCase.kext` from the 119 | kernel: 120 | 121 | `sudo kmutil unload -p /usr/local/sbin/HookCase.kext` 122 | 123 | `sudo kumtil unload -b org.smichaud.HookCase` 124 | 125 | `HookCase.kext` will not be loaded automatically when you once again 126 | restart your computer. You will need to load it (and unload it) 127 | manually as per step 4. 128 | 129 | ## Increasing the kernel stack size 130 | 131 | For some reason, HookCase 5.0 and above sometimes require that you 132 | increase the kernel stack size. I've seen kernel stack underflows 133 | hooking methods in 32-bit applications (like TextWrangler) on older 134 | versions of macOS (which still support them). The symptom of a kernel 135 | stack underflow is a double-fault kernel panic with CR2 set to an 136 | address on the stack. One way to increase the kernel stack size is as 137 | follows. `kernel_stack_pages` default to `4`. You will need to 138 | disable SIP at least temporarily to make changes to your "kernel boot 139 | args". 140 | 141 | 1. `sudo nvram boot-args="keepsyms=1 kernel_stack_pages=6"` 142 | 143 | 2. Reboot your computer. 144 | 145 | ## Using the [OpenCore Legacy Patcher](https://github.com/dortania/OpenCore-Legacy-Patcher) 146 | 147 | As of version 7.3, HookCase now works with OCLP. But it requires a 148 | *substantial* increase in the kernel stack size (to at least 16 149 | pages). And you need to use a non-standard way to set it, or any other 150 | kernel boot arg. In fact you need to directly edit the OCLP 151 | `config.plist` settings file on the EFI partition. 152 | 153 | 1. Run `diskutil list` to display all the partitions on your system, 154 | mounted and unmounted. For each "physical" disk you should find 155 | something like the following. Note the device name of the EFI 156 | partition on the "physical" disk from which you've currently 157 | booted. In the following it's `disk0s1`. I will use this as an example 158 | in the following instructions. 159 | 160 | ``` 161 | /dev/disk0 (internal, physical): 162 | #: TYPE NAME SIZE IDENTIFIER 163 | 0: GUID_partition_scheme *1.0 TB disk0 164 | 1: EFI EFI 209.7 MB disk0s1 165 | ... 166 | ``` 167 | 168 | 2. Run `diskutil mount disk0s1`. This should mount the EFI partition 169 | at `/Volumes/EFI`. Then `cd /Volumes/EFI/EFI/OC`. 170 | 171 | 3. Note the `config.plist` file, which should contain a section like 172 | the following. 173 | 174 | ``` 175 | NVRAM 176 | 177 | Add 178 | 179 | ... 180 | 7C436110-AB2A-4BBB-A880-FE41995C9F82 181 | 182 | boot-args 183 | keepsyms=1 debug=0x100 ipc_control_port_options=0 -nokcmismatchpanic amfi=0x80 184 | ... 185 | 186 | 187 | ... 188 | 189 | ``` 190 | 191 | 4. Edit it so it looks like this: 192 | 193 | ``` 194 | NVRAM 195 | 196 | Add 197 | 198 | ... 199 | 7C436110-AB2A-4BBB-A880-FE41995C9F82 200 | 201 | boot-args 202 | keepsyms=1 debug=0x100 ipc_control_port_options=0 -nokcmismatchpanic amfi=0x80 kernel_stack_pages=16 203 | ... 204 | 205 | 206 | ... 207 | 208 | ``` 209 | 210 | 5. Reboot your computer. 211 | 212 | 6. Once your computer has finished rebooting, run `sysctl 213 | kern.stack_size`. It should return `kern.stack_size 65536`. 214 | 215 | ### Important 216 | 217 | Any changes you've made to the `config.plist` file will disappear when 218 | you use the OpenCore-patcher app to change settings. You'll need to 219 | follow these instructions once again to (re)set the 220 | `kernel_stack_pages` kernel boot arg. 221 | -------------------------------------------------------------------------------- /4-using.md: -------------------------------------------------------------------------------- 1 | # Using 2 | 3 | To use HookCase (once it's loaded into the kernel) you need to write 4 | an "interpose library" (aka a "hook library") containing hook 5 | functions for the methods you wish to hook. The syntax is very 6 | similar to that of `DYLD_INSERT_LIBRARIES` interpose libraries. 7 | There's a annotated template in 8 | [`HookLibraryTemplate`](HookLibraryTemplate/), and there are more 9 | examples under [`Examples`](Examples/). 10 | 11 | Once you have a hook library, you need to set environment variables to 12 | load it into a new process and to determine how it behaves there. 13 | 14 | Here are the environment variables that HookCase pays attention to: 15 | 16 | * `HC_INSERT_LIBRARY` - Full path to hook library 17 | 18 | * `HC_ADDKIDS` - Colon-separated list of full paths to additional children 19 | 20 | * `HC_NOKIDS` - Don't effect child processes 21 | 22 | * `HC_NO_NUMERICAL_ADDRS` - Disable numerical address naming convention 23 | 24 | By default, if HookCase sets hooks in a parent process, it will also 25 | set them in all its child processes. But if you set `HC_NOKIDS` (to 26 | any value), HookCase only effects the parent process (not its 27 | children). 28 | 29 | You'll generally not want to set `HC_NOKIDS`. These days many 30 | applications use multiple processes, and it's particularly useful to 31 | be able to log activity in child processes at the same time as you do 32 | so in their parent. But note that, even with `HC_NOKIDS` unset, this 33 | really only works properly on OS X 10.11 (El Capitan) and up. The 34 | child processes of Apple applications (like Safari) are often XPC 35 | services, which don't inherit their parent's environment. HookCase 36 | can keep track of "XPC children", but only on OS X 10.11 and up (not 37 | on OS X 10.10 (Yosemite) or 10.9 (Mavericks)). 38 | 39 | Sometimes a process that isn't a child of the parent process is 40 | engaged in an interaction that you're trying to debug -- often some 41 | kind of server process. If you can arrange for this process to start 42 | (or restart) after the parent process has started, you can make 43 | `HookCase.kext` treat it like a child of the parent process and load 44 | the same hook library into it as gets loaded into the parent process 45 | and its "real" children. Use `HC_ADDKIDS` to load your hook library 46 | into one or more additional "child" processes, and potentially also 47 | into their own children. `HC_NOKIDS` is ignored for the processes 48 | explicitly listed in `HC_ADDKIDS`. But it can be used to stop your 49 | hook library getting loaded into these "child" processes' own 50 | children. 51 | 52 | In order to load a hook library into a system server/daemon, previous 53 | versions of HookCase required you to alter `plist` files that govern 54 | their behavior (in `/System/Library/LaunchDaemons` or 55 | `/System/Library/LaunchAgents`). As of macOS 11 (Big Sur), Apple has 56 | made this almost impossible. Now you can work around the problem by 57 | using `HC_ADDKIDS`. I've rewritten two of HookCase's examples to use 58 | `HC_ADDKIDS` -- the [secinit subsystem example](examples-secinit.md) 59 | and the [kernel logging example](examples-kernel-logging.md). 60 | 61 | Recent versions of HookCase support creating a patch hook for an 62 | (un-named) method at a particular address in a given module. (For 63 | more information see 64 | [Hooked_sub_123abc() in the hook library template](HookLibraryTemplate/hook.mm#L1296).) 65 | So, for example, creating a patch hook for a function named 66 | "sub_123abc" would (by default) specify that the hook should be 67 | inserted at offset 0x123abc (hexadecimal notation) in the module. But 68 | this convention prevents you from creating a patch hook for a method 69 | that's actually named "sub_123abc" (in its module's symbol table). To 70 | do so, you'll need turn off this behavior by setting the 71 | `HC_NO_NUMERICAL_ADDRS` environment variable. 72 | 73 | HookCase now supports dynamically adding patch hooks for raw function 74 | pointers. This is useful in hooks for methods that use callbacks -- 75 | for example `CFMachPortCreate()` and `CFRunLoopObserverCreate()`. It's 76 | best to patch callbacks in their "create" methods, before they start 77 | being used. Otherwise there's some danger of a race condition, 78 | especially if the callback can be used on different threads from the 79 | one that calls add_patch_hook(). For more information see 80 | [dynamic_patch_example() in the hook library template](HookLibraryTemplate/hook.mm#L1257) 81 | and [the dynamic patch hooks example](examples-dynamic-hooking.md). 82 | 83 | HookCase now supports watchpoints. You can set a watchpoint on a range 84 | of memory and get information on the code that writes to it or reads 85 | from it -- for example a stack trace of the access and the id of the 86 | thread on which it ran. You can also set a watchpoint that catches 87 | only write accesses. Watchpoint support is imprecise: You set one on a 88 | page of memory (typically 4096 bytes), not on a particular byte. So it 89 | won't catch all memory accesses to that page -- only the first 90 | one. And it has limitations: It's only appropriate for page-aligned 91 | blocks of memory -- for example those shared between processes, or 92 | between the kernel and a process. But it can be useful for reverse 93 | engineering the use of shared memory blocks -- for example the 94 | "sideband buffer" that's used by OpenGL accelerated graphics. For more 95 | information see 96 | [config_watcher() in the hook library template](HookLibraryTemplate/hook.mm#L1130), 97 | [Hooked_watcher_example() in the hook library template](HookLibraryTemplate/hook.mm#L1313) 98 | and [the watchpoints example](examples-watchpoints.md). 99 | -------------------------------------------------------------------------------- /5-resources.md: -------------------------------------------------------------------------------- 1 | # Resources 2 | 3 | Here are the resources I found most useful writing HookCase. You'll 4 | also need them (or something like them) to write hook libraries for 5 | HookCase, and to interpret the results you get from them. 6 | 7 | ## Documentation 8 | 9 | #### Apple documentation 10 | 11 | * [Mac debugging techniques](http://developer.apple.com/technotes/tn2004/tn2124.html) 12 | * [Current documents, search by title](https://developer.apple.com/library/mac/navigation/) 13 | * ["Retired" documents, search by title](http://developer.apple.com/legacy/library/navigation/) 14 | 15 | #### Calling conventions (stack frames and registers) 16 | 17 | Basic information is scattered through the 18 | [Mac debugging techniques article](http://developer.apple.com/technotes/tn2004/tn2124.html). 19 | Much fuller information is available here: 20 | 21 | * [X86_64 Stack Frame Layout](http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/) 22 | * [X86 Calling Conventions](https://en.wikipedia.org/wiki/X86_calling_conventions) 23 | * [OS X ABI Function Call Guide](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/LowLevelABI/000-Introduction/introduction.html) 24 | * [AMD64 Processor ABI](https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf) 25 | * [AMD64 Processor ABI (alternate site)](http://x86-64.org/documentation/abi.pdf) 26 | 27 | #### X86/X86_64 assembler instructions 28 | 29 | * [X86 Instruction Listings](http://en.wikipedia.org/wiki/X86_instruction_listings) 30 | * [X86 Assembly Control Flow](https://en.wikibooks.org/wiki/X86_Assembly/Control_Flow) 31 | * [Intel Instruction Set Reference](http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf) 32 | 33 | ## Other Resources 34 | 35 | #### [class-dump](http://stevenygard.com/projects/class-dump/) 36 | 37 | This excellent utility dumps the equivalent of full header 38 | information from an Objective-C binary. 39 | 40 | #### Disassemblers 41 | 42 | I can't find any decent open-source disassemblers, but Hopper 43 | Disassembler is excellent and not too expensive. It's very good at 44 | following cross-references, so that (for example) you can find both a 45 | method's implementation and the code it's called from, or a string and 46 | the methods that use it. It's well suited to reconstructing a C/C++ 47 | method's parameters and return values. 48 | 49 | [Hopper Disassembler](http://www.hopperapp.com/) 50 | 51 | You can also get a reasonably good assembly code listing of a 52 | particular function in a binary using 53 | 54 | `otool -t -v -V -p function_name binary | less` 55 | 56 | #### [Apple Open Source](http://opensource.apple.com/) 57 | 58 | OS X isn't open source. But this site has source code (sometimes 59 | incomplete) for a lot of its components, which can provide crucial 60 | information on the undocumented stuff that shows up in hook library 61 | calls and stack traces. It doesn't contain source code for any kernel 62 | extensions, but it does have source for the kernel itself (the `xnu` 63 | kernel). I found this extremely useful writing `HookCase.kext`. 64 | 65 | #### CoreSymbolication framework 66 | 67 | This is an undocumented Apple framework, available on SnowLeopard (OS 68 | X 10.6) and up, that can be used to programmatically examine the call 69 | stack in a running program -- for example to display a trace of the 70 | current call stack. 71 | 72 | The best guides on how to use this are my hook library template in 73 | `HookLibraryTemplate` and the examples under `Examples`. 74 | 75 | #### gdb 76 | 77 | `gdb` is Apple's default command-line debugger on OS X 10.8.5 and below. 78 | I don't know of any really good documentation on it. I generally rely 79 | on its internal documentation and search on the web (as the need 80 | arises) for whatever that doesn't cover. 81 | 82 | The 83 | [Mac Debugging Techniques article](http://developer.apple.com/technotes/tn2004/tn2124.html) 84 | does have a lot of information on Apple-specific additions to `gdb`, 85 | though. 86 | 87 | #### lldb 88 | 89 | `lldb` is Apple's default command-line debugger on OS X 10.9.5 and up. 90 | It has even less documentation than `gdb`, and the internal 91 | documentation only covers the basics. 92 | 93 | I find I rely heavily on the 94 | [LLDB to GDB Command Map](http://lldb.llvm.org/lldb-gdb.html) 95 | 96 | #### [Kernel Debug Kits](http://developer.apple.com/download/more/) 97 | 98 | These in effect let you load a Mach kernel running on a remote machine 99 | (the target computer) into `lldb` running in a Terminal window on your 100 | local machine (the development computer). Apple's documentation is 101 | poor, and there are technical restrictions that can make it cumbersome 102 | to use. But there are times when there's no substitute for doing 103 | this. 104 | 105 | It's probably best to start by reading 106 | [Debugging a Kernel Extension with GDB](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html). 107 | This is badly out of date, but it's reasonably well written and gives 108 | you a good overview. Then read the ReadMe file that comes with the 109 | version of the Kernel Debug Kit that you'll be using. The Kernel 110 | Debug Kit gets installed on the development computer (where you'll be 111 | running `lldb`), and must match the version of OS X or macOS on the 112 | target computer. The development computer should also be running the 113 | same version of Apple's OS, but this isn't absolutely necessary. 114 | 115 | Apple's instructions won't work with a target computer that's a 116 | virtual machine. But with a slightly modified procedure, remote 117 | kernel debugging works with a VMware Fusion virtual machine. 118 | 119 | * Make sure the target virtual machine isn't running, then add the 120 | following two lines to the `.vmx` config file in its `.vmwarevm` 121 | package directory: 122 | 123 | debugStub.listen.guest64 = "TRUE" 124 | debugStub.listen.guest64.remote = "TRUE" 125 | 126 | * Download [x86_64_target_definition.py](http://llvm.org/svn/llvm-project/lldb/trunk/examples/python/x86_64_target_definition.py) 127 | to some convenient location on your development computer. 128 | 129 | * On the development computer, run the following two commands: 130 | 131 | lldb /Library/Developer/KDKs/KDK_[version].kdk/System/Library/Kernels/kernel 132 | settings set plugin.process.gdb-remote.target-definition-file /path/to/x86_64_target_definition.py 133 | 134 | * Then enter one of the following commands instead of `kdp-remote 135 | {name_or_ip_address}`. Use the first if your development computer 136 | is the VMware Fusion host. Use the second if it's some other 137 | computer. 138 | 139 | gdb-remote 8864 140 | gdb-remote [fusionhost]:8864 141 | 142 | For more information see the following: 143 | 144 | [Using the VMware Fusion GDB stub for kernel debugging with LLDB](http://ddeville.me/2015/08/using-the-vmware-fusion-gdb-stub-for-kernel-debugging-with-lldb) 145 | 146 | [VMware](http://wiki.osdev.org/VMware) 147 | 148 | -------------------------------------------------------------------------------- /6-examples.md: -------------------------------------------------------------------------------- 1 | # Example Hook Libraries 2 | 3 | Here are some examples of how HookCase hook libraries can be used to 4 | reverse engineer features of the macOS/OS X operating system. The 5 | details are specific to OS X 10.11 (El Capitan) and up, or sometimes 6 | to macOS 10.12 (Sierra) or macOS 11 (Big Sur) and up. Source code for 7 | these hook libaries is available under [`Examples`](Examples/). 8 | 9 | * [Dynamic patch hooks](examples-dynamic-hooking.md) 10 | * [Watchpoints](examples-watchpoints.md) 11 | * [xpcproxy trampoline](examples-xpcproxy.md) 12 | * [secinit subsystem](examples-secinit.md) 13 | * [System events](examples-events.md) 14 | * [Kernel logging](examples-kernel-logging.md) 15 | 16 | -------------------------------------------------------------------------------- /Examples/bugzilla1201401/hook-library/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC=/usr/bin/clang++ 24 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 25 | # 32-bit builds no longer work on Mojave with some versions of XCode 26 | ifeq ($(MOJAVE_OR_ABOVE), 1) 27 | ARCHS=-arch x86_64 28 | else 29 | ARCHS=-arch i386 -arch x86_64 30 | endif 31 | 32 | hook.dylib : hook.o 33 | $(CC) $(ARCHS) -o hook.dylib hook.o \ 34 | -lobjc -framework Cocoa -framework Carbon -framework CoreVideo \ 35 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 36 | -dynamiclib 37 | 38 | hook.o : hook.mm 39 | $(CC) $(ARCHS) -o hook.o \ 40 | -Wno-deprecated-declarations -c hook.mm 41 | 42 | clean : 43 | rm hook.o hook.dylib 44 | -------------------------------------------------------------------------------- /Examples/bugzilla1201401/hook-library/hook.mm.diff: -------------------------------------------------------------------------------- 1 | --- /Users/smichaud/Documents/ReverseEngineering/HookCase-master/HookLibraryTemplate/hook.mm 2019-10-31 15:11:37.000000000 -0500 2 | +++ hook.mm 2020-02-05 15:48:12.000000000 -0600 3 | @@ -62,6 +62,8 @@ 4 | #include 5 | #include 6 | 7 | +#import 8 | + 9 | pthread_t gMainThreadID = 0; 10 | 11 | bool IsMainThread() 12 | @@ -77,7 +79,7 @@ 13 | { 14 | if (!sGlobalInitDone) { 15 | gMainThreadID = pthread_self(); 16 | - CreateGlobalSymbolicator(); 17 | + //CreateGlobalSymbolicator(); 18 | sGlobalInitDone = true; 19 | } 20 | } 21 | @@ -924,6 +926,272 @@ 22 | 23 | // Put other hooked methods and swizzled classes here 24 | 25 | +// Define this if you want to emulate the conditions that trigger the crashes 26 | +// in CVCGDisplayLink::getDisplayTimes() of Mozilla bug 1201401. 27 | +//#define BUG_1201401_CRASHTEST 1 28 | + 29 | +CVReturn (*CVDisplayLinkStart_caller)(CVDisplayLinkRef displayLink) = NULL; 30 | + 31 | +static CVReturn Hooked_CVDisplayLinkStart(CVDisplayLinkRef displayLink) 32 | +{ 33 | + CVReturn retval = CVDisplayLinkStart_caller(displayLink); 34 | + LogWithFormat(true, "Hook.mm: CVDisplayLinkStart(): displayLink \'%p\', returning \'%i\'", 35 | + displayLink, retval); 36 | + PrintStackTrace(); 37 | + return retval; 38 | +} 39 | + 40 | +CVReturn (*CVDisplayLinkStop_caller)(CVDisplayLinkRef displayLink) = NULL; 41 | + 42 | +static CVReturn Hooked_CVDisplayLinkStop(CVDisplayLinkRef displayLink) 43 | +{ 44 | + CVReturn retval = CVDisplayLinkStop_caller(displayLink); 45 | + LogWithFormat(true, "Hook.mm: CVDisplayLinkStop(): displayLink \'%p\', returning \'%i\'", 46 | + displayLink, retval); 47 | + PrintStackTrace(); 48 | + return retval; 49 | +} 50 | + 51 | +CVReturn (*_ZN13CVDisplayLink4stopEv_caller)(void *self) = NULL; 52 | + 53 | +CVReturn Hooked__ZN13CVDisplayLink4stopEv(void *self) 54 | +{ 55 | + CVReturn retval = _ZN13CVDisplayLink4stopEv_caller(self); 56 | + LogWithFormat(true, "Hook.mm: CVDisplayLink::stop(): self \'%p\', returning \'%i\'", 57 | + self, retval); 58 | + PrintStackTrace(); 59 | + return retval; 60 | +} 61 | + 62 | +void (*CVDisplayLinkRelease_caller)(CVDisplayLinkRef displayLink) = NULL; 63 | + 64 | +void Hooked_CVDisplayLinkRelease(CVDisplayLinkRef displayLink) 65 | +{ 66 | + CVDisplayLinkRelease_caller(displayLink); 67 | + LogWithFormat(true, "Hook.mm: CVDisplayLinkRelease(): displayLink \'%p\'", displayLink); 68 | + PrintStackTrace(); 69 | +} 70 | + 71 | +CVReturn (*CVDisplayLinkCreateWithCGDisplays_caller)(CGDirectDisplayID *displayArray, CFIndex count, 72 | + CVDisplayLinkRef *displayLinkOut) = NULL; 73 | + 74 | +CVReturn Hooked_CVDisplayLinkCreateWithCGDisplays(CGDirectDisplayID *displayArray, CFIndex count, 75 | + CVDisplayLinkRef *displayLinkOut) 76 | +{ 77 | + CVReturn retval = CVDisplayLinkCreateWithCGDisplays_caller(displayArray, count, displayLinkOut); 78 | + LogWithFormat(true, "Hook.mm: CVDisplayLinkCreateWithCGDisplays(): count \'%u\', displayLinkOut \'%p\', returning \'%i\'", 79 | + count, displayLinkOut ? *displayLinkOut : NULL, retval); 80 | + if (displayLinkOut && *displayLinkOut) { 81 | + CGDirectDisplayID current = CVDisplayLinkGetCurrentCGDisplay(*displayLinkOut); 82 | + LogWithFormat(false, " Current display now \'0x%x\'", current); 83 | + } 84 | + PrintStackTrace(); 85 | + return retval; 86 | +} 87 | + 88 | +pthread_key_t s_in_setCurrentDisplay; 89 | +int32_t s_in_setCurrentDisplay_initialized = 0; 90 | + 91 | +bool get_in_setCurrentDisplay() 92 | +{ 93 | + if (!s_in_setCurrentDisplay_initialized) { 94 | + OSAtomicIncrement32(&s_in_setCurrentDisplay_initialized); 95 | + pthread_key_create(&s_in_setCurrentDisplay, NULL); 96 | + } 97 | + return (pthread_getspecific(s_in_setCurrentDisplay) != NULL); 98 | +} 99 | + 100 | +void set_in_setCurrentDisplay(bool flag) 101 | +{ 102 | + if (!s_in_setCurrentDisplay_initialized) { 103 | + OSAtomicIncrement32(&s_in_setCurrentDisplay_initialized); 104 | + pthread_key_create(&s_in_setCurrentDisplay, NULL); 105 | + } 106 | + pthread_setspecific(s_in_setCurrentDisplay, (void *) flag); 107 | +} 108 | + 109 | +CVReturn (*_ZN15CVCGDisplayLink17setCurrentDisplayEj_caller)(void *self, CGDirectDisplayID displayID) = NULL; 110 | + 111 | +CVReturn Hooked__ZN15CVCGDisplayLink17setCurrentDisplayEj(void *self, CGDirectDisplayID displayID) 112 | +{ 113 | + set_in_setCurrentDisplay(true); 114 | + CVReturn retval = _ZN15CVCGDisplayLink17setCurrentDisplayEj_caller(self, displayID); 115 | + set_in_setCurrentDisplay(false); 116 | + LogWithFormat(true, "Hook.mm: CVCGDisplayLink::setCurrentDisplay(): self \'%p\', displayID \'0x%x\', returning \'%i\'", 117 | + self, displayID, retval); 118 | + PrintStackTrace(); 119 | + return retval; 120 | +} 121 | + 122 | +CGDirectDisplayID (*_ZN15CVCGDisplayLink17getCurrentDisplayEv_caller)(void *self) = NULL; 123 | + 124 | +CGDirectDisplayID Hooked__ZN15CVCGDisplayLink17getCurrentDisplayEv(void *self) 125 | +{ 126 | + CGDirectDisplayID retval = _ZN15CVCGDisplayLink17getCurrentDisplayEv_caller(self); 127 | + LogWithFormat(true, "Hook.mm: CVCGDisplayLink::getCurrentDisplay(): self \'%p\', returning \'0x%x\'", 128 | + self, retval); 129 | + PrintStackTrace(); 130 | + return retval; 131 | +} 132 | + 133 | +pthread_key_t s_in_DisplayIDToOpenGLDisplayMask; 134 | +int32_t s_in_DisplayIDToOpenGLDisplayMask_initialized = 0; 135 | + 136 | +bool get_in_DisplayIDToOpenGLDisplayMask() 137 | +{ 138 | + if (!s_in_DisplayIDToOpenGLDisplayMask_initialized) { 139 | + OSAtomicIncrement32(&s_in_DisplayIDToOpenGLDisplayMask_initialized); 140 | + pthread_key_create(&s_in_DisplayIDToOpenGLDisplayMask, NULL); 141 | + } 142 | + return (pthread_getspecific(s_in_DisplayIDToOpenGLDisplayMask) != NULL); 143 | +} 144 | + 145 | +void set_in_DisplayIDToOpenGLDisplayMask(bool flag) 146 | +{ 147 | + if (!s_in_DisplayIDToOpenGLDisplayMask_initialized) { 148 | + OSAtomicIncrement32(&s_in_DisplayIDToOpenGLDisplayMask_initialized); 149 | + pthread_key_create(&s_in_DisplayIDToOpenGLDisplayMask, NULL); 150 | + } 151 | + pthread_setspecific(s_in_DisplayIDToOpenGLDisplayMask, (void *) flag); 152 | +} 153 | + 154 | +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200 155 | +#define DisplayIDToOpenGLDisplayMask_hook Hooked_SLDisplayIDToOpenGLDisplayMask 156 | +#define DisplayIDToOpenGLDisplayMask_caller SLDisplayIDToOpenGLDisplayMask_caller 157 | +#else 158 | +#define DisplayIDToOpenGLDisplayMask_hook Hooked_CGDisplayIDToOpenGLDisplayMask 159 | +#define DisplayIDToOpenGLDisplayMask_caller CGDisplayIDToOpenGLDisplayMask_caller 160 | +#endif 161 | + 162 | +CGOpenGLDisplayMask (*DisplayIDToOpenGLDisplayMask_caller)(CGDirectDisplayID display) = NULL; 163 | + 164 | +CGOpenGLDisplayMask DisplayIDToOpenGLDisplayMask_hook(CGDirectDisplayID display) 165 | +{ 166 | + set_in_DisplayIDToOpenGLDisplayMask(true); 167 | + CGOpenGLDisplayMask retval = DisplayIDToOpenGLDisplayMask_caller(display); 168 | + set_in_DisplayIDToOpenGLDisplayMask(false); 169 | + if (get_in_setCurrentDisplay()) { 170 | + LogWithFormat(true, "Hook.mm: CGDisplayIDToOpenGLDisplayMask(): display \'0x%x\', returning \'0x%x\'", 171 | + display, retval); 172 | + PrintStackTrace(); 173 | + } 174 | + return retval; 175 | +} 176 | + 177 | +void (*_ZN15CVCGDisplayLink15getDisplayTimesEPyS0_S0__caller)(void *self, unsigned long long *arg2, 178 | + unsigned long long *arg3, 179 | + unsigned long long *arg4) = NULL; 180 | + 181 | +void Hooked__ZN15CVCGDisplayLink15getDisplayTimesEPyS0_S0_(void *self, unsigned long long *arg2, 182 | + unsigned long long *arg3, 183 | + unsigned long long *arg4) 184 | +{ 185 | + _ZN15CVCGDisplayLink15getDisplayTimesEPyS0_S0__caller(self, arg2, arg3, arg4); 186 | + // This method is called often, so its output is very noisy. 187 | + //LogWithFormat(true, "Hook.mm: CVCGDisplayLink::getDisplayTimes(): self \'%p\'", self); 188 | + //PrintStackTrace(); 189 | +} 190 | + 191 | +CVReturn Hooked_CVDisplayLinkOutputCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, 192 | + const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, 193 | + CVOptionFlags *flagsOut, void *displayLinkContext) 194 | +{ 195 | + CVDisplayLinkOutputCallback caller = (CVDisplayLinkOutputCallback) 196 | + get_dynamic_caller(reinterpret_cast(Hooked_CVDisplayLinkOutputCallback)); 197 | + CVReturn retval = caller(displayLink, inNow, inOutputTime, flagsIn, flagsOut, displayLinkContext); 198 | + // This method is called often, so its output is very noisy. 199 | + //LogWithFormat(true, "Hook.mm: CVDisplayLinkOutputCallback(): displayLink \'%p\', displayLinkContext \'%p\', returning \'%i\'", 200 | + // displayLink, displayLinkContext, retval); 201 | + //PrintStackTrace(); 202 | + return retval; 203 | +} 204 | + 205 | +CVReturn (*CVDisplayLinkSetOutputCallback_caller)(CVDisplayLinkRef displayLink, 206 | + CVDisplayLinkOutputCallback callback, 207 | + void *userInfo) = NULL; 208 | + 209 | +CVReturn Hooked_CVDisplayLinkSetOutputCallback(CVDisplayLinkRef displayLink, 210 | + CVDisplayLinkOutputCallback callback, 211 | + void *userInfo) 212 | +{ 213 | + if (callback) { 214 | + add_patch_hook(reinterpret_cast(callback), 215 | + reinterpret_cast(Hooked_CVDisplayLinkOutputCallback)); 216 | + } 217 | + CVReturn retval = CVDisplayLinkSetOutputCallback_caller(displayLink, callback, userInfo); 218 | + LogWithFormat(true, "Hook.mm: CVDisplayLinkSetOutputCallback(): displayLink \'%p\', callback \'%p\', userInfo \'%p\', returning %i", 219 | + displayLink, callback, userInfo, retval); 220 | + PrintStackTrace(); 221 | + return retval; 222 | +} 223 | + 224 | +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400 225 | +#define get_current_display_system_state_hook Hooked__ZL32get_current_display_system_statev 226 | +#define get_current_display_system_state_caller _ZL32get_current_display_system_statev_caller 227 | +#else 228 | +#define get_current_display_system_state_hook Hooked_get_current_display_system_state 229 | +//#define get_current_display_system_state_caller get_current_display_system_state_caller 230 | +#endif 231 | + 232 | +void *(*get_current_display_system_state_caller)() = NULL; 233 | + 234 | +void *get_current_display_system_state_hook() 235 | +{ 236 | +#ifdef __LP64__ 237 | + const struct mach_header_64 *mh = NULL; 238 | +#else 239 | + const struct mach_header *mh = NULL; 240 | +#endif 241 | + intptr_t vmaddr_slide = 0; 242 | + GetModuleHeaderAndSlide("crashreporter", &mh, &vmaddr_slide); 243 | + bool is_crashreporter = (mh != NULL); 244 | + 245 | + void *retval = get_current_display_system_state_caller(); 246 | + 247 | +#ifdef BUG_1201401_CRASHTEST 248 | + if (!is_crashreporter) { 249 | + static unsigned int counter = 0; 250 | + unsigned int remainder = 0; 251 | + bool docrash = false; 252 | +#if (0) 253 | + // Original test, which shows that these crashes happen much more often in 254 | + // unpatched Firefox than in Safari or Google Chrome. 255 | + OSAtomicIncrement32((int32_t *) &counter); 256 | + remainder = (counter % 10); 257 | + if (remainder == 0) { 258 | + docrash = true; 259 | + retval = NULL; 260 | + } 261 | +#else 262 | + // Revised test, which shows that these crashes happen only when 263 | + // get_current_display_system_state() returns NULL during a call to 264 | + // SLDisplayIDToOpenGLDisplayMask() (via CGDisplayIDToOpenGLDisplayMask()), 265 | + // during a call to CVCGDisplayLink::setCurrentDisplay(unsigned int), which 266 | + // itself happens during a call to CVDisplayLinkCreateWithCGDisplays() 267 | + // (which is called by CVDisplayLinkCreateWithActiveCGDisplays()). 268 | + // This test crashes Firefox (unpatched), Safari and Google Chrome (and 269 | + // presumably all other apps that use the CVDisplayLink methods). It can 270 | + // also be used to test the patch for bug 1201401. 271 | + if (get_in_setCurrentDisplay() && get_in_DisplayIDToOpenGLDisplayMask()) { 272 | + OSAtomicIncrement32((int32_t *) &counter); 273 | + remainder = (counter % 5); 274 | + if (remainder == 0) { 275 | + docrash = true; 276 | + retval = NULL; 277 | + } 278 | + } 279 | +#endif 280 | + if (docrash) { 281 | + LogWithFormat(true, "Hook.mm: get_current_display_system_state(): returning \'%p\', counter \'%u\', remainder \'%u\'", 282 | + retval, counter, remainder); 283 | + PrintStackTrace(); 284 | + } 285 | + } 286 | +#endif 287 | + 288 | + return retval; 289 | +} 290 | + 291 | #pragma mark - 292 | 293 | typedef struct _hook_desc { 294 | @@ -955,6 +1223,28 @@ 295 | { 296 | INTERPOSE_FUNCTION(NSPushAutoreleasePool), 297 | PATCH_FUNCTION(__CFInitialize, /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation), 298 | + 299 | + PATCH_FUNCTION(CVDisplayLinkStart, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 300 | + //PATCH_FUNCTION(CVDisplayLinkStop, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 301 | + PATCH_FUNCTION(_ZN13CVDisplayLink4stopEv, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 302 | + PATCH_FUNCTION(CVDisplayLinkRelease, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 303 | + PATCH_FUNCTION(CVDisplayLinkCreateWithCGDisplays, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 304 | + PATCH_FUNCTION(_ZN15CVCGDisplayLink17setCurrentDisplayEj, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 305 | + PATCH_FUNCTION(_ZN15CVCGDisplayLink17getCurrentDisplayEv, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 306 | + PATCH_FUNCTION(_ZN15CVCGDisplayLink15getDisplayTimesEPyS0_S0_, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 307 | + PATCH_FUNCTION(CVDisplayLinkSetOutputCallback, /System/Library/Frameworks/CoreVideo.framework/CoreVideo), 308 | +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200 309 | + PATCH_FUNCTION(SLDisplayIDToOpenGLDisplayMask, /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight), 310 | +#else 311 | + PATCH_FUNCTION(CGDisplayIDToOpenGLDisplayMask, /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics), 312 | +#endif 313 | +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101400 314 | + PATCH_FUNCTION(_ZL32get_current_display_system_statev, /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight), 315 | +#elif __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200 316 | + PATCH_FUNCTION(get_current_display_system_state, /System/Library/PrivateFrameworks/SkyLight.framework/SkyLight), 317 | +#else 318 | + PATCH_FUNCTION(get_current_display_system_state, /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics), 319 | +#endif 320 | }; 321 | 322 | // What follows are declarations of the CoreSymbolication APIs that we use to 323 | -------------------------------------------------------------------------------- /Examples/bugzilla1201401/output/Hook-library-crash-output-Firefox-Nightly.txt: -------------------------------------------------------------------------------- 1 | # Output of hook library for https://bugzilla.mozilla.org/show_bug.cgi?id=1201401 2 | # in the 2019-09-04 Firefox Nightly, recording the steps leading up to a crash. 3 | # get_current_display_system_state() returns null, CGDisplayIDToOpenGLDisplayMask() 4 | # returns 0, CVCGDisplayLink::setCurrentDisplay() returns -6670, and the crash 5 | # happens shortly afterwards. The 2019-09-04 Firefox Nightly didn't yet have the 6 | # workaround for bug 1201401. 7 | 8 | (Wed Feb 5 14:16:45 2020) /Users/smichaud/Desktop/Firefox Nightly 2019-09-04.app/Contents/MacOS/firefox[1885] [0x110f7ddc0] get_current_display_system_state(): returning '0x0', counter '5', remainder '0' 9 | (hook.dylib) PrintStackTrace() + 0xae 10 | (hook.dylib) Hooked__ZL32get_current_display_system_statev() + 0xea 11 | (SkyLight) SLSGetDisplayOpenGLDisplayMask + 0x1e 12 | (hook.dylib) Hooked_SLDisplayIDToOpenGLDisplayMask(unsigned int) + 0x41 13 | (CoreVideo) CVCGDisplayLink::setCurrentDisplay(unsigned int) + 0x25 14 | (hook.dylib) Hooked__ZN15CVCGDisplayLink17setCurrentDisplayEj(void*, unsigned int) + 0x29 15 | (CoreVideo) CVCGDisplayLink::initWithCGDisplays(unsigned int*, long, int*) + 0x1c3 16 | (CoreVideo) CVDisplayLinkCreateWithCGDisplays + 0x86 17 | (hook.dylib) Hooked_CVDisplayLinkCreateWithCGDisplays(unsigned int*, long, __CVDisplayLink**) + 0x34 18 | (CoreVideo) CVDisplayLinkCreateWithActiveCGDisplays + 0x55 19 | (XUL) OSXVsyncSource::OSXDisplay::EnableVsync() + 0x22 20 | (XUL) mozilla::RefreshTimerVsyncDispatcher::UpdateVsyncStatus() + 0x16d 21 | (XUL) mozilla::detail::RunnableMethodImpl::Run() + 0x27 22 | (XUL) nsThread::ProcessNextEvent(bool, bool*) + 0xd91 23 | (XUL) NS_ProcessPendingEvents(nsIThread*, unsigned int) + 0x62 24 | (XUL) nsBaseAppShell::NativeEventCallback() + 0xb7 25 | (XUL) nsAppShell::ProcessGeckoEvents(void*) + 0xfc 26 | (CoreFoundation) __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 0x11 27 | (CoreFoundation) __CFRunLoopDoSource0 + 0x67 28 | (CoreFoundation) __CFRunLoopDoSources0 + 0xd1 29 | (CoreFoundation) __CFRunLoopRun + 0x4f8 30 | (CoreFoundation) CFRunLoopRunSpecific + 0x1f3 31 | (HIToolbox) RunCurrentEventLoopInMode + 0x124 32 | (HIToolbox) ReceiveNextEventCommon + 0x258 33 | (HIToolbox) _BlockUntilNextEventMatchingListInModeWithFilter + 0x40 34 | (AppKit) _DPSNextEvent + 0x3de 35 | (AppKit) -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 0x548 36 | (XUL) -[GeckoNSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 0x117 37 | (AppKit) -[NSApplication run] + 0x292 38 | (XUL) nsAppShell::Run() + 0x80 39 | (XUL) nsAppStartup::Run() + 0x3e 40 | (XUL) XREMain::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x14f5 41 | (XUL) mozilla::BootstrapImpl::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x10d 42 | (firefox) main + 0x56e 43 | (libdyld.dylib) start + 0x1 44 | (firefox) unknown 0x1 45 | (Wed Feb 5 14:16:45 2020) /Users/smichaud/Desktop/Firefox Nightly 2019-09-04.app/Contents/MacOS/firefox[1885] [0x110f7ddc0] CGDisplayIDToOpenGLDisplayMask(): display '0x4280bc6', returning '0x0' 46 | (hook.dylib) PrintStackTrace() + 0xae 47 | (hook.dylib) Hooked_SLDisplayIDToOpenGLDisplayMask(unsigned int) + 0x85 48 | (CoreVideo) CVCGDisplayLink::setCurrentDisplay(unsigned int) + 0x25 49 | (hook.dylib) Hooked__ZN15CVCGDisplayLink17setCurrentDisplayEj(void*, unsigned int) + 0x29 50 | (CoreVideo) CVCGDisplayLink::initWithCGDisplays(unsigned int*, long, int*) + 0x1c3 51 | (CoreVideo) CVDisplayLinkCreateWithCGDisplays + 0x86 52 | (hook.dylib) Hooked_CVDisplayLinkCreateWithCGDisplays(unsigned int*, long, __CVDisplayLink**) + 0x34 53 | (CoreVideo) CVDisplayLinkCreateWithActiveCGDisplays + 0x55 54 | (XUL) OSXVsyncSource::OSXDisplay::EnableVsync() + 0x22 55 | (XUL) mozilla::RefreshTimerVsyncDispatcher::UpdateVsyncStatus() + 0x16d 56 | (XUL) mozilla::detail::RunnableMethodImpl::Run() + 0x27 57 | (XUL) nsThread::ProcessNextEvent(bool, bool*) + 0xd91 58 | (XUL) NS_ProcessPendingEvents(nsIThread*, unsigned int) + 0x62 59 | (XUL) nsBaseAppShell::NativeEventCallback() + 0xb7 60 | (XUL) nsAppShell::ProcessGeckoEvents(void*) + 0xfc 61 | (CoreFoundation) __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 0x11 62 | (CoreFoundation) __CFRunLoopDoSource0 + 0x67 63 | (CoreFoundation) __CFRunLoopDoSources0 + 0xd1 64 | (CoreFoundation) __CFRunLoopRun + 0x4f8 65 | (CoreFoundation) CFRunLoopRunSpecific + 0x1f3 66 | (HIToolbox) RunCurrentEventLoopInMode + 0x124 67 | (HIToolbox) ReceiveNextEventCommon + 0x258 68 | (HIToolbox) _BlockUntilNextEventMatchingListInModeWithFilter + 0x40 69 | (AppKit) _DPSNextEvent + 0x3de 70 | (AppKit) -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 0x548 71 | (XUL) -[GeckoNSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 0x117 72 | (AppKit) -[NSApplication run] + 0x292 73 | (XUL) nsAppShell::Run() + 0x80 74 | (XUL) nsAppStartup::Run() + 0x3e 75 | (XUL) XREMain::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x14f5 76 | (XUL) mozilla::BootstrapImpl::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x10d 77 | (firefox) main + 0x56e 78 | (libdyld.dylib) start + 0x1 79 | (firefox) unknown 0x1 80 | (Wed Feb 5 14:16:45 2020) /Users/smichaud/Desktop/Firefox Nightly 2019-09-04.app/Contents/MacOS/firefox[1885] [0x110f7ddc0] Hook.mm: CVCGDisplayLink::setCurrentDisplay(): self '0x11d786020', displayID '0x4280bc6', returning '-6670' 81 | (hook.dylib) PrintStackTrace() + 0xae 82 | (hook.dylib) Hooked__ZN15CVCGDisplayLink17setCurrentDisplayEj(void*, unsigned int) + 0x56 83 | (CoreVideo) CVCGDisplayLink::initWithCGDisplays(unsigned int*, long, int*) + 0x1c3 84 | (CoreVideo) CVDisplayLinkCreateWithCGDisplays + 0x86 85 | (hook.dylib) Hooked_CVDisplayLinkCreateWithCGDisplays(unsigned int*, long, __CVDisplayLink**) + 0x34 86 | (CoreVideo) CVDisplayLinkCreateWithActiveCGDisplays + 0x55 87 | (XUL) OSXVsyncSource::OSXDisplay::EnableVsync() + 0x22 88 | (XUL) mozilla::RefreshTimerVsyncDispatcher::UpdateVsyncStatus() + 0x16d 89 | (XUL) mozilla::detail::RunnableMethodImpl::Run() + 0x27 90 | (XUL) nsThread::ProcessNextEvent(bool, bool*) + 0xd91 91 | (XUL) NS_ProcessPendingEvents(nsIThread*, unsigned int) + 0x62 92 | (XUL) nsBaseAppShell::NativeEventCallback() + 0xb7 93 | (XUL) nsAppShell::ProcessGeckoEvents(void*) + 0xfc 94 | (CoreFoundation) __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 0x11 95 | (CoreFoundation) __CFRunLoopDoSource0 + 0x67 96 | (CoreFoundation) __CFRunLoopDoSources0 + 0xd1 97 | (CoreFoundation) __CFRunLoopRun + 0x4f8 98 | (CoreFoundation) CFRunLoopRunSpecific + 0x1f3 99 | (HIToolbox) RunCurrentEventLoopInMode + 0x124 100 | (HIToolbox) ReceiveNextEventCommon + 0x258 101 | (HIToolbox) _BlockUntilNextEventMatchingListInModeWithFilter + 0x40 102 | (AppKit) _DPSNextEvent + 0x3de 103 | (AppKit) -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 0x548 104 | (XUL) -[GeckoNSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 0x117 105 | (AppKit) -[NSApplication run] + 0x292 106 | (XUL) nsAppShell::Run() + 0x80 107 | (XUL) nsAppStartup::Run() + 0x3e 108 | (XUL) XREMain::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x14f5 109 | (XUL) mozilla::BootstrapImpl::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x10d 110 | (firefox) main + 0x56e 111 | (libdyld.dylib) start + 0x1 112 | (firefox) unknown 0x1 113 | (Wed Feb 5 14:16:45 2020) /Users/smichaud/Desktop/Firefox Nightly 2019-09-04.app/Contents/MacOS/firefox[1885] [0x110f7ddc0] Hook.mm: CVDisplayLinkCreateWithCGDisplays(): count '1', displayLinkOut '0x11d786000', returning '0' 114 | Current display now '0x0' 115 | (hook.dylib) PrintStackTrace() + 0xae 116 | (hook.dylib) Hooked_CVDisplayLinkCreateWithCGDisplays(unsigned int*, long, __CVDisplayLink**) + 0xc9 117 | (CoreVideo) CVDisplayLinkCreateWithActiveCGDisplays + 0x55 118 | (XUL) OSXVsyncSource::OSXDisplay::EnableVsync() + 0x22 119 | (XUL) mozilla::RefreshTimerVsyncDispatcher::UpdateVsyncStatus() + 0x16d 120 | (XUL) mozilla::detail::RunnableMethodImpl::Run() + 0x27 121 | (XUL) nsThread::ProcessNextEvent(bool, bool*) + 0xd91 122 | (XUL) NS_ProcessPendingEvents(nsIThread*, unsigned int) + 0x62 123 | (XUL) nsBaseAppShell::NativeEventCallback() + 0xb7 124 | (XUL) nsAppShell::ProcessGeckoEvents(void*) + 0xfc 125 | (CoreFoundation) __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 0x11 126 | (CoreFoundation) __CFRunLoopDoSource0 + 0x67 127 | (CoreFoundation) __CFRunLoopDoSources0 + 0xd1 128 | (CoreFoundation) __CFRunLoopRun + 0x4f8 129 | (CoreFoundation) CFRunLoopRunSpecific + 0x1f3 130 | (HIToolbox) RunCurrentEventLoopInMode + 0x124 131 | (HIToolbox) ReceiveNextEventCommon + 0x258 132 | (HIToolbox) _BlockUntilNextEventMatchingListInModeWithFilter + 0x40 133 | (AppKit) _DPSNextEvent + 0x3de 134 | (AppKit) -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 0x548 135 | (XUL) -[GeckoNSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 0x117 136 | (AppKit) -[NSApplication run] + 0x292 137 | (XUL) nsAppShell::Run() + 0x80 138 | (XUL) nsAppStartup::Run() + 0x3e 139 | (XUL) XREMain::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x14f5 140 | (XUL) mozilla::BootstrapImpl::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x10d 141 | (firefox) main + 0x56e 142 | (libdyld.dylib) start + 0x1 143 | (firefox) unknown 0x1 144 | (Wed Feb 5 14:16:45 2020) /Users/smichaud/Desktop/Firefox Nightly 2019-09-04.app/Contents/MacOS/firefox[1885] [0x110f7ddc0] Hook.mm: CVDisplayLinkSetOutputCallback(): displayLink '0x11d786000', callback '0x102f95a00', userInfo '0x115673390', returning 0 145 | (hook.dylib) PrintStackTrace() + 0xae 146 | (hook.dylib) Hooked_CVDisplayLinkSetOutputCallback(__CVDisplayLink*, int (*)(__CVDisplayLink*, CVTimeStamp const*, CVTimeStamp const*, unsigned long long, unsigned long long*, void*), void*) + 0x75 147 | (XUL) OSXVsyncSource::OSXDisplay::EnableVsync() + 0x72 148 | (XUL) mozilla::RefreshTimerVsyncDispatcher::UpdateVsyncStatus() + 0x16d 149 | (XUL) mozilla::detail::RunnableMethodImpl::Run() + 0x27 150 | (XUL) nsThread::ProcessNextEvent(bool, bool*) + 0xd91 151 | (XUL) NS_ProcessPendingEvents(nsIThread*, unsigned int) + 0x62 152 | (XUL) nsBaseAppShell::NativeEventCallback() + 0xb7 153 | (XUL) nsAppShell::ProcessGeckoEvents(void*) + 0xfc 154 | (CoreFoundation) __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 0x11 155 | (CoreFoundation) __CFRunLoopDoSource0 + 0x67 156 | (CoreFoundation) __CFRunLoopDoSources0 + 0xd1 157 | (CoreFoundation) __CFRunLoopRun + 0x4f8 158 | (CoreFoundation) CFRunLoopRunSpecific + 0x1f3 159 | (HIToolbox) RunCurrentEventLoopInMode + 0x124 160 | (HIToolbox) ReceiveNextEventCommon + 0x258 161 | (HIToolbox) _BlockUntilNextEventMatchingListInModeWithFilter + 0x40 162 | (AppKit) _DPSNextEvent + 0x3de 163 | (AppKit) -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 0x548 164 | (XUL) -[GeckoNSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 0x117 165 | (AppKit) -[NSApplication run] + 0x292 166 | (XUL) nsAppShell::Run() + 0x80 167 | (XUL) nsAppStartup::Run() + 0x3e 168 | (XUL) XREMain::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x14f5 169 | (XUL) mozilla::BootstrapImpl::XRE_main(int, char**, mozilla::BootstrapConfig const&) + 0x10d 170 | (firefox) main + 0x56e 171 | (libdyld.dylib) start + 0x1 172 | (firefox) unknown 0x1 173 | (Wed Feb 5 14:16:45 2020) /Users/smichaud/Desktop/Firefox Nightly 2019-09-04.app/Contents/MacOS/firefox[1885] [0x110f7ddc0] Hook.mm: CVDisplayLinkStart(): displayLink '0x11d786000', returning '0' 174 | 175 | # Crash happens here, before the hook for CVDisplayLinkStart() can log its stack trace. 176 | 177 | Exiting due to channel error. 178 | Exiting due to channel error. 179 | Exiting due to channel error. 180 | -------------------------------------------------------------------------------- /Examples/dynamic-hooking/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC=/usr/bin/clang++ 24 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 25 | # 32-bit builds no longer work on Mojave with some versions of XCode 26 | ifeq ($(MOJAVE_OR_ABOVE), 1) 27 | ARCHS=-arch x86_64 28 | else 29 | ARCHS=-arch i386 -arch x86_64 30 | endif 31 | 32 | hook.dylib : hook.o 33 | $(CC) $(ARCHS) -o hook.dylib hook.o \ 34 | -lobjc -framework Cocoa -framework Carbon \ 35 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 36 | -dynamiclib 37 | 38 | hook.o : hook.mm 39 | $(CC) $(ARCHS) -o hook.o \ 40 | -Wno-deprecated-declarations -c hook.mm 41 | 42 | clean : 43 | rm hook.o hook.dylib 44 | -------------------------------------------------------------------------------- /Examples/events/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | # Alternate Makefile that allows us to use low-level llvm tools to build 24 | # step-by-step. One of the steps generates LLVM intermediate language 25 | # files (ending in *.ii), which support calling conventions that aren't 26 | # supported in C/C++ files, but which sometimes get used by clang for internal 27 | # (non-public) functions -- for example fastcc or coldcc. 28 | # 29 | # http://llvm.org/docs/LangRef.html#calling-conventions 30 | # https://clang.llvm.org/docs/AttributeReference.html#calling-conventions 31 | # 32 | # To make a C/C++ function in your hook library use the fastcc calling 33 | # convention, label it with __attribute__((fastcall)) in C/C++ code and use 34 | # this makefile to build the hook library. Under the hook-i386.ii target we 35 | # use sed to replace all instances of "x86_fastcallcc" (the internal name of 36 | # the fastcall calling convention) with "fastcc". 37 | # 38 | # Internal calling conventions are deliberately non-standardized. So it's 39 | # important, when building code that hooks methods which use them, to use 40 | # tools that are as compatible as possible with the code that you're hooking. 41 | # I've had good luck up through Sierra with the LLVM 3.9.0 Clang download: 42 | # http://releases.llvm.org/3.9.0/clang+llvm-3.9.0-x86_64-apple-darwin.tar.xz. 43 | # HighSierra and Mojave seem to require the LLVM 4.0.0 Clang download: 44 | # http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-apple-darwin.tar.xz. 45 | 46 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 47 | # 32-bit builds no longer work on Mojave with some versions of XCode 48 | ifeq ($(MOJAVE_OR_ABOVE), 1) 49 | ARCHS=-arch x86_64 50 | else 51 | ARCHS=-arch i386 -arch x86_64 52 | endif 53 | 54 | HIGHSIERRA_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 17) 55 | ifeq ($(HIGHSIERRA_OR_ABOVE), 1) 56 | LLVM_HOME=/usr/local/clang+llvm-4.0.0-x86_64-apple-darwin 57 | else 58 | LLVM_HOME=/usr/local/clang+llvm-3.9.0-x86_64-apple-darwin 59 | endif 60 | 61 | ifeq ($(MOJAVE_OR_ABOVE), 1) 62 | CLANG=/usr/bin/clang++ 63 | else 64 | CLANG=$(LLVM_HOME)/bin/clang++ 65 | LLAS=$(LLVM_HOME)/bin/llvm-as 66 | LLC=$(LLVM_HOME)/bin/llc 67 | endif 68 | LIPO=lipo 69 | SED=sed 70 | MV=mv 71 | 72 | hook.dylib : hook.o 73 | ifeq ($(MOJAVE_OR_ABOVE), 1) 74 | $(CLANG) $(ARCHS) -o hook.dylib hook.o \ 75 | -lobjc -framework Cocoa -framework Carbon \ 76 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 77 | -dynamiclib 78 | else 79 | $(CLANG) $(ARCHS) -o hook.dylib hook.o \ 80 | -Wl,-read_only_relocs,suppress \ 81 | -lobjc -framework Cocoa -framework Carbon \ 82 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 83 | -dynamiclib 84 | endif 85 | 86 | ifeq ($(MOJAVE_OR_ABOVE), 1) 87 | 88 | hook.o : hook.mm 89 | $(CLANG) $(ARCHS) -o hook.o \ 90 | -DOBJC_OLD_DISPATCH_PROTOTYPES \ 91 | -Wno-deprecated-declarations -c hook.mm 92 | 93 | else #ifeq ($(MOJAVE_OR_ABOVE), 1) 94 | 95 | hook.o : hook-i386.o hook-x86_64.o 96 | $(LIPO) -create -arch i386 hook-i386.o -arch x86_64 hook-x86_64.o \ 97 | -output hook.o 98 | 99 | hook-x86_64.o : hook-x86_64.s 100 | $(CLANG) -arch x86_64 -o hook-x86_64.o -c hook-x86_64.s 101 | 102 | hook-i386.o : hook-i386.s 103 | $(CLANG) -arch i386 -o hook-i386.o -c hook-i386.s 104 | 105 | hook-x86_64.s : hook-x86_64.bc 106 | $(LLC) hook-x86_64.bc -o hook-x86_64.s 107 | 108 | hook-i386.s : hook-i386.bc 109 | $(LLC) hook-i386.bc -o hook-i386.s 110 | 111 | hook-x86_64.bc : hook-x86_64.ii 112 | $(LLAS) -o=hook-x86_64.bc hook-x86_64.ii 113 | 114 | hook-i386.bc : hook-i386.ii 115 | $(LLAS) -o=hook-i386.bc hook-i386.ii 116 | 117 | hook-x86_64.ii : hook.mm 118 | $(CLANG) -arch x86_64 -S -emit-llvm -o hook-x86_64.ii \ 119 | -Wno-deprecated-declarations -c hook.mm 120 | 121 | hook-i386.ii : hook.mm 122 | $(CLANG) -arch i386 -S -emit-llvm -o hook-i386.iii \ 123 | -Wno-deprecated-declarations -c hook.mm 124 | $(SED) -e 's/ x86_fastcallcc / fastcc /' hook-i386.iii > hook-i386.ii 125 | 126 | endif #ifeq ($(MOJAVE_OR_ABOVE), 1) 127 | 128 | clean : 129 | ifeq ($(MOJAVE_OR_ABOVE), 1) 130 | rm hook.o hook.dylib 131 | else 132 | rm hook.o hook.dylib hook-i386* hook-x86_64* 133 | endif 134 | -------------------------------------------------------------------------------- /Examples/kernel-logging/KernelLogging/KernelLogging.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DF20C594202B9A700069779A /* KernelLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = DF20C593202B9A700069779A /* KernelLogging.c */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXFileReference section */ 14 | DF20C590202B9A700069779A /* KernelLogging.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KernelLogging.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 15 | DF20C593202B9A700069779A /* KernelLogging.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = KernelLogging.c; sourceTree = ""; }; 16 | DF20C595202B9A700069779A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 17 | /* End PBXFileReference section */ 18 | 19 | /* Begin PBXFrameworksBuildPhase section */ 20 | DF20C58C202B9A700069779A /* Frameworks */ = { 21 | isa = PBXFrameworksBuildPhase; 22 | buildActionMask = 2147483647; 23 | files = ( 24 | ); 25 | runOnlyForDeploymentPostprocessing = 0; 26 | }; 27 | /* End PBXFrameworksBuildPhase section */ 28 | 29 | /* Begin PBXGroup section */ 30 | DF20C586202B9A700069779A = { 31 | isa = PBXGroup; 32 | children = ( 33 | DF20C592202B9A700069779A /* KernelLogging */, 34 | DF20C591202B9A700069779A /* Products */, 35 | ); 36 | sourceTree = ""; 37 | }; 38 | DF20C591202B9A700069779A /* Products */ = { 39 | isa = PBXGroup; 40 | children = ( 41 | DF20C590202B9A700069779A /* KernelLogging.kext */, 42 | ); 43 | name = Products; 44 | sourceTree = ""; 45 | }; 46 | DF20C592202B9A700069779A /* KernelLogging */ = { 47 | isa = PBXGroup; 48 | children = ( 49 | DF20C593202B9A700069779A /* KernelLogging.c */, 50 | DF20C595202B9A700069779A /* Info.plist */, 51 | ); 52 | path = KernelLogging; 53 | sourceTree = ""; 54 | }; 55 | /* End PBXGroup section */ 56 | 57 | /* Begin PBXHeadersBuildPhase section */ 58 | DF20C58D202B9A700069779A /* Headers */ = { 59 | isa = PBXHeadersBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXHeadersBuildPhase section */ 66 | 67 | /* Begin PBXNativeTarget section */ 68 | DF20C58F202B9A700069779A /* KernelLogging */ = { 69 | isa = PBXNativeTarget; 70 | buildConfigurationList = DF20C598202B9A700069779A /* Build configuration list for PBXNativeTarget "KernelLogging" */; 71 | buildPhases = ( 72 | DF20C58B202B9A700069779A /* Sources */, 73 | DF20C58C202B9A700069779A /* Frameworks */, 74 | DF20C58D202B9A700069779A /* Headers */, 75 | DF20C58E202B9A700069779A /* Resources */, 76 | ); 77 | buildRules = ( 78 | ); 79 | dependencies = ( 80 | ); 81 | name = KernelLogging; 82 | productName = KernelLogging; 83 | productReference = DF20C590202B9A700069779A /* KernelLogging.kext */; 84 | productType = "com.apple.product-type.kernel-extension"; 85 | }; 86 | /* End PBXNativeTarget section */ 87 | 88 | /* Begin PBXProject section */ 89 | DF20C587202B9A700069779A /* Project object */ = { 90 | isa = PBXProject; 91 | attributes = { 92 | LastUpgradeCheck = 0720; 93 | TargetAttributes = { 94 | DF20C58F202B9A700069779A = { 95 | CreatedOnToolsVersion = 7.2; 96 | }; 97 | }; 98 | }; 99 | buildConfigurationList = DF20C58A202B9A700069779A /* Build configuration list for PBXProject "KernelLogging" */; 100 | compatibilityVersion = "Xcode 3.2"; 101 | developmentRegion = English; 102 | hasScannedForEncodings = 0; 103 | knownRegions = ( 104 | en, 105 | ); 106 | mainGroup = DF20C586202B9A700069779A; 107 | productRefGroup = DF20C591202B9A700069779A /* Products */; 108 | projectDirPath = ""; 109 | projectRoot = ""; 110 | targets = ( 111 | DF20C58F202B9A700069779A /* KernelLogging */, 112 | ); 113 | }; 114 | /* End PBXProject section */ 115 | 116 | /* Begin PBXResourcesBuildPhase section */ 117 | DF20C58E202B9A700069779A /* Resources */ = { 118 | isa = PBXResourcesBuildPhase; 119 | buildActionMask = 2147483647; 120 | files = ( 121 | ); 122 | runOnlyForDeploymentPostprocessing = 0; 123 | }; 124 | /* End PBXResourcesBuildPhase section */ 125 | 126 | /* Begin PBXSourcesBuildPhase section */ 127 | DF20C58B202B9A700069779A /* Sources */ = { 128 | isa = PBXSourcesBuildPhase; 129 | buildActionMask = 2147483647; 130 | files = ( 131 | DF20C594202B9A700069779A /* KernelLogging.c in Sources */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXSourcesBuildPhase section */ 136 | 137 | /* Begin XCBuildConfiguration section */ 138 | DF20C596202B9A700069779A /* Debug */ = { 139 | isa = XCBuildConfiguration; 140 | buildSettings = { 141 | ALWAYS_SEARCH_USER_PATHS = NO; 142 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 143 | CLANG_CXX_LIBRARY = "libc++"; 144 | CLANG_ENABLE_MODULES = YES; 145 | CLANG_ENABLE_OBJC_ARC = YES; 146 | CLANG_WARN_BOOL_CONVERSION = YES; 147 | CLANG_WARN_CONSTANT_CONVERSION = YES; 148 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 149 | CLANG_WARN_EMPTY_BODY = YES; 150 | CLANG_WARN_ENUM_CONVERSION = YES; 151 | CLANG_WARN_INT_CONVERSION = YES; 152 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 153 | CLANG_WARN_UNREACHABLE_CODE = YES; 154 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 155 | CODE_SIGN_IDENTITY = "-"; 156 | COPY_PHASE_STRIP = NO; 157 | DEBUG_INFORMATION_FORMAT = dwarf; 158 | ENABLE_STRICT_OBJC_MSGSEND = YES; 159 | ENABLE_TESTABILITY = YES; 160 | GCC_C_LANGUAGE_STANDARD = gnu99; 161 | GCC_DYNAMIC_NO_PIC = NO; 162 | GCC_NO_COMMON_BLOCKS = YES; 163 | GCC_OPTIMIZATION_LEVEL = 0; 164 | GCC_PREPROCESSOR_DEFINITIONS = ( 165 | "DEBUG=1", 166 | "$(inherited)", 167 | ); 168 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 169 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 170 | GCC_WARN_UNDECLARED_SELECTOR = YES; 171 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 172 | GCC_WARN_UNUSED_FUNCTION = YES; 173 | GCC_WARN_UNUSED_VARIABLE = YES; 174 | MTL_ENABLE_DEBUG_INFO = YES; 175 | ONLY_ACTIVE_ARCH = YES; 176 | SDKROOT = macosx; 177 | }; 178 | name = Debug; 179 | }; 180 | DF20C597202B9A700069779A /* Release */ = { 181 | isa = XCBuildConfiguration; 182 | buildSettings = { 183 | ALWAYS_SEARCH_USER_PATHS = NO; 184 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 185 | CLANG_CXX_LIBRARY = "libc++"; 186 | CLANG_ENABLE_MODULES = YES; 187 | CLANG_ENABLE_OBJC_ARC = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_CONSTANT_CONVERSION = YES; 190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 191 | CLANG_WARN_EMPTY_BODY = YES; 192 | CLANG_WARN_ENUM_CONVERSION = YES; 193 | CLANG_WARN_INT_CONVERSION = YES; 194 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 195 | CLANG_WARN_UNREACHABLE_CODE = YES; 196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 197 | CODE_SIGN_IDENTITY = "-"; 198 | COPY_PHASE_STRIP = NO; 199 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 200 | ENABLE_NS_ASSERTIONS = NO; 201 | ENABLE_STRICT_OBJC_MSGSEND = YES; 202 | GCC_C_LANGUAGE_STANDARD = gnu99; 203 | GCC_NO_COMMON_BLOCKS = YES; 204 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 205 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 206 | GCC_WARN_UNDECLARED_SELECTOR = YES; 207 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 208 | GCC_WARN_UNUSED_FUNCTION = YES; 209 | GCC_WARN_UNUSED_VARIABLE = YES; 210 | MTL_ENABLE_DEBUG_INFO = NO; 211 | SDKROOT = macosx; 212 | }; 213 | name = Release; 214 | }; 215 | DF20C599202B9A700069779A /* Debug */ = { 216 | isa = XCBuildConfiguration; 217 | buildSettings = { 218 | CODE_SIGN_IDENTITY = ""; 219 | INFOPLIST_FILE = KernelLogging/Info.plist; 220 | INSTALL_PATH = /usr/local/sbin; 221 | MODULE_NAME = org.smichaud.KernelLogging; 222 | MODULE_START = KernelLogging_start; 223 | MODULE_STOP = KernelLogging_stop; 224 | MODULE_VERSION = 1.0.0d1; 225 | PRODUCT_BUNDLE_IDENTIFIER = org.smichaud.KernelLogging; 226 | PRODUCT_NAME = "$(TARGET_NAME)"; 227 | WRAPPER_EXTENSION = kext; 228 | }; 229 | name = Debug; 230 | }; 231 | DF20C59A202B9A700069779A /* Release */ = { 232 | isa = XCBuildConfiguration; 233 | buildSettings = { 234 | CODE_SIGN_IDENTITY = ""; 235 | INFOPLIST_FILE = KernelLogging/Info.plist; 236 | INSTALL_PATH = /usr/local/sbin; 237 | MODULE_NAME = org.smichaud.KernelLogging; 238 | MODULE_START = KernelLogging_start; 239 | MODULE_STOP = KernelLogging_stop; 240 | MODULE_VERSION = 1.0.0d1; 241 | PRODUCT_BUNDLE_IDENTIFIER = org.smichaud.KernelLogging; 242 | PRODUCT_NAME = "$(TARGET_NAME)"; 243 | WRAPPER_EXTENSION = kext; 244 | }; 245 | name = Release; 246 | }; 247 | /* End XCBuildConfiguration section */ 248 | 249 | /* Begin XCConfigurationList section */ 250 | DF20C58A202B9A700069779A /* Build configuration list for PBXProject "KernelLogging" */ = { 251 | isa = XCConfigurationList; 252 | buildConfigurations = ( 253 | DF20C596202B9A700069779A /* Debug */, 254 | DF20C597202B9A700069779A /* Release */, 255 | ); 256 | defaultConfigurationIsVisible = 0; 257 | defaultConfigurationName = Release; 258 | }; 259 | DF20C598202B9A700069779A /* Build configuration list for PBXNativeTarget "KernelLogging" */ = { 260 | isa = XCConfigurationList; 261 | buildConfigurations = ( 262 | DF20C599202B9A700069779A /* Debug */, 263 | DF20C59A202B9A700069779A /* Release */, 264 | ); 265 | defaultConfigurationIsVisible = 0; 266 | defaultConfigurationName = Release; 267 | }; 268 | /* End XCConfigurationList section */ 269 | }; 270 | rootObject = DF20C587202B9A700069779A /* Project object */; 271 | } 272 | -------------------------------------------------------------------------------- /Examples/kernel-logging/KernelLogging/KernelLogging/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | KEXT 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | NSHumanReadableCopyright 24 | Copyright © 2018 Steven Michaud. All rights reserved. 25 | OSBundleLibraries 26 | 27 | com.apple.kpi.bsd 28 | 8.0b1 29 | com.apple.kpi.iokit 30 | 7.0 31 | com.apple.kpi.libkern 32 | 8.0d0 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Examples/kernel-logging/KernelLogging/KernelLogging/KernelLogging.c: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2018 Steven Michaud 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #if (defined(MAC_OS_X_VERSION_10_12) || defined(MAC_OS_X_VERSION_10_13)) && \ 29 | (MAC_OS_X_VERSION_MAX_ALLOWED / 100) >= (MAC_OS_X_VERSION_10_12 / 100) 30 | #include 31 | #endif 32 | 33 | kern_return_t KernelLogging_start(kmod_info_t * ki, void *d); 34 | kern_return_t KernelLogging_stop(kmod_info_t *ki, void *d); 35 | 36 | #define FAIL_START 1 37 | 38 | kern_return_t KernelLogging_start(kmod_info_t * ki, void *d) 39 | { 40 | printf("KernelLogging(printf): KernelLogging_start()\n"); 41 | IOLog("KernelLogging(IOLog): KernelLogging_start()\n"); 42 | #if (defined(MAC_OS_X_VERSION_10_12) || defined(MAC_OS_X_VERSION_10_13)) && \ 43 | (MAC_OS_X_VERSION_MAX_ALLOWED / 100) >= (MAC_OS_X_VERSION_10_12 / 100) 44 | os_log(OS_LOG_DEFAULT, "KernelLogging(os_log): KernelLogging_start()\n"); 45 | #endif 46 | #ifdef FAIL_START 47 | return KERN_FAILURE; 48 | #else 49 | return KERN_SUCCESS; 50 | #endif 51 | } 52 | 53 | kern_return_t KernelLogging_stop(kmod_info_t *ki, void *d) 54 | { 55 | printf("KernelLogging(printf): KernelLogging_stop()\n"); 56 | IOLog("KernelLogging(IOLog): KernelLogging_stop()\n"); 57 | #if (defined(MAC_OS_X_VERSION_10_12) || defined(MAC_OS_X_VERSION_10_13)) && \ 58 | (MAC_OS_X_VERSION_MAX_ALLOWED / 100) >= (MAC_OS_X_VERSION_10_12 / 100) 59 | os_log(OS_LOG_DEFAULT, "KernelLogging(os_log): KernelLogging_stop()\n"); 60 | #endif 61 | return KERN_SUCCESS; 62 | } 63 | -------------------------------------------------------------------------------- /Examples/kernel-logging/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC=/usr/bin/clang++ 24 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 25 | # 32-bit builds no longer work on Mojave with some versions of XCode 26 | ifeq ($(MOJAVE_OR_ABOVE), 1) 27 | ARCHS=-arch x86_64 28 | else 29 | ARCHS=-arch i386 -arch x86_64 30 | endif 31 | 32 | hook.dylib : hook.o 33 | $(CC) $(ARCHS) -o hook.dylib hook.o \ 34 | -lobjc -framework Cocoa -framework Carbon \ 35 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 36 | -dynamiclib 37 | 38 | hook.o : hook.mm 39 | $(CC) $(ARCHS) -o hook.o \ 40 | -Wno-deprecated-declarations -c hook.mm 41 | 42 | clean : 43 | rm hook.o hook.dylib 44 | -------------------------------------------------------------------------------- /Examples/kernel-logging/runtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SUDO=("/usr/bin/sudo") 4 | OPEN=("/usr/bin/open") 5 | LAUNCHCTL=("/bin/launchctl") 6 | 7 | abort() { 8 | printf "%s\n" "$@" >&2 9 | exit 1 10 | } 11 | 12 | ${SUDO} -v 13 | if [[ $? -ne 0 ]] 14 | then 15 | abort "Incorrect password for sudo" 16 | fi 17 | 18 | # 'launchctl kickstart -k' no longer works as of macOS 14.4 :-( 19 | PID=`${SUDO} ${LAUNCHCTL} kickstart -p system/com.apple.diagnosticd | cut -f 4` 20 | ${SUDO} kill -9 ${PID} 21 | 22 | ${OPEN} /System/Applications/Utilities/Console.app 23 | -------------------------------------------------------------------------------- /Examples/secinit/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC=/usr/bin/clang++ 24 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 25 | # 32-bit builds no longer work on Mojave with some versions of XCode 26 | ifeq ($(MOJAVE_OR_ABOVE), 1) 27 | ARCHS=-arch x86_64 28 | else 29 | ARCHS=-arch i386 -arch x86_64 30 | endif 31 | 32 | hook.dylib : hook.o 33 | $(CC) $(ARCHS) -o hook.dylib hook.o \ 34 | -lobjc -framework Cocoa -framework Carbon \ 35 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 36 | -dynamiclib 37 | 38 | hook.o : hook.mm 39 | $(CC) $(ARCHS) -o hook.o \ 40 | -Wno-deprecated-declarations -c hook.mm 41 | 42 | clean : 43 | rm hook.o hook.dylib 44 | -------------------------------------------------------------------------------- /Examples/secinit/runtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OPEN=("/usr/bin/open") 4 | LAUNCHCTL=("/bin/launchctl") 5 | 6 | rm -rf ~/Library/Containers/com.apple.calculator 7 | 8 | # 'launchctl kickstart -k' no longer works as of macOS 14.4 :-( 9 | PID=`${LAUNCHCTL} kickstart -p user/${UID}/com.apple.secinitd | cut -f 4` 10 | kill -9 ${PID} 11 | 12 | ${OPEN} /System/Applications/Calculator.app 13 | -------------------------------------------------------------------------------- /Examples/watchpoints/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC=/usr/bin/clang++ 24 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 25 | # 32-bit builds no longer work on Mojave with some versions of XCode 26 | ifeq ($(MOJAVE_OR_ABOVE), 1) 27 | ARCHS=-arch x86_64 28 | else 29 | ARCHS=-arch i386 -arch x86_64 30 | endif 31 | 32 | hook.dylib : hook.o 33 | $(CC) $(ARCHS) -o hook.dylib hook.o \ 34 | -lobjc -framework Cocoa -framework Carbon \ 35 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 36 | -dynamiclib 37 | 38 | hook.o : hook.mm 39 | $(CC) $(ARCHS) -o hook.o \ 40 | -Wno-deprecated-declarations -c hook.mm 41 | 42 | clean : 43 | rm hook.o hook.dylib 44 | -------------------------------------------------------------------------------- /Examples/xpcproxy/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC=/usr/bin/clang++ 24 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 25 | # 32-bit builds no longer work on Mojave with some versions of XCode 26 | ifeq ($(MOJAVE_OR_ABOVE), 1) 27 | ARCHS=-arch x86_64 28 | else 29 | ARCHS=-arch i386 -arch x86_64 30 | endif 31 | 32 | hook.dylib : hook.o 33 | $(CC) $(ARCHS) -o hook.dylib hook.o \ 34 | -lobjc -framework Cocoa -framework Carbon \ 35 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 36 | -dynamiclib 37 | 38 | hook.o : hook.mm 39 | $(CC) $(ARCHS) -o hook.o \ 40 | -Wno-deprecated-declarations -c hook.mm 41 | 42 | clean : 43 | rm hook.o hook.dylib 44 | -------------------------------------------------------------------------------- /HookCase/HookCase.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DF4829B01D8710D400BA7558 /* HookCase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF4829AF1D8710D400BA7558 /* HookCase.cpp */; }; 11 | DFE647A61D89AD4F00111DDF /* HookCase.s in Sources */ = {isa = PBXBuildFile; fileRef = DFE647A51D89AD4F00111DDF /* HookCase.s */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | DF4829AC1D8710D400BA7558 /* HookCase.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HookCase.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 16 | DF4829AF1D8710D400BA7558 /* HookCase.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HookCase.cpp; sourceTree = ""; }; 17 | DF4829B11D8710D400BA7558 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 18 | DFA122D01D89F5C7007F4AB6 /* HookCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HookCase.h; sourceTree = ""; }; 19 | DFE647A51D89AD4F00111DDF /* HookCase.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = HookCase.s; sourceTree = ""; }; 20 | /* End PBXFileReference section */ 21 | 22 | /* Begin PBXFrameworksBuildPhase section */ 23 | DF4829A81D8710D400BA7558 /* Frameworks */ = { 24 | isa = PBXFrameworksBuildPhase; 25 | buildActionMask = 2147483647; 26 | files = ( 27 | ); 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXFrameworksBuildPhase section */ 31 | 32 | /* Begin PBXGroup section */ 33 | DF4829A21D8710D400BA7558 = { 34 | isa = PBXGroup; 35 | children = ( 36 | DF4829AE1D8710D400BA7558 /* HookCase */, 37 | DF4829AD1D8710D400BA7558 /* Products */, 38 | ); 39 | sourceTree = ""; 40 | }; 41 | DF4829AD1D8710D400BA7558 /* Products */ = { 42 | isa = PBXGroup; 43 | children = ( 44 | DF4829AC1D8710D400BA7558 /* HookCase.kext */, 45 | ); 46 | name = Products; 47 | sourceTree = ""; 48 | }; 49 | DF4829AE1D8710D400BA7558 /* HookCase */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | DFA122D01D89F5C7007F4AB6 /* HookCase.h */, 53 | DF4829AF1D8710D400BA7558 /* HookCase.cpp */, 54 | DFE647A51D89AD4F00111DDF /* HookCase.s */, 55 | DF4829B11D8710D400BA7558 /* Info.plist */, 56 | ); 57 | path = HookCase; 58 | sourceTree = ""; 59 | }; 60 | /* End PBXGroup section */ 61 | 62 | /* Begin PBXHeadersBuildPhase section */ 63 | DF4829A91D8710D400BA7558 /* Headers */ = { 64 | isa = PBXHeadersBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXHeadersBuildPhase section */ 71 | 72 | /* Begin PBXNativeTarget section */ 73 | DF4829AB1D8710D400BA7558 /* HookCase */ = { 74 | isa = PBXNativeTarget; 75 | buildConfigurationList = DF4829B41D8710D400BA7558 /* Build configuration list for PBXNativeTarget "HookCase" */; 76 | buildPhases = ( 77 | DF4829A71D8710D400BA7558 /* Sources */, 78 | DF4829A81D8710D400BA7558 /* Frameworks */, 79 | DF4829A91D8710D400BA7558 /* Headers */, 80 | DF4829AA1D8710D400BA7558 /* Resources */, 81 | ); 82 | buildRules = ( 83 | ); 84 | dependencies = ( 85 | ); 86 | name = HookCase; 87 | productName = HookCase; 88 | productReference = DF4829AC1D8710D400BA7558 /* HookCase.kext */; 89 | productType = "com.apple.product-type.kernel-extension"; 90 | }; 91 | /* End PBXNativeTarget section */ 92 | 93 | /* Begin PBXProject section */ 94 | DF4829A31D8710D400BA7558 /* Project object */ = { 95 | isa = PBXProject; 96 | attributes = { 97 | LastUpgradeCheck = 0720; 98 | TargetAttributes = { 99 | DF4829AB1D8710D400BA7558 = { 100 | CreatedOnToolsVersion = 7.2; 101 | }; 102 | }; 103 | }; 104 | buildConfigurationList = DF4829A61D8710D400BA7558 /* Build configuration list for PBXProject "HookCase" */; 105 | compatibilityVersion = "Xcode 3.2"; 106 | developmentRegion = English; 107 | hasScannedForEncodings = 0; 108 | knownRegions = ( 109 | en, 110 | ); 111 | mainGroup = DF4829A21D8710D400BA7558; 112 | productRefGroup = DF4829AD1D8710D400BA7558 /* Products */; 113 | projectDirPath = ""; 114 | projectRoot = ""; 115 | targets = ( 116 | DF4829AB1D8710D400BA7558 /* HookCase */, 117 | ); 118 | }; 119 | /* End PBXProject section */ 120 | 121 | /* Begin PBXResourcesBuildPhase section */ 122 | DF4829AA1D8710D400BA7558 /* Resources */ = { 123 | isa = PBXResourcesBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | ); 127 | runOnlyForDeploymentPostprocessing = 0; 128 | }; 129 | /* End PBXResourcesBuildPhase section */ 130 | 131 | /* Begin PBXSourcesBuildPhase section */ 132 | DF4829A71D8710D400BA7558 /* Sources */ = { 133 | isa = PBXSourcesBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | DFE647A61D89AD4F00111DDF /* HookCase.s in Sources */, 137 | DF4829B01D8710D400BA7558 /* HookCase.cpp in Sources */, 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | /* End PBXSourcesBuildPhase section */ 142 | 143 | /* Begin XCBuildConfiguration section */ 144 | DF4829B21D8710D400BA7558 /* Debug */ = { 145 | isa = XCBuildConfiguration; 146 | buildSettings = { 147 | ALWAYS_SEARCH_USER_PATHS = NO; 148 | ARCHS = "$(NATIVE_ARCH_ACTUAL)"; 149 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 150 | CLANG_CXX_LIBRARY = "libc++"; 151 | CLANG_ENABLE_MODULES = YES; 152 | CLANG_ENABLE_OBJC_ARC = YES; 153 | CLANG_WARN_BOOL_CONVERSION = YES; 154 | CLANG_WARN_CONSTANT_CONVERSION = YES; 155 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 156 | CLANG_WARN_EMPTY_BODY = YES; 157 | CLANG_WARN_ENUM_CONVERSION = YES; 158 | CLANG_WARN_INT_CONVERSION = YES; 159 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 160 | CLANG_WARN_UNREACHABLE_CODE = YES; 161 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 162 | CODE_SIGN_IDENTITY = "-"; 163 | COPY_PHASE_STRIP = NO; 164 | DEBUG_INFORMATION_FORMAT = dwarf; 165 | ENABLE_STRICT_OBJC_MSGSEND = YES; 166 | ENABLE_TESTABILITY = YES; 167 | GCC_C_LANGUAGE_STANDARD = gnu99; 168 | GCC_DYNAMIC_NO_PIC = NO; 169 | GCC_NO_COMMON_BLOCKS = YES; 170 | GCC_OPTIMIZATION_LEVEL = 0; 171 | GCC_PREPROCESSOR_DEFINITIONS = ( 172 | "DEBUG=1", 173 | "$(inherited)", 174 | ); 175 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 176 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 177 | GCC_WARN_UNDECLARED_SELECTOR = YES; 178 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 179 | GCC_WARN_UNUSED_FUNCTION = YES; 180 | GCC_WARN_UNUSED_VARIABLE = YES; 181 | MACOSX_DEPLOYMENT_TARGET = 10.9; 182 | MTL_ENABLE_DEBUG_INFO = YES; 183 | ONLY_ACTIVE_ARCH = YES; 184 | SDKROOT = macosx; 185 | }; 186 | name = Debug; 187 | }; 188 | DF4829B31D8710D400BA7558 /* Release */ = { 189 | isa = XCBuildConfiguration; 190 | buildSettings = { 191 | ALWAYS_SEARCH_USER_PATHS = NO; 192 | ARCHS = "$(NATIVE_ARCH_ACTUAL)"; 193 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 194 | CLANG_CXX_LIBRARY = "libc++"; 195 | CLANG_ENABLE_MODULES = YES; 196 | CLANG_ENABLE_OBJC_ARC = YES; 197 | CLANG_WARN_BOOL_CONVERSION = YES; 198 | CLANG_WARN_CONSTANT_CONVERSION = YES; 199 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 200 | CLANG_WARN_EMPTY_BODY = YES; 201 | CLANG_WARN_ENUM_CONVERSION = YES; 202 | CLANG_WARN_INT_CONVERSION = YES; 203 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 204 | CLANG_WARN_UNREACHABLE_CODE = YES; 205 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 206 | CODE_SIGN_IDENTITY = "-"; 207 | COPY_PHASE_STRIP = NO; 208 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 209 | ENABLE_NS_ASSERTIONS = NO; 210 | ENABLE_STRICT_OBJC_MSGSEND = YES; 211 | GCC_C_LANGUAGE_STANDARD = gnu99; 212 | GCC_NO_COMMON_BLOCKS = YES; 213 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 214 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 215 | GCC_WARN_UNDECLARED_SELECTOR = YES; 216 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 217 | GCC_WARN_UNUSED_FUNCTION = YES; 218 | GCC_WARN_UNUSED_VARIABLE = YES; 219 | MACOSX_DEPLOYMENT_TARGET = 10.9; 220 | MTL_ENABLE_DEBUG_INFO = NO; 221 | SDKROOT = macosx; 222 | }; 223 | name = Release; 224 | }; 225 | DF4829B51D8710D400BA7558 /* Debug */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | CODE_SIGN_IDENTITY = ""; 229 | INFOPLIST_FILE = HookCase/Info.plist; 230 | INSTALL_PATH = /usr/local/sbin; 231 | MODULE_NAME = org.smichaud.HookCase; 232 | MODULE_START = HookCase_start; 233 | MODULE_STOP = HookCase_stop; 234 | MODULE_VERSION = 9.0.2; 235 | PRODUCT_BUNDLE_IDENTIFIER = org.smichaud.HookCase; 236 | PRODUCT_NAME = "$(TARGET_NAME)"; 237 | WRAPPER_EXTENSION = kext; 238 | }; 239 | name = Debug; 240 | }; 241 | DF4829B61D8710D400BA7558 /* Release */ = { 242 | isa = XCBuildConfiguration; 243 | buildSettings = { 244 | CODE_SIGN_IDENTITY = ""; 245 | INFOPLIST_FILE = HookCase/Info.plist; 246 | INSTALL_PATH = /usr/local/sbin; 247 | MODULE_NAME = org.smichaud.HookCase; 248 | MODULE_START = HookCase_start; 249 | MODULE_STOP = HookCase_stop; 250 | MODULE_VERSION = 9.0.2; 251 | PRODUCT_BUNDLE_IDENTIFIER = org.smichaud.HookCase; 252 | PRODUCT_NAME = "$(TARGET_NAME)"; 253 | WRAPPER_EXTENSION = kext; 254 | }; 255 | name = Release; 256 | }; 257 | /* End XCBuildConfiguration section */ 258 | 259 | /* Begin XCConfigurationList section */ 260 | DF4829A61D8710D400BA7558 /* Build configuration list for PBXProject "HookCase" */ = { 261 | isa = XCConfigurationList; 262 | buildConfigurations = ( 263 | DF4829B21D8710D400BA7558 /* Debug */, 264 | DF4829B31D8710D400BA7558 /* Release */, 265 | ); 266 | defaultConfigurationIsVisible = 0; 267 | defaultConfigurationName = Release; 268 | }; 269 | DF4829B41D8710D400BA7558 /* Build configuration list for PBXNativeTarget "HookCase" */ = { 270 | isa = XCConfigurationList; 271 | buildConfigurations = ( 272 | DF4829B51D8710D400BA7558 /* Debug */, 273 | DF4829B61D8710D400BA7558 /* Release */, 274 | ); 275 | defaultConfigurationIsVisible = 0; 276 | defaultConfigurationName = Release; 277 | }; 278 | /* End XCConfigurationList section */ 279 | }; 280 | rootObject = DF4829A31D8710D400BA7558 /* Project object */; 281 | } 282 | -------------------------------------------------------------------------------- /HookCase/HookCase/HookCase.h: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2024 Steven Michaud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /* This file must be includable in HookCase.s. So basically everything but 25 | * #defines should be isolated in "#ifndef __ASSEMBLER__" blocks. And 26 | * don't use // comments. 27 | */ 28 | 29 | #ifndef HookCase_h 30 | #define HookCase_h 31 | 32 | /* From the xnu kernel's osfmk/i386/seg.h (begin) */ 33 | 34 | #define SZ_64 0x2 /* 64-bit segment */ 35 | #define SZ_32 0x4 /* 32-bit segment */ 36 | #define SZ_G 0x8 /* 4K limit field */ 37 | 38 | #define ACC_A 0x01 /* accessed */ 39 | #define ACC_TYPE 0x1e /* type field: */ 40 | 41 | #define ACC_TYPE_SYSTEM 0x00 /* system descriptors: */ 42 | 43 | #define ACC_LDT 0x02 /* LDT */ 44 | #define ACC_CALL_GATE_16 0x04 /* 16-bit call gate */ 45 | #define ACC_TASK_GATE 0x05 /* task gate */ 46 | #define ACC_TSS 0x09 /* task segment */ 47 | #define ACC_CALL_GATE 0x0c /* call gate */ 48 | #define ACC_INTR_GATE 0x0e /* interrupt gate */ 49 | #define ACC_TRAP_GATE 0x0f /* trap gate */ 50 | 51 | #define ACC_TSS_BUSY 0x02 /* task busy */ 52 | 53 | #define ACC_TYPE_USER 0x10 /* user descriptors */ 54 | 55 | #define ACC_DATA 0x10 /* data */ 56 | #define ACC_DATA_W 0x12 /* data, writable */ 57 | #define ACC_DATA_E 0x14 /* data, expand-down */ 58 | #define ACC_DATA_EW 0x16 /* data, expand-down, 59 | writable */ 60 | #define ACC_CODE 0x18 /* code */ 61 | #define ACC_CODE_R 0x1a /* code, readable */ 62 | #define ACC_CODE_C 0x1c /* code, conforming */ 63 | #define ACC_CODE_CR 0x1e /* code, conforming, 64 | readable */ 65 | #define ACC_PL 0x60 /* access rights: */ 66 | #define ACC_PL_K 0x00 /* kernel access only */ 67 | #define ACC_PL_U 0x60 /* user access */ 68 | #define ACC_P 0x80 /* segment present */ 69 | 70 | /* 71 | * Components of a selector 72 | */ 73 | #define SEL_LDTS 0x04 /* local selector */ 74 | #define SEL_PL 0x03 /* privilege level: */ 75 | #define SEL_PL_K 0x00 /* kernel selector */ 76 | #define SEL_PL_U 0x03 /* user selector */ 77 | 78 | /* 79 | * Convert selector to descriptor table index. 80 | */ 81 | #define sel_idx(sel) (selector_to_sel(sel).index) 82 | #define SEL_TO_INDEX(s) ((s)>>3) 83 | 84 | #define NULL_SEG 0 85 | 86 | /* 87 | * Kernel descriptors for MACH - 64-bit flat address space. 88 | */ 89 | #define KERNEL64_CS 0x08 /* 1: K64 code */ 90 | #define SYSENTER_CS 0x0b /* U32 sysenter pseudo-segment */ 91 | #define KERNEL64_SS 0x10 /* 2: KERNEL64_CS+8 for syscall */ 92 | #define USER_CS 0x1b /* 3: U32 code */ 93 | #define USER_DS 0x23 /* 4: USER_CS+8 for sysret */ 94 | #define USER64_CS 0x2b /* 5: USER_CS+16 for sysret */ 95 | #define USER64_DS USER_DS /* U64 data pseudo-segment */ 96 | #define KERNEL_LDT 0x30 /* 6: */ 97 | /* 7: other 8 bytes of KERNEL_LDT */ 98 | #define KERNEL_TSS 0x40 /* 8: */ 99 | /* 9: other 8 bytes of KERNEL_TSS */ 100 | #define KERNEL32_CS 0x50 /* 10: */ 101 | #define USER_LDT 0x58 /* 11: */ 102 | /* 12: other 8 bytes of USER_LDT */ 103 | #define KERNEL_DS 0x68 /* 13: 32-bit kernel data */ 104 | 105 | #define SYSENTER_TF_CS (USER_CS|0x10000) 106 | #define SYSENTER_DS KERNEL64_SS /* sysenter kernel data segment */ 107 | 108 | /* 109 | * 64-bit kernel LDT descriptors 110 | */ 111 | #define SYSCALL_CS 0x07 /* syscall pseudo-segment */ 112 | #define USER_CTHREAD 0x0f /* user cthread area */ 113 | #define USER_SETTABLE 0x1f /* start of user settable ldt entries */ 114 | 115 | /* From the xnu kernel's osfmk/i386/seg.h (end) */ 116 | 117 | /* From the xnu kernel's osfmk/i386/proc_reg.h */ 118 | #define CR0_TS 0x00000008 /* Task switch */ 119 | #define CR4_PGE 0x00000080 /* Page Global Enable */ 120 | 121 | /* From the xnu kernel's osfmk/i386/mp_desc.c */ 122 | #define K_INTR_GATE (ACC_P|ACC_PL_K|ACC_INTR_GATE) 123 | #define U_INTR_GATE (ACC_P|ACC_PL_U|ACC_INTR_GATE) 124 | 125 | /* From the xnu kernel's osfmk/i386/cpu_data.h (begin) */ 126 | 127 | #ifdef __ASSEMBLER__ 128 | #define TASK_MAP_32BIT 0 129 | #define TASK_MAP_64BIT 1 130 | #else 131 | typedef enum { 132 | TASK_MAP_32BIT = 0, /* 32-bit user, compatibility mode */ 133 | TASK_MAP_64BIT = 1, /* 64-bit user thread, shared space */ 134 | } task_map_t; 135 | #endif 136 | 137 | /* From the xnu kernel's osfmk/i386/cpu_data.h (end) */ 138 | 139 | /* From the xnu kernel's osfmk/mach/i386/thread_status.h (begin) */ 140 | 141 | #define THREAD_STATE_NONE 13 142 | 143 | #define x86_SAVED_STATE32 THREAD_STATE_NONE + 1 144 | #define x86_SAVED_STATE64 THREAD_STATE_NONE + 2 145 | 146 | /* From the xnu kernel's osfmk/mach/i386/thread_status.h (end) */ 147 | 148 | /* These are the two GS bases that get swapped by the 'swapgs' instruction */ 149 | #define MSR_IA32_GS_BASE 0xC0000101 /* Current GS base -- kernel or user */ 150 | #define MSR_IA32_KERNEL_GS_BASE 0xC0000102 /* "Stored" GS base */ 151 | 152 | /* 153 | * Prior to version 2.1, HookCase used the interrupts from 0x20 through 0x23. 154 | * But this caused trouble with VMware Fusion running as host, so now we use 155 | * 0x30 through 0x35. The problem with VMware Fusion is reported at bug #5 156 | * (https://github.com/steven-michaud/HookCase/issues/5). 157 | */ 158 | 159 | /* Define the interrupts that HookCase will use internally. Interrupts in the 160 | * ranges 0x40 - 0x4F, 0x50 - 0x5F and 0xD0 - 0xDF are reserved for APIC 161 | * interrupts (see osfmk/x86_64/idt_table.h and osfmk/i386/lapic.h). VMWare 162 | * uses at least one interrupt in the range 0x20 - 0x2F. 163 | */ 164 | #define HC_INT1 0x30UL 165 | #define HC_INT2 0x31UL 166 | #define HC_INT3 0x32UL 167 | #define HC_INT4 0x33UL 168 | #define HC_INT5 0x34UL 169 | #define HC_INT6 0x35UL 170 | 171 | #ifndef __ASSEMBLER__ 172 | 173 | /* From the xnu kernel's osfmk/i386/trap.h (begin) */ 174 | 175 | #define T_PAGE_FAULT 14 176 | #define T_PF_PROT 0x1 177 | #define T_PF_WRITE 0x2 178 | #define T_PF_USER 0x4 179 | 180 | /* From the xnu kernel's osfmk/i386/trap.h (end) */ 181 | 182 | /* From the xnu kernel's osfmk/i386/thread_status.h (begin) */ 183 | 184 | /* 185 | * The format in which thread state is saved by Mach on this machine. This 186 | * state flavor is most efficient for exception RPC's to kernel-loaded 187 | * servers, because copying can be avoided: 188 | */ 189 | struct x86_saved_state32 { 190 | uint32_t gs; 191 | uint32_t fs; 192 | uint32_t es; 193 | uint32_t ds; 194 | uint32_t edi; 195 | uint32_t esi; 196 | uint32_t ebp; 197 | uint32_t cr2; /* kernel esp stored by pusha - we save cr2 here later */ 198 | uint32_t ebx; 199 | uint32_t edx; 200 | uint32_t ecx; 201 | uint32_t eax; 202 | uint16_t trapno; 203 | uint16_t cpu; 204 | uint32_t err; 205 | uint32_t eip; 206 | uint32_t cs; 207 | uint32_t efl; 208 | uint32_t uesp; 209 | uint32_t ss; 210 | }; 211 | typedef struct x86_saved_state32 x86_saved_state32_t; 212 | 213 | #define x86_SAVED_STATE32_COUNT ((mach_msg_type_number_t) \ 214 | (sizeof (x86_saved_state32_t)/sizeof(unsigned int))) 215 | 216 | #pragma pack(4) 217 | /* 218 | * This is the state pushed onto the 64-bit interrupt stack 219 | * on any exception/trap/interrupt. 220 | */ 221 | struct x86_64_intr_stack_frame { 222 | uint16_t trapno; 223 | uint16_t cpu; 224 | uint32_t _pad; 225 | uint64_t trapfn; 226 | uint64_t err; 227 | uint64_t rip; 228 | uint64_t cs; 229 | uint64_t rflags; 230 | uint64_t rsp; 231 | uint64_t ss; 232 | }; 233 | typedef struct x86_64_intr_stack_frame x86_64_intr_stack_frame_t; 234 | /* Note: sizeof(x86_64_intr_stack_frame_t) must be a multiple of 16 bytes */ 235 | 236 | /* 237 | * thread state format for task running in 64bit long mode 238 | * in long mode, the same hardware frame is always pushed regardless 239 | * of whether there was a change in privlege level... therefore, there 240 | * is no need for an x86_saved_state64_from_kernel variant 241 | */ 242 | struct x86_saved_state64 { 243 | uint64_t rdi; /* arg0 for system call */ 244 | uint64_t rsi; 245 | uint64_t rdx; 246 | uint64_t r10; /* R10 := RCX prior to syscall trap */ 247 | uint64_t r8; 248 | uint64_t r9; /* arg5 for system call */ 249 | 250 | uint64_t cr2; 251 | uint64_t r15; 252 | uint64_t r14; 253 | uint64_t r13; 254 | uint64_t r12; 255 | uint64_t r11; 256 | uint64_t rbp; 257 | uint64_t rbx; 258 | uint64_t rcx; 259 | uint64_t rax; 260 | 261 | uint32_t gs; 262 | uint32_t fs; 263 | 264 | uint64_t _pad; 265 | 266 | struct x86_64_intr_stack_frame isf; 267 | }; 268 | typedef struct x86_saved_state64 x86_saved_state64_t; 269 | #define x86_SAVED_STATE64_COUNT ((mach_msg_type_number_t) \ 270 | (sizeof (struct x86_saved_state64)/sizeof(unsigned int))) 271 | 272 | /* 273 | * Unified, tagged saved state: 274 | */ 275 | typedef struct { 276 | uint32_t flavor; /* x86_SAVED_STATE64 or x86_SAVED_STATE32 */ 277 | uint32_t _pad_for_16byte_alignment[3]; 278 | union { 279 | x86_saved_state32_t ss_32; 280 | x86_saved_state64_t ss_64; 281 | } uss; 282 | } x86_saved_state_t; 283 | #define ss_32 uss.ss_32 284 | #define ss_64 uss.ss_64 285 | #pragma pack() 286 | 287 | /* From the xnu kernel's osfmk/i386/thread_status.h (end) */ 288 | 289 | /* Derived from the xnu kernel's osfmk/i386/cpu_data.h (start) */ 290 | 291 | // Before KPTI 292 | typedef struct cpu_data_fake 293 | { 294 | void *cpu_this; // Pointer to myself (offset 0x0) 295 | thread_t cpu_active_thread; // Offset 0x8 296 | thread_t cpu_nthread; // Offset 0x10 297 | volatile int cpu_preemption_level; // Offset 0x18 298 | int cpu_number; // Logical CPU (offset 0x1c) 299 | x86_saved_state_t *cpu_int_state; // Interrupt state (offset 0x20) 300 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 301 | vm_offset_t cpu_active_stack; // Kernel stack base (offset 0x28) 302 | vm_offset_t cpu_kernel_stack; // Kernel stack top (offset 0x30) 303 | vm_offset_t cpu_int_stack_top; // Offset 0x38 304 | uint64_t pad1[24]; 305 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); // Offset 0x100 306 | union { // Offset 0x108 307 | volatile uint32_t cpu_tlb_invalid; 308 | struct { 309 | volatile uint16_t cpu_tlb_invalid_local; 310 | volatile uint16_t cpu_tlb_invalid_global; 311 | }; 312 | }; 313 | volatile task_map_t cpu_task_map; // Offset 0x10c 314 | volatile addr64_t cpu_task_cr3; // Offset 0x110 315 | addr64_t cpu_kernel_cr3; // Offset 0x118 316 | addr64_t cpu_uber_isf; // Offset 0x120 317 | uint64_t cpu_uber_tmp; // Offset 0x128 318 | addr64_t cpu_uber_user_gs_base; // Offset 0x130 319 | } cpu_data_fake_t; 320 | 321 | // With KPTI support as backported to OS X 10.11 and 10.12 322 | typedef struct cpu_data_fake_kpti_elcapitan_sierra 323 | { 324 | void *cpu_this; // Pointer to myself (offset 0x0) 325 | thread_t cpu_active_thread; // Offset 0x8 326 | thread_t cpu_nthread; // Offset 0x10 327 | volatile int cpu_preemption_level; // Offset 0x18 328 | int cpu_number; // Logical CPU (offset 0x1c) 329 | x86_saved_state_t *cpu_int_state; // Interrupt state (offset 0x20) 330 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 331 | vm_offset_t cpu_active_stack; // Kernel stack base (offset 0x28) 332 | vm_offset_t cpu_kernel_stack; // Kernel stack top (offset 0x30) 333 | vm_offset_t cpu_int_stack_top; // Offset 0x38 334 | uint64_t pad1[24]; 335 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); // Offset 0x100 336 | union { // Offset 0x108 337 | volatile uint32_t cpu_tlb_invalid; 338 | struct { 339 | volatile uint16_t cpu_tlb_invalid_local; 340 | volatile uint16_t cpu_tlb_invalid_global; 341 | }; 342 | }; 343 | volatile task_map_t cpu_task_map; // Offset 0x10c 344 | volatile addr64_t cpu_task_cr3; // Offset 0x110 345 | addr64_t cpu_kernel_cr3; // Offset 0x118 346 | // User-mode (per-task) CR3 with kernel unmapped 347 | volatile addr64_t cpu_user_cr3; // Offset 0x120, cpu_ucr3 348 | boolean_t cpu_pagezero_mapped; // Offset 0x128 349 | addr64_t cpu_uber_isf; // Offset 0x130 350 | uint64_t cpu_uber_tmp; // Offset 0x138 351 | addr64_t cpu_uber_user_gs_base; // Offset 0x140 352 | uint64_t pad2[1]; 353 | addr64_t cpu_excstack; // Offset 0x150, cd_estack 354 | } cpu_data_fake_kpti_elcapitan_sierra_t; 355 | 356 | // With KPTI support as implemented in OS X 10.13.2 and 10.13.3. 357 | // 'cpu_kernel_cr3' and 'cpu_user_cr3' were inadvertently swapped. 358 | typedef struct cpu_data_fake_kpti_highsierra 359 | { 360 | void *cpu_this; // Pointer to myself (offset 0x0) 361 | thread_t cpu_active_thread; // Offset 0x8 362 | thread_t cpu_nthread; // Offset 0x10 363 | volatile int cpu_preemption_level; // Offset 0x18 364 | int cpu_number; // Logical CPU (offset 0x1c) 365 | x86_saved_state_t *cpu_int_state; // Interrupt state (offset 0x20) 366 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 367 | vm_offset_t cpu_active_stack; // Kernel stack base (offset 0x28) 368 | vm_offset_t cpu_kernel_stack; // Kernel stack top (offset 0x30) 369 | vm_offset_t cpu_int_stack_top; // Offset 0x38 370 | uint64_t pad1[24]; 371 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); // Offset 0x100 372 | union { // Offset 0x108 373 | volatile uint32_t cpu_tlb_invalid; 374 | struct { 375 | volatile uint16_t cpu_tlb_invalid_local; 376 | volatile uint16_t cpu_tlb_invalid_global; 377 | }; 378 | }; 379 | volatile task_map_t cpu_task_map; // Offset 0x10c 380 | volatile addr64_t cpu_task_cr3; // Offset 0x110 381 | // User-mode (per-task) CR3 with kernel unmapped 382 | volatile addr64_t cpu_user_cr3; // Offset 0x118, cpu_ucr3 383 | addr64_t cpu_kernel_cr3; // Offset 0x120 384 | boolean_t cpu_pagezero_mapped; // Offset 0x128 385 | addr64_t cpu_uber_isf; // Offset 0x130 386 | uint64_t cpu_uber_tmp; // Offset 0x138 387 | addr64_t cpu_uber_user_gs_base; // Offset 0x140 388 | addr64_t cpu_excstack; // Offset 0x148, cd_estack 389 | } cpu_data_fake_kpti_highsierra_t; 390 | 391 | // With KPTI support as implemented in OS X 10.13.4 through 10.14.4. 392 | // 'cpu_kernel_cr3' and 'cpu_user_cr3' were swapped back. 393 | typedef struct cpu_data_fake_highsierra_mojave 394 | { 395 | void *cpu_this; // Pointer to myself (offset 0x0) 396 | thread_t cpu_active_thread; // Offset 0x8 397 | thread_t cpu_nthread; // Offset 0x10 398 | volatile int cpu_preemption_level; // Offset 0x18 399 | int cpu_number; // Logical CPU (offset 0x1c) 400 | x86_saved_state_t *cpu_int_state; // Interrupt state (offset 0x20) 401 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 402 | vm_offset_t cpu_active_stack; // Kernel stack base (offset 0x28) 403 | vm_offset_t cpu_kernel_stack; // Kernel stack top (offset 0x30) 404 | vm_offset_t cpu_int_stack_top; // Offset 0x38 405 | uint64_t pad1[24]; 406 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); // Offset 0x100 407 | union { // Offset 0x108 408 | volatile uint32_t cpu_tlb_invalid; 409 | struct { 410 | volatile uint16_t cpu_tlb_invalid_local; 411 | volatile uint16_t cpu_tlb_invalid_global; 412 | }; 413 | }; 414 | volatile task_map_t cpu_task_map; // Offset 0x10c 415 | volatile addr64_t cpu_task_cr3; // Offset 0x110 416 | addr64_t cpu_kernel_cr3; // Offset 0x118 417 | // User-mode (per-task) CR3 with kernel unmapped 418 | volatile addr64_t cpu_user_cr3; // Offset 0x120, cpu_ucr3 419 | boolean_t cpu_pagezero_mapped; // Offset 0x128 420 | addr64_t cpu_uber_isf; // Offset 0x130 421 | uint64_t cpu_uber_tmp; // Offset 0x138 422 | addr64_t cpu_uber_user_gs_base; // Offset 0x140 423 | addr64_t cpu_excstack; // Offset 0x148, cd_estack 424 | } cpu_data_fake_highsierra_mojave_t; 425 | 426 | // With KPTI support as implemented in macOS Mojave 10.14.5 and up through 427 | // Catalina 10.15.3. 428 | typedef struct cpu_data_fake_mojave_catalina 429 | { 430 | void *cpu_this; // Pointer to myself (offset 0x0) 431 | thread_t cpu_active_thread; // Offset 0x8 432 | thread_t cpu_nthread; // Offset 0x10 433 | volatile int cpu_preemption_level; // Offset 0x18 434 | int cpu_number; // Logical CPU (offset 0x1c) 435 | x86_saved_state_t *cpu_int_state; // Interrupt state (offset 0x20) 436 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 437 | vm_offset_t cpu_active_stack; // Kernel stack base (offset 0x28) 438 | vm_offset_t cpu_kernel_stack; // Kernel stack top (offset 0x30) 439 | vm_offset_t cpu_int_stack_top; // Offset 0x38 440 | uint64_t pad1[24]; 441 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); // Offset 0x100 442 | union { // Offset 0x108 443 | volatile uint32_t cpu_tlb_invalid; 444 | struct { 445 | volatile uint16_t cpu_tlb_invalid_local; 446 | volatile uint16_t cpu_tlb_invalid_global; 447 | }; 448 | }; 449 | __uint128_t cpu_invpcid_target; // Offset 0x110, cpu_ip_desc 450 | volatile task_map_t cpu_task_map; // Offset 0x120 451 | volatile uint64_t cpu_task_cr3; // Offset 0x128 452 | addr64_t cpu_kernel_cr3; // Offset 0x130 453 | // User-mode (per-task) CR3 with kernel unmapped 454 | volatile addr64_t cpu_user_cr3; // Offset 0x138, cpu_ucr3 455 | // User-mode (per-task) CR3 with kernel mapped in 456 | volatile addr64_t cpu_shadowtask_cr3; // Offset 0x140 457 | boolean_t cpu_pagezero_mapped; // Offset 0x148 458 | addr64_t cpu_uber_isf; // Offset 0x150 459 | uint64_t cpu_uber_tmp; // Offset 0x158 460 | addr64_t cpu_uber_user_gs_base; // Offset 0x160 461 | addr64_t cpu_excstack; // Offset 0x168, cd_estack 462 | } cpu_data_fake_mojave_catalina_t; 463 | 464 | // With KPTI support as implemented on macOS Catalina 10.15.4 and up. 465 | // 'cpu_preemption_level' was moved, so the offset of 'cpu_number' changed. 466 | typedef struct cpu_data_fake_catalina 467 | { 468 | void *cpu_this; // Pointer to myself (offset 0x0) 469 | thread_t cpu_active_thread; // Offset 0x8 470 | thread_t cpu_nthread; // Offset 0x10 471 | int cpu_number; // Logical CPU (offset 0x18) 472 | x86_saved_state_t *cpu_int_state; // Interrupt state (offset 0x20) 473 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 474 | vm_offset_t cpu_active_stack; // Kernel stack base (offset 0x28) 475 | vm_offset_t cpu_kernel_stack; // Kernel stack top (offset 0x30) 476 | vm_offset_t cpu_int_stack_top; // Offset 0x38 477 | uint64_t pad1[24]; 478 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); // Offset 0x100 479 | union { // Offset 0x108 480 | volatile uint32_t cpu_tlb_invalid; 481 | struct { 482 | volatile uint16_t cpu_tlb_invalid_local; 483 | volatile uint16_t cpu_tlb_invalid_global; 484 | }; 485 | }; 486 | __uint128_t cpu_invpcid_target; // Offset 0x110, cpu_ip_desc 487 | volatile task_map_t cpu_task_map; // Offset 0x120 488 | volatile uint64_t cpu_task_cr3; // Offset 0x128 489 | addr64_t cpu_kernel_cr3; // Offset 0x130 490 | // User-mode (per-task) CR3 with kernel unmapped 491 | volatile addr64_t cpu_user_cr3; // Offset 0x138, cpu_ucr3 492 | // User-mode (per-task) CR3 with kernel mapped in 493 | volatile addr64_t cpu_shadowtask_cr3; // Offset 0x140 494 | boolean_t cpu_pagezero_mapped; // Offset 0x148 495 | addr64_t cpu_uber_isf; // Offset 0x150 496 | uint64_t cpu_uber_tmp; // Offset 0x158 497 | addr64_t cpu_uber_user_gs_base; // Offset 0x160 498 | addr64_t cpu_excstack; // Offset 0x168, cd_estack 499 | } cpu_data_fake_catalina_t; 500 | 501 | // With KPTI support as implemented on macOS 11 Big Sur (aka 10.16). 502 | typedef struct cpu_data_fake_bigsur 503 | { 504 | void *cpu_this; // Pointer to myself (offset 0x0) 505 | uint64_t pad1[1]; 506 | thread_t cpu_active_thread; // Offset 0x10 507 | thread_t cpu_nthread; // Offset 0x18 508 | int cpu_number; // Logical CPU (offset 0x20) 509 | x86_saved_state_t *cpu_int_state; // Interrupt state (offset 0x28) 510 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 511 | vm_offset_t cpu_active_stack; // Kernel stack base (offset 0x30) 512 | vm_offset_t cpu_kernel_stack; // Kernel stack top (offset 0x38) 513 | vm_offset_t cpu_int_stack_top; // Offset 0x40 514 | uint64_t pad2[23]; 515 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); // Offset 0x100 516 | union { // Offset 0x108 517 | volatile uint32_t cpu_tlb_invalid; 518 | struct { 519 | volatile uint16_t cpu_tlb_invalid_local; 520 | volatile uint16_t cpu_tlb_invalid_global; 521 | }; 522 | }; 523 | __uint128_t cpu_invpcid_target; // Offset 0x110, cpu_ip_desc 524 | volatile task_map_t cpu_task_map; // Offset 0x120 525 | volatile uint64_t cpu_task_cr3; // Offset 0x128 526 | addr64_t cpu_kernel_cr3; // Offset 0x130 527 | // User-mode (per-task) CR3 with kernel unmapped 528 | volatile addr64_t cpu_user_cr3; // Offset 0x138, cpu_ucr3 529 | // User-mode (per-task) CR3 with kernel mapped in 530 | volatile addr64_t cpu_shadowtask_cr3; // Offset 0x140 531 | boolean_t cpu_pagezero_mapped; // Offset 0x148 532 | addr64_t cpu_uber_isf; // Offset 0x150 533 | uint64_t cpu_uber_tmp; // Offset 0x158 534 | addr64_t cpu_uber_user_gs_base; // Offset 0x160 535 | addr64_t cpu_excstack; // Offset 0x168, cd_estack 536 | } cpu_data_fake_bigsur_t; 537 | 538 | // With KPTI support as implemented on macOS 13 Ventura. 539 | typedef struct cpu_data_fake_ventura 540 | { 541 | void *cpu_this; // Pointer to myself (offset 0x0) 542 | uint64_t pad1[1]; 543 | thread_t cpu_active_thread; // Offset 0x10 544 | thread_t cpu_nthread; // Offset 0x18 545 | int cpu_number; // Logical CPU (offset 0x20) 546 | x86_saved_state_t *cpu_int_state; // Interrupt state (offset 0x28) 547 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 548 | vm_offset_t cpu_active_stack; // Kernel stack base (offset 0x30) 549 | vm_offset_t cpu_kernel_stack; // Kernel stack top (offset 0x38) 550 | vm_offset_t cpu_int_stack_top; // Offset 0x40 551 | uint64_t pad2[15]; 552 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); // Offset 0xc0 553 | union { // Offset 0xc8 554 | volatile uint32_t cpu_tlb_invalid; 555 | struct { 556 | volatile uint16_t cpu_tlb_invalid_local; 557 | volatile uint16_t cpu_tlb_invalid_global; 558 | }; 559 | }; 560 | __uint128_t cpu_invpcid_target; // Offset 0xd0, cpu_ip_desc 561 | volatile task_map_t cpu_task_map; // Offset 0xe0 562 | volatile uint64_t cpu_task_cr3; // Offset 0xe8 563 | addr64_t cpu_kernel_cr3; // Offset 0xf0 564 | // User-mode (per-task) CR3 with kernel unmapped 565 | volatile addr64_t cpu_user_cr3; // Offset 0xf8, cpu_ucr3 566 | // User-mode (per-task) CR3 with kernel mapped in 567 | volatile addr64_t cpu_shadowtask_cr3; // Offset 0x100 568 | boolean_t cpu_pagezero_mapped; // Offset 0x108 569 | addr64_t cpu_uber_isf; // Offset 0x110 570 | uint64_t cpu_uber_tmp; // Offset 0x118 571 | addr64_t cpu_uber_user_gs_base; // Offset 0x120 572 | addr64_t cpu_excstack; // Offset 0x128, cd_estack 573 | } cpu_data_fake_ventura_t; 574 | 575 | #define CPU_DATA_GET_FUNC_BODY(object,member,type) \ 576 | type ret; \ 577 | __asm__ volatile ("mov %%gs:%P1,%0" \ 578 | : "=r" (ret) \ 579 | : "i" (offsetof(object,member))); \ 580 | return ret; 581 | 582 | bool macOS_Catalina_5_or_greater(); 583 | bool macOS_BigSur(); 584 | bool macOS_Monterey(); 585 | bool macOS_Ventura(); 586 | bool macOS_Sonoma(); 587 | bool macOS_Sequoia(); 588 | 589 | static inline int get_cpu_number(void) 590 | { 591 | if (macOS_Sequoia() || macOS_Sonoma() || macOS_Ventura()) { 592 | CPU_DATA_GET_FUNC_BODY(cpu_data_fake_ventura_t,cpu_number,int) 593 | } else if (macOS_BigSur() || macOS_Monterey()) { 594 | CPU_DATA_GET_FUNC_BODY(cpu_data_fake_bigsur_t,cpu_number,int) 595 | } else if (macOS_Catalina_5_or_greater()) { 596 | CPU_DATA_GET_FUNC_BODY(cpu_data_fake_catalina_t,cpu_number,int) 597 | } else { 598 | CPU_DATA_GET_FUNC_BODY(cpu_data_fake_t,cpu_number,int) 599 | } 600 | } 601 | 602 | /* Derived from the xnu kernel's osfmk/i386/cpu_data.h (end) */ 603 | 604 | extern "C" void hc_int1_raw_handler(void); 605 | extern "C" void hc_int2_raw_handler(void); 606 | extern "C" void hc_int3_raw_handler(void); 607 | extern "C" void hc_int4_raw_handler(void); 608 | extern "C" void hc_int5_raw_handler(void); 609 | extern "C" void hc_int6_raw_handler(void); 610 | 611 | extern "C" void stub_handler(void); 612 | 613 | extern "C" Boolean OSCompareAndSwap_fixed(UInt32 oldValue, UInt32 newValue, 614 | volatile UInt32 *address); 615 | extern "C" Boolean OSCompareAndSwap64_fixed(UInt64 oldValue, UInt64 newValue, 616 | volatile UInt64 *address); 617 | extern "C" Boolean OSCompareAndSwapPtr_fixed(void *oldValue, void *newValue, 618 | void * volatile *address); 619 | 620 | #undef OSCompareAndSwap 621 | #define OSCompareAndSwap OSCompareAndSwap_fixed 622 | #undef OSCompareAndSwap64 623 | #define OSCompareAndSwap64 OSCompareAndSwap64_fixed 624 | #undef OSCompareAndSwapPtr 625 | #define OSCompareAndSwapPtr OSCompareAndSwapPtr_fixed 626 | 627 | extern "C" Boolean OSCompareAndSwap128(__uint128_t oldValue, __uint128_t newValue, 628 | volatile __uint128_t *address); 629 | 630 | // From bsd/sys/proc.h (begin) 631 | typedef int (*syscall_filter_cbfunc_t)(proc_t p, int num); 632 | typedef int (*kobject_filter_cbfunc_t)(proc_t p, int msgid, int idx); 633 | struct syscall_filter_callbacks { 634 | int version; 635 | const syscall_filter_cbfunc_t unix_filter_cbfunc; 636 | const syscall_filter_cbfunc_t mach_filter_cbfunc; 637 | const kobject_filter_cbfunc_t kobj_filter_cbfunc; 638 | }; 639 | typedef struct syscall_filter_callbacks *syscall_filter_cbs_t; 640 | // From bsd/sys/proc.h (end) 641 | 642 | // From iokit/IOKit/IOUserClient.h (begin) 643 | typedef uintptr_t io_filter_policy_t; 644 | enum io_filter_type_t { 645 | io_filter_type_external_method = 1, 646 | io_filter_type_external_async_method = 2, 647 | io_filter_type_trap = 3, 648 | }; 649 | typedef IOReturn (*io_filter_resolver_t)(task_t task, void *client, uint32_t type, 650 | io_filter_policy_t *filterp); 651 | typedef IOReturn (*io_filter_applier_t)(void *client, io_filter_policy_t filter, 652 | io_filter_type_t type, uint32_t selector); 653 | typedef void (*io_filter_release_t)(io_filter_policy_t filter); 654 | struct io_filter_callbacks { 655 | const io_filter_resolver_t io_filter_resolver; 656 | const io_filter_applier_t io_filter_applier; 657 | const io_filter_release_t io_filter_release; 658 | }; 659 | // From iokit/IOKit/IOUserClient.h (end) 660 | 661 | typedef struct vm_page *vm_page_t; 662 | extern "C" void vm_page_validate_cs_caller(vm_page_t page); 663 | struct fileglob; 664 | extern "C" int mac_file_check_library_validation_caller(proc_t proc, 665 | struct fileglob *fg, 666 | off_t slice_offset, 667 | user_long_t error_message, 668 | size_t error_message_size); 669 | extern "C" int mac_file_check_mmap_caller(struct ucred *cred, struct fileglob *fg, 670 | int prot, int flags, uint64_t offset, 671 | int *maxprot); 672 | extern "C" int mac_vnode_check_open_ptr_caller(vfs_context_t ctx, struct vnode *vp, 673 | int acc_mode); 674 | extern "C" int mac_vnode_check_ioctl_ptr_caller(vfs_context_t ctx, struct vnode *vp, 675 | u_long cmd); 676 | extern "C" int mac_proc_check_syscall_unix_ptr_caller(proc_t proc, int scnum); 677 | extern "C" int proc_check_syscall_mach_ptr_caller(proc_t proc, int num); 678 | extern "C" int proc_check_migroutine_invoke_ptr_caller(proc_t proc, int msgid, int idx); 679 | extern "C" int io_filter_applier_ptr_caller(void *client, io_filter_policy_t filter, 680 | io_filter_type_t type, uint32_t selector); 681 | extern "C" void user_trap_caller(x86_saved_state_t *state); 682 | 683 | #endif /* #ifndef __ASSEMBLER__ */ 684 | 685 | #endif /* HookCase_h */ 686 | -------------------------------------------------------------------------------- /HookCase/HookCase/HookCase.s: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2020 Steven Michaud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /* This file mostly contains support for HookCase.kext's use of software 25 | * interrupts, including the raw handlers for interrupts HC_INT1 through 26 | * HC_INT6. This is modeled to some extent on code in the xnu kernel's 27 | * osfmk/x86_64/idt64.s, but is much simpler (since that code also supports 28 | * hardware interrupts and syscalls). In both user mode and kernel mode, we 29 | * treat our software interrupts more like syscalls than like interrupts. 30 | * So, for example, we don't change %gs:CPU_PREEMPTION_LEVEL or 31 | * %gs:CPU_INTERRUPT_LEVEL. 32 | * 33 | * There are also miscellaneous methods, callable from C/C++ code, that needed 34 | * to be written in assembler. 35 | */ 36 | 37 | #define ALIGN 4,0x90 38 | #include 39 | #include "HookCase.h" 40 | 41 | /* These definitions are generated from genassym.c. See it for more 42 | * information. 43 | */ 44 | 45 | /* Offsets of x86_saved_state32 fields in x86_saved_state_t */ 46 | #define R32_CS 76 47 | #define R32_SS 88 48 | #define R32_DS 28 49 | #define R32_ES 24 50 | #define R32_FS 20 51 | #define R32_GS 16 52 | #define R32_UESP 84 53 | #define R32_EBP 40 54 | #define R32_EAX 60 55 | #define R32_EBX 48 56 | #define R32_ECX 56 57 | #define R32_EDX 52 58 | #define R32_ESI 36 59 | #define R32_EDI 32 60 | #define R32_TRAPNO 64 61 | #define R32_CPU 66 62 | #define R32_ERR 68 63 | #define R32_EFLAGS 80 64 | #define R32_EIP 72 65 | #define R32_CR2 44 66 | 67 | #define ISS32_SIZE 76 68 | 69 | /* Offsets of x86_saved_state64 fields in x86_saved_state_t */ 70 | #define R64_FS 148 71 | #define R64_GS 144 72 | #define R64_R8 48 73 | #define R64_R9 56 74 | #define R64_R10 40 75 | #define R64_R11 104 76 | #define R64_R12 96 77 | #define R64_R13 88 78 | #define R64_R14 80 79 | #define R64_R15 72 80 | #define R64_RBP 112 81 | #define R64_RAX 136 82 | #define R64_RBX 120 83 | #define R64_RCX 128 84 | #define R64_RDX 32 85 | #define R64_RSI 24 86 | #define R64_RDI 16 87 | #define R64_CS 192 88 | #define R64_SS 216 89 | #define R64_RSP 208 90 | #define R64_TRAPNO 160 91 | #define R64_CPU 162 92 | #define R64_TRAPFN 168 93 | #define R64_ERR 176 94 | #define R64_RFLAGS 200 95 | #define R64_RIP 184 96 | #define R64_CR2 64 97 | 98 | #define ISS64_OFFSET 160 99 | #define ISS64_SIZE 208 100 | 101 | /* Offsets of x86_64_intr_stack_frame fields in x86_64_intr_stack_frame */ 102 | #define ISF64_TRAPNO 0 103 | #define ISF64_CPU 2 104 | #define ISF64_TRAPFN 8 105 | #define ISF64_ERR 16 106 | #define ISF64_RIP 24 107 | #define ISF64_CS 32 108 | #define ISF64_RFLAGS 40 109 | #define ISF64_RSP 48 110 | #define ISF64_SS 56 111 | 112 | #define ISF64_SIZE 64 113 | 114 | #define SS_FLAVOR 0 115 | #define SS_32 14 116 | #define SS_64 15 117 | 118 | /* Offsets of cpu_data_fake_t fields in cpu_data_fake_t */ 119 | #define CPU_ACTIVE_THREAD 8 120 | #define CPU_PREEMPTION_LEVEL 24 121 | #define CPU_NUMBER 28 122 | #define CPU_INT_STATE 32 123 | #define CPU_ACTIVE_STACK 40 124 | #define CPU_KERNEL_STACK 48 125 | #define CPU_INT_STACK_TOP 56 126 | #define CPU_INTERRUPT_LEVEL 64 127 | #define CPU_ACTIVE_CR3 256 128 | #define CPU_TLB_INVALID 264 129 | #define CPU_TLB_INVALID_LOCAL 264 130 | #define CPU_TLB_INVALID_GLOBAL 266 131 | #define CPU_TASK_MAP 268 132 | #define CPU_TASK_CR3 272 133 | #define CPU_KERNEL_CR3 280 134 | #define CPU_TASK_CR3_NOKERNEL 280 135 | #define CPU_KERNEL_CR3_KPTI 288 136 | #define CPU_UBER_ISF 304 137 | #define CPU_UBER_TMP 312 138 | #define CPU_USER_STACK 328 139 | #define CPU_KERNEL_CR3_KPTI_BP 280 140 | #define CPU_TASK_CR3_NOKERNEL_BP 288 141 | #define CPU_USER_STACK_BP 336 142 | 143 | /* On getting an interrupt, the Intel processor first ANDs RSP with 144 | * 0xFFFFFFFFFFFFFFF0, to make it 16-byte aligned. Then it pushes the 145 | * following registers onto the (current) stack (user or kernel): 146 | * 147 | * SS 148 | * RSP 149 | * RFLAGS 150 | * CS 151 | * RIP 152 | * 153 | * Because the processor may have re-aligned RSP, we can't just add members 154 | * to the x86_64_intr_stack_frame structure (past SS) to get at what RSP 155 | * originally pointed to. Instead we should dereference the value of RSP 156 | * from that structure. That's actually the original value -- *not* what 157 | * you'd expect if the processor used "push %rsp" and so forth (which it 158 | * apparently doesn't). 159 | */ 160 | 161 | /* Called first thing on entering a raw interrupt handler. "lea blah(%rip)" 162 | * uses "RIP-relative addressing". Called with the interrupt flag cleared. 163 | * The original value of IF is restored (along with all the other flags in 164 | * the flags register) by calling IRET. 165 | */ 166 | #define SETUP(trapno) \ 167 | pushq $0 /* err */ ;\ 168 | sub $8, %rsp ;\ 169 | push %rax ;\ 170 | lea EXT(user_trampoline)(%rip), %rax ;\ 171 | mov %rax, 8(%rsp) /* trapfn */ ;\ 172 | pop %rax ;\ 173 | pushq $(trapno) /* trapno, cpu, pad */ ;\ 174 | jmp EXT(setup_continues) ; 175 | 176 | Entry(setup_continues) 177 | /* Jump to kernel_trampoline if we have a kernel interrupt, after setting 178 | * up an x86_saved_state_t structure on the stack and saving the 64-bit 179 | * registers. We don't do any of the other fancy state saving and 180 | * restoring that we do with user interrupts. That's not needed, because 181 | * we user kernel interrupts like function calls -- so we don't need to 182 | * restore the "caller's" state in every particular. We also, of course, 183 | * aren't changing privilege levels. 184 | */ 185 | cmpl $(KERNEL64_CS), ISF64_CS(%rsp) 186 | jne 1f 187 | 188 | sub $(ISS64_OFFSET), %rsp 189 | mov %r15, R64_R15(%rsp) 190 | mov %rsp, %r15 191 | movl $(SS_64), SS_FLAVOR(%r15) 192 | mov %rax, R64_RAX(%r15) 193 | mov %rbx, R64_RBX(%r15) 194 | mov %rcx, R64_RCX(%r15) 195 | mov %rdx, R64_RDX(%r15) 196 | mov %rbp, R64_RBP(%r15) 197 | mov %rdi, R64_RDI(%r15) 198 | mov %rsi, R64_RSI(%r15) 199 | mov %r8, R64_R8(%r15) 200 | mov %r9, R64_R9(%r15) 201 | mov %r10, R64_R10(%r15) 202 | mov %r11, R64_R11(%r15) 203 | mov %r12, R64_R12(%r15) 204 | mov %r13, R64_R13(%r15) 205 | mov %r14, R64_R14(%r15) 206 | mov %cr2, %rax /* CR2 is only useful for page faults, */ 207 | mov %rax, R64_CR2(%r15) /* but save it anyway. */ 208 | mov %fs, R64_FS(%r15) /* These segment registers don't need to */ 209 | mov %gs, %rax /* be saved, but for completeness ... */ 210 | mov %eax, R64_GS(%r15) 211 | 212 | lea EXT(kernel_trampoline)(%rip), %rax 213 | mov %rax, R64_TRAPFN(%r15) 214 | mov EXT(g_cpu_number_offset)(%rip), %rax 215 | mov %gs:(%rax), %ax 216 | mov %ax, R64_CPU(%r15) 217 | jmp EXT(kernel_trampoline) 218 | 219 | /* Swap the GS register's current value with the value contained in the 220 | * IA32_KERNEL_GS_BASE MSR (machine-specific register) address. This makes 221 | * %gs: references point to the 'cpu_data' structure (as defined in the xnu 222 | * kernel's osfmk/i386/cpu_data.h). We'll switch back before returning. 223 | * (In user space, GS is normally used for thread-local storage.) 224 | */ 225 | 1: swapgs 226 | 227 | push %rax 228 | mov EXT(g_cpu_number_offset)(%rip), %rax 229 | mov %gs:(%rax), %ax 230 | mov %ax, ISF64_CPU+8(%rsp) 231 | 232 | /* Make room on the stack for the rest of the interrupt stack frame, 233 | * then fill it out with saved registers (64-bit or 32-bit). Also switch 234 | * to the kernel stack. 235 | */ 236 | mov EXT(g_cpu_task_map_offset)(%rip), %rax 237 | cmpl $(TASK_MAP_32BIT), %gs:(%rax) 238 | pop %rax 239 | je 3f 240 | 241 | cmpl $0, EXT(g_kpti_enabled)(%rip) 242 | jz 2f 243 | /* Deal with an interrupt coming in from our dispatcher in the HIB segment, 244 | * with KPTI enabled. 245 | */ 246 | push %rcx 247 | mov EXT(g_cpu_uber_tmp_offset)(%rip), %rcx 248 | mov %rax, %gs:(%rcx) 249 | mov EXT(g_cpu_uber_isf_offset)(%rip), %rcx 250 | mov %gs:(%rcx), %rax 251 | pop %rcx 252 | add $(ISF64_SIZE), %rax 253 | xchg %rsp, %rax 254 | push ISF64_SS(%rax) 255 | push ISF64_RSP(%rax) 256 | push ISF64_RFLAGS(%rax) 257 | push ISF64_CS(%rax) 258 | push ISF64_RIP(%rax) 259 | push ISF64_ERR(%rax) 260 | push ISF64_TRAPFN(%rax) 261 | push ISF64_TRAPNO(%rax) /* trapno, cpu, pad */ 262 | push %rcx 263 | mov EXT(g_cpu_uber_tmp_offset)(%rip), %rcx 264 | mov %gs:(%rcx), %rax 265 | pop %rcx 266 | 267 | /* 64-bit user interrupt */ 268 | 2: sub $(ISS64_OFFSET), %rsp 269 | mov %r15, R64_R15(%rsp) 270 | mov %rsp, %r15 271 | movl $(SS_64), SS_FLAVOR(%r15) 272 | mov %rax, R64_RAX(%r15) 273 | mov EXT(g_cpu_kernel_stack_offset)(%rip), %rax 274 | mov %gs:(%rax), %rsp /* Switch to kernel stack */ 275 | 276 | mov %rbx, R64_RBX(%r15) 277 | mov %rcx, R64_RCX(%r15) 278 | mov %rdx, R64_RDX(%r15) 279 | mov %rbp, R64_RBP(%r15) 280 | mov %rdi, R64_RDI(%r15) 281 | mov %rsi, R64_RSI(%r15) 282 | mov %r8, R64_R8(%r15) 283 | mov %r9, R64_R9(%r15) 284 | mov %r10, R64_R10(%r15) 285 | mov %r11, R64_R11(%r15) 286 | mov %r12, R64_R12(%r15) 287 | mov %r13, R64_R13(%r15) 288 | mov %r14, R64_R14(%r15) 289 | mov %cr2, %rax /* CR2 is only useful for page faults, */ 290 | mov %rax, R64_CR2(%r15) /* but save it anyway. */ 291 | 292 | mov %fs, R64_FS(%r15) /* These segment registers don't need to */ 293 | swapgs /* be saved, but for completeness ... */ 294 | mov %gs, %rax 295 | mov %eax, R64_GS(%r15) 296 | swapgs 297 | 298 | mov R64_TRAPFN(%r15), %rdx /* RDX := trapfn for later */ 299 | jmp 4f 300 | 301 | /* 32-bit user interrupt */ 302 | 3: sub $(ISS64_OFFSET), %rsp 303 | mov %rsp, %r15 304 | movl $(SS_32), SS_FLAVOR(%r15) 305 | mov %eax, R32_EAX(%r15) 306 | mov EXT(g_cpu_kernel_stack_offset)(%rip), %rax 307 | mov %gs:(%rax), %rsp /* Switch to kernel stack */ 308 | 309 | mov %ebx, R32_EBX(%r15) 310 | mov %ecx, R32_ECX(%r15) 311 | mov %edx, R32_EDX(%r15) 312 | mov %ebp, R32_EBP(%r15) 313 | mov %esi, R32_ESI(%r15) 314 | mov %edi, R32_EDI(%r15) 315 | mov %cr2, %rax /* CR2 is only useful for page faults, */ 316 | mov %eax, R32_CR2(%r15) /* but save it anyway. */ 317 | 318 | mov %ds, R32_DS(%r15) 319 | mov %es, R32_ES(%r15) 320 | mov %fs, R32_FS(%r15) 321 | swapgs 322 | mov %gs, %rax 323 | mov %eax, R32_GS(%r15) 324 | swapgs 325 | 326 | /* The offset of the 'isf' element in x86_saved_state_t's 'ss_64' is larger 327 | * than the whole of 'ss_32'. So its contents won't get overwritten by 328 | * what we write to 'ss_32' above, even though 'ss_32' and 'ss_64' are a 329 | * union. 330 | */ 331 | mov R64_RIP(%r15), %eax 332 | mov %eax, R32_EIP(%r15) 333 | mov R64_RFLAGS(%r15), %eax 334 | mov %eax, R32_EFLAGS(%r15) 335 | mov R64_RSP(%r15), %eax 336 | mov %eax, R32_UESP(%r15) 337 | mov R64_SS(%r15), %eax 338 | mov %eax, R32_SS(%r15) 339 | mov R64_CS(%r15), %eax 340 | mov %eax, R32_CS(%r15) 341 | mov R64_TRAPNO(%r15), %ax 342 | mov %ax, R32_TRAPNO(%r15) 343 | mov R64_CPU(%r15), %ax 344 | mov %ax, R32_CPU(%r15) 345 | mov R64_ERR(%r15), %eax 346 | mov %eax, R32_ERR(%r15) 347 | mov R64_TRAPFN(%r15), %rdx /* RDX := trapfn for later */ 348 | 349 | /* Misc additional setup */ 350 | 4: cld 351 | xor %rbp, %rbp 352 | 353 | mov EXT(g_cpu_kernel_cr3_offset)(%rip), %rcx 354 | mov %gs:(%rcx), %rcx 355 | mov %rcx, %gs:CPU_ACTIVE_CR3 356 | /* Set kernel's CR3 if no_shared_cr3 is true */ 357 | mov EXT(g_no_shared_cr3_ptr)(%rip), %rax 358 | cmp $(-1), %rax 359 | je 5f 360 | test %rax, %rax 361 | jz 5f 362 | mov (%rax), %eax 363 | test %eax, %eax 364 | jz 5f 365 | mov %rcx, %cr3 366 | jmp 8f 367 | /* If the kernel and user-space share the same CR3, we need to check if the 368 | * kernel's memory mapping has changed since the kernel was last entered. 369 | */ 370 | 5: mov %gs:CPU_TLB_INVALID, %ecx 371 | test %ecx, %ecx /* Invalid either globally or locally? */ 372 | jz 8f 373 | shr $16, %ecx 374 | test $1, %ecx /* Invalid globally? */ 375 | jz 7f 376 | /* If invalid globally, use invpcid or play games with CR4_PGE */ 377 | cmpl $0, EXT(g_use_invpcid)(%rip) 378 | jz 6f 379 | mov EXT(g_cpu_invpcid_target_offset)(%rip), %rax 380 | mov $(2), %ecx 381 | invpcid %gs:(%rax), %rcx 382 | jmp 8f 383 | 6: movl $0, %gs:CPU_TLB_INVALID 384 | mov %cr4, %rcx 385 | and $(~CR4_PGE), %rcx 386 | mov %rcx, %cr4 387 | or $(CR4_PGE), %rcx 388 | mov %rcx, %cr4 389 | jmp 8f 390 | /* If only invalid locally, just reset CR3 to the same value */ 391 | 7: movb $0, %gs:CPU_TLB_INVALID_LOCAL 392 | mov %cr3, %rcx 393 | mov %rcx, %cr3 394 | 395 | /* Clear EFLAGS.AC if SMAP is present/enabled */ 396 | 8: mov EXT(g_pmap_smap_enabled_ptr)(%rip), %rax 397 | cmp $(-1), %rax 398 | je 9f 399 | test %rax, %rax 400 | jz 9f 401 | mov (%rax), %eax 402 | test %eax, %eax 403 | jz 9f 404 | clac 405 | 406 | 9: /* Set the Task Switch bit in CR0 to keep floating point happy */ 407 | /* We don't seem to need this, and it can cause trouble in hooked 408 | * functions that have floating point parameters. 409 | */ 410 | /* mov %cr0, %rax 411 | or $(CR0_TS), %eax 412 | mov %rax, %cr0 413 | */ 414 | mov EXT(g_iotier_override_offset)(%rip), %rax 415 | mov EXT(g_cpu_active_thread_offset)(%rip), %rcx 416 | add %gs:(%rcx), %rax 417 | movl $(-1), (%rax) 418 | 419 | /* R15 == x86_saved_state_t */ 420 | /* RDX == trapfn */ 421 | jmp *%rdx 422 | 423 | /* R15 == x86_saved_state_t */ 424 | Entry(teardown) 425 | mov EXT(g_iotier_override_offset)(%rip), %rax 426 | mov EXT(g_cpu_active_thread_offset)(%rip), %rcx 427 | add %gs:(%rcx), %rax 428 | movl $(-1), (%rax) 429 | 430 | /* Restore the floating point state */ 431 | /* As best I can tell we don't really need this. */ 432 | /* lea EXT(fp_load)(%rip), %rax 433 | mov (%rax), %rax 434 | mov EXT(g_cpu_active_thread_offset)(%rip), %rcx 435 | mov %gs:(%rcx), %rdi 436 | push %r15 437 | mov %rsp, %r15 438 | and $0xFFFFFFFFFFFFFFF0, %rsp 439 | call *%rax 440 | mov %r15, %rsp 441 | pop %r15 442 | */ 443 | /* Switch back to the user CR3, if appropriate */ 444 | mov EXT(g_cpu_task_cr3_offset)(%rip), %rax 445 | mov %gs:(%rax), %rcx 446 | mov %rcx, %gs:CPU_ACTIVE_CR3 447 | mov EXT(g_no_shared_cr3_ptr)(%rip), %rax 448 | cmp $(-1), %rax 449 | je 1f 450 | test %rax, %rax 451 | jz 1f 452 | mov (%rax), %eax 453 | test %eax, %eax 454 | jz 1f 455 | mov %rcx, %cr3 456 | 457 | 1: cmpl $(SS_32), SS_FLAVOR(%r15) 458 | je 3f 459 | 460 | /* Return from 64-bit user interrupt */ 461 | /* Segment registers don't need restoring */ 462 | mov R64_R14(%r15), %r14 463 | mov R64_R13(%r15), %r13 464 | mov R64_R12(%r15), %r12 465 | mov R64_R11(%r15), %r11 466 | mov R64_R10(%r15), %r10 467 | mov R64_R9(%r15), %r9 468 | mov R64_R8(%r15), %r8 469 | mov R64_RSI(%r15), %rsi 470 | mov R64_RDI(%r15), %rdi 471 | mov R64_RBP(%r15), %rbp 472 | mov R64_RDX(%r15), %rdx 473 | mov R64_RCX(%r15), %rcx 474 | mov R64_RBX(%r15), %rbx 475 | mov R64_RAX(%r15), %rax 476 | 477 | cmpl $0, EXT(g_kpti_enabled)(%rip) 478 | jz 2f 479 | 480 | /* If KPTI is enabled, jump to our handler in the HIB segment. We can't 481 | * iretq directly from here -- that causes kernel panics. I'm not entirely 482 | * sure why, but I think it has something to do with returning from code 483 | * that's no longer accessible from the user-mode CR3 (after we've changed 484 | * to it). 485 | */ 486 | mov EXT(g_cpu_excstack_offset)(%rip), %rax 487 | mov %gs:(%rax), %rsp 488 | mov R64_RAX(%r15), %rax 489 | pushq R64_SS(%r15) 490 | pushq R64_RSP(%r15) 491 | pushq R64_RFLAGS(%r15) 492 | pushq R64_CS(%r15) 493 | pushq R64_RIP(%r15) 494 | mov R64_R15(%r15), %r15 495 | 496 | push %rax 497 | mov EXT(g_cpu_user_cr3_offset)(%rip), %rax 498 | push %rax 499 | mov EXT(g_return_from_kext_addr)(%rip), %rax 500 | jmp *%rax 501 | 502 | 2: /* Switch back to user stack and restore R15 */ 503 | mov R64_R15(%r15), %rsp 504 | xchg %r15, %rsp 505 | 506 | swapgs 507 | 508 | /* Restore RSP as of entry to raw interrupt handlers. IRETQ will restore 509 | * it to its original value, along with RFLAGS, SS, CS and RIP (or the 510 | * 32-bit equivalents in that mode). 511 | */ 512 | add $(ISS64_OFFSET)+8+8+8, %rsp 513 | 514 | iretq 515 | 516 | /* Return from 32-bit user interrupt */ 517 | 3: swapgs 518 | 519 | mov R32_DS(%r15), %ds 520 | mov R32_ES(%r15), %es 521 | mov R32_FS(%r15), %fs 522 | mov R32_GS(%r15), %gs 523 | 524 | mov R32_EIP(%r15), %eax 525 | mov %eax, R64_RIP(%r15) 526 | mov R32_EFLAGS(%r15), %eax 527 | mov %eax, R64_RFLAGS(%r15) 528 | mov R32_CS(%r15), %eax 529 | mov %eax, R64_CS(%r15) 530 | mov R32_UESP(%r15), %eax 531 | mov %eax, R64_RSP(%r15) 532 | mov R32_SS(%r15), %eax 533 | mov %eax, R64_SS(%r15) 534 | 535 | mov R32_EAX(%r15), %eax 536 | mov R32_EBX(%r15), %ebx 537 | mov R32_ECX(%r15), %ecx 538 | mov R32_EDX(%r15), %edx 539 | mov R32_EBP(%r15), %ebp 540 | mov R32_ESI(%r15), %esi 541 | mov R32_EDI(%r15), %edi 542 | 543 | /* Switch back to user stack */ 544 | mov %r15, %rsp 545 | 546 | /* Restore RSP as of entry to raw interrupt handlers. IRETQ will restore 547 | * it to its original value, along with RFLAGS, SS, CS and RIP (or the 548 | * 32-bit equivalents in that mode). 549 | */ 550 | add $(ISS64_OFFSET)+8+8+8, %rsp 551 | 552 | iretq 553 | 554 | /* Calls one of our user interrupt handlers in HookCase.cpp. Called with: 555 | * R15 == x86_saved_state_t 556 | * RSP == kernel stack 557 | */ 558 | Entry(user_trampoline) 559 | mov R64_TRAPNO(%r15), %cx 560 | cmpw $(HC_INT1), %cx 561 | jne 1f 562 | lea EXT(handle_user_hc_int1)(%rip), %rax 563 | jmp 6f 564 | 1: cmpw $(HC_INT2), %cx 565 | jne 2f 566 | lea EXT(handle_user_hc_int2)(%rip), %rax 567 | jmp 6f 568 | 2: cmpw $(HC_INT3), %cx 569 | jne 3f 570 | lea EXT(handle_user_hc_int3)(%rip), %rax 571 | jmp 6f 572 | 3: cmpw $(HC_INT4), %cx 573 | jne 4f 574 | lea EXT(handle_user_hc_int4)(%rip), %rax 575 | jmp 6f 576 | 4: cmpw $(HC_INT5), %cx 577 | jne 5f 578 | lea EXT(handle_user_hc_int5)(%rip), %rax 579 | jmp 6f 580 | 5: cmpw $(HC_INT6), %cx 581 | jne 7f 582 | lea EXT(handle_user_hc_int6)(%rip), %rax 583 | 584 | 6: mov %r15, %rdi 585 | 586 | push %r15 587 | mov %rsp, %r15 /* Apparently the Apple ABI requires */ 588 | and $0xFFFFFFFFFFFFFFF0, %rsp /* a 16-byte aligned stack on calls. */ 589 | sti 590 | call *%rax 591 | cli 592 | mov %r15, %rsp 593 | pop %r15 594 | 595 | 7: jmp EXT(teardown) 596 | 597 | /* Calls one of our kernel interrupt handlers in HookCase.cpp. Called with: 598 | * R15 == x86_saved_state_t 599 | */ 600 | Entry(kernel_trampoline) 601 | mov R64_TRAPNO(%r15), %cx 602 | cmpw $(HC_INT1), %cx 603 | jne 2f 604 | lea EXT(handle_kernel_hc_int1)(%rip), %rax 605 | 606 | 1: mov %r15, %rdi 607 | 608 | cld 609 | 610 | push %r15 611 | mov %rsp, %r15 /* Apparently the Apple ABI requires */ 612 | and $0xFFFFFFFFFFFFFFF0, %rsp /* a 16-byte aligned stack on calls. */ 613 | /* We need to invoke 'cli' after 'call *%rax', as we have always done in 614 | * 'user_trampoline'. Not doing so causes intermittent failures restoring 615 | * registers in 'kernel_teardown'. One case of this was issue #14, where 616 | * R15 was restored incorrectly and caused kernel panics. 617 | */ 618 | sti 619 | call *%rax 620 | cli 621 | mov %r15, %rsp 622 | pop %r15 623 | 624 | 2: jmp EXT(kernel_teardown) 625 | 626 | /* Called with: 627 | * R15 == x86_saved_state_t 628 | */ 629 | Entry(kernel_teardown) 630 | /* IRETQ apparently doesn't restore RSP from the stack when returning from 631 | * intra-privilege-level interrupts (as we're doing here). Instead it just 632 | * seems to copy the original RSP from some other location. So if we want 633 | * to apply changes we may have made to RSP in the x86_64_intr_stack_frame 634 | * structure, we need to do it "by hand". For this we sacrifice R10 and 635 | * R11. The C/C++ ABI doesn't require these registers be preserved across 636 | * function calls, and we don't need them for values returned from our 637 | * hooks. 638 | */ 639 | mov R64_RFLAGS(%r15), %r10 640 | mov R64_RIP(%r15), %r11 641 | 642 | mov R64_R14(%r15), %r14 643 | mov R64_R13(%r15), %r13 644 | mov R64_R12(%r15), %r12 645 | mov R64_R9(%r15), %r9 646 | mov R64_R8(%r15), %r8 647 | mov R64_RSI(%r15), %rsi 648 | mov R64_RDI(%r15), %rdi 649 | mov R64_RBP(%r15), %rbp 650 | mov R64_RDX(%r15), %rdx 651 | mov R64_RCX(%r15), %rcx 652 | mov R64_RBX(%r15), %rbx 653 | mov R64_RAX(%r15), %rax 654 | mov R64_RSP(%r15), %rsp 655 | mov R64_R15(%r15), %r15 656 | 657 | push %r10 658 | popfq 659 | push %r11 660 | 661 | retq 662 | 663 | Entry(hc_int1_raw_handler) 664 | SETUP(HC_INT1) 665 | 666 | Entry(hc_int2_raw_handler) 667 | SETUP(HC_INT2) 668 | 669 | Entry(hc_int3_raw_handler) 670 | SETUP(HC_INT3) 671 | 672 | Entry(hc_int4_raw_handler) 673 | SETUP(HC_INT4) 674 | 675 | Entry(hc_int5_raw_handler) 676 | SETUP(HC_INT5) 677 | 678 | Entry(hc_int6_raw_handler) 679 | SETUP(HC_INT6) 680 | 681 | /* In developer and debug kernels, the OSCompareAndSwap...() all enforce a 682 | * requirement that 'address' be 4-byte aligned. But this is actually only 683 | * needed by Intel hardware in user mode, and it's much more convenient for 684 | * us to be able to ignore it. So we need "fixed" versions of these methods 685 | * that don't (ever) enforce this requirement. 686 | */ 687 | 688 | /* Boolean OSCompareAndSwap(UInt32 oldValue, UInt32 newValue, 689 | * volatile UInt32 *address); 690 | * 691 | * Called with: 692 | * 693 | * EDI == oldValue 694 | * ESI == newValue 695 | * RDX == address 696 | */ 697 | Entry(OSCompareAndSwap_fixed) 698 | push %rbp 699 | mov %rsp, %rbp 700 | 701 | cmp $0, %rdx 702 | jne 1f 703 | 704 | xor %rax, %rax 705 | pop %rbp 706 | retq 707 | 708 | 1: mov %edi, %eax /* EAX == oldValue */ 709 | 710 | lock 711 | cmpxchg %esi, (%rdx) 712 | 713 | setz %al 714 | pop %rbp 715 | retq 716 | 717 | /* Boolean OSCompareAndSwap64(UInt64 oldValue, UInt64 newValue, 718 | * volatile UInt64 *address); 719 | * Boolean OSCompareAndSwapPtr(void *oldValue, void *newValue, 720 | * void * volatile *address); 721 | * 722 | * Called with: 723 | * 724 | * RDI == oldValue 725 | * RSI == newValue 726 | * RDX == address 727 | */ 728 | Entry(OSCompareAndSwap64_fixed) 729 | Entry(OSCompareAndSwapPtr_fixed) 730 | push %rbp 731 | mov %rsp, %rbp 732 | 733 | cmp $0, %rdx 734 | jne 1f 735 | 736 | xor %rax, %rax 737 | pop %rbp 738 | retq 739 | 740 | 1: mov %rdi, %rax /* RAX == oldValue */ 741 | 742 | lock 743 | cmpxchg %rsi, (%rdx) 744 | 745 | setz %al 746 | pop %rbp 747 | retq 748 | 749 | /* Boolean OSCompareAndSwap128(__uint128_t oldValue, __uint128_t newValue, 750 | * volatile __uint128_t *address); 751 | * 752 | * Called with: 753 | * 754 | * RSI:RDI == oldValue (RSI is high qword) 755 | * RCX:RDX == newValue (RCX is high qword) 756 | * R8 == address 757 | * 758 | * 'address' (R8) must be 16-byte aligned. 759 | */ 760 | Entry(OSCompareAndSwap128) 761 | push %rbp 762 | mov %rsp, %rbp 763 | push %rbx 764 | 765 | cmp $0, %r8 766 | jne 1f 767 | 768 | xor %rax, %rax 769 | pop %rbx 770 | pop %rbp 771 | retq 772 | 773 | 1: mov %rdx, %rbx /* RCX:RBX == newValue */ 774 | mov %rsi, %rdx 775 | mov %rdi, %rax /* RDX:RAX == oldValue */ 776 | 777 | lock 778 | cmpxchg16b (%r8) 779 | 780 | setz %al 781 | pop %rbx 782 | pop %rbp 783 | retq 784 | 785 | /* If KPTI is enabled, 'stub_handler' becomes the target of the "stub 786 | * dispatcher" we install in the HIB segment. See install_stub_dispatcher() 787 | * in HookCase.cpp. 788 | */ 789 | Entry(stub_handler) 790 | pop %rax 791 | cmpq $(HC_INT1), %rax 792 | jne 1f 793 | pop %rax 794 | jmp EXT(hc_int1_raw_handler) 795 | 1: cmpq $(HC_INT2), %rax 796 | jne 2f 797 | pop %rax 798 | jmp EXT(hc_int2_raw_handler) 799 | 2: cmpq $(HC_INT3), %rax 800 | jne 3f 801 | pop %rax 802 | jmp EXT(hc_int3_raw_handler) 803 | 3: cmpq $(HC_INT4), %rax 804 | jne 4f 805 | pop %rax 806 | jmp EXT(hc_int4_raw_handler) 807 | 4: cmpq $(HC_INT5), %rax 808 | jne 5f 809 | pop %rax 810 | jmp EXT(hc_int5_raw_handler) 811 | 5: cmpq $(HC_INT6), %rax 812 | jne 6f 813 | pop %rax 814 | jmp EXT(hc_int6_raw_handler) 815 | 6: pop %rax 816 | iretq 817 | 818 | /* CALLER is for kernel methods we've breakpointed which have a standard C/C++ 819 | * prologue. We can use it to skip past the breakpoint and call the 820 | * "original" method, without having to unset and reset the breakpoint. 821 | * A caller must, like the method it calls, have code equivalent to a C/C++ 822 | * prologue. And it should be at its beginning, otherwise debugging and crash 823 | * logging code can get confused. 824 | */ 825 | 826 | #define CALLER(func) \ 827 | Entry(func ## _caller) ;\ 828 | push %rbp ;\ 829 | mov %rsp, %rbp ;\ 830 | lea EXT(func)(%rip), %r10 ;\ 831 | mov (%r10), %r10 ;\ 832 | add $4, %r10 ;\ 833 | jmp *%r10 ; 834 | 835 | CALLER(vm_page_validate_cs) 836 | 837 | CALLER(mac_file_check_library_validation) 838 | 839 | CALLER(mac_file_check_mmap) 840 | 841 | CALLER(mac_vnode_check_open_ptr) 842 | 843 | CALLER(mac_vnode_check_ioctl_ptr) 844 | 845 | CALLER(mac_proc_check_syscall_unix_ptr) 846 | 847 | CALLER(proc_check_syscall_mach_ptr) 848 | 849 | CALLER(proc_check_migroutine_invoke_ptr) 850 | 851 | CALLER(io_filter_applier_ptr) 852 | 853 | CALLER(user_trap) 854 | 855 | -------------------------------------------------------------------------------- /HookCase/HookCase/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | KEXT 17 | CFBundleShortVersionString 18 | 9.0.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 9.0.2 23 | NSHumanReadableCopyright 24 | Copyright © 2023 Steven Michaud. All rights reserved. 25 | OSBundleLibraries 26 | 27 | com.apple.kpi.bsd 28 | 8.0b1 29 | com.apple.kpi.iokit 30 | 7.0 31 | com.apple.kpi.libkern 32 | 8.0d0 33 | com.apple.kpi.mach 34 | 8.0.0d0 35 | com.apple.kpi.dsep 36 | 8.0.0b1 37 | com.apple.kpi.unsupported 38 | 8.0.0b1 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /HookLibraryTemplate/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC=/usr/bin/clang++ 24 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 25 | # 32-bit builds no longer work on Mojave with some versions of XCode 26 | ifeq ($(MOJAVE_OR_ABOVE), 1) 27 | ARCHS=-arch x86_64 28 | else 29 | ARCHS=-arch i386 -arch x86_64 30 | endif 31 | 32 | hook.dylib : hook.o 33 | $(CC) $(ARCHS) -o hook.dylib hook.o \ 34 | -lobjc -framework Cocoa -framework Carbon \ 35 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 36 | -dynamiclib 37 | 38 | hook.o : hook.mm 39 | $(CC) $(ARCHS) -o hook.o \ 40 | -Wno-deprecated-declarations -c hook.mm 41 | 42 | clean : 43 | rm hook.o hook.dylib 44 | -------------------------------------------------------------------------------- /HookLibraryTemplate/Makefile.alt: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2019 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | # Alternate Makefile that allows us to use low-level llvm tools to build 24 | # step-by-step. One of the steps generates LLVM intermediate language 25 | # files (ending in *.ii), which support calling conventions that aren't 26 | # supported in C/C++ files, but which sometimes get used by clang for internal 27 | # (non-public) functions -- for example fastcc or coldcc. 28 | # 29 | # http://llvm.org/docs/LangRef.html#calling-conventions 30 | # https://clang.llvm.org/docs/AttributeReference.html#calling-conventions 31 | # 32 | # To make a C/C++ function in your hook library use the fastcc calling 33 | # convention, label it with __attribute__((fastcall)) in C/C++ code and use 34 | # this makefile to build the hook library. Under the hook-i386.ii target we 35 | # use sed to replace all instances of "x86_fastcallcc" (the internal name of 36 | # the fastcall calling convention) with "fastcc". 37 | # 38 | # Internal calling conventions are deliberately non-standardized. So it's 39 | # important, when building code that hooks methods which use them, to use 40 | # tools that are as compatible as possible with the code that you're hooking. 41 | # I've had good luck up through Sierra with the LLVM 3.9.0 Clang download: 42 | # http://releases.llvm.org/3.9.0/clang+llvm-3.9.0-x86_64-apple-darwin.tar.xz. 43 | # HighSierra seems to require the LLVM 4.0.0 Clang download: 44 | # http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-apple-darwin.tar.xz. 45 | 46 | MOJAVE_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 18) 47 | # 32-bit builds no longer work on Mojave with some versions of XCode 48 | ifeq ($(MOJAVE_OR_ABOVE), 1) 49 | ARCHS=-arch x86_64 50 | else 51 | ARCHS=-arch i386 -arch x86_64 52 | endif 53 | 54 | HIGHSIERRA_OR_ABOVE=$(shell expr `uname -r | cut -c 1-2` \>= 17) 55 | ifeq ($(HIGHSIERRA_OR_ABOVE), 1) 56 | LLVM_HOME=/usr/local/clang+llvm-4.0.0-x86_64-apple-darwin 57 | else 58 | LLVM_HOME=/usr/local/clang+llvm-3.9.0-x86_64-apple-darwin 59 | endif 60 | CLANG=$(LLVM_HOME)/bin/clang++ 61 | LLAS=$(LLVM_HOME)/bin/llvm-as 62 | LLC=$(LLVM_HOME)/bin/llc 63 | LIPO=lipo 64 | SED=sed 65 | MV=mv 66 | 67 | hook.dylib : hook.o 68 | ifeq ($(MOJAVE_OR_ABOVE), 1) 69 | $(CLANG) $(ARCHS) -o hook.dylib hook.o \ 70 | -lobjc -framework Cocoa -framework Carbon \ 71 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 72 | -dynamiclib 73 | else 74 | $(CLANG) $(ARCHS) -o hook.dylib hook.o \ 75 | -Wl,-read_only_relocs,suppress \ 76 | -lobjc -framework Cocoa -framework Carbon \ 77 | -Wl,-F/System/Library/PrivateFrameworks -framework CoreSymbolication \ 78 | -dynamiclib 79 | endif 80 | 81 | ifeq ($(MOJAVE_OR_ABOVE), 1) 82 | hook.o : hook-x86_64.o 83 | $(MV) hook-x86_64.o hook.o 84 | else 85 | hook.o : hook-i386.o hook-x86_64.o 86 | $(LIPO) -create -arch i386 hook-i386.o -arch x86_64 hook-x86_64.o \ 87 | -output hook.o 88 | endif 89 | 90 | hook-x86_64.o : hook-x86_64.s 91 | $(CLANG) -arch x86_64 -o hook-x86_64.o -c hook-x86_64.s 92 | 93 | ifneq ($(MOJAVE_OR_ABOVE), 1) 94 | hook-i386.o : hook-i386.s 95 | $(CLANG) -arch i386 -o hook-i386.o -c hook-i386.s 96 | endif 97 | 98 | hook-x86_64.s : hook-x86_64.bc 99 | $(LLC) hook-x86_64.bc -o hook-x86_64.s 100 | 101 | ifneq ($(MOJAVE_OR_ABOVE), 1) 102 | hook-i386.s : hook-i386.bc 103 | $(LLC) hook-i386.bc -o hook-i386.s 104 | endif 105 | 106 | hook-x86_64.bc : hook-x86_64.ii 107 | $(LLAS) -o=hook-x86_64.bc hook-x86_64.ii 108 | 109 | ifneq ($(MOJAVE_OR_ABOVE), 1) 110 | hook-i386.bc : hook-i386.ii 111 | $(LLAS) -o=hook-i386.bc hook-i386.ii 112 | endif 113 | 114 | hook-x86_64.ii : hook.mm 115 | $(CLANG) -arch x86_64 -S -emit-llvm -o hook-x86_64.ii \ 116 | -Wno-deprecated-declarations -c hook.mm 117 | 118 | ifneq ($(MOJAVE_OR_ABOVE), 1) 119 | hook-i386.ii : hook.mm 120 | $(CLANG) -arch i386 -S -emit-llvm -o hook-i386.iii \ 121 | -Wno-deprecated-declarations -c hook.mm 122 | $(SED) -e 's/ x86_fastcallcc / fastcc /' hook-i386.iii > hook-i386.ii 123 | endif 124 | 125 | clean : 126 | ifeq ($(MOJAVE_OR_ABOVE), 1) 127 | rm hook.o hook.dylib hook-x86_64* 128 | else 129 | rm hook.o hook.dylib hook-i386* hook-x86_64* 130 | endif 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HookCase 2 | 3 | HookCase is a tool for debugging and reverse engineering applications 4 | on macOS (aka OS X), and the operating system itself. It re-implements 5 | and extends 6 | [Apple's `DYLD_INSERT_LIBRARIES` functionality](https://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73&lpg=PA73&dq="dyld+interposing"+Singh.). 7 | It can be used to hook any method in any module (even non-exported 8 | ones, and even those that don't have an entry in their own module's 9 | symbol table). In a single operation, it can be applied to a parent 10 | process and all its child processes, whether or not the child 11 | processes inherit their parent's environment. It supports 12 | watchpoints. So HookCase is considerably more powerful than 13 | `DYLD_INSERT_LIBRARIES`. It also doesn't have the restrictions Apple 14 | has placed on `DYLD_INSERT_LIBRARIES`. So, for example, HookCase can 15 | be used with applications that have 16 | [entitlements](https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AboutEntitlements.html). 17 | HookCase runs on OS X 10.9 (Mavericks) through macOS 15 (Sequoia). 18 | 19 | Steven Michaud, 10/2024 20 | 21 | ## Table of Contents 22 | 23 | * [What's New](0-whats-new.md) 24 | 25 | * [More About HookCase](1-more-about.md) 26 | 27 | * [Building](2-building.md) 28 | 29 | * [Installing](3-installing.md) 30 | 31 | * [Using](4-using.md) 32 | 33 | * [Resources](5-resources.md) 34 | 35 | * [Example Hook Libraries](6-examples.md) 36 | -------------------------------------------------------------------------------- /call_orig/call_orig.s: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2018 Steven Michaud 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | * SOFTWARE. 22 | */ 23 | 24 | /* Template to test and generate code for user-mode "callers" that skip over the 25 | * breakpoint at the beginning of a method when calling it. This allows a patch 26 | * hook to call the "original" method without having to unset and reset the 27 | * breakpoint. 28 | * 29 | * To accomplish this we need 1) to have a way to skip over the breakpoint and 30 | * 2) to use RIP/EIP relative addressing to make the caller expect to find a 31 | * pointer to the method's address at a given offset from the caller's own 32 | * address. 33 | * 34 | * 1) Skipping over the breakpoint 35 | * 36 | * For it to be possible to skip over the breakpoint at the beginning of a 37 | * method, at least the first two bytes of that method must have a predictable 38 | * content (two bytes is the length of our "int 0x2N" breakpoints). For now, 39 | * at least, we only consider methods with standard C/C++ prologues to meet 40 | * this criterion. 41 | * 42 | * Standard C/C++ Prologues 43 | * 44 | * 64-bit 32-bit 45 | * 46 | * push %rbp push %ebp 47 | * mov %rsp, %rbp mov %esp, %ebp 48 | * 49 | * {0x55, 0x48, 0x89, 0xe5} {0x55, 0x89, 0xe5} 50 | * 51 | * Given that the first N bytes of a breakpointed method are known, we can 52 | * have the caller "run" them itself, then jump to an address N bytes after 53 | * the beginning of the breakpointed method. Since the caller's code must 54 | * contain the equivalent of a C/C++ prologue, it should be at the caller's 55 | * beginning -- otherwise debuggers and crash loggers may get confused. 56 | * 57 | * Caller Methods 58 | * 59 | * 64-bit 32-bit 60 | * 61 | * push %rbp push %ebp 62 | * mov %rsp, %rbp mov %esp, %ebp 63 | * lea _orig_addr(%rip), %r10 call L_call_orig_1 64 | * mov (%r10), %r10 L_call_orig_1: 65 | * add $4, %r10 pop %eax 66 | * jmp *%r10 lea _orig_addr-L_call_orig_1(%eax), %eax 67 | * mov (%eax), %eax 68 | * add $3, %eax 69 | * jmp *%eax 70 | * 71 | * 2) Using RIP/EIP Relative Addressing to Access "Data" 72 | * 73 | * The two caller methods above use RIP/EIP relative addressing to access 74 | * 'orig_addr'. To guarantee that the machine code for these methods expects 75 | * to find 'orig_addr' at a predictable address, we embed their source code 76 | * in this template and assemble/compile it. The way we align them guarantees 77 | * that the code in the caller will expect to find 'orig_addr' at PAGE_SIZE 78 | * bytes from its own beginning. See get_call_orig_func() in HookCase.cpp for 79 | * more information. 80 | */ 81 | 82 | /* Compile this with the following parameters: 83 | * gcc -arch x86_64 -arch i386 call_orig.s -o call_orig 84 | * 85 | * Then load the 'call_orig' binary into a disassembler and copy the 86 | * 'call_orig' method's machine code into the appropriate locations in 87 | * HookCase.cpp -- g_call_orig_func_64bit and g_call_orig_func_32bit. 88 | */ 89 | 90 | /* void stuff(char *) */ 91 | _stuff: 92 | #ifdef __x86_64__ 93 | push %rbp 94 | mov %rsp, %rbp 95 | 96 | call _puts 97 | 98 | pop %rbp 99 | ret 100 | #elif __i386__ 101 | push %ebp 102 | mov %esp, %ebp 103 | sub $8, %esp 104 | 105 | mov 8(%ebp), %eax 106 | mov %eax, (%esp) 107 | call _puts 108 | 109 | add $8, %esp 110 | pop %ebp 111 | ret 112 | #endif 113 | 114 | _call_stuff: 115 | #ifdef __x86_64__ 116 | push %rbp 117 | mov %rsp, %rbp 118 | 119 | lea _stuff_addr(%rip), %r10 120 | mov (%r10), %r10 121 | add $4, %r10 122 | jmp *%r10 123 | #elif __i386__ 124 | push %ebp 125 | mov %esp, %ebp 126 | 127 | call L_call_stuff_1 128 | L_call_stuff_1: 129 | pop %eax 130 | lea _stuff_addr-L_call_stuff_1(%eax), %eax 131 | mov (%eax), %eax 132 | add $3, %eax 133 | jmp *%eax 134 | #endif 135 | 136 | .globl _main 137 | 138 | _main: 139 | #ifdef __x86_64__ 140 | push %rbp 141 | mov %rsp, %rbp 142 | 143 | lea _stuff(%rip), %rax 144 | mov %rax, _stuff_addr(%rip) 145 | 146 | lea _msg(%rip), %rdi 147 | call _call_stuff 148 | 149 | xor %rax, %rax 150 | pop %rbp 151 | ret 152 | #elif __i386__ 153 | push %ebp 154 | mov %esp, %ebp 155 | sub $8, %esp 156 | 157 | call L_main_1 158 | L_main_1: 159 | pop %ecx 160 | 161 | lea _stuff-L_main_1(%ecx), %eax 162 | mov %eax, _stuff_addr-L_main_1(%ecx) 163 | 164 | lea _msg-L_main_1(%ecx), %eax 165 | mov %eax, (%esp) 166 | call _call_stuff 167 | 168 | xor %eax, %eax 169 | add $8, %esp 170 | pop %ebp 171 | ret 172 | #endif 173 | 174 | /* Page-align 'call_orig' */ 175 | .align 12 176 | 177 | _call_orig: 178 | #ifdef __x86_64__ 179 | push %rbp 180 | mov %rsp, %rbp 181 | 182 | lea _orig_addr(%rip), %r10 183 | mov (%r10), %r10 184 | add $4, %r10 185 | jmp *%r10 186 | #elif __i386__ 187 | push %ebp 188 | mov %esp, %ebp 189 | 190 | call L_call_orig_1 191 | L_call_orig_1: 192 | pop %eax 193 | lea _orig_addr-L_call_orig_1(%eax), %eax 194 | mov (%eax), %eax 195 | add $3, %eax 196 | jmp *%eax 197 | #endif 198 | 199 | /* Page-align 'orig_addr' -- which places it PAGE_SIZE bytes from the 200 | * beginning of 'call_orig' 201 | */ 202 | .align 12 203 | 204 | _orig_addr: 205 | .quad 0 206 | 207 | .data 208 | 209 | _msg: 210 | .asciz "Hello World!\n" 211 | 212 | _stuff_addr: 213 | .quad 0 214 | 215 | -------------------------------------------------------------------------------- /examples-dynamic-hooking.md: -------------------------------------------------------------------------------- 1 | # Dynamic patch hooks 2 | 3 | As of version 3.3, HookCase now supports dynamically adding patch 4 | hooks for raw function pointers. This is useful in hooks for methods 5 | that use callbacks -- for example CFMachPortCreate() and 6 | CFRunLoopObserverCreate(). 7 | 8 | Hooks for these methods, and their callbacks, are implemented 9 | by the example hook library under 10 | [`Examples/dynamic-hooking`](Examples/dynamic-hooking/). Build the 11 | library, then use it with any complex application (say a web browser) 12 | to see how it works. 13 | 14 | For example: 15 | 16 | HC_INSERT_LIBRARY=/full/path/to/hook.dylib /Applications/Safari.app/Contents/MacOS/Safari 17 | 18 | -------------------------------------------------------------------------------- /examples-events.md: -------------------------------------------------------------------------------- 1 | # System events 2 | 3 | System events are created by the OS and passed to applications with 4 | user interfaces. There are two basic kinds -- "high level" and "low 5 | level". High-level events are 6 | [Apple events](https://developer.apple.com/legacy/library/documentation/AppleScript/Conceptual/AppleEvents/intro_aepg/intro_aepg.html), 7 | which may be used for application scripting. They're delivered to 8 | applications via the Mach messaging system (as described under "AE 9 | Mach API" in the `AE` framework's `AEMach.h`). Low-level events are 10 | everything else -- things like keyboard and mouse events. 11 | Applications pull them from `WindowServer` (a system daemon from the 12 | `CoreGraphics` framework). 13 | 14 | It can be extremely useful to know what kinds of events are delivered 15 | to applications, and how they get there. The hook library in 16 | [`Examples/events`](Examples/events/) provides a comprehensive 17 | overview. To get a more detailed view of particular kinds of events, 18 | you'll want to comment out some hooks and add more logging. 19 | 20 | The [`Examples/events`](Examples/events/) hook library is also a good 21 | example of the additional work needed to successfully hook 22 | non-exported methods in 32-bit mode. These often use a non-standard 23 | ABI to speed things up -- something called `fastcc`. If we hook such 24 | methods, our hooks will crash unless we also use that ABI. The basic 25 | rules are to put the first two integer/pointer parameters into `ECX` 26 | and `EDX`, and to put floating point parameters into the `XMM` 27 | registers. Also, `fastcc` is never used with `varargs` functions. 28 | But it's only for internal use, and is deliberately non-standardized. 29 | Moreover clang chooses on its own which methods to "optimize" using 30 | `fastcc`. 31 | 32 | So working with `fastcc` can be tricky. For every private method you 33 | wish to hook in 32-bit mode, you'll need to use a disassembler (like 34 | [Hopper Disassembler](http://www.hopperapp.com/)) to check whether or 35 | not it uses `fastcc`. One also needs to build hook libraries with tools 36 | that are as compatible as possible with those used to build the OS 37 | itself. I've had good luck with the 38 | [LLVM 3.9.0 Clang download](http://releases.llvm.org/3.9.0/clang+llvm-3.9.0-x86_64-apple-darwin.tar.xz) 39 | on OS X 10.11 and macOS 10.12, and the 40 | [LLVM 4.0.0 Clang download](http://releases.llvm.org/4.0.0/clang+llvm-4.0.0-x86_64-apple-darwin.tar.xz) 41 | on macOS 10.13. 42 | 43 | 32-bit binaries only build on macOS 10.13 (HighSierra) and below. So 44 | you won't need any of these special tools on macOS 10.14 (Mojave) and 45 | above. 46 | 47 | Do the following (with various applications) to see the events hook 48 | library in action. As always, on OS X 10.12 (Sierra) and above you'll 49 | want to run the Console app before starting. Filter on 50 | "events". Alternatively, you can use `serialportlogger` from 51 | [PySerialPortLogger](https://github.com/steven-michaud/PySerialPortLogger) 52 | and 53 | [define VIRTUAL_SERIAL_PORT in the hook library](Examples/events/hook.mm#L298). 54 | Most output should be visible in the Terminal window from which you 55 | run the main application's main process, though. 56 | 57 | HC_INSERT_LIBRARY=/full/path/to/hook.dylib /Applications/Safari.app/Contents/MacOS/Safari 58 | 59 | -------------------------------------------------------------------------------- /examples-kernel-logging.md: -------------------------------------------------------------------------------- 1 | # Apple Bug in Kernel Logging 2 | 3 | ## The Bug 4 | 5 | Apple implemented a new logging subsystem on macOS Sierra (10.12) and 6 | up -- the ["unified logging system"](https://developer.apple.com/documentation/os/logging). 7 | It's controlled by the `/usr/libexec/diagnosticd` daemon, which gets 8 | launched on demand at the behest of log message clients like the 9 | "Console" and "log" apps. While at least one `diagnosticd` client is 10 | active, various other subsystems send log messages to `diagnosticd`, 11 | which it in turn passes to its clients. `diagnosticd` also monitors 12 | `/dev/oslog_stream` for messages from the kernel. 13 | 14 | This new logging subsystem has a bug (or design flaw) in how logging 15 | from kernel extensions is handled: All logging is suppressed from 16 | kernel extensions whose `start()` method fails. 17 | 18 | Note that there's a workaround, which involves installing a serial 19 | port and using `kprintf()` to write to it. For more information see 20 | [HookCase_start()](HookCase/HookCase/HookCase.cpp#L17072). 21 | 22 | The root of the problem is that the messages received by Apple's new 23 | logging subsystem no longer contain any strings. Each message is 24 | "encoded" with partial information, which requires additional 25 | information to decode. This additional information is stored in a 26 | "uuiddb", or uuid database, which only contains entries for 27 | "recognized" executables (applications and kernel extensions). No 28 | executable is "recognized" until it's been successfully loaded at 29 | least once. 30 | 31 | The uuid database is stored in directories and files under 32 | `/var/db/uuidtext/`. Each "recognized executable" has a file in this 33 | database, which (among other things) stores every string that might be 34 | used in a message sent to the logging subsystem by that executable. 35 | The database is organized by the executable's UUID. Apple's linker 36 | (`ld`) generates an executable's UUID 37 | [mostly from a checksum of that executable (search on "OutputFile::computeContentUUID")](https://opensource.apple.com/source/ld64/ld64-274.2/src/ld/OutputFile.cpp.auto.html) 38 | So an executable's UUID changes every time you change its code, but 39 | mostly only then. And so, generally speaking, no executable that 40 | fails to load will have an entry in the "uuid database". 41 | 42 | Loading and starting are separate operations for kernel extensions, 43 | either of which might succeed or fail. But under normal circumstances, 44 | a kext's `start()` method is called immediately after it's been 45 | successfully loaded (in `OSKext::load()`). And if the kext's `start()` 46 | method fails, it immediately gets unloaded again (by a call to 47 | `OSKext::removeKext()` from `OSKext::loadKextWithIdentifier()`). 48 | `diagnosticd` knows that a kext has been loaded when it receives a 49 | "metadata" message to that effect via `/dev/oslog_stream`. It then 50 | queries the newly loaded kext, and if need be creates a new entry for 51 | it in the uuid database. But output only appears at 52 | `/dev/oslog_stream` after a considerable delay. So if a kext's 53 | `start()` method fails, it will already have been unloaded by the time 54 | `diagnosticd` tries to query it to get information for the uuid 55 | database. `diagnosticd` does receive log messages from kexts whose 56 | `start()` method failed. But it can't decode them without information 57 | from the uuid database, and so doesn't pass them along to any of its 58 | clients (the "log" or "Console" apps). 59 | 60 | I can think of several ways to fix this bug. One would be to delay 61 | unloading a kext for some time after its `start()` method fails, so 62 | `diagnosticd` can find it when it tries to decode messages from it. 63 | But that would be cumbersome -- how would the kernel know when the 64 | kext should be unloaded? It'd be better to allow components of the new 65 | logging subsystem to once again send logging messages that contain 66 | full strings, which wouldn't need to be decoded upon receipt by 67 | `diagnosticd`. Their use could be limited to before the `start()` 68 | method has succeeded or failed. Failing that, Apple could provide a 69 | list of canned error messages to be used to indicate various kinds of 70 | failure -- for example "Unsupported version of macOS" or "Unexpected 71 | error". 72 | 73 | ## Using HookCase to Diagnose the Bug 74 | 75 | [This example](Examples/kernel-logging/) contains a "hello world" 76 | kernel extension, [KernelLogging](Examples/kernel-logging/KernelLogging/), 77 | which can be used to test logging. It also contains a hook library to 78 | be loaded into logging apps like Console and (via `HC_ADDKIDS`) into 79 | the `diagnosticd` daemon. 80 | 81 | Build KernelLogging by running `xcodebuild` in 82 | [`Examples/kernel-logging/KernelLogging`](Examples/kernel-logging/KernelLogging/). 83 | Note that you can define `FAIL_START` or not, depending on whether you 84 | want its `start()` method to fail or succeed. Once KernelLogging is 85 | built, change to the `build/Release` subdirectory and copy it to a 86 | directory from which it can be loaded into the kernel: 87 | 88 | ``` 89 | sudo cp -R KernelLogging.kext /usr/local/sbin/ 90 | ``` 91 | 92 | Then build the hook library. You'll first need to configure it to 93 | redirect its output to a virtual serial port like 94 | [PySerialPortLogger](https://github.com/steven-michaud/PySerialPortLogger), 95 | [here](Examples/kernel-logging/hook.mm#L349). Otherwise you won't see 96 | any output from `diagnosticd`. 97 | 98 | To load this example's hook library into both the application you're 99 | testing with (say Console) and the `diagnosticd` daemon, you need to 100 | first restart the daemon and then kill it. Then running Console will 101 | cause it to be restarted yet again with the hook library loaded. 102 | 103 | ``` 104 | % sudo launchctl kickstart -p system/com.apple.diagnosticd 105 | service spawned with pid: [pid] 106 | % sudo kill -9 [pid] 107 | ``` 108 | 109 | Now run the Console app: 110 | 111 | ``` 112 | HC_ADDKIDS=/usr/libexec/diagnosticd HC_INSERT_LIBRARY=/full/path/to/hook.dylib /System/Applications/Utilities/Console.app/Contents/MacOS/Console 113 | ``` 114 | 115 | You can also do all the above "automatically" via a shell script, as 116 | follows. You will be prompted for your `sudo` password. 117 | 118 | ``` 119 | HC_ADDKIDS=/usr/libexec/diagnosticd HC_INSERT_LIBRARY=/full/path/to/hook.dylib ./runtest.sh 120 | ``` 121 | 122 | Before you click "Start Streaming" in the Console window, set the 123 | filter (under "Search", in the upper right) to Process Equals 124 | "kernel". This should reduce the hook library's output to manageable 125 | proportions, and exclude most irrelevant content. 126 | 127 | Now load and unload `KernelLogging.kext`, to see how `diagnosticd` 128 | behaves. Note that the first time you try to load a newly built or 129 | altered `KernelLogging.kext`, you'll be forced to approve it and 130 | restart your computer (on macOS 11 and up). 131 | 132 | ``` 133 | % sudo kmutil load -p /usr/local/sbin/KernelLogging.kext 134 | % sudo kmutil unload -b org.smichaud.KernelLogging 135 | ``` 136 | 137 | Once you're done testing, quit Console and restart `diagnosticd` yet 138 | again by doing the following. This latest instance of `diagnosticd` 139 | will no longer have the hook library loaded into it. 140 | 141 | ``` 142 | % sudo launchctl kickstart -p system/com.apple.diagnosticd 143 | service spawned with pid: [pid] 144 | % sudo kill -9 [pid] 145 | ``` 146 | 147 | -------------------------------------------------------------------------------- /examples-secinit.md: -------------------------------------------------------------------------------- 1 | # secinit subsystem 2 | 3 | The "secinit subsystem" (if we may call it that) has two parts: 4 | 5 | * `/usr/libexec/secinitd` -- a system daemon that (according to its 6 | `man` page) "initializes the runtime security policies for 7 | processes". A more precise and accurate description is that it 8 | serves requests to configure and turn on the 9 | [App Sandbox](https://developer.apple.com/library/mac/documentation/Security/Conceptual/AppSandboxDesignGuide/AboutAppSandbox/AboutAppSandbox.html). 10 | 11 | * `/usr/lib/system/libsystem_secinit.dylib` -- a component of 12 | `/usr/lib/libSystem.dylib` whose code runs automatically as 13 | `libSystem.dylib` is initialized, or in other words as every Mach 14 | binary starts up. 15 | 16 | Part of `libsystem_secinit.dylib`'s initialization code 17 | (`_libsecinit_setup_secinitd_client()`) calls 18 | `xpc_copy_entitlements_for_pid()` to find out if the current process has 19 | any entitlements. If it doesn't, or if the entitlements don't include 20 | one of the following three keys set to "true", then nothing more 21 | happens. 22 | 23 | com.apple.security.app-sandbox 24 | com.apple.security.app-sandbox.optional 25 | com.apple.security.app-protection 26 | 27 | If any of these keys is "true", that means the current process wants 28 | to use the 29 | [App Sandbox](https://developer.apple.com/library/mac/documentation/Security/Conceptual/AppSandboxDesignGuide/AboutAppSandbox/AboutAppSandbox.html). 30 | This is a high level way to configure and enable Apple's sandbox, 31 | using 32 | [entitlements](https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AboutEntitlements.html). 33 | In this case `_libsecinit_setup_secinitd_client()` goes on to call 34 | `xpc_pipe_routine()` to send a message to "com.apple.secinitd" via XPC 35 | (in other words to the `secinitd` system daemon). The message 36 | contains (among other things) the process's entitlements. Normally 37 | `secinitd` will respond, and its response will appear in 38 | `xpc_pipe_routine()`'s 'reply' parameter. Among other things, this 39 | reply will include a blob of Scheme byte code compiled from the 40 | sandbox-specific parts of the current process's entitlements. Then 41 | `_libsecinit_setup_app_sandbox()` will call `__mac_syscall()` with 42 | 'call' set to '0' and 'arg' pointing (among other things) to the blob 43 | of Scheme byte code. This initializes and turns on Apple's sandbox. 44 | (For some reason this doesn't happen on macOS 13 and above. I haven't 45 | yet figured out why.) 46 | 47 | On the server side, `secinitd` runs as the currently logged in user 48 | and sits waiting for requests from "secinitd clients". When a new 49 | client process makes a connection, `secinitd` calls 50 | `xpc_connection_set_event_handler()` to set an event handler for it, 51 | and the handler gets called on the first message -- the one sent via 52 | `xpc_pipe_routine()` from the client. The handler checks if a 53 | "container" has already been created for the client. If so, it sends 54 | back the contents using `xpc_connection_send_message()`. 55 | 56 | Containers are stored in directories under `~/Library/Containers`, 57 | each named for an application that uses the App Sandbox (for example 58 | "com.apple.calculator"). If a directory doesn't exist, `secinitd` 59 | needs to (re)generate it. Among other things this involves a call to 60 | `sandbox_compile_entitlements()`. 61 | 62 | The hook library in [`Examples/secinit`](Examples/secinit/) gets 63 | loaded into whichever application you're testing with, and also into 64 | `secinitd` (via `HC_ADDKIDS`). In this kind of environment it's best 65 | to configure it to redirect its output to a virtual serial port like 66 | [PySerialPortLogger](https://github.com/steven-michaud/PySerialPortLogger), 67 | [here](Examples/secinit/hook.mm#L348). 68 | 69 | Multiple instances of `secinitd` may already be running, each serving 70 | a different purpose. So first you need to identify the one you'll use 71 | in these tests -- the one for the domain of the user you're currently 72 | logged in as. 73 | 74 | The first step is to (re)start `secinitd` for your domain. The command 75 | will return the `pid` of the new instance. Then you'll kill this 76 | instance, so that running Calculator will cause it to be restarted yet 77 | again with the hook library loaded. 78 | 79 | ``` 80 | % launchctl kickstart -p user/${UID}/com.apple.secinitd 81 | service spawned with pid: [pid] 82 | % kill -9 [pid] 83 | ``` 84 | 85 | Now run the Calculator app (or whichever app you're testing with): 86 | 87 | ``` 88 | HC_ADDKIDS=/usr/libexec/secinitd HC_INSERT_LIBRARY=/full/path/to/hook.dylib open /System/Applications/Calculator.app 89 | ``` 90 | 91 | To see `sandbox_compile_entitlements()` in action, first delete the 92 | `secinitd` client's Containers directory. For example: 93 | 94 | ``` 95 | rm -rf ~/Library/Containers/com.apple.calculator 96 | ``` 97 | 98 | It's also possible to do all the above "automatically" via a shell 99 | script, as follows: 100 | 101 | ``` 102 | HC_ADDKIDS=/usr/libexec/secinitd HC_INSERT_LIBRARY=/full/path/to/hook.dylib ./runtest.sh 103 | ``` 104 | 105 | Also test with other applications that may or may not be `secinitd` 106 | clients, to see what happens. Examples of applications that have 107 | entitlements and use the App Sandbox are Calculator and Notes. Firefox 108 | and Google Chrome have entitlements but don't use the App Sandbox -- 109 | instead they use Apple's sandbox at a lower level by calling 110 | `sandbox_init_with_parameters()`. Safari also used to do this, but now 111 | uses the App Sandbox. 112 | 113 | When you're done testing, quit all the applications containing the 114 | hook library and restart `secinitd` for your domain one more time by 115 | doing the following. This latest instance of `secinitd` will no longer 116 | have the hook library loaded into it. 117 | 118 | ``` 119 | % launchctl kickstart -p user/${UID}/com.apple.secinitd 120 | service spawned with pid: [pid] 121 | % kill -9 [pid] 122 | ``` 123 | 124 | -------------------------------------------------------------------------------- /examples-watchpoints.md: -------------------------------------------------------------------------------- 1 | # Watchpoints 2 | 3 | As of version 4.1, HookCase now supports watchpoints. This is useful 4 | for finding out which functions write to a particular location in 5 | memory -- for example the "sideband buffer" that's used to implement 6 | accelerated OpenGL graphics. 7 | 8 | The watchpoint example [`Examples/watchpoints`](Examples/watchpoints/) 9 | contains two sets of hooks. One implements a trivial example of 10 | watchpoint use, for purposes of illustration and testing. The other 11 | shows how watchpoints can be used to reverse engineer the sideband 12 | buffer. The first hook works on any system with any application. The 13 | second (and more interesting) set of hooks only works on systems with 14 | hardware acceleration, and in applications that use OpenGL (for 15 | example Firefox and Chrome, but not Safari). 16 | 17 | Build the example using `make`, then use it with a recent Firefox 18 | Nightly to see how it works. (Firefox and Chrome releases have their 19 | symbols stripped, but Firefox Nightlies don't. So stack traces of 20 | Firefox Nightlies contain much more information.) 21 | 22 | HC_INSERT_LIBRARY=/full/path/to/hook.dylib "/Applications/Firefox Nightly.app/Contents/MacOS/firefox" 23 | 24 | Firefox Nightlies are available for download 25 | [here](https://nightly.mozilla.org/). 26 | -------------------------------------------------------------------------------- /examples-xpcproxy.md: -------------------------------------------------------------------------------- 1 | # xpcproxy trampoline 2 | 3 | `/usr/libexec/xpcproxy` is a "trampoline" that's used by `launchd` to 4 | launch XPC services. `xpcproxy` is spawned from `launchd` (using 5 | `posix_spawnp()`). It uses `xpc_pipe_routine()` to communicate back 6 | to `launchd` to get the information needed to spawn the XPC service -- 7 | notably the path to the XPC service executable and the values that 8 | should be set in its environment. Then `xpcproxy` uses 9 | `posix_spawn()` with a special flag (`POSIX_SPAWN_SETEXEC`) to `exec` 10 | the XPC service over itself (so that the XPC service keeps the same 11 | process id as `xpcproxy`). 12 | 13 | You can see all this in action by building and using the hook library 14 | under [`Examples/xpcproxy`](Examples/xpcproxy/). There's more 15 | information in the hook library's comments. 16 | 17 | Once the hook library is built, you should use it with an application 18 | (like Safari) that uses XPC services. Here's one way to do that, from 19 | a Terminal session. Note that all the logging will go to the Console 20 | app, which on macOS 10.12 (Sierra) and above needs to be running 21 | before you do the following. Use "xpcproxy" to filter the Console 22 | app's output. Alternatively, you can use `serialportlogger` from 23 | [PySerialPortLogger](https://github.com/steven-michaud/PySerialPortLogger) 24 | and 25 | [define VIRTUAL_SERIAL_PORT in the hook library](Examples/xpcproxy/hook.mm#L302). 26 | 27 | HC_INSERT_LIBRARY=/full/path/to/hook.dylib open /Applications/Safari.app 28 | 29 | Since the first two XPC services are launched almost immediately, the 30 | logs for these services can get interleaved, and hard to read. To get 31 | a log that's easier to read, visit some web page a few seconds after 32 | launching Safari -- which will result in another XPC service being 33 | launched. 34 | -------------------------------------------------------------------------------- /genassym/Makefile: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2017 Steven Michaud 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | # Modified from the xnu kernel's osfmk/conf/Makefile.template 24 | 25 | CC=/usr/bin/clang 26 | 27 | assym.s : genassym.o 28 | sed -e '/^[[:space:]]*DEFINITION__define__/!d;{N;s/\n//;}' -e 's/^[[:space:]]*DEFINITION__define__\([^:]*\):.*ascii.*\"[\$$]*\([-0-9]*\).*$\"/#define \1 \2/' genassym.o > assym.s 29 | 30 | genassym.o : genassym.c 31 | $(CC) -S -o genassym.o genassym.c 32 | 33 | clean : 34 | rm genassym.o assym.s 35 | -------------------------------------------------------------------------------- /genassym/genassym.c: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2017 Steven Michaud 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | // Modified from the xnu kernel's osfmk/i386/genassym.c. Used to generate 24 | // defines for structure offsets used in assembly code (like HookCase.s). 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #define THREAD_STATE_NONE 13 33 | 34 | #define x86_SAVED_STATE32 THREAD_STATE_NONE + 1 35 | #define x86_SAVED_STATE64 THREAD_STATE_NONE + 2 36 | 37 | /* 38 | * The format in which thread state is saved by Mach on this machine. This 39 | * state flavor is most efficient for exception RPC's to kernel-loaded 40 | * servers, because copying can be avoided: 41 | */ 42 | struct x86_saved_state32 { 43 | uint32_t gs; 44 | uint32_t fs; 45 | uint32_t es; 46 | uint32_t ds; 47 | uint32_t edi; 48 | uint32_t esi; 49 | uint32_t ebp; 50 | uint32_t cr2; /* kernel esp stored by pusha - we save cr2 here later */ 51 | uint32_t ebx; 52 | uint32_t edx; 53 | uint32_t ecx; 54 | uint32_t eax; 55 | uint16_t trapno; 56 | uint16_t cpu; 57 | uint32_t err; 58 | uint32_t eip; 59 | uint32_t cs; 60 | uint32_t efl; 61 | uint32_t uesp; 62 | uint32_t ss; 63 | }; 64 | typedef struct x86_saved_state32 x86_saved_state32_t; 65 | 66 | #define x86_SAVED_STATE32_COUNT ((mach_msg_type_number_t) \ 67 | (sizeof (x86_saved_state32_t)/sizeof(unsigned int))) 68 | 69 | #pragma pack(4) 70 | /* 71 | * This is the state pushed onto the 64-bit interrupt stack 72 | * on any exception/trap/interrupt. 73 | */ 74 | struct x86_64_intr_stack_frame { 75 | uint16_t trapno; 76 | uint16_t cpu; 77 | uint32_t _pad; 78 | uint64_t trapfn; 79 | uint64_t err; 80 | uint64_t rip; 81 | uint64_t cs; 82 | uint64_t rflags; 83 | uint64_t rsp; 84 | uint64_t ss; 85 | }; 86 | typedef struct x86_64_intr_stack_frame x86_64_intr_stack_frame_t; 87 | /* Note: sizeof(x86_64_intr_stack_frame_t) must be a multiple of 16 bytes */ 88 | 89 | /* 90 | * thread state format for task running in 64bit long mode 91 | * in long mode, the same hardware frame is always pushed regardless 92 | * of whether there was a change in privlege level... therefore, there 93 | * is no need for an x86_saved_state64_from_kernel variant 94 | */ 95 | struct x86_saved_state64 { 96 | uint64_t rdi; /* arg0 for system call */ 97 | uint64_t rsi; 98 | uint64_t rdx; 99 | uint64_t r10; /* R10 := RCX prior to syscall trap */ 100 | uint64_t r8; 101 | uint64_t r9; /* arg5 for system call */ 102 | 103 | uint64_t cr2; 104 | uint64_t r15; 105 | uint64_t r14; 106 | uint64_t r13; 107 | uint64_t r12; 108 | uint64_t r11; 109 | uint64_t rbp; 110 | uint64_t rbx; 111 | uint64_t rcx; 112 | uint64_t rax; 113 | 114 | uint32_t gs; 115 | uint32_t fs; 116 | 117 | uint64_t _pad; 118 | 119 | struct x86_64_intr_stack_frame isf; 120 | }; 121 | typedef struct x86_saved_state64 x86_saved_state64_t; 122 | #define x86_SAVED_STATE64_COUNT ((mach_msg_type_number_t) \ 123 | (sizeof (struct x86_saved_state64)/sizeof(unsigned int))) 124 | 125 | /* 126 | * Unified, tagged saved state: 127 | */ 128 | typedef struct { 129 | uint32_t flavor; /* x86_SAVED_STATE64 or x86_SAVED_STATE32 */ 130 | uint32_t _pad_for_16byte_alignment[3]; 131 | union { 132 | x86_saved_state32_t ss_32; 133 | x86_saved_state64_t ss_64; 134 | } uss; 135 | } x86_saved_state_t; 136 | #define ss_32 uss.ss_32 137 | #define ss_64 uss.ss_64 138 | #pragma pack() 139 | 140 | 141 | typedef enum { 142 | TASK_MAP_32BIT, /* 32-bit user, compatibility mode */ 143 | TASK_MAP_64BIT, /* 64-bit user thread, shared space */ 144 | } task_map_t; 145 | 146 | typedef struct cpu_data_fake 147 | { 148 | uint32_t pad1[6]; 149 | volatile int cpu_preemption_level; 150 | int cpu_number; /* Logical CPU */ 151 | x86_saved_state_t *cpu_int_state; /* interrupt state */ 152 | /* A stack's "top" is where it grows or shrinks with each push or pop */ 153 | vm_offset_t cpu_active_stack; /* kernel stack base */ 154 | vm_offset_t cpu_kernel_stack; /* kernel stack top */ 155 | vm_offset_t cpu_int_stack_top; 156 | int cpu_interrupt_level; 157 | uint32_t pad2[47]; 158 | volatile addr64_t cpu_active_cr3 __attribute((aligned(64))); 159 | union { 160 | volatile uint32_t cpu_tlb_invalid; 161 | struct { 162 | volatile uint16_t cpu_tlb_invalid_local; 163 | volatile uint16_t cpu_tlb_invalid_global; 164 | }; 165 | }; 166 | volatile task_map_t cpu_task_map; 167 | volatile addr64_t cpu_task_cr3; 168 | union { 169 | struct { 170 | addr64_t cpu_kernel_cr3; 171 | }; 172 | struct { 173 | volatile addr64_t cpu_task_cr3_nokernel; 174 | addr64_t cpu_kernel_cr3_kpti; 175 | boolean_t cpu_pagezero_mapped; 176 | addr64_t cpu_uber_isf; 177 | uint64_t cpu_uber_tmp; 178 | addr64_t cpu_uber_user_gs_base; 179 | addr64_t cpu_user_stack; 180 | }; 181 | struct { 182 | addr64_t cpu_kernel_cr3_kpti_bp; 183 | volatile addr64_t cpu_task_cr3_nokernel_bp; 184 | uint64_t pad3[5]; 185 | addr64_t cpu_user_stack_bp; 186 | }; 187 | }; 188 | } cpu_data_fake_t; 189 | 190 | 191 | #define DECLARE(SYM,VAL) \ 192 | __asm("DEFINITION__define__" SYM ":\t .ascii \"%0\"" : : "n" ((u_int)(VAL))) 193 | 194 | int main( 195 | int argc, 196 | char ** argv); 197 | 198 | int 199 | main( 200 | int argc, 201 | char **argv) 202 | { 203 | 204 | #define R_(x) offsetof(x86_saved_state_t, ss_32.x) 205 | DECLARE("R32_CS", R_(cs)); 206 | DECLARE("R32_SS", R_(ss)); 207 | DECLARE("R32_DS", R_(ds)); 208 | DECLARE("R32_ES", R_(es)); 209 | DECLARE("R32_FS", R_(fs)); 210 | DECLARE("R32_GS", R_(gs)); 211 | DECLARE("R32_UESP", R_(uesp)); 212 | DECLARE("R32_EBP", R_(ebp)); 213 | DECLARE("R32_EAX", R_(eax)); 214 | DECLARE("R32_EBX", R_(ebx)); 215 | DECLARE("R32_ECX", R_(ecx)); 216 | DECLARE("R32_EDX", R_(edx)); 217 | DECLARE("R32_ESI", R_(esi)); 218 | DECLARE("R32_EDI", R_(edi)); 219 | DECLARE("R32_TRAPNO", R_(trapno)); 220 | DECLARE("R32_CPU", R_(cpu)); 221 | DECLARE("R32_ERR", R_(err)); 222 | DECLARE("R32_EFLAGS", R_(efl)); 223 | DECLARE("R32_EIP", R_(eip)); 224 | DECLARE("R32_CR2", R_(cr2)); 225 | DECLARE("ISS32_SIZE", sizeof (x86_saved_state32_t)); 226 | 227 | #define R64_(x) offsetof(x86_saved_state_t, ss_64.x) 228 | DECLARE("R64_FS", R64_(fs)); 229 | DECLARE("R64_GS", R64_(gs)); 230 | DECLARE("R64_R8", R64_(r8)); 231 | DECLARE("R64_R9", R64_(r9)); 232 | DECLARE("R64_R10", R64_(r10)); 233 | DECLARE("R64_R11", R64_(r11)); 234 | DECLARE("R64_R12", R64_(r12)); 235 | DECLARE("R64_R13", R64_(r13)); 236 | DECLARE("R64_R14", R64_(r14)); 237 | DECLARE("R64_R15", R64_(r15)); 238 | DECLARE("R64_RBP", R64_(rbp)); 239 | DECLARE("R64_RAX", R64_(rax)); 240 | DECLARE("R64_RBX", R64_(rbx)); 241 | DECLARE("R64_RCX", R64_(rcx)); 242 | DECLARE("R64_RDX", R64_(rdx)); 243 | DECLARE("R64_RSI", R64_(rsi)); 244 | DECLARE("R64_RDI", R64_(rdi)); 245 | DECLARE("R64_CS", R64_(isf.cs)); 246 | DECLARE("R64_SS", R64_(isf.ss)); 247 | DECLARE("R64_RSP", R64_(isf.rsp)); 248 | DECLARE("R64_TRAPNO", R64_(isf.trapno)); 249 | DECLARE("R64_CPU", R64_(isf.cpu)); 250 | DECLARE("R64_TRAPFN", R64_(isf.trapfn)); 251 | DECLARE("R64_ERR", R64_(isf.err)); 252 | DECLARE("R64_RFLAGS", R64_(isf.rflags)); 253 | DECLARE("R64_RIP", R64_(isf.rip)); 254 | DECLARE("R64_CR2", R64_(cr2)); 255 | DECLARE("ISS64_OFFSET", R64_(isf)); 256 | DECLARE("ISS64_SIZE", sizeof (x86_saved_state64_t)); 257 | 258 | #define ISF64_(x) offsetof(x86_64_intr_stack_frame_t, x) 259 | DECLARE("ISF64_TRAPNO", ISF64_(trapno)); 260 | DECLARE("ISF64_CPU", ISF64_(cpu)); 261 | DECLARE("ISF64_TRAPFN", ISF64_(trapfn)); 262 | DECLARE("ISF64_ERR", ISF64_(err)); 263 | DECLARE("ISF64_RIP", ISF64_(rip)); 264 | DECLARE("ISF64_CS", ISF64_(cs)); 265 | DECLARE("ISF64_RFLAGS", ISF64_(rflags)); 266 | DECLARE("ISF64_RSP", ISF64_(rsp)); 267 | DECLARE("ISF64_SS", ISF64_(ss)); 268 | DECLARE("ISF64_SIZE", sizeof(x86_64_intr_stack_frame_t)); 269 | 270 | DECLARE("SS_FLAVOR", offsetof(x86_saved_state_t, flavor)); 271 | DECLARE("SS_32", x86_SAVED_STATE32); 272 | DECLARE("SS_64", x86_SAVED_STATE64); 273 | 274 | DECLARE("CPU_PREEMPTION_LEVEL", 275 | offsetof(cpu_data_fake_t, cpu_preemption_level)); 276 | DECLARE("CPU_NUMBER", 277 | offsetof(cpu_data_fake_t, cpu_number)); 278 | DECLARE("CPU_INT_STATE", 279 | offsetof(cpu_data_fake_t, cpu_int_state)); 280 | DECLARE("CPU_ACTIVE_STACK", 281 | offsetof(cpu_data_fake_t, cpu_active_stack)); 282 | DECLARE("CPU_KERNEL_STACK", 283 | offsetof(cpu_data_fake_t, cpu_kernel_stack)); 284 | DECLARE("CPU_INT_STACK_TOP", 285 | offsetof(cpu_data_fake_t, cpu_int_stack_top)); 286 | DECLARE("CPU_INTERRUPT_LEVEL", 287 | offsetof(cpu_data_fake_t, cpu_interrupt_level)); 288 | DECLARE("CPU_ACTIVE_CR3", 289 | offsetof(cpu_data_fake_t, cpu_active_cr3)); 290 | DECLARE("CPU_TLB_INVALID", 291 | offsetof(cpu_data_fake_t, cpu_tlb_invalid)); 292 | DECLARE("CPU_TLB_INVALID_LOCAL", 293 | offsetof(cpu_data_fake_t, cpu_tlb_invalid_local)); 294 | DECLARE("CPU_TLB_INVALID_GLOBAL", 295 | offsetof(cpu_data_fake_t, cpu_tlb_invalid_global)); 296 | DECLARE("CPU_TASK_MAP", 297 | offsetof(cpu_data_fake_t, cpu_task_map)); 298 | DECLARE("CPU_TASK_CR3", 299 | offsetof(cpu_data_fake_t, cpu_task_cr3)); 300 | DECLARE("CPU_KERNEL_CR3", 301 | offsetof(cpu_data_fake_t, cpu_kernel_cr3)); 302 | DECLARE("CPU_TASK_CR3_NOKERNEL", 303 | offsetof(cpu_data_fake_t, cpu_task_cr3_nokernel)); 304 | DECLARE("CPU_KERNEL_CR3_KPTI", 305 | offsetof(cpu_data_fake_t, cpu_kernel_cr3_kpti)); 306 | DECLARE("CPU_UBER_ISF", 307 | offsetof(cpu_data_fake_t, cpu_uber_isf)); 308 | DECLARE("CPU_UBER_TMP", 309 | offsetof(cpu_data_fake_t, cpu_uber_tmp)); 310 | DECLARE("CPU_USER_STACK", 311 | offsetof(cpu_data_fake_t, cpu_user_stack)); 312 | DECLARE("CPU_KERNEL_CR3_KPTI_BP", 313 | offsetof(cpu_data_fake_t, cpu_kernel_cr3_kpti_bp)); 314 | DECLARE("CPU_TASK_CR3_NOKERNEL_BP", 315 | offsetof(cpu_data_fake_t, cpu_task_cr3_nokernel_bp)); 316 | DECLARE("CPU_USER_STACK_BP", 317 | offsetof(cpu_data_fake_t, cpu_user_stack_bp)); 318 | 319 | return (0); 320 | } 321 | --------------------------------------------------------------------------------