Skip to content

Commit

Permalink
Deal with breakage caused by macOS 12.4, fixes issue steven-michaud#34
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-michaud committed May 21, 2022
1 parent 52f3b98 commit 669087c
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 27 deletions.
21 changes: 16 additions & 5 deletions 0-whats-new.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# What's New in Version 6.0.3

macOS 12.4 once again broke HookCase, by making changes that normally
only happen in major releases. This time none of the breakage was
caused by changes to internal kernel structures (though some of those
used by HookCase did change). Instead it was caused by two changes in
behavior. HookCase 6.0.3 works around them.

For more information see
[Issue #34](https://github.com/steven-michaud/HookCase/issues/34).

# What's New in Version 6.0.2

macOS 12.3 once again broke HookCase, by making changes that normally
Expand Down Expand Up @@ -48,14 +59,14 @@ information see

This version of HookCase fixes a bug that caused intermittent
instability, though not kernel panics. I fixed it by tweaking the
[code at the heart of HookCase's watchpoint support](HookCase/HookCase/HookCase.cpp#L12881).
[code at the heart of HookCase's watchpoint support](HookCase/HookCase/HookCase.cpp#L12994).
See [Issue #26](https://github.com/steven-michaud/HookCase/issues/26)
for more information.

HookCase's watchpoint code is quite complex. So if you see any sort of
instability short of kernel panics, especially if it resembles what's
reported at Issue #26, you should try
[disabling watchpoint support](HookCase/HookCase/HookCase.cpp#L13803)
[disabling watchpoint support](HookCase/HookCase/HookCase.cpp#L13916)

# What's New in Version 5.0.3

Expand Down Expand Up @@ -231,7 +242,7 @@ HookCase now supports macOS Mojave (10.14).

But Mojave's Debug kernel is currently very flaky -- lots of panics,
with and without HookCase. So support for the Debug kernel
[has been disabled](HookCase/HookCase/HookCase.cpp#L451), at least
[has been disabled](HookCase/HookCase/HookCase.cpp#L469), at least
temporarily.

# What's New in Version 2.1
Expand Down Expand Up @@ -261,12 +272,12 @@ can now hook methods that aren't in their module's symbol table. For
more information see
[Hooked_sub_123abc() in the hook library template](HookLibraryTemplate/hook.mm#L1125).

* Version 2.0 [fixes a bug](HookCase/HookCase/HookCase.cpp#L10951) that
* Version 2.0 [fixes a bug](HookCase/HookCase/HookCase.cpp#L11065) that
prevented interpose hooks from working outside the shared cache of
system modules.

* Version 2.0
[fixes a previously undiscovered edge case](HookCase/HookCase/HookCase.cpp#L12622)
[fixes a previously undiscovered edge case](HookCase/HookCase/HookCase.cpp#L12735)
of an Apple kernel panic bug that was partially fixed in version 1.

* Version 2.0
Expand Down
2 changes: 1 addition & 1 deletion 1-more-about.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,5 @@ hook that method as a process starts up, perform our own
initialization, then allow the original method to run.

For more information, the best place to start is the
[long series of comments](HookCase/HookCase/HookCase.cpp#L7865)
[long series of comments](HookCase/HookCase/HookCase.cpp#L7979)
in `HookCase.cpp` before the definition of `C_64_REDZONE_LEN`.
4 changes: 2 additions & 2 deletions HookCase/HookCase.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@
MODULE_NAME = org.smichaud.HookCase;
MODULE_START = HookCase_start;
MODULE_STOP = HookCase_stop;
MODULE_VERSION = 6.0.2;
MODULE_VERSION = 6.0.3;
PRODUCT_BUNDLE_IDENTIFIER = org.smichaud.HookCase;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = kext;
Expand All @@ -247,7 +247,7 @@
MODULE_NAME = org.smichaud.HookCase;
MODULE_START = HookCase_start;
MODULE_STOP = HookCase_stop;
MODULE_VERSION = 6.0.2;
MODULE_VERSION = 6.0.3;
PRODUCT_BUNDLE_IDENTIFIER = org.smichaud.HookCase;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = kext;
Expand Down
145 changes: 129 additions & 16 deletions HookCase/HookCase/HookCase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,24 @@ bool macOS_Monterey_3_or_greater()
return ((OSX_Version() & 0xFF) >= 0x40);
}

bool macOS_Monterey_less_than_4()
{
if (!((OSX_Version() & 0xFF00) == MAC_OS_X_VERSION_12_HEX)) {
return false;
}
// The output of "uname -r" for macOS 12.4 is actually "21.5.0".
return ((OSX_Version() & 0xFF) < 0x50);
}

bool macOS_Monterey_4_or_greater()
{
if (!((OSX_Version() & 0xFF00) == MAC_OS_X_VERSION_12_HEX)) {
return false;
}
// The output of "uname -r" for macOS 12.4 is actually "21.5.0".
return ((OSX_Version() & 0xFF) >= 0x50);
}

bool OSX_Version_Unsupported()
{
return (((OSX_Version() & 0xFF00) < MAC_OS_X_VERSION_10_9_HEX) ||
Expand Down Expand Up @@ -3549,6 +3567,45 @@ typedef struct _vm_object_fake_catalina_dev_debug {
__object2_unused_bits:7; /* for expansion */
} *vm_object_fake_catalina_dev_debug_t;

typedef struct _vm_object_fake_monterey_4 {
uint64_t pad1[1];
lck_rw_t Lock;
uint64_t pad2[5];
vm_object_t shadow; // Offset 0x40
uint64_t pad3[1];
union {
vm_object_offset_t vou_shadow_offset; /* Offset into shadow (offset 0x50) */
clock_sec_t vou_cache_ts; /* age of an external object
* present in cache
*/
task_t vou_owner; /* If the object is purgeable
* or has a "ledger_tag", this
* is the task that owns it.
*/
} vo_un2;
uint32_t pad4[19];
/* hold object lock when altering */
unsigned int // Offset 0xa4
wimg_bits:8, /* cache WIMG bits */
code_signed:1, /* pages are signed and should be
validated; the signatures are stored
with the pager */
transposed:1, /* object was transposed with another */
mapping_in_progress:1, /* pager being mapped/unmapped */
phantom_isssd:1,
volatile_empty:1,
volatile_fault:1,
all_reusable:1,
blocked_access:1,
set_cache_attr:1,
object_is_shared_cache:1,
purgeable_queue_type:2,
purgeable_queue_group:3,
io_tracking:1,
no_tag_update:1,
__object2_unused_bits:7; /* for expansion */
} *vm_object_fake_monterey_4_t;

bool object_is_code_signed(vm_object_t object)
{
if (!object) {
Expand Down Expand Up @@ -3589,7 +3646,9 @@ bool object_is_code_signed(vm_object_t object)
(vm_object_fake_highsierra_dev_debug_t) object;
retval = object_local->code_signed;
}
} else if (macOS_Catalina() || macOS_BigSur() || macOS_Monterey()) {
} else if (macOS_Catalina() || macOS_BigSur() ||
macOS_Monterey_less_than_4())
{
if (kernel_type_is_release()) {
vm_object_fake_catalina_t object_local =
(vm_object_fake_catalina_t) object;
Expand All @@ -3601,6 +3660,10 @@ bool object_is_code_signed(vm_object_t object)
(vm_object_fake_catalina_dev_debug_t) object;
retval = object_local->code_signed;
}
} else if (macOS_Monterey_4_or_greater()) {
vm_object_fake_monterey_4_t object_local =
(vm_object_fake_monterey_4_t) object;
retval = object_local->code_signed;
} else {
if (kernel_type_is_release()) {
vm_object_fake_yosemite_t object_local =
Expand Down Expand Up @@ -3656,7 +3719,9 @@ void object_set_code_signed(vm_object_t object, bool flag)
(vm_object_fake_highsierra_dev_debug_t) object;
object_local->code_signed = flag;
}
} else if (macOS_Catalina() || macOS_BigSur() || macOS_Monterey()) {
} else if (macOS_Catalina() || macOS_BigSur() ||
macOS_Monterey_less_than_4())
{
if (kernel_type_is_release()) {
vm_object_fake_catalina_t object_local =
(vm_object_fake_catalina_t) object;
Expand All @@ -3668,6 +3733,10 @@ void object_set_code_signed(vm_object_t object, bool flag)
(vm_object_fake_catalina_dev_debug_t) object;
object_local->code_signed = flag;
}
} else if (macOS_Monterey_4_or_greater()) {
vm_object_fake_monterey_4_t object_local =
(vm_object_fake_monterey_4_t) object;
object_local->code_signed = flag;
} else {
if (kernel_type_is_release()) {
vm_object_fake_yosemite_t object_local =
Expand Down Expand Up @@ -3750,7 +3819,7 @@ vm_object_t object_get_shadow(vm_object_t object)
}
vm_object_t retval = NULL;
if (macOS_Sierra() || macOS_HighSierra() || macOS_Mojave() ||
macOS_Catalina() || macOS_BigSur() || macOS_Monterey())
macOS_Catalina() || macOS_BigSur() || macOS_Monterey_less_than_4())
{
if (kernel_type_is_release()) {
vm_object_fake_sierra_t object_local =
Expand All @@ -3763,6 +3832,10 @@ vm_object_t object_get_shadow(vm_object_t object)
(vm_object_fake_sierra_dev_debug_t) object;
retval = object_local->shadow;
}
} else if (macOS_Monterey_4_or_greater()) {
vm_object_fake_monterey_4_t object_local =
(vm_object_fake_monterey_4_t) object;
retval = object_local->shadow;
} else {
vm_object_fake_yosemite_t object_local =
(vm_object_fake_yosemite_t) object;
Expand All @@ -3777,7 +3850,13 @@ vm_object_offset_t object_get_shadow_offset(vm_object_t object)
return 0;
}
vm_object_offset_t retval = 0;
if (macOS_Catalina() || macOS_BigSur() || macOS_Monterey()) {
if (macOS_Monterey_4_or_greater()) {
vm_object_fake_monterey_4_t object_local =
(vm_object_fake_monterey_4_t) object;
retval = object_local->vo_un2.vou_shadow_offset;
} else if (macOS_Catalina() || macOS_BigSur() ||
macOS_Monterey_less_than_4())
{
if (kernel_type_is_release()) {
vm_object_fake_catalina_t object_local =
(vm_object_fake_catalina_t) object;
Expand Down Expand Up @@ -4368,6 +4447,32 @@ typedef struct thread_fake_monterey_dev_1
vm_map_t map; // Offset 0x6d0
} thread_fake_monterey_dev_1_t;

typedef struct thread_fake_monterey_4
{
uint32_t pad1[24];
integer_t options; // Offset 0x60
uint32_t pad2[15];
// Actually a member of thread_t's 'machine' member.
void *ifps; // Offset 0xa0
uint32_t pad3[231];
int iotier_override; // Offset 0x444
uint32_t pad4[126];
vm_map_t map; // Offset 0x640
} thread_fake_monterey_4_t;

typedef struct thread_fake_monterey_dev_4
{
uint32_t pad1[26];
integer_t options; // Offset 0x68
uint32_t pad2[15];
// Actually a member of thread_t's 'machine' member.
void *ifps; // Offset 0xa8
uint32_t pad3[249];
int iotier_override; // Offset 0x494
uint32_t pad4[140];
vm_map_t map; // Offset 0x6c8
} thread_fake_monterey_dev_4_t;

typedef struct thread_fake_bigsur
{
uint32_t pad1[24];
Expand Down Expand Up @@ -5533,11 +5638,15 @@ bool proc_copyout(vm_map_t proc_map, const void *source,
}
// If we're writing to a "private" region that's codesigned, we should
// first "unsign" it -- otherwise the OS may give us trouble for setting
// write permission on a region that should remain unchanged. We don't
// write permission on a region that should remain unchanged. We don't
// need to worry about this for a shared region, because the region we
// write to will be a private copy of it (generated via COW). On Mojave
// and above we need to do this for all private regions.
if (info.share_mode == SM_PRIVATE) {
// write to will be a private copy of it (generated via COW). On Mojave
// and above we need to do this for all private regions. Starting on
// Monterey 12.4, "private" regions have the share_mode SM_PRIVATE or
// SM_PRIVATE_ALIASED.
if ((info.share_mode == SM_PRIVATE) ||
(info.share_mode == SM_PRIVATE_ALIASED))
{
if (macOS_Mojave() || macOS_Catalina() || macOS_BigSur() ||
macOS_Monterey() || codesigned)
{
Expand Down Expand Up @@ -5615,8 +5724,7 @@ bool proc_copyout(vm_map_t proc_map, const void *source,
}

bool proc_mapout(vm_map_t proc_map, const void *source,
vm_map_offset_t *target, size_t len,
bool src_destroy)
vm_map_offset_t *target, size_t len)
{
if (!proc_map || !source || !target || !len) {
return false;
Expand All @@ -5627,8 +5735,14 @@ bool proc_mapout(vm_map_t proc_map, const void *source,
*target = 0;

vm_map_copy_t copy;
// On Monterey 12.4 and above, setting 'src_destroy' to 'true' triggers
// a kernel panic with an error message about "attempting to remove
// permanent VM map entry". This error presumably also happened on earlier
// versions of macOS, but was non-fatal. We should probably never trigger
// it. So set 'src_destroy' to 'false' here and release 'source' in the
// caller.
kern_return_t rv = vm_map_copyin(kernel_map, (vm_map_address_t) source,
len, src_destroy, &copy);
len, false, &copy);
if (rv != KERN_SUCCESS) {
return false;
}
Expand Down Expand Up @@ -10633,16 +10747,16 @@ bool setup_call_orig_func_block(vm_map_t proc_map, hook_t *cast_hookp)

bool rv = true;
vm_map_offset_t block = 0;
if (proc_mapout(proc_map, page_buffer, &block, 2 * PAGE_SIZE, true)) {
if (proc_mapout(proc_map, page_buffer, &block, 2 * PAGE_SIZE)) {
vm_protect(proc_map, block, PAGE_SIZE, false,
VM_PROT_READ | VM_PROT_EXECUTE);
vm_protect(proc_map, block + PAGE_SIZE, PAGE_SIZE, false,
VM_PROT_READ);
cast_hookp->call_orig_func_block = block;
} else {
IOFreePageable(page_buffer, 2 * PAGE_SIZE);
rv = false;
}
IOFreePageable(page_buffer, 2 * PAGE_SIZE);

return rv;
}
Expand Down Expand Up @@ -11245,13 +11359,12 @@ bool setup_register_for_add_image(hook_t *hookp, x86_saved_state_t *intr_state,
} else { // flavor == x86_SAVED_STATE32
func_buffer[0] = HC_INT2_FUNC_32BIT_LONG;
}
if (proc_mapout(proc_map, func_buffer, &on_add_image, PAGE_SIZE, true)) {
if (proc_mapout(proc_map, func_buffer, &on_add_image, PAGE_SIZE)) {
vm_protect(proc_map, on_add_image, PAGE_SIZE, false,
VM_PROT_READ | VM_PROT_EXECUTE);
hookp->add_image_func_addr = on_add_image;
} else {
IOFreePageable(func_buffer, PAGE_SIZE);
}
IOFreePageable(func_buffer, PAGE_SIZE);
}

if (!dyld_register_func_for_add_image || !on_add_image) {
Expand Down
4 changes: 2 additions & 2 deletions HookCase/HookCase/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleShortVersionString</key>
<string>6.0.2</string>
<string>6.0.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>6.0.2</string>
<string>6.0.3</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2021 Steven Michaud. All rights reserved.</string>
<key>OSBundleLibraries</key>
Expand Down
2 changes: 1 addition & 1 deletion examples-kernel-logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ kernel extensions whose `start()` method fails.

Note that there's a workaround, which involves installing a serial
port and using `kprintf()` to write to it. For more information see
[HookCase_start()](HookCase/HookCase/HookCase.cpp#L13934).
[HookCase_start()](HookCase/HookCase/HookCase.cpp#L14047).

The root of the problem is that the messages received by Apple's new
logging subsystem no longer contain full strings. Instead each
Expand Down

0 comments on commit 669087c

Please sign in to comment.