Skip to content

Instantly share code, notes, and snippets.

@nall
Created April 10, 2010 19:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nall/362233 to your computer and use it in GitHub Desktop.
Save nall/362233 to your computer and use it in GitHub Desktop.
How to perform breakpoints on ia32/OSX
#include <pthread.h>
#include <stdio.h>
#include <inttypes.h>
#include <signal.h>
#include <mach/mach_types.h>
// compile:
// monoco% gcc -m32 -o break break.m -lpthread -F/Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/Kernel.framework
thread_t mythread;
void clear_breakpoint()
{
struct x86_debug_state dr;
mach_msg_type_number_t dr_count = x86_DEBUG_STATE_COUNT;
kern_return_t rc = thread_get_state(mythread, x86_DEBUG_STATE, &dr, &dr_count);
// Clear out the state and disable the breakpoint
dr.uds.ds32.__dr6 &= ~(1 << 0);
dr.uds.ds32.__dr7 &= ~(1 << 0);
dr_count = x86_DEBUG_STATE_COUNT;
rc = thread_set_state(mythread, x86_DEBUG_STATE, &dr, dr_count);
printf("Breakpoint cleared\n");
}
void set_breakpoint(void* addr)
{
struct x86_debug_state dr;
mach_msg_type_number_t dr_count = x86_DEBUG_STATE_COUNT;
kern_return_t rc = thread_get_state(mythread, x86_DEBUG_STATE, &dr, &dr_count);
// set the address to break on
dr.uds.ds32.__dr0 = (uint32_t)addr;
// set some enables
dr.uds.ds32.__dr7 = (1 << 9) | (1 << 8) | (1 << 0);
rc = thread_set_state(mythread, x86_DEBUG_STATE, &dr, dr_count);
printf("rc: %d\n", rc);
}
void breakpoint_handler(int signum)
{
printf("***** HIT BREAKPOINT *****\n");
clear_breakpoint();
}
uint32_t dummy(char c)
{
printf("in dummy. c is %c\n", c);
return 0xBABE;
}
void* trampoline(void* arg)
{
uint32_t* start = (uint32_t*)arg;
// Wait until we're supposed to start
while(*start != 1)
{}
printf("About to call dummy()\n");
uint32_t result = dummy('N');
printf("Dummy complete: 0x%08x\n", result);
return (void*)result;
}
int main(int argc, char** argv)
{
// Create a new thread
//
pthread_t thread;
uint32_t start = 0;
int rc = pthread_create(&thread, NULL, &trampoline, &start);
if(rc != 0)
{
perror("pthread_create: ");
return(1);
}
mythread = pthread_mach_thread_np(thread);
// Set our breakpoint on dummy
//
set_breakpoint(&dummy);
// Setup a breakpoint handler
{
struct sigaction action;
action.sa_handler = breakpoint_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
sigaction(SIGTRAP, &action, NULL);
}
// Start the thread running.
//
start = 1;
// Wait until it's done and cleanup.
//
void* result = 0;
pthread_join(thread, &result);
printf("All threads complete: 0x%08x\n", (uint32_t)result);
return(0);
}
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <inttypes.h>
#include <signal.h>
#include <mach/mach_types.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
// compile:
// monoco% gcc -m32 -o break2 break2.m
uint8_t orig_dummy_byte = 0;
thread_t mythread;
void clear_breakpoint(void* pc)
{
*((uint8_t*)pc) = orig_dummy_byte;
}
void set_breakpoint(void* addr)
{
// mprotect requires page-aligned addresses, so round it
//
uint32_t pagesize = getpagesize();
uint32_t addrToProtect = (uint32_t)addr;
addrToProtect -= (addrToProtect % pagesize);
// mprotect the page to give ourselves write-access
// since we'll be writing
//
int rc = mprotect((void*)addrToProtect, 1, PROT_READ | PROT_WRITE | PROT_EXEC);
if(rc != 0)
{
perror("mprotect: ");
return;
}
// Update the first byte of the target to be 0xCC
// which is the opcode for INT3 which will generate
// SIGTRAP when executed by the CPU
//
uint8_t* fptr = (uint8_t*)addr;
orig_dummy_byte = *fptr;
*fptr = (uint8_t)0xCC; // INT 3
}
void breakpoint_handler(int signum, siginfo_t* signal_info, void* context)
{
printf("***** HIT BREAKPOINT *****\n");
// Dig through the context pointer info to get the
// instruction pointer. It's going to point to the
// address AFTER where we wrote 0xCC. So back it up
// one and store that to the context
//
ucontext_t* ucontext = (ucontext_t*)context;
mcontext_t mcontext = ucontext->uc_mcontext;
uint32_t pc = (uint32_t)mcontext->__ss.__eip;
--pc;
mcontext->__ss.__eip = pc;
// Now replace the byte we overwrote in set_breakpoint
//
clear_breakpoint((void*)pc);
}
uint32_t dummy(char c)
{
printf("in dummy. c is %c [0x%x]\n", c, c);
return 0xBABE;
}
void* trampoline(void* arg)
{
uint32_t* start = (uint32_t*)arg;
// Wait until we're supposed to start
while(*start != 1)
{}
printf("About to call dummy()\n");
uint32_t result = dummy('N');
printf("Dummy complete: 0x%08x\n", result);
return (void*)result;
}
int main(int argc, char** argv)
{
// Create a new thread
//
pthread_t thread;
uint32_t start = 0;
int rc = pthread_create(&thread, NULL, &trampoline, &start);
if(rc != 0)
{
perror("pthread_create: ");
return(1);
}
mythread = pthread_mach_thread_np(thread);
// Set our breakpoint on dummy
//
set_breakpoint(&dummy);
// Setup a breakpoint handler
{
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_flags |= SA_SIGINFO;
action.sa_sigaction = &breakpoint_handler;
sigaction(SIGTRAP, &action, NULL);
}
// Start the thread running.
//
start = 1;
// Wait until it's done and cleanup.
//
void* result = 0;
pthread_join(thread, &result);
printf("All threads complete: 0x%08x\n", (uint32_t)result);
return(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment