Skip to content

Instantly share code, notes, and snippets.

@Wowfunhappy
Last active February 20, 2023 14:37
Show Gist options
  • Save Wowfunhappy/8212f5bea4c601ac9a6297789f232321 to your computer and use it in GitHub Desktop.
Save Wowfunhappy/8212f5bea4c601ac9a6297789f232321 to your computer and use it in GitHub Desktop.
KQueueScanContinuePatch
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>wowfunhappy.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.kpi.iokit</key>
<string>10.0.0</string>
<key>com.apple.kpi.libkern</key>
<string>10.0.0</string>
<key>com.apple.kpi.unsupported</key>
<string>10.0.0</string>
</dict>
</dict>
</plist>
//
// KQueueScanContinuePatch.c
// KQueueScanContinuePatch
//
// Created by Wowfunhappy with assistance from krackers.
//
#include <IOKit/IOLib.h>
#include <mach-o/loader.h>
#include <i386/proc_reg.h>
#define LENGTH(arr) sizeof(arr) / sizeof(*arr)
// Copied from github.com/leiless/ksymresolver/blob/master/ksymresolver/ksymresolver.h
#define KERN_TEXT_BASE ((vm_offset_t) 0xffffff8000200000ULL)
#define CR0_WP 0x00010000
static size_t KASLRAlignment = 0x100000;
#if __LP64__
#define NUM_SUPPORTED_KERNELS 5
#else
#define NUM_SUPPORTED_KERNELS 1
#endif
// Taken from Hopper
static vm_offset_t possible_kqueue_scan_continue_panic_start_locations[NUM_SUPPORTED_KERNELS] = {
#if __LP64__
0xffffff800053b590, //10.7.5 11G63, xnu-1699.32.7
0xffffff8000557afe, //10.8.4 12F2560, xnu-2050.48.19
0xffffff80005c7a9c, //10.9.5 13F1911, xnu-2422.115.15
0xffffff8000615928, //10.9.5 Bronya-Ryzen
0xffffff80005bf9bc, //10.9.5 IPCA
#else
0x0055bb7e, //10.7.5 11G63, xnu-1699.32.7 (32 bit)
#endif
};
static vm_offset_t possible_kqueue_scan_continue_panic_end_locations[NUM_SUPPORTED_KERNELS] = {
#if __LP64__
0xffffff800053b5a2, //10.7.5 11G63, xnu-1699.32.7
0xffffff8000557b11, //10.8.4 12F2560, xnu-2050.48.19
0xffffff80005c7ab7, //10.9.5 13F1911, xnu-2422.115.15
0xffffff8000615942, //10.9.5 Bronya-Ryzen
0xffffff80005bf9d7, //10.9.5 IPCA
#else
0x0055bb90, //10.7.5 11G63, xnu-1699.32.7 (32 bit)
#endif
};
static char possible_search_bytes[NUM_SUPPORTED_KERNELS][30] = {
#if __LP64__
//10.7.5 11G63, xnu-1699.32.7
{
0x48, 0x8d, 0x3d, 0x69, 0xf4, 0x19, 0x00, //lea rdi,qword [aKeventscancont]
0x30, 0xc0, //xor al,al
0x89, 0xde, //mov esi,ebx
0xe8, 0xa0, 0x4f, 0xce, 0xff, //call _panic
0x31, 0xdb, 0x89, //xor ebx,ebx
0xda, 0x49, 0x8b, 0xb7, 0xc0, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xf7,
},
//10.8.4 12F2560, xnu-2050.48.19
{
0x48, 0x8d, 0x3d, 0x77, 0xe9, 0x19, 0x00, //lea rdi,qword [aKeventscancont]
0x89, 0xde, //mov esi,ebx
0x30, 0xc0, //xor al,al
0xe8, 0x02, 0x5b, 0xcc, 0xff, //call _panic
0x45, 0x31, 0xed, //xor r13d,r13d
0x49, 0x8b, 0xb6, 0xc0, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xff, 0x44,
},
//10.9.5 13F1911, xnu-2422.115.15
{
0x48, 0x8d, 0x3d, 0xbb, 0xf3, 0x19, 0x00, //lea rdi,qword
0x48, 0x8d, 0x35, 0x11, 0xf4, 0x19, 0x00, //lea rsi,qword
0x44, 0x89, 0xfa, //mov edx,r15d
0x30, 0xc0, //xor al,al
0xe8, 0xbc, 0xb5, 0xc5, 0xff, //call _panic
0x45, 0x31, 0xe4, //xor r12d,r12d
0x49, 0x8b, 0xb5,
},
//10.9.5 Bronya-Ryzen
{
0x48, 0x8d, 0x3d, 0xbb, 0xf3, 0x19, 0x00, //lea rdi,qword
0x48, 0x8d, 0x35, 0x11, 0xf4, 0x19, 0x00, //lea rsi,qword
0x44, 0x89, 0xfa, //mov edx,r15d
0x30, 0xc0, //xor al,al
0xe8, 0xbc, 0xb5, 0xc5, 0xff, //call _panic
0x49, 0x8b, 0x47, 0x70, 0x49, 0x8b,
},
//10.9.5 IPCA
{
0x48, 0x8d, 0x3d, 0x7c, 0x08, 0x1a, 0x00, //lea rdi,qword [aSInvalidWaitre]
0x48, 0x8d, 0x35, 0xdd, 0x08, 0x1a, 0x00, //lea rsi, qword [aKqueuescancont]
0x44, 0x89, 0xfa, //mov edx,r15d
0x30, 0xc0, //xor al,al
0xe8, 0xdc, 0x34, 0xc6, 0xff, //call _panic
0x45, 0x31, 0xe4, //xor r12d, r12d
0x49, 0x8b, 0xb5,
},
#else
//10.7.5 11G63, xnu-1699.32.7 (32 bit)
{
0x89, 0x4c, 0x24, 0x04, //mov dword[esp+0x28+var_24],ecx
0xc7, 0x04, 0x24, 0xa4, 0x06, 0x70, 0x00, //mov dword[esp+0x28+var_28],aKeventscancont
0xe8, 0x32, 0x46, 0xcc, 0xff, //call _panic
0x31, 0xf6, //xor esi,esi
0x8b, 0x45, 0xec, 0x8b, 0x88, 0x9c, 0x00, 0x00, 0x00, 0x89, 0x74, 0x24,
},
#endif
};
static char possible_replacement_bytes[NUM_SUPPORTED_KERNELS][7] = {
#if __LP64__
{0x48, 0xc7, 0xc3, 0x09, 0x00, 0x00, 0x00}, // mov rbx,0x9 | 10.7.5 11G63, xnu-1699.32.7
{0x41, 0xbd, 0x09, 0x00, 0x00, 0x00, 0x90}, // mov r13d,0x9 | 10.8.4 12F2560, xnu-2050.48.19
{0x41, 0xbc, 0x09, 0x00, 0x00, 0x00, 0x90}, // mov r12d,0x9 | 10.9.5 13F1911, xnu-2422.115.15
{0x41, 0xbd, 0x09, 0x00, 0x00, 0x00, 0x90}, // mov r13d,0x9 | 10.9.5 Bronya-Ryzen
{0x41, 0xbc, 0x09, 0x00, 0x00, 0x00, 0x90}, // mov r12d,0x9 | 10.9.5 IPCA
#else
{0xbe, 0x09, 0x00, 0x00, 0x00, 0x90, 0x90}, // mov esi,0x9 | 10.7.5 11G63, xnu-1699.32.7 (32 bit)
#endif
};
// Adapted from github.com/acidanthera/Lilu/blob/137b4d9deb7022bb97fa9899303934534ff20ec7/Lilu/Sources/kern_mach.cpp
static vm_offset_t get_kernel_base() {
#if __LP64__
// The function choice is mostly arbitrary, but IOLog frequently has a low address.
vm_offset_t tmp = (vm_offset_t)(IOLog);
// Align the address
tmp &= ~(KASLRAlignment - 1);
// Search backwards for the kernel base address (mach-o header)
while (tmp >= KASLRAlignment) {
if (*(uint32_t *)(tmp) == MH_MAGIC_64) {
// make sure it's the header and not some reference to the MAGIC number
struct segment_command_64 segmentCommand = *(struct segment_command_64 *)(tmp + sizeof(struct mach_header_64));
if (!strncmp(segmentCommand.segname, "__TEXT", sizeof(segmentCommand.segname))) {
printf("KQueueScanContinuePatch: found kernel mach-o header address at %lx\n", tmp);
break;
}
}
tmp -= KASLRAlignment;
}
return tmp - KERN_TEXT_BASE;
#else
// No 32-bit XNU kernels have KASLR
return 0;
#endif
}
boolean_t write_protection_is_enabled() {
return (get_cr0() & CR0_WP) != 0;
}
kern_return_t KQueueScanContinuePatch_start(kmod_info_t * ki, void *d)
{
printf("KQueueScanContinuePatch START\n");
vm_offset_t kernel_base = get_kernel_base();
vm_offset_t kqueue_scan_continue_panic_start_location = 0;
vm_offset_t kqueue_scan_continue_panic_end_location = 0;
char search_bytes[sizeof(possible_search_bytes[0])];
char replacement_bytes[sizeof(possible_replacement_bytes[0])];
uint8_t *kscpb = NULL;
for (int i = 0; i < LENGTH(possible_kqueue_scan_continue_panic_start_locations); i++) {
kqueue_scan_continue_panic_start_location = kernel_base + possible_kqueue_scan_continue_panic_start_locations[i];
kqueue_scan_continue_panic_end_location = kernel_base + possible_kqueue_scan_continue_panic_end_locations[i];
memcpy(search_bytes, possible_search_bytes[i], sizeof(search_bytes));
memcpy(replacement_bytes, possible_replacement_bytes[i], sizeof(replacement_bytes));
kscpb = (uint8_t*) kqueue_scan_continue_panic_start_location;
if (memcmp(kscpb, search_bytes, sizeof(search_bytes)) == 0) {
break;
}
if (i == LENGTH(possible_kqueue_scan_continue_panic_start_locations) - 1) {
printf("KQueueScanContinuePatch: Memory region not found. You are probably using an unsupported kernel, or your kernel has already been patched.\n");
return KERN_FAILURE;
}
}
printf("KQueueScanContinuePatch: Pre-Patch: Bytes at kqueue_scan_continue panic location: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", kscpb[0], kscpb[1], kscpb[2], kscpb[3], kscpb[4], kscpb[5], kscpb[6], kscpb[7], kscpb[8], kscpb[9], kscpb[10], kscpb[11], kscpb[12], kscpb[13], kscpb[14], kscpb[15], kscpb[16], kscpb[17], kscpb[18], kscpb[19], kscpb[20], kscpb[21], kscpb[22], kscpb[23], kscpb[24], kscpb[25], kscpb[26], kscpb[27], kscpb[28], kscpb[29], kscpb[30], kscpb[31], kscpb[32], kscpb[33], kscpb[34], kscpb[35], kscpb[36], kscpb[37], kscpb[38], kscpb[39]);
unsigned long extra_space_to_fill = kqueue_scan_continue_panic_end_location - kqueue_scan_continue_panic_start_location - sizeof(replacement_bytes);
if (kqueue_scan_continue_panic_start_location + sizeof(replacement_bytes) + extra_space_to_fill != kqueue_scan_continue_panic_end_location) {
printf("kqueue_scan_continue_panic_start_location + sizeof(replacement_bytes) + extra_space_to_fill != kqueue_scan_continue_panic_end_location\n");
return KERN_FAILURE;
}
boolean_t interrupts_were_enabled = ml_get_interrupts_enabled();
if (interrupts_were_enabled) {
ml_set_interrupts_enabled(false);
if (! ml_get_interrupts_enabled()) {
printf("KQueueScanContinuePatch: Disabled interrupts\n");
} else {
printf("KQueueScanContinuePatch: Failed to disable interrupts\n");
return KERN_FAILURE;
}
}
boolean_t write_protection_was_enabled = write_protection_is_enabled();
if (write_protection_was_enabled) {
set_cr0(get_cr0() & ~CR0_WP); // disable write protection
if (!write_protection_is_enabled()) {
printf("KQueueScanContinuePatch: Disabled write protection\n");
} else {
printf("KQueueScanContinuePatch: Failed to disable write protection\n");
//Re-enable interrupts before exiting.
if (interrupts_were_enabled && !ml_get_interrupts_enabled()) {
ml_set_interrupts_enabled(true);
if (ml_get_interrupts_enabled()) {
printf("KQueueScanContinuePatch: Re-enabled interrupts after failing to disable write protection.\n");
} else {
panic("KQueueScanContinuePatch: Failed to re-enable interrupts after failing to disable write protection!\n");
}
}
return KERN_FAILURE;
}
}
//commence memory rewriting
memcpy((void *)kqueue_scan_continue_panic_start_location, replacement_bytes, sizeof(replacement_bytes));
memset((void *)kqueue_scan_continue_panic_start_location + sizeof(replacement_bytes), 0x90 /*nop*/, extra_space_to_fill);
printf("KQueueScanContinuePatch: Memory rewritten\n");
//conclude memory rewriting
if (write_protection_was_enabled && !write_protection_is_enabled()) {
set_cr0(get_cr0() | CR0_WP); // re-enable write protection
if (write_protection_is_enabled()) {
printf("KQueueScanContinuePatch: Re-enabled write protection\n");
} else {
panic("KQueueScanContinuePatch: failed to re-enable write protection!\n");
}
}
if (interrupts_were_enabled && !ml_get_interrupts_enabled()) {
ml_set_interrupts_enabled(true);
if (ml_get_interrupts_enabled()) {
printf("KQueueScanContinuePatch: Re-enabled interrupts\n");
} else {
panic("KQueueScanContinuePatch: Failed to re-enable interrupts!\n");
}
}
printf("KQueueScanContinuePatch: Post-Patch: Bytes at kqueue_scan_continue panic location: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", kscpb[0], kscpb[1], kscpb[2], kscpb[3], kscpb[4], kscpb[5], kscpb[6], kscpb[7], kscpb[8], kscpb[9], kscpb[10], kscpb[11], kscpb[12], kscpb[13], kscpb[14], kscpb[15], kscpb[16], kscpb[17], kscpb[18], kscpb[19], kscpb[20], kscpb[21], kscpb[22], kscpb[23], kscpb[24], kscpb[25], kscpb[26], kscpb[27], kscpb[28], kscpb[29], kscpb[30], kscpb[31], kscpb[32], kscpb[33], kscpb[34], kscpb[35], kscpb[36], kscpb[37], kscpb[38], kscpb[39]);
return KERN_SUCCESS;
}
kern_return_t KQueueScanContinuePatch_stop(kmod_info_t *ki, void *d)
{
printf("KQueueScanContinuePatch STOP\n");
return KERN_SUCCESS;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>wowfunhappy.KQueueScanContinuePatch-loader</string>
<key>ProgramArguments</key>
<array>
<string>/bin/sh</string>
<string>-c</string>
<string>/bin/cp -R /Library/Extensions/KQueueScanContinuePatch.kext /tmp/ &amp;&amp; /sbin/kextload /tmp/KQueueScanContinuePatch.kext ; /bin/rm -r /tmp/KQueueScanContinuePatch.kext</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
@Wowfunhappy
Copy link
Author

...you know, there was that one report of odd stuff happening after the kqueue crash:

blueboxd/chromium-legacy#97 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment