static char *__system(RIO *io, RIODesc *fd, const char *cmd) { if (!io || !fd || !cmd || !fd->data) { return NULL; } RIODescData *iodd = fd->data; if (iodd->magic != R_MACH_MAGIC) { return NULL; } task_t task = pid_to_task (fd, iodd->tid); /* XXX ugly hack for testing purposes */ if (!strncmp (cmd, "perm", 4)) { int perm = r_str_rwx (cmd + 4); if (perm) { int pagesize = tsk_pagesize (fd); tsk_setperm (io, task, io->off, pagesize, perm); } else { eprintf ("Usage: =!perm [rwx]\n"); } return NULL; } if (!strncmp (cmd, "pid", 3)) { RIODescData *iodd = fd->data; RIOMach *riom = iodd->data; const char *pidstr = cmd + 3; int pid = -1; if (*pidstr) { pid = __get_pid (fd); //return NULL; } else { eprintf ("%d\n", iodd->pid); return NULL; } if (!strcmp (pidstr, "0")) { pid = 0; } else { pid = atoi (pidstr); if (!pid) { pid = -1; } } if (pid != -1) { task_t task = pid_to_task (fd, pid); if (task != -1) { riom->task = task; iodd->pid = pid; iodd->tid = pid; return NULL; } } eprintf ("io_mach_system: Invalid pid %d\n", pid); } else { eprintf ("Try: '=!pid' or '=!perm'\n"); } return NULL; }
int xnu_reg_write (RDebug *dbg, int type, const ut8 *buf, int size) { int ret; thread_array_t inferior_threads = NULL; unsigned int inferior_thread_count = 0; R_DEBUG_REG_T *regs = (R_DEBUG_REG_T*)buf; unsigned int gp_count = R_DEBUG_STATE_SZ; ret = task_threads (pid_to_task (dbg->pid), &inferior_threads, &inferior_thread_count); if (ret != KERN_SUCCESS) { eprintf ("debug_getregs\n"); return R_FALSE; } /* TODO: thread cannot be selected */ if (inferior_thread_count > 0) { gp_count = ((dbg->bits == R_SYS_BITS_64)) ? 44 : 16; // XXX: kinda spaguetti coz multi-arch int tid = inferior_threads[0]; #if __i386__ || __x86_64__ switch (type) { case R_REG_TYPE_DRX: if (dbg->bits == R_SYS_BITS_64) { ret = THREAD_SET_STATE(x86_DEBUG_STATE64); } else { ret = THREAD_SET_STATE(x86_DEBUG_STATE32); } break; default: if (dbg->bits == R_SYS_BITS_64) { ret = THREAD_SET_STATE(x86_THREAD_STATE); } else { ret = THREAD_SET_STATE(i386_THREAD_STATE); } break; } #else ret = THREAD_SET_STATE(R_DEBUG_STATE_T); #endif if (ret != KERN_SUCCESS) { eprintf ("debug_setregs: Failed to set thread %d %d.error (%x). (%s)\n", (int)dbg->pid, pid_to_task (dbg->pid), (int)ret, MACH_ERROR_STRING (ret)); perror ("thread_set_state"); return R_FALSE; } } else { eprintf ("There are no threads!\n"); } return sizeof (R_DEBUG_REG_T); }
// s/inferior_task/port/ static unsigned int debug_attach(int pid) { task_t task = pid_to_task (pid); if (!task) return 0; //eprintf ("pid: %d\ntask: %d\n", pid, task); return task; }
static int __system(RIO *io, RIODesc *fd, const char *cmd) { RIOMach *riom = (RIOMach*)fd->data; //printf("ptrace io command (%s)\n", cmd); /* XXX ugly hack for testing purposes */ if (!strcmp (cmd, "pid")) { if (!cmd[3]) { int pid = RIOMACH_PID (fd->data); eprintf ("%d\n", pid); return 0; } int pid = atoi (cmd+4); if (pid != 0) { task_t task = pid_to_task (pid); if (task != -1) { eprintf ("PID=%d\n", pid); riom->pid = pid; riom->task = task; return 0; } } eprintf ("io_mach_system: Invalid pid %d\n", pid); return 1; } else eprintf ("Try: '=!pid'\n"); return 1; }
static bool xnu_save_exception_ports (int pid) { kern_return_t kr; task_t task = pid_to_task (pid); ex.count = (sizeof (ex.ports) / sizeof (ex.ports[0])); kr = task_get_exception_ports (task, EXC_MASK_ALL, ex.masks, &ex.count, ex.ports, ex.behaviors, ex.flavors); return (kr == KERN_SUCCESS); }
static int tsk_pagesize(RIODesc *desc) { int tid = __get_pid (desc); task_t task = pid_to_task (desc, tid); static vm_size_t pagesize = 0; return pagesize ? pagesize : (host_page_size (task, &pagesize) == KERN_SUCCESS) ? pagesize : 4096; }
static ut64 getNextValid(RIO *io, RIODesc *fd, ut64 addr) { struct vm_region_submap_info_64 info; vm_address_t address = MACH_VM_MIN_ADDRESS; vm_size_t size = (vm_size_t) 0; vm_size_t osize = (vm_size_t) 0; natural_t depth = 0; kern_return_t kr; int tid = RIOMACH_PID (fd->data); task_t task = pid_to_task (tid); ut64 lower = addr; #if __arm64__ || __aarch64__ size = osize = 16384; // acording to frida #else size = osize = 4096; #endif if (the_lower) { if (addr < the_lower) return the_lower; return addr; } for (;;) { mach_msg_type_number_t info_count; info_count = VM_REGION_SUBMAP_INFO_COUNT_64; memset (&info, 0, sizeof (info)); kr = vm_region_recurse_64 (task, &address, &size, &depth, (vm_region_recurse_info_t) &info, &info_count); if (kr != KERN_SUCCESS) { break; } if (lower == addr) { lower = address; } if (info.is_submap) { depth++; continue; } if (addr >= address && addr < address + size) { return addr; } if (address < lower) { lower = address; } if (size < 1) { size = osize; // f**k } address += size; size = 0; } the_lower = lower; return lower; }
//FIXME this will not compile static bool xnu_save_exception_ports (RDebug *dbg) { kern_return_t kr; task_t task = pid_to_task (dbg->pid); dbg->ex->count = (sizeof (dbg->ex->ports) / sizeof (dbg->ex->ports[0])); if (task == -1) { perror ("xnu_save_exception_ports: pid_to_task:"); return false; } kr = task_get_exception_ports (task, EXC_MASK_ALL, dbg->ex->masks, &dbg->ex->count, dbg->ex->ports, dbg->ex->behaviors, dbg->ex->flavors); return (kr == KERN_SUCCESS); }
int xnu_map_dealloc (RDebug *dbg, ut64 addr, int size) { int ret; ret = vm_deallocate (pid_to_task (dbg->tid), (vm_address_t)addr, (vm_size_t)size); if (ret != KERN_SUCCESS) { printf("vm_deallocate failed\n"); return R_FALSE; } return R_TRUE; }
static RIODesc *__open(RIO *io, const char *file, int rw, int mode) { RIODesc *ret = NULL; RIOMach *riom; const char *pidfile; char *pidpath, *endptr; int pid; task_t task; if (!__plugin_open (io, file, 0)) { return NULL; } pidfile = file + (file[0] == 'a' ? 9 : 7); pid = (int)strtol (pidfile, &endptr, 10); if (endptr == pidfile || pid < 0) { return NULL; } task = pid_to_task (pid); if (task == -1) { return NULL; } if (!task) { if (pid > 0 && io->referer && !strncmp (io->referer, "dbg://", 6)) { eprintf ("Child killed\n"); kill (pid, 9); } switch (errno) { case EPERM: eprintf ("Operation not permitted\n"); break; case EINVAL: perror ("ptrace: Cannot attach"); eprintf ("Possibly unsigned r2. Please see doc/osx.md\n"); eprintf ("ERRNO: %d (EINVAL)\n", errno); break; default: eprintf ("unknown error in debug_attach\n"); break; } return NULL; } riom = R_NEW0 (RIOMach); riom->pid = pid; riom->task = task; // sleep 1s to get proper path (program name instead of ls) (racy) pidpath = pid ? r_sys_pid_to_path (pid) : strdup ("kernel"); ret = r_io_desc_new (&r_io_plugin_mach, riom->pid, pidpath, rw | R_IO_EXEC, mode, riom); free (pidpath); return ret; }
int xnu_map_protect (RDebug *dbg, ut64 addr, int size, int perms) { int ret; // TODO: align pointers ret = vm_protect (pid_to_task (dbg->tid), (vm_address_t)addr, (vm_size_t)size, (boolean_t)0, /* maximum protection */ VM_PROT_COPY|perms); //unix_prot_to_darwin (perms)); if (ret != KERN_SUCCESS) { printf("vm_protect failed\n"); return R_FALSE; } return R_TRUE; }
static bool xnu_restore_exception_ports (int pid) { kern_return_t kr; int i; task_t task = pid_to_task (pid); for (i = 0; i < ex.count; i++) { kr = task_set_exception_ports (task, ex.masks[i], ex.ports[i], ex.behaviors[i], ex.flavors[i]); if (kr != KERN_SUCCESS) { eprintf ("fail to restore exception ports\n"); return false; } } return true; }
static bool validate_mach_message (RDebug *dbg, exc_msg *msg) { kern_return_t kr; #if __POWERPC__ return false; #else /*check if the message is for us*/ if (msg->hdr.msgh_local_port != ex.exception_port) return false; /*gdb from apple check this so why not us*/ if (!(msg->hdr.msgh_bits & MACH_MSGH_BITS_COMPLEX)) return false; /*mach exception we are interested*/ //XXX for i386 this id seems to be different if (msg->hdr.msgh_id > 2405 || msg->hdr.msgh_id < 2401) return false; /* check descriptors. */ if (msg->hdr.msgh_size < sizeof (mach_msg_header_t) + sizeof (mach_msg_body_t) + 2 * sizeof (mach_msg_port_descriptor_t) + sizeof (NDR_record_t) + sizeof (exception_type_t) + sizeof (mach_msg_type_number_t) + sizeof (mach_exception_data_t)) return false; /* check data representation. */ if (msg->NDR.mig_vers != NDR_PROTOCOL_2_0 || msg->NDR.if_vers != NDR_PROTOCOL_2_0 || msg->NDR.mig_encoding != NDR_record.mig_encoding || msg->NDR.int_rep != NDR_record.int_rep || msg->NDR.char_rep != NDR_record.char_rep || msg->NDR.float_rep != NDR_record.float_rep) { return false; } if (pid_to_task (dbg->pid) != msg->task.name) { //we receive a exception from an unknown process this could //happen if the child fork, as the created process will inherit //its exception port /*we got new rights to the task, get rid of it.*/ kr = mach_port_deallocate (mach_task_self (), msg->task.name); if (kr != KERN_SUCCESS) { eprintf ("validate_mach_message: failed to deallocate task port\n"); } kr = mach_port_deallocate (mach_task_self (), msg->thread.name); if (kr != KERN_SUCCESS) { eprintf ("validate_mach_message2: failed to deallocated task port\n"); } return false; } return true; #endif }
static int xnu_get_bits(RDebug *dbg) { struct task_dyld_info info; mach_msg_type_number_t count; kern_return_t kr; count = TASK_DYLD_INFO_COUNT; task_t task = pid_to_task (dbg->tid); kr = task_info (task, TASK_DYLD_INFO, (task_info_t) &info, &count); if (kr != KERN_SUCCESS) return 0; if (info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_64) { return 64; } return 32; // 16 for ARM? }
static bool xnu_create_exception_thread(RDebug *dbg) { kern_return_t kr; int ret; mach_port_t exception_port = MACH_PORT_NULL; // Got the mach port for the current process mach_port_t task_self = mach_task_self (); task_t task = pid_to_task (dbg->pid); if (task == -1) { eprintf ("error to get task for the debugging process" " xnu_start_exception_thread\n"); return false; } if (!MACH_PORT_VALID (task_self)) { eprintf ("error to get the task for the current process" " xnu_start_exception_thread\n"); return false; } // Allocate an exception port that we will use to track our child process kr = mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &exception_port); RETURN_ON_MACH_ERROR ("error to allocate mach_port exception\n", R_FALSE); // Add the ability to send messages on the new exception port kr = mach_port_insert_right (task_self, exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); RETURN_ON_MACH_ERROR ("error to allocate insert right\n", R_FALSE); // Save the original state of the exception ports for our child process ret = xnu_save_exception_ports (dbg); if (ret == R_FALSE) { eprintf ("error to save exception port info\n"); return false; } // Set the ability to get all exceptions on this port kr = task_set_exception_ports (task, EXC_MASK_ALL, exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); RETURN_ON_MACH_ERROR ("error to set port to receive exceptions\n", R_FALSE); // Create the exception thread //TODO where to save the exception thread //TODO see options pthread_create ret = pthread_create (&dbg->ex->thread, NULL, xnu_exception_thread, dbg); if (ret) { perror ("pthread_create"); return false; } return true; }
static int __system(RIO *io, RIODesc *fd, const char *cmd) { RIOMach *riom; if (!io || !fd || cmd || !fd->data) { return 0; } riom = (RIOMach*)fd->data; /* XXX ugly hack for testing purposes */ if (!strncmp (cmd, "perm", 4)) { int perm = r_str_rwx (cmd + 4); if (perm) { int pagesize = tsk_pagesize(riom); tsk_setperm (io, riom->task, io->off, pagesize, perm); } else { eprintf ("Usage: =!perm [rwx]\n"); } return 0; } if (!strncmp (cmd, "pid", 3)) { const char *pidstr = cmd + 3; int pid = -1; if (*pidstr) { int pid = RIOMACH_PID (fd->data); eprintf ("%d\n", pid); return 0; } if (!strcmp (pidstr, "0")) { pid = 0; } else { pid = atoi (pidstr); if (!pid) pid = -1; } if (pid != -1) { task_t task = pid_to_task (pid); if (task != -1) { eprintf ("PID=%d\n", pid); riom->pid = pid; riom->task = task; return 0; } } eprintf ("io_mach_system: Invalid pid %d\n", pid); } else { eprintf ("Try: '=!pid' or '=!perm'\n"); } return 1; }
bool xnu_create_exception_thread(RDebug *dbg) { #if __POWERPC__ return false; #else kern_return_t kr; mach_port_t exception_port = MACH_PORT_NULL; mach_port_t req_port; // Got the mach port for the current process mach_port_t task_self = mach_task_self (); task_t task = pid_to_task (dbg->pid); if (!task) { eprintf ("error to get task for the debuggee process" " xnu_start_exception_thread\n"); return false; } if (!MACH_PORT_VALID (task_self)) { eprintf ("error to get the task for the current process" " xnu_start_exception_thread\n"); return false; } // Allocate an exception port that we will use to track our child process kr = mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &exception_port); RETURN_ON_MACH_ERROR ("error to allocate mach_port exception\n", false); // Add the ability to send messages on the new exception port kr = mach_port_insert_right (task_self, exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); RETURN_ON_MACH_ERROR ("error to allocate insert right\n", false); // Atomically swap out (and save) the child process's exception ports // for the one we just created. We'll want to receive all exceptions. ex.count = (sizeof (ex.ports) / sizeof (*ex.ports)); kr = task_swap_exception_ports (task, EXC_MASK_ALL, exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE, ex.masks, &ex.count, ex.ports, ex.behaviors, ex.flavors); RETURN_ON_MACH_ERROR ("failed to swap exception ports\n", false); //get notification when process die kr = mach_port_request_notification (task_self, task, MACH_NOTIFY_DEAD_NAME, 0, exception_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &req_port); if (kr != KERN_SUCCESS) { eprintf ("Termination notification request failed\n"); } ex.exception_port = exception_port; return true; #endif }
RList *xnu_thread_list (RDebug *dbg, int pid, RList *list) { #if __arm__ #define OSX_PC state.__pc #elif __arm64__ #define OSX_PC state.__pc #elif __POWERPC__ #define OSX_PC state.srr0 #elif __x86_64__ #define OSX_PC state.__rip #undef OSX_PC #define OSX_PC state.x64[REG_PC] #else #define OSX_PC state.__eip #undef OSX_PC #define OSX_PC state.x32[REG_PC] #endif int i, tid; //, err; //unsigned int gp_count; static thread_array_t inferior_threads = NULL; static unsigned int inferior_thread_count = 0; R_DEBUG_REG_T state; if (task_threads (pid_to_task (pid), &inferior_threads, &inferior_thread_count) != KERN_SUCCESS) { eprintf ("Failed to get list of task's threads.\n"); return list; } for (i = 0; i < inferior_thread_count; i++) { tid = inferior_threads[i]; /* XXX overflow here gp_count = R_DEBUG_STATE_SZ; //sizeof (R_DEBUG_REG_T); if ((err = thread_get_state (tid, R_DEBUG_STATE_T, (thread_state_t) &state, &gp_count)) != KERN_SUCCESS) { // eprintf ("debug_list_threads: %s\n", MACH_ERROR_STRING(err)); OSX_PC = 0; } */ r_list_append (list, r_debug_pid_new ("???", tid, 's', OSX_PC)); } return list; }
// s/inferior_task/port/ static int debug_attach(int pid) { task_t task = pid_to_task (pid); if (task == -1) return -1; eprintf ("pid: %d\ntask: %d\n", pid, task); #if 0 // TODO : move this code into debug if (task_threads (task, &inferior_threads, &inferior_thread_count) != KERN_SUCCESS) { eprintf ("Failed to get list of task's threads.\n"); return -1; } eprintf ("Thread count: %d\n", inferior_thread_count); #endif #if SUSPEND if (task_suspend (this->port) != KERN_SUCCESS) { eprintf ("cannot suspend task\n"); return -1; // R_FALSE } #endif /* is this required for arm ? */ #if EXCEPTION_PORT int exception_port; if (mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &exception_port) != KERN_SUCCESS) { eprintf ("Failed to create exception port.\n"); return -1; } if (mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) { eprintf ("Failed to acquire insertion rights on the port.\n"); return -1; } if (task_set_exception_ports(inferior_task, EXC_MASK_ALL, exception_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE) != KERN_SUCCESS) { eprintf ("Failed to set the inferior's exception ports.\n"); return -1; } #endif return task; }
static int __close(RIODesc *fd) { if (!fd) { return false; } RIODescData *iodd = fd->data; kern_return_t kr; if (!iodd) { return false; } if (iodd->magic != R_MACH_MAGIC) { return false; } task_t task = pid_to_task (fd, iodd->pid); kr = mach_port_deallocate (mach_task_self (), task); if (kr != KERN_SUCCESS) { perror ("__close io_mach"); } R_FREE (fd->data); return kr == KERN_SUCCESS; }
static int mach_write_at(RIO *io, RIODesc *desc, const void *buf, int len, ut64 addr) { vm_address_t vaddr = addr; vm_address_t pageaddr; vm_size_t pagesize; vm_size_t total_size; int operms = 0; int pid = __get_pid (desc); if (!desc || pid < 0) { return 0; } task_t task = pid_to_task (desc, pid); if (len < 1 || task_is_dead (desc, task)) { return 0; } pageaddr = tsk_getpagebase (desc, addr); pagesize = tsk_pagesize (desc); total_size = (len > pagesize) ? pagesize * (1 + (len / pagesize)) : pagesize; if (tsk_write (task, vaddr, buf, len)) { return len; } operms = tsk_getperm (io, task, pageaddr); if (!tsk_setperm (io, task, pageaddr, total_size, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY)) { eprintf ("io.mach: Cannot set page perms for %d byte(s) at 0x%08" PFMT64x"\n", (int)pagesize, (ut64)pageaddr); return -1; } if (!tsk_write (task, vaddr, buf, len)) { eprintf ("io.mach: Cannot write on memory\n"); len = -1; } if (operms) { if (!tsk_setperm (io, task, pageaddr, total_size, operms)) { eprintf ("io.mach: Cannot restore page perms\n"); return -1; } } return len; }
RDebugMap *xnu_map_alloc (RDebug *dbg, ut64 addr, int size) { RDebugMap *map = NULL; kern_return_t ret; unsigned char *base = (unsigned char *)addr; boolean_t anywhere = !VM_FLAGS_ANYWHERE; if (addr == -1) anywhere = VM_FLAGS_ANYWHERE; ret = vm_allocate (pid_to_task (dbg->tid), (vm_address_t*)&base, (vm_size_t)size, anywhere); if (ret != KERN_SUCCESS) { printf("vm_allocate failed\n"); return NULL; } r_debug_map_sync (dbg); // update process memory maps map = r_debug_map_get (dbg, (ut64)base); return map; }
static RList *ios_dbg_maps(RDebug *dbg, int only_modules) { boolt contiguous = R_FALSE; ut32 oldprot = UT32_MAX; ut32 oldmaxprot = UT32_MAX; char buf[1024]; char module_name[MAXPATHLEN]; mach_vm_address_t address = MACH_VM_MIN_ADDRESS; mach_vm_size_t size = (mach_vm_size_t) 0; mach_vm_size_t osize = (mach_vm_size_t) 0; natural_t depth = 0; int tid = dbg->pid; task_t task = pid_to_task (tid); RDebugMap *mr = NULL; RList *list = NULL; int i = 0; if (only_modules) { return xnu_dbg_modules (dbg); } #if __arm64__ || __aarch64__ size = osize = 16384; // acording to frida #else size = osize = 4096; #endif #if 0 if (dbg->pid == 0) { vm_address_t base = get_kernel_base (task); eprintf ("Kernel Base Address: 0x%"PFMT64x"\n", (ut64)base); return NULL; } #endif kern_return_t kr; for (;;) { struct vm_region_submap_info_64 info; mach_msg_type_number_t info_count; info_count = VM_REGION_SUBMAP_INFO_COUNT_64; memset (&info, 0, sizeof (info)); kr = mach_vm_region_recurse (task, &address, &size, &depth, (vm_region_recurse_info_t) &info, &info_count); if (kr != KERN_SUCCESS) { //eprintf ("Cannot kern succ recurse\n"); break; } if (info.is_submap) { depth++; continue; } if (!list) { list = r_list_new (); //list->free = (RListFree*)r_debug_map_free; } { module_name[0] = 0; int ret = proc_regionfilename (tid, address, module_name, sizeof (module_name)); module_name[ret] = 0; } #if 0 oldprot = info.protection; oldmaxprot = info.max_protection; // contiguous pages seems to hide some map names if (mr) { if (address == mr->addr + mr->size) { if (oldmaxprot == info.max_protection) { contiguous = R_FALSE; } else if (oldprot != UT32_MAX && oldprot == info.protection) { /* expand region */ mr->size += size; contiguous = R_TRUE; } else { contiguous = R_FALSE; } } else { contiguous = R_FALSE; } } else contiguous = R_FALSE; //if (info.max_protection == oldprot && !contiguous) { #endif if (1) { #define xwr2rwx(x) ((x&1)<<2) | (x&2) | ((x&4)>>2) // XXX: if its shared, it cannot be read? snprintf (buf, sizeof (buf), "%s %02x %s%s%s%s%s %s depth=%d", r_str_rwx_i (xwr2rwx (info.max_protection)), i, unparse_inheritance (info.inheritance), info.user_tag? " user": "", info.is_submap? " sub": "", info.inheritance? " inherit": "", info.is_submap ? " submap": "", module_name, depth); //info.shared ? "shar" : "priv", //info.reserved ? "reserved" : "not-reserved", //""); //module_name); mr = r_debug_map_new (buf, address, address+size, xwr2rwx (info.protection), 0); if (mr == NULL) { eprintf ("Cannot create r_debug_map_new\n"); break; } mr->file = strdup (module_name); i++; r_list_append (list, mr); } if (size<1) { eprintf ("EFUCK\n"); size = osize; // f**k } address += size; size = 0; } return list; }
static RList *xnu_dbg_modules(RDebug *dbg) { struct task_dyld_info info; mach_msg_type_number_t count; kern_return_t kr; int size, info_array_count, info_array_size, i; ut64 info_array_address; void *info_array = NULL; void *header_data = NULL; char file_path[MAXPATHLEN]; count = TASK_DYLD_INFO_COUNT; task_t task = pid_to_task (dbg->tid); ut64 addr, file_path_address; RDebugMap *mr = NULL; RList *list = NULL; kr = task_info (task, TASK_DYLD_INFO, (task_info_t) &info, &count); if (kr != KERN_SUCCESS) return NULL; if (info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_64) { DyldAllImageInfos64 all_infos; dbg->iob.read_at (dbg->iob.io, info.all_image_info_addr, (ut8*)&all_infos, sizeof (DyldAllImageInfos64)); info_array_count = all_infos.info_array_count; info_array_size = info_array_count * DYLD_IMAGE_INFO_64_SIZE; info_array_address = all_infos.info_array; } else { DyldAllImageInfos32 all_info; dbg->iob.read_at (dbg->iob.io, info.all_image_info_addr, (ut8*)&all_info, sizeof (DyldAllImageInfos32)); info_array_count = all_info.info_array_count; info_array_size = info_array_count * DYLD_IMAGE_INFO_32_SIZE; info_array_address = all_info.info_array; } if (info_array_address == 0) { return NULL; } info_array = malloc (info_array_size); if (!info_array) { eprintf ("Cannot allocate info_array_size %d\n", info_array_size); return NULL; } dbg->iob.read_at (dbg->iob.io, info_array_address, info_array, info_array_size); list = r_list_new (); for (i=0; i < info_array_count; i++) { if (info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_64) { DyldImageInfo64 * info = info_array + (i * DYLD_IMAGE_INFO_64_SIZE); addr = info->image_load_address; file_path_address = info->image_file_path; } else { DyldImageInfo32 * info = info_array + (i * DYLD_IMAGE_INFO_32_SIZE); addr = info->image_load_address; file_path_address = info->image_file_path; } dbg->iob.read_at (dbg->iob.io, file_path_address, (ut8*)file_path, MAXPATHLEN); //eprintf ("--> %d 0x%08"PFMT64x" %s\n", i, addr, file_path); size = mach0_size (dbg, addr); mr = r_debug_map_new (file_path, addr, addr+size, 7, 0); if (mr == NULL) { eprintf ("Cannot create r_debug_map_new\n"); break; } mr->file = strdup (file_path); r_list_append (list, mr); } free (info_array); return list; }
static bool task_is_dead (int pid) { unsigned int count = 0; kern_return_t kr = mach_port_get_refs (mach_task_self (), pid_to_task (pid), MACH_PORT_RIGHT_SEND, &count); return (kr != KERN_SUCCESS || !count); }
static RIODesc *__open(RIO *io, const char *file, int rw, int mode) { RIODesc *ret = NULL; RIOMach *riom = NULL; const char *pidfile; char *pidpath, *endptr; int pid; task_t task; if (!__plugin_open (io, file, false) && !__plugin_open (io, (const char *)&file[1], false)) { return NULL; } pidfile = file + (file[0] == 'a' ? 9 : (file[0] == 's' ? 8 : 7)); pid = (int)strtol (pidfile, &endptr, 10); if (endptr == pidfile || pid < 0) { return NULL; } task = pid_to_task (NULL, pid); if (task == -1) { return NULL; } if (!task) { if (pid > 0 && !strncmp (file, "smach://", 8)) { kill (pid, SIGKILL); eprintf ("Child killed\n"); } #if 0 /* this is broken, referer gets set in the riodesc after this function returns the riodesc * the pid > 0 check doesn't seem to be reasonable to me too * what was this intended to check anyway ? */ if (pid > 0 && io->referer && !strncmp (io->referer, "dbg://", 6)) { eprintf ("Child killed\n"); kill (pid, SIGKILL); } #endif switch (errno) { case EPERM: eprintf ("Operation not permitted\n"); break; case EINVAL: perror ("ptrace: Cannot attach"); eprintf ("Possibly unsigned r2. Please see doc/macos.md\n"); eprintf ("ERRNO: %d (EINVAL)\n", errno); break; default: eprintf ("unknown error in debug_attach\n"); break; } return NULL; } RIODescData *iodd = R_NEW0 (RIODescData); if (iodd) { iodd->pid = pid; iodd->tid = pid; iodd->data = NULL; } riom = R_NEW0 (RIOMach); if (!riom) { return NULL; } riom->task = task; iodd->magic = r_str_hash ("mach"); iodd->data = riom; // sleep 1s to get proper path (program name instead of ls) (racy) pidpath = pid ? r_sys_pid_to_path (pid) : strdup ("kernel"); if (!strncmp (file, "smach://", 8)) { ret = r_io_desc_new (io, &r_io_plugin_mach, &file[1], rw | R_PERM_X, mode, iodd); } else { ret = r_io_desc_new (io, &r_io_plugin_mach, file, rw | R_PERM_X, mode, iodd); } ret->name = pidpath; return ret; }
int xnu_reg_read (RDebug *dbg, int type, ut8 *buf, int size) { int ret; int pid = dbg->pid; thread_array_t inferior_threads = NULL; unsigned int inferior_thread_count = 0; R_DEBUG_REG_T *regs = (R_DEBUG_REG_T*)buf; unsigned int gp_count = R_DEBUG_STATE_SZ; int tid = dbg->tid; ret = task_threads (pid_to_task (pid), &inferior_threads, &inferior_thread_count); if (ret != KERN_SUCCESS) { return R_FALSE; } if (tid < 0 || tid >= inferior_thread_count) { dbg->tid = tid = dbg->pid; } if (tid == dbg->pid) { tid = 0; } if (inferior_thread_count > 0) { /* TODO: allow to choose the thread */ gp_count = R_DEBUG_STATE_SZ; // XXX: kinda spaguetti coz multi-arch #if __i386__ || __x86_64__ switch (type) { case R_REG_TYPE_SEG: case R_REG_TYPE_FLG: case R_REG_TYPE_GPR: ret = THREAD_GET_STATE ((dbg->bits == R_SYS_BITS_64)? x86_THREAD_STATE: i386_THREAD_STATE); break; case R_REG_TYPE_DRX: ret = THREAD_GET_STATE ((dbg->bits == R_SYS_BITS_64)? x86_DEBUG_STATE64: x86_DEBUG_STATE32); break; } #elif __arm__ || __arm64__ || __aarch64__ switch (type) { case R_REG_TYPE_FLG: case R_REG_TYPE_GPR: if (dbg->bits == R_SYS_BITS_64) { ret = THREAD_GET_STATE (ARM_THREAD_STATE64); } else { ret = THREAD_GET_STATE (ARM_THREAD_STATE); } break; case R_REG_TYPE_DRX: if (dbg->bits == R_SYS_BITS_64) { ret = THREAD_GET_STATE (ARM_DEBUG_STATE64); } else { ret = THREAD_GET_STATE (ARM_DEBUG_STATE32); } break; } #else eprintf ("Unknown architecture\n"); #endif if (ret != KERN_SUCCESS) { eprintf ( "debug_getregs: Failed to get thread %d %d.error (%x). (%s)\n", (int)pid, pid_to_task (pid), (int)ret, MACH_ERROR_STRING (ret) ); perror ("thread_get_state"); return R_FALSE; } } else eprintf ("There are no threads!\n"); return sizeof (R_DEBUG_REG_T); }
static bool decode_exception_message (RDebug *dbg, exc_msg *msg) { kern_return_t kret; int ret; /* check if the message is for us */ if (msg->hdr.msgh_local_port != ex.exception_port) return false; //XXX gdb from apple check this dunno why /* check message header. */ if (!(msg->hdr.msgh_bits & MACH_MSGH_BITS_COMPLEX)) return false; /* check descriptors. */ if (msg->hdr.msgh_size < sizeof (mach_msg_header_t) + sizeof (mach_msg_body_t) + 2 * sizeof (mach_msg_port_descriptor_t) + sizeof (NDR_record_t) + sizeof (exception_type_t) + sizeof (mach_msg_type_number_t) + sizeof (mach_exception_data_t)) return false; /* check data representation. */ if (msg->NDR.mig_vers != NDR_PROTOCOL_2_0 || msg->NDR.if_vers != NDR_PROTOCOL_2_0 || msg->NDR.mig_encoding != NDR_record.mig_encoding || msg->NDR.int_rep != NDR_record.int_rep || msg->NDR.char_rep != NDR_record.char_rep || msg->NDR.float_rep != NDR_record.float_rep) return -1; /* We got new rights to the task, get rid of it.*/ kret = mach_port_deallocate (mach_task_self (), msg->task.name); if (kret != KERN_SUCCESS) { eprintf ("faild to deallocate task port " "decode_exception_message\n"); } if (pid_to_task (dbg->pid) != msg->task.name) { //we receive a exception from an unkown process this could //happen if the child fork, as the created process will inherit //its exception port //XXX should we manage this in somehow? mig_reply_error_t reply; kret = mach_port_deallocate (mach_task_self (), msg->thread.name); if (kret != KERN_SUCCESS) { eprintf ("failed to deallocate thread port " "decode_exception_message\n"); return false; } encode_reply (&reply, &msg->hdr, KERN_SUCCESS); kret = mach_msg (&reply.Head, MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply.Head.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (kret != KERN_SUCCESS) { eprintf ("failed to reply decode_exception_message\n"); return false; } } kret = mach_port_deallocate (mach_task_self (), msg->thread.name); if (kret != KERN_SUCCESS) { eprintf ("failed to deallocate thread port " "decode_exception_message two\n"); return false; } decode_exception_type (msg->exception); ret = write (exc_pipe[1], &msg->exception, sizeof(int)); if (ret == -1) eprintf ("failed to write exception into the pipe\n"); return true; }
static int __read(RIO *io, RIODesc *desc, ut8 *buf, int len) { vm_size_t size = 0; int blen, err, copied = 0; int blocksize = 32; RIODescData *dd = (RIODescData *)desc->data; if (!io || !desc || !buf || !dd) { return -1; } if (dd ->magic != r_str_hash ("mach")) { return -1; } memset (buf, 0xff, len); int pid = __get_pid (desc); task_t task = pid_to_task (desc, pid); if (task_is_dead (desc, pid)) { return -1; } if (pid == 0) { if (io->off < 4096) { return len; } } copied = getNextValid (io, desc, io->off) - io->off; if (copied < 0) { copied = 0; } while (copied < len) { blen = R_MIN ((len - copied), blocksize); //blen = len; err = vm_read_overwrite (task, (ut64)io->off + copied, blen, (pointer_t)buf + copied, &size); switch (err) { case KERN_PROTECTION_FAILURE: //eprintf ("r_io_mach_read: kern protection failure.\n"); break; case KERN_INVALID_ADDRESS: if (blocksize == 1) { memset (buf+copied, 0xff, len-copied); return size+copied; } blocksize = 1; blen = 1; buf[copied] = 0xff; break; } if (err == -1 || size < 1) { return -1; } if (size == 0) { if (blocksize == 1) { memset (buf + copied, 0xff, len - copied); return len; } blocksize = 1; blen = 1; buf[copied] = 0xff; } copied += blen; } return len; }