| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884 |
- /*
- * Copyright (C)2005-2020 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdbool.h>
- #include <unistd.h>
- #include <time.h>
- #include <signal.h>
- #include <semaphore.h>
- #include <errno.h>
- #include <assert.h>
- #include <sys/types.h>
- #include <sys/user.h>
- #include <sys/ptrace.h>
- #include <sys/event.h>
- #include <sys/sysctl.h>
- #include <mach/mach.h>
- #include <mach/mach_types.h>
- #include <mach/mach_vm.h>
- #include <mach/vm_prot.h>
- #include <mach/exception_types.h>
- #include <mdbg/mach_exc.h>
- #include "mdbg.h"
- #pragma mark Defines
- #define STATUS_TIMEOUT -1
- #define STATUS_EXIT 0
- #define STATUS_BREAKPOINT 1
- #define STATUS_SINGLESTEP 2
- #define STATUS_ERROR 3
- #define STATUS_HANDLED 4
- #define STATUS_STACKOVERFLOW 5
- #define STATUS_WATCHBREAK 0x100
- #define SINGLESTEP_TRAP 0x00000100
- #define MAX_EXCEPTION_PORTS 16
- /* Forward declarations */
- boolean_t mach_exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
- static struct debug_session *find_session(mach_port_t task);
- static mach_port_t get_task(pid_t pid);
- static mach_port_t get_thread(mach_port_t mach_task, uint thread_num);
- static uint64_t get_thread_id(thread_t thread);
- static x86_thread_state64_t* get_thread_state(mach_port_t mach_thread);
- static kern_return_t set_thread_state(thread_t mach_thread, x86_thread_state64_t *break_state);
- static x86_debug_state64_t* get_debug_state(thread_t mach_thread);
- static kern_return_t set_debug_state(thread_t mach_thread, x86_debug_state64_t *break_state);
- static void* task_exception_server (mach_port_t exception_port);
- #pragma mark Structs
- typedef struct {
- debug_session **list;
- unsigned int count;
- } pointer_list;
- pointer_list sessions;
- typedef struct {
- mach_msg_type_number_t count;
- exception_mask_t masks[MAX_EXCEPTION_PORTS];
- exception_handler_t ports[MAX_EXCEPTION_PORTS];
- exception_behavior_t behaviors[MAX_EXCEPTION_PORTS];
- thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS];
- } exception_ports_info;
- #pragma mark Helpers
- static void* safe_malloc(size_t x) {
- void *mal = malloc(x);
- if(mal == NULL) {
- fprintf(stderr, "[-safe_malloc] Error Exiting\n");
- exit(-1);
- }
- memset(mal, 0, x);
- return mal;
- }
- #pragma mark Debug
- #if MDBG_DEBUG
- # define DEBUG_PRINT(fmt,...) \
- do { fprintf(stdout, "%s[%d] %s(): " fmt "\n",__FILE__, __LINE__, __func__, ##__VA_ARGS__); } while (0);
- # if MDBG_LOG_LEVEL > 0
- # define DEBUG_PRINT_VERBOSE(fmt,...) \
- do { fprintf(stdout, "%s[%d] %s(): " fmt "\n",__FILE__, __LINE__, __func__, ##__VA_ARGS__); } while (0);
- # else
- # define DEBUG_PRINT_VERBOSE(fmt,...)
- #endif
- #else
- # define DEBUG_PRINT(fmt,...)
- # define DEBUG_PRINT_VERBOSE(fmt,...)
- #endif
- #define RETURN_ON_MACH_ERROR(code,msg,...) \
- if (code != KERN_SUCCESS) {\
- DEBUG_PRINT(msg,##__VA_ARGS__);\
- return code;\
- }
- #define EXIT_ON_MACH_ERROR(code,msg,...) \
- if (code != KERN_SUCCESS) {\
- DEBUG_PRINT(msg,##__VA_ARGS__);\
- exit(0);\
- }
- #if MDBG_DEBUG
- static void log_buffer(unsigned char *buf, int size) {
- for (int i=0; i<size; i++) {
- printf("%02lx\t", (unsigned long)buf[i]);
- if ((i+1)%16 == 0) printf("\n");
- }
- }
- static char* get_signal_name(int sig) {
- switch(sig) {
- case 1: return "SIGHUP";
- case 2: return "SIGINT";
- case 3: return "SIGQUIT";
- case 4: return "SIGILL";
- case 5: return "SIGTRAP";
- case 6: return "SIGABRT";
- case 7: return "SIGEMT";
- case 8: return "SIGFPE";
- case 9: return "SIGKILL";
- case 10: return "SIGBUS";
- case 11: return "SIGSEGV";
- case 12: return "SIGSYS";
- case 13: return "SIGPIPE";
- case 14: return "SIGALRM";
- case 15: return "SIGTERM";
- case 16: return "SIGURG";
- case 17: return "SIGSTOP";
- case 18: return "SIGTSTP";
- case 19: return "SIGCONT";
- case 20: return "SIGCHLD";
- case 21: return "SIGTTIN";
- case 22: return "SIGTTOU";
- case 23: return "SIGIO";
- case 24: return "SIGXCPU";
- case 25: return "SIGXFSZ";
- case 26: return "SIGVTALRM";
- case 27: return "SIGPROF";
- case 28: return "SIGWINCH";
- case 29: return "SIGINFO";
- case 30: return "SIGUSR1";
- case 31: return "SIGUSR2";
- default: return "UNKNOWN";
- }
- }
- static char* exception_to_string(exception_type_t exc) {
- switch(exc) {
- case EXC_BREAKPOINT : return "EXC_BREAKPOINT";
- case EXC_BAD_ACCESS : return "EXC_BAD_ACCESS";
- case EXC_BAD_INSTRUCTION: return "EXC_BAD_INSTRUCTION";
- case EXC_ARITHMETIC : return "EXC_ARITHMETIC";
- case EXC_EMULATION : return "EXC_EMULATION";
- case EXC_SOFTWARE : return "EXC_SOFTWARE";
- case EXC_SYSCALL : return "EXC_SYSCALL";
- case EXC_MACH_SYSCALL : return "EXC_MACH_SYSCALL";
- case EXC_RPC_ALERT : return "EXC_RPC_ALERT";
- case EXC_CRASH : return "EXC_CRASH";
- case EXC_RESOURCE : return "EXC_RESOURCE";
- case EXC_GUARD : return "EXC_GUARD";
- case NOTE_EXEC : return "EXEC";
- case NOTE_FORK : return "FORK";
- case NOTE_SIGNAL : return "SIGNAL";
- case NOTE_EXIT : return "EXIT";
- default:
- return "[-exception_to_string] unknown exception type!";
- }
- }
- static char* get_register_name(int reg) {
- switch(reg) {
- case REG_RAX: return "Rax";
- case REG_RBX: return "Rbx";
- case REG_RCX: return "Rcx";
- case REG_RDX: return "Rdx";
- case REG_RDI: return "Rdi";
- case REG_RSI: return "Rsi";
- case REG_RBP: return "Rbp";
- case REG_RSP: return "Rsp";
- case REG_R8: return "R8";
- case REG_R9: return "R9";
- case REG_R10: return "R10";
- case REG_R11: return "R11";
- case REG_R12: return "R12";
- case REG_R13: return "R13";
- case REG_R14: return "R14";
- case REG_R15: return "R15";
- case REG_RIP: return "Rip";
- case REG_RFLAGS: return "Rflags";
- case REG_DR0: return "Dr0";
- case REG_DR1: return "Dr1";
- case REG_DR2: return "Dr2";
- case REG_DR3: return "Dr3";
- case REG_DR4: return "Dr4";
- case REG_DR5: return "Dr5";
- case REG_DR6: return "Dr6";
- case REG_DR7: return "Dr7";
- default: return "invalid register";
- }
- }
- #endif
- #pragma mark Debug helpers
- // From: https://developer.apple.com/library/archive/qa/qa1361/_index.html
- // Returns true if the current process is being debugged (either
- // running under the debugger or has a debugger attached post facto).
- bool is_debugger_attached(void) {
- int junk;
- int mib[4];
- struct kinfo_proc info;
- size_t size;
- info.kp_proc.p_flag = 0;
- mib[0] = CTL_KERN;
- mib[1] = KERN_PROC;
- mib[2] = KERN_PROC_PID;
- mib[3] = getpid();
- size = sizeof(info);
- junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
- assert(junk == 0);
- // We're being debugged if the P_TRACED flag is set.
- return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
- }
- #pragma mark Debug sessions
- static debug_session *create_debug_session(mach_port_t task, pid_t pid) {
- debug_session *exists = find_session(task);
- if(exists != NULL) {
- return exists;
- }
- debug_session *sess = malloc(sizeof(debug_session));
- memset(sess, 0, sizeof(debug_session));
- sess->task = task;
- sess->pid = pid;
- sess->exception_port = 0;
- sess->old_exception_ports = (struct exception_ports_info*)malloc(sizeof(exception_ports_info));
- sess->process_status = STATUS_HANDLED;
- // create a semaphore `wait_sem` to handle signaling between exception port thread and main thread
- kern_return_t kret = semaphore_create(task, &sess->wait_sem, 0, 0);
- EXIT_ON_MACH_ERROR(kret, "Fatal error: failed to create semaphore!");
- // add `debug_session` to `sessions` list for future lookup
- sessions.list = realloc(sessions.list, sizeof(debug_session*) * (sessions.count + 1));
- sessions.list[sessions.count++] = sess;
- DEBUG_PRINT("created session num: %i", sessions.count);
- return sess;
- }
- static bool destroy_debug_session(debug_session *sess) {
- semaphore_destroy(sess->task, sess->wait_sem);
- free(sess);
- if(sessions.count > 1) {
- debug_session **new_list = malloc(sizeof(debug_session*) * (sessions.count-1));
- int j = 0;
- for(int i = 0; i < sessions.count; i++) {
- if(sessions.list[i] != sess) {
- new_list[j++] = sessions.list[i];
- }
- }
- free(sessions.list);
- sessions.list = new_list;
- } else {
- free(sessions.list);
- sessions.list = NULL;
- }
- sessions.count--;
- return true;
- }
- static debug_session *find_session(mach_port_t task) {
- int i = 0;
- while(i < sessions.count) {
- if(sessions.list[i]->task == task) {
- return sessions.list[i];
- }
- i++;
- }
- return NULL;
- }
- static debug_session *find_session_by_pid(pid_t pid) {
- int i = 0;
- while(i < sessions.count) {
- if(sessions.list[i]->pid == pid) {
- return sessions.list[i];
- }
- i++;
- }
- return NULL;
- }
- #pragma mark Registers
- __uint64_t *get_reg( x86_thread_state64_t *regs, int r ) {
- switch( r ) {
- case REG_RAX: return ®s->__rax;
- case REG_RBX: return ®s->__rbx;
- case REG_RCX: return ®s->__rcx;
- case REG_RDX: return ®s->__rdx;
- case REG_RDI: return ®s->__rdi;
- case REG_RSI: return ®s->__rsi;
- case REG_RBP: return ®s->__rbp;
- case REG_RSP: return ®s->__rsp;
- case REG_R8: return ®s->__r8;
- case REG_R9: return ®s->__r9;
- case REG_R10: return ®s->__r10;
- case REG_R11: return ®s->__r11;
- case REG_R12: return ®s->__r12;
- case REG_R13: return ®s->__r13;
- case REG_R14: return ®s->__r14;
- case REG_R15: return ®s->__r15;
- case REG_RIP: return ®s->__rip;
- case REG_RFLAGS: return ®s->__rflags;
- }
- return NULL;
- }
- __uint64_t *get_debug_reg( x86_debug_state64_t *regs, int r ) {
- switch( r ) {
- case REG_DR0: return ®s->__dr0;
- case REG_DR1: return ®s->__dr1;
- case REG_DR2: return ®s->__dr2;
- case REG_DR3: return ®s->__dr3;
- case REG_DR4: return ®s->__dr4;
- case REG_DR5: return ®s->__dr5;
- case REG_DR6: return ®s->__dr6;
- case REG_DR7: return ®s->__dr7;
- }
- return NULL;
- }
- __uint64_t read_register(mach_port_t task, int thread, int reg, bool is64 ) {
- __uint64_t *rdata;
- mach_port_t mach_thread = get_thread(task, thread);
- if(reg >= REG_DR0) {
- x86_debug_state64_t *regs = get_debug_state(mach_thread);
- rdata = get_debug_reg(regs, reg - 4);
- } else {
- x86_thread_state64_t *regs = get_thread_state(mach_thread);
- rdata = get_reg(regs, reg);
- }
- DEBUG_PRINT_VERBOSE("register %s is: 0x%08x\n", get_register_name(reg), *rdata);
- return *rdata;
- }
- static kern_return_t write_register(mach_port_t task, int thread, int reg, void *value, bool is64 ) {
- DEBUG_PRINT_VERBOSE("write register %i (%s) on thread %i", reg, get_register_name(reg), thread);
- __uint64_t *rdata;
- mach_port_t mach_thread = get_thread(task, thread);
- if(reg >= REG_DR0) {
- x86_debug_state64_t *regs = get_debug_state(mach_thread);
- rdata = get_debug_reg(regs, reg - 4);
- DEBUG_PRINT_VERBOSE("register flag for %s was: 0x%08x\n",get_register_name(reg), *rdata);
- *rdata = (__uint64_t)value;
- set_debug_state(mach_thread, regs);
- } else {
- x86_thread_state64_t *regs = get_thread_state(mach_thread);
- rdata = get_reg(regs, reg);
- DEBUG_PRINT_VERBOSE("register flag for %s was: 0x%08x\n",get_register_name(reg), *rdata);
- *rdata = (__uint64_t)value;
- set_thread_state(mach_thread, regs);
- }
- DEBUG_PRINT_VERBOSE("register flag for %s now is: 0x%08x\n",get_register_name(reg), *rdata);
- return KERN_SUCCESS;
- }
- #pragma mark Memory IO
- static kern_return_t read_memory(mach_port_t task, mach_vm_address_t addr, mach_vm_address_t dest, int size) {
- mach_vm_size_t nread;
- kern_return_t kret = mach_vm_read_overwrite(task, addr, size, dest, &nread);
-
- EXIT_ON_MACH_ERROR(kret,"Error: probably reading from invalid address!");
- DEBUG_PRINT_VERBOSE("read %i bytes from %p", nread, addr);
- #if MDBG_DEBUG && MDBG_LOG_LEVEL > 1
- log_buffer(dest, size);
- printf("\n\n");
- #endif
- return kret;
- }
- static kern_return_t write_memory(mach_port_t task, mach_vm_address_t addr, mach_vm_address_t src, int size) {
- kern_return_t kret = mach_vm_protect(task, addr, size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
- EXIT_ON_MACH_ERROR(kret,"Fatal error: failed to acquire write permission!");
- kret = mach_vm_write(task, addr, src, size);
- EXIT_ON_MACH_ERROR(kret,"Fatal error: failed to write to traced process memory!");
- kret = mach_vm_protect(task, addr, size, 0, VM_PROT_READ | VM_PROT_EXECUTE);
- EXIT_ON_MACH_ERROR(kret,"Fatal error: failed to reset write permission!");
- DEBUG_PRINT_VERBOSE("wrote %i bytes to %p",size, addr);
- #if MDBG_DEBUG && MDBG_LOG_LEVEL
- log_buffer(src, size);
- printf("\n\n");
- #endif
- return kret;
- }
- #pragma mark Threads
- static thread_act_port_array_t get_task_threads(mach_port_t mach_task, mach_msg_type_number_t *threadCount) {
- thread_act_port_array_t threadList;
- task_threads(mach_task, &threadList, threadCount);
- return threadList;
- }
- static mach_port_t get_thread(mach_port_t mach_task, uint thread_id) {
- thread_act_port_array_t threadList;
- mach_msg_type_number_t threadCount;
- kern_return_t kret = task_threads(mach_task, &threadList, &threadCount);
- if (kret != KERN_SUCCESS) {
- DEBUG_PRINT("get_thread() failed with message %s!\n", mach_error_string(kret));
- exit(0);
- }
- for(int i=0;i<threadCount;i++) {
- if(get_thread_id(threadList[i]) == thread_id) {
- return threadList[i];
- }
- }
- exit(0); // TODO: catch better
- }
- static thread_identifier_info_data_t* get_thread_info(thread_t thread) {
- thread_identifier_info_data_t *tident = safe_malloc(sizeof(thread_identifier_info_data_t));
- mach_msg_type_number_t tident_count = THREAD_IDENTIFIER_INFO_COUNT;
- kern_return_t kret = thread_info (thread, THREAD_IDENTIFIER_INFO, (thread_info_t)tident, &tident_count);
- EXIT_ON_MACH_ERROR(kret, "failed to get thread info");
- return tident;
- }
- static uint64_t get_thread_id(thread_t thread) {
- thread_identifier_info_data_t *tinfo = get_thread_info(thread);
- return tinfo->thread_id;
- }
- static x86_thread_state64_t* get_thread_state(thread_t mach_thread) {
- x86_thread_state64_t* state;
- mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
- state = safe_malloc(sizeof(x86_thread_state64_t));
- kern_return_t kret = thread_get_state( mach_thread, x86_THREAD_STATE64, (thread_state_t)state, &stateCount);
- if (kret != KERN_SUCCESS) {
- DEBUG_PRINT("Error failed with message %s!\n", mach_error_string(kret));
- exit(0);
- }
- return state;
- }
- static kern_return_t set_thread_state(thread_t mach_thread, x86_thread_state64_t *break_state) {
- kern_return_t kret = thread_set_state(mach_thread, x86_THREAD_STATE64, (thread_state_t)break_state, x86_THREAD_STATE64_COUNT);
- if (kret != KERN_SUCCESS) {
- DEBUG_PRINT("Error failed with message %s!\n", mach_error_string(kret));
- exit(0);
- }
- return kret;
- }
- // Debug register state
- static x86_debug_state64_t* get_debug_state(thread_t mach_thread) {
- x86_debug_state64_t* state;
- mach_msg_type_number_t stateCount = x86_DEBUG_STATE64_COUNT;
- state = safe_malloc(sizeof(x86_debug_state64_t));
- kern_return_t kret = thread_get_state( mach_thread, x86_DEBUG_STATE64, (thread_state_t)state, &stateCount);
- if (kret != KERN_SUCCESS) {
- DEBUG_PRINT("Error failed with message %s!\n", mach_error_string(kret));
- exit(0);
- }
- return state;
- }
- static kern_return_t set_debug_state(thread_t mach_thread, x86_debug_state64_t *break_state) {
- kern_return_t kret = thread_set_state(mach_thread, x86_DEBUG_STATE64, (thread_state_t)break_state, x86_DEBUG_STATE64_COUNT);
- if (kret != KERN_SUCCESS) {
- DEBUG_PRINT("Error failed with message %s!\n", mach_error_string(kret));
- exit(0);
- }
- return kret;
- }
- #pragma mark Exception ports
- static kern_return_t save_exception_ports(task_t task, exception_ports_info *info) {
- info->count = (sizeof (info->ports) / sizeof (info->ports[0]));
- return task_get_exception_ports(task, EXC_MASK_ALL, info->masks, &info->count, info->ports, info->behaviors, info->flavors);
- }
- static kern_return_t restore_exception_ports(task_t task, exception_ports_info *info) {
- int i;
- kern_return_t kret;
- for (i = 0; i < info->count; i++) {
- kret = task_set_exception_ports(task, info->masks[i], info->ports[i], info->behaviors[i], info->flavors[i]);
- if (kret != KERN_SUCCESS)
- return kret;
- }
- return KERN_SUCCESS;
- }
- #pragma mark Tasks
- static mach_port_t get_task(pid_t pid) {
- debug_session *sess = find_session_by_pid(pid);
- if(sess != NULL) {
- return sess->task;
- }
- mach_port_t task;
- kern_return_t kret = task_for_pid(mach_task_self(), pid, &task);
- EXIT_ON_MACH_ERROR(kret,"Fatal error: failed to get task for pid %i",pid);
- return task;
- }
- static kern_return_t attach_to_task(mach_port_t task, pid_t pid) {
- if(find_session(task) != NULL) {
- DEBUG_PRINT("Warning already attached to task (%i). Not attaching again!",task);
- return KERN_SUCCESS;
- }
- debug_session *sess = create_debug_session(task, pid);
- kern_return_t kret;
- int err;
- kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sess->exception_port);
- RETURN_ON_MACH_ERROR(kret,"mach_port_allocate failed");
- kret = mach_port_insert_right(mach_task_self(), sess->exception_port, sess->exception_port, MACH_MSG_TYPE_MAKE_SEND);
- RETURN_ON_MACH_ERROR(kret,"mach_port_insert_right failed");
- // store current exception ports
- save_exception_ports(task, (exception_ports_info*)sess->old_exception_ports);
- kret = task_set_exception_ports(task, EXC_MASK_ALL, sess->exception_port, EXCEPTION_STATE_IDENTITY|MACH_EXCEPTION_CODES, x86_THREAD_STATE64);
- RETURN_ON_MACH_ERROR(kret,"task_set_exception_ports failed");
- // launch mach exception port thread //
- err = pthread_create(&sess->exception_handler_thread, NULL, (void *(*)(void*))task_exception_server, (void *(*)(void*))(unsigned long long)sess->exception_port);
- EXIT_ON_MACH_ERROR(err,"can't create *task_exception_server* thread :[%s]",strerror(err));
- DEBUG_PRINT("successfully created mach exception port thread %d\n", 0);
- return KERN_SUCCESS;
- }
- static kern_return_t attach_to_pid(pid_t pid) {
- attach_to_task(get_task(pid), pid);
- return ptrace(PT_ATTACHEXC, pid, 0, 0) == 0 ? KERN_SUCCESS : KERN_FAILURE;
- }
- static kern_return_t detach_from_pid(pid_t pid) {
- debug_session *sess = find_session_by_pid( pid );
- if(sess != NULL) {
- DEBUG_PRINT("cleaning up debug session...");
- restore_exception_ports(sess->task, (exception_ports_info*)sess->old_exception_ports);
- ptrace(PT_DETACH, pid, 0, 0);
- destroy_debug_session(sess);
- DEBUG_PRINT("successfully ended session!");
- } else {
- DEBUG_PRINT("found no session to cleanup!");
- }
- return KERN_SUCCESS;
- }
- #pragma mark Exceptions
- extern kern_return_t catch_mach_exception_raise /* stub – will not be called */
- (
- 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
- ) {
- DEBUG_PRINT("this handler should not be called");
- return MACH_RCV_INVALID_TYPE;
- }
- extern kern_return_t catch_mach_exception_raise_state /* stub – will not be called */
- (
- 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
- ) {
- DEBUG_PRINT("this handler should not be called");
- return MACH_RCV_INVALID_TYPE;
- }
- extern 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,
- 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
- ) {
- x86_thread_state64_t *state = (x86_thread_state64_t *) old_state;
- x86_thread_state64_t *newState = (x86_thread_state64_t *) new_state;
- debug_session *sess = find_session(task);
- sess->current_thread = get_thread_id(thread); /* set system-wide thread id */
- DEBUG_PRINT("exception occured on thread (%i): %s",sess->current_thread, exception_to_string(exception));
- DEBUG_PRINT("stack address: 0x%02lx", state->__rip);
- if (exception == EXC_SOFTWARE && code[0] == EXC_SOFT_SIGNAL) { // handling UNIX soft signal
- int subcode = code[2];
- DEBUG_PRINT("EXC_SOFTWARE signal: %s",get_signal_name(code[2]));
- if (subcode == SIGSTOP || subcode == SIGTRAP) {
- // clear signal to prevent default OS handling //
- ptrace(PT_THUPDATE,
- sess->pid,
- (caddr_t)(uintptr_t)thread,
- 0);
- task_suspend(sess->task);
- sess->process_status = STATUS_BREAKPOINT;
- semaphore_signal(sess->wait_sem);
- return KERN_SUCCESS;
- }
- /*else if(subcode == SIGTERM) { // TODO: Check if we need this
- sess->process_status = STATUS_EXIT;
- return KERN_SUCCESS;
- }*/
- }
- else if(exception == EXC_BREAKPOINT) {
- task_suspend(sess->task);
- // check if single step mode
- if(state->__rflags & SINGLESTEP_TRAP) {
- state->__rflags &= ~SINGLESTEP_TRAP; // clear single-step
- sess->process_status = STATUS_SINGLESTEP;
- DEBUG_PRINT("SINGLE STEP");
- } else {
- sess->process_status = STATUS_BREAKPOINT;
- }
- // move past breakpoint by setting old to new thread state
- *newState = *state;
- *new_stateCnt = old_stateCnt;
- *flavor = x86_THREAD_STATE64;
- semaphore_signal(sess->wait_sem);
- return KERN_SUCCESS;
- }
- else if(exception == EXC_BAD_INSTRUCTION) {
- task_suspend(sess->task);
- sess->process_status = STATUS_BREAKPOINT;
- return KERN_SUCCESS;
- }
- else if(exception == EXC_BAD_ACCESS) {
- task_suspend(sess->task);
- sess->process_status = STATUS_ERROR;
- return KERN_SUCCESS;
- }
- else {
- DEBUG_PRINT("not handling this exception (%s)!", exception_to_string(exception));
- }
- return KERN_FAILURE;
- }
- static void* task_exception_server (mach_port_t exception_port) {
- mach_msg_return_t rt;
- mach_msg_header_t *msg;
- mach_msg_header_t *reply;
- msg = safe_malloc(sizeof(union __RequestUnion__mach_exc_subsystem));
- reply = safe_malloc(sizeof(union __ReplyUnion__mach_exc_subsystem));
- DEBUG_PRINT("launching exception server...");
- int i = 0;
- while (1) {
- DEBUG_PRINT("waiting for next exception (%i)...",i);
- i++;
- rt = mach_msg(msg, MACH_RCV_MSG, 0, sizeof(union __RequestUnion__mach_exc_subsystem), exception_port, 0, MACH_PORT_NULL);
- if (rt!= MACH_MSG_SUCCESS) {
- DEBUG_PRINT("MACH_RCV_MSG stopped, exit from task_exception_server thread :%d\n", 1);
- return "MACH_RCV_MSG_FAILURE";
- }
- /*
- * Call out to the mach_exc_server generated by mig and mach_exc.defs.
- * This will in turn invoke one of:
- * mach_catch_exception_raise()
- * mach_catch_exception_raise_state()
- * mach_catch_exception_raise_state_identity()
- * .. depending on the behavior specified when registering the Mach exception port.
- */
- mach_exc_server(msg, reply);
- // Send the now-initialized reply
- rt = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
- if (rt!= MACH_MSG_SUCCESS) {
- return "MACH_SEND_MSG_FAILURE";
- }
- }
- }
- static void wait_for_exception(debug_session *sess, int timeout /*in millis*/) {
- DEBUG_PRINT("waiting for next exception...");
- kern_return_t kret = semaphore_timedwait(sess->wait_sem, (struct mach_timespec){0,timeout * 1000000});
- if(kret == KERN_OPERATION_TIMED_OUT) {
- sess->process_status = STATUS_TIMEOUT;
- DEBUG_PRINT("wait timed out!");
- } else {
- DEBUG_PRINT("got notified of an exception!");
- }
- }
- #pragma mark Debug API
- status_t MDBG_API(session_attach)( pid_t pid ) {
- return attach_to_pid(pid) == KERN_SUCCESS;
- }
- status_t MDBG_API(session_detach)( pid_t pid ) {
- return detach_from_pid(pid) == KERN_SUCCESS;
- }
- status_t MDBG_API(session_pause)( pid_t pid ) {
- return kill(pid, SIGTRAP) == 0;
- }
- int MDBG_API(session_wait)( pid_t pid, int *thread, int timeout ) {
- debug_session *sess = find_session_by_pid( pid );
- if(sess != NULL) {
- wait_for_exception(sess, timeout);
- *thread = sess->current_thread;
- return sess->process_status;
- }
- return 4;
- }
- status_t MDBG_API(session_resume)( pid_t pid ) {
- debug_session *sess = find_session_by_pid( pid );
- if(sess != NULL) {
- sess->process_status = STATUS_HANDLED;
- task_resume(sess->task);
- return true;
- }
- return false;
- }
- debug_session *MDBG_API(session_get)( pid_t pid ) {
- return find_session_by_pid( pid );
- }
- status_t MDBG_API(read_memory)( pid_t pid, unsigned char* addr, unsigned char* dest, int size ) {
- return read_memory( get_task(pid), (mach_vm_address_t)addr, (mach_vm_address_t)dest, size ) == KERN_SUCCESS;
- }
- status_t MDBG_API(write_memory)( pid_t pid, unsigned char* addr, unsigned char* src, int size ) {
- return write_memory( get_task(pid), (mach_vm_address_t)addr, (mach_vm_address_t)src, size ) == KERN_SUCCESS;
- }
- void* MDBG_API(read_register)( pid_t pid, int thread, int reg, bool is64 ) {
- return (void*)read_register( get_task(pid), thread, reg, is64 );
- }
- status_t MDBG_API(write_register)( pid_t pid, int thread, int reg, void *value, bool is64 ) {
- return write_register( get_task(pid), thread, reg, value, is64 ) == KERN_SUCCESS;
- }
|