Example #1
0
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;
}
Example #2
0
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);

}
Example #3
0
// 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;
}
Example #4
0
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;
}
Example #5
0
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);
}
Example #6
0
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;
}
Example #7
0
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;
}
Example #8
0
//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);
}
Example #9
0
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;

}
Example #10
0
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;
}
Example #11
0
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;
}
Example #12
0
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;
}
Example #13
0
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
}
Example #14
0
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?
}
Example #15
0
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;
}
Example #16
0
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;
}
Example #17
0
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
}
Example #18
0
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;

}
Example #19
0
// 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;
}
Example #20
0
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;
}
Example #21
0
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;
}
Example #22
0
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;

}
Example #23
0
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;
}
Example #24
0
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;
}
Example #25
0
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);
}
Example #26
0
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;
}
Example #27
0
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);
}
Example #28
0
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;

}
Example #29
0
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;
}