Skip to content

Instantly share code, notes, and snippets.

@EthanArbuckle
Created January 8, 2025 23:22
Show Gist options
  • Save EthanArbuckle/463e1f9b03aa6541839a5f45405221f1 to your computer and use it in GitHub Desktop.
Save EthanArbuckle/463e1f9b03aa6541839a5f45405221f1 to your computer and use it in GitHub Desktop.
set and catch breakpoints
//
// main.m
// breakpoints
//
// Created by @objc on 5/01/23.
//
#import <Foundation/Foundation.h>
#include <mach/mach.h>
#include <pthread.h>
#include "mach_excServer.h"
void my_func1(void) {
printf("original func running: %s\n", __func__);
}
void new_func1(void) {
printf("replacement func running: %s\n", __func__);
}
struct hook {
uintptr_t original;
uintptr_t replacement;
};
static struct hook hooks[16];
static int breakpoints;
mach_port_t server;
kern_return_t catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt) {
return KERN_FAILURE;
}
kern_return_t catch_mach_exception_raise_state_identity(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state, mach_msg_type_number_t old_stateCnt, thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) {
return KERN_FAILURE;
}
kern_return_t catch_mach_exception_raise_state(mach_port_t exception_port, exception_type_t exception, const mach_exception_data_t code, mach_msg_type_number_t codeCnt, int *flavor, const thread_state_t old_state, mach_msg_type_number_t old_stateCnt, thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) {
for (int i = 0; i < breakpoints; ++i) {
if (hooks[i].original == arm_thread_state64_get_pc(*(arm_thread_state64_t *)old_state)) {
*((arm_thread_state64_t *)new_state) = *((arm_thread_state64_t *)old_state);
*new_stateCnt = old_stateCnt;
arm_thread_state64_set_pc_fptr(*((arm_thread_state64_t *)new_state), hooks[i].replacement);
return KERN_SUCCESS;
}
}
return KERN_FAILURE;
}
void *exception_handler(void *unused) {
mach_msg_server(mach_exc_server, sizeof(union __RequestUnion__catch_mach_exc_subsystem), server, MACH_MSG_OPTION_NONE);
abort();
}
bool hook(void *old, void *new) {
arm_debug_state64_t state = {};
state.__bvr[breakpoints] = (uintptr_t)old;
state.__bcr[breakpoints] = 0x1e5;
task_set_state(mach_task_self(), ARM_DEBUG_STATE64, (thread_state_t)&state, ARM_DEBUG_STATE64_COUNT);
bool success = true;
thread_act_array_t threads;
mach_msg_type_number_t thread_count = ARM_DEBUG_STATE64_COUNT;
task_threads(mach_task_self(), &threads, &thread_count);
for (int i = 0; i < thread_count; ++i) {
success = thread_set_state(threads[i], ARM_DEBUG_STATE64, (thread_state_t)&state, ARM_DEBUG_STATE64_COUNT) == KERN_SUCCESS;
if (!success) {
NSLog(@"hook failed: could not set thread 0x%x debug state", threads[i]);
break;
}
}
hooks[breakpoints++] = (struct hook){(uintptr_t)old, (uintptr_t)new};
for (int i = 0; i < thread_count; ++i) {
mach_port_deallocate(mach_task_self(), threads[i]);
}
vm_deallocate(mach_task_self(), (vm_address_t)threads, thread_count * sizeof(*threads));
return success;
}
int main(int argc, const char * argv[]) {
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server);
mach_port_insert_right(mach_task_self(), server, server, MACH_MSG_TYPE_MAKE_SEND);
task_set_exception_ports(mach_task_self(), EXC_MASK_BREAKPOINT, server, EXCEPTION_STATE | MACH_EXCEPTION_CODES, ARM_THREAD_STATE64);
pthread_t thread;
pthread_create(&thread, NULL, exception_handler, NULL);
hook((void *)my_func1, (void *)new_func1);
my_func1();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment