-
-
Save kybernetyk/1062666 to your computer and use it in GitHub Desktop.
How to perform breakpoints on ia32/OSX
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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