/* radare - LGPL - Copyright 2009-2018 - pancake */ #include #if DEBUGGER #include #include #include #include #include #include #include #include #include "linux_debug.h" #include "../procfs.h" #include #include char *linux_reg_profile (RDebug *dbg) { #if __arm__ #include "reg/linux-arm.h" #elif __arm64__ || __aarch64__ #include "reg/linux-arm64.h" #elif __MIPS__ || __mips__ if ((dbg->bits & R_SYS_BITS_32) && (dbg->bp->endian == 1)) { #include "reg/linux-mips.h" } else { #include "reg/linux-mips64.h" } #elif (__i386__ || __x86_64__) if (dbg->bits & R_SYS_BITS_32) { #if __x86_64__ #include "reg/linux-x64-32.h" #else #include "reg/linux-x86.h" #endif } else { #include "reg/linux-x64.h" } #elif __powerpc__ if (dbg->bits & R_SYS_BITS_32) { #include "reg/linux-ppc.h" } else { #include "reg/linux-ppc64.h" } #else #error "Unsupported Linux CPU" #endif } static void linux_detach_all (RDebug *dbg); static char *read_link (int pid, const char *file); static int linux_attach_single_pid (RDebug *dbg, int ptid); static void linux_attach_all (RDebug *dbg); static void linux_remove_thread (RDebug *dbg, int pid); static void linux_add_and_attach_new_thread (RDebug *dbg, int tid); static int linux_stop_process(int pid); int linux_handle_signals (RDebug *dbg) { siginfo_t siginfo = {0}; int ret = ptrace (PTRACE_GETSIGINFO, dbg->pid, 0, &siginfo); if (ret == -1) { /* ESRCH means the process already went away :-/ */ if (errno == ESRCH) { dbg->reason.type = R_DEBUG_REASON_DEAD; return true; } r_sys_perror ("ptrace GETSIGINFO"); return false; } if (siginfo.si_signo > 0) { //siginfo_t newsiginfo = {0}; //ptrace (PTRACE_SETSIGINFO, dbg->pid, 0, &siginfo); dbg->reason.type = R_DEBUG_REASON_SIGNAL; dbg->reason.signum = siginfo.si_signo; //dbg->stopaddr = siginfo.si_addr; //dbg->errno = siginfo.si_errno; // siginfo.si_code -> HWBKPT, USER, KERNEL or WHAT #warning DO MORE RDEBUGREASON HERE switch (dbg->reason.signum) { case SIGTRAP: { if (dbg->glob_libs || dbg->glob_unlibs) { ut64 pc_addr = r_debug_reg_get (dbg, "PC"); RBreakpointItem *b = r_bp_get_at (dbg->bp, pc_addr - dbg->bpsize); if (b && b->internal) { char *p = strstr (b->data, "dbg."); if (p) { if (r_str_startswith (p, "dbg.libs")) { const char *name; if (strstr (b->data, "sym.imp.dlopen")) { name = r_reg_get_name (dbg->reg, R_REG_NAME_A0); } else { name = r_reg_get_name (dbg->reg, R_REG_NAME_A1); } b->data = r_str_appendf (b->data, ";ps@r:%s", name); dbg->reason.type = R_DEBUG_REASON_NEW_LIB; } else if (r_str_startswith (p, "dbg.unlibs")) { dbg->reason.type = R_DEBUG_REASON_EXIT_LIB; } } } } if (dbg->reason.type != R_DEBUG_REASON_NEW_LIB && dbg->reason.type != R_DEBUG_REASON_EXIT_LIB) { dbg->reason.bp_addr = (ut64)(size_t)siginfo.si_addr; dbg->reason.type = R_DEBUG_REASON_BREAKPOINT; } } break; case SIGABRT: // 6 / SIGIOT // SIGABRT dbg->reason.type = R_DEBUG_REASON_ABORT; break; case SIGSEGV: dbg->reason.type = R_DEBUG_REASON_SEGFAULT; break; case SIGCHLD: dbg->reason.type = R_DEBUG_REASON_SIGNAL; default: break; } if (dbg->reason.signum != SIGTRAP) { eprintf ("[+] SIGNAL %d errno=%d addr=0x%08"PFMT64x " code=%d ret=%d\n", siginfo.si_signo, siginfo.si_errno, (ut64)siginfo.si_addr, siginfo.si_code, ret); } return true; } return false; } #if __ANDROID__ #undef PT_GETEVENTMSG #define PT_GETEVENTMSG #endif #ifdef PT_GETEVENTMSG /* * Handle PTRACE_EVENT_* * * Returns R_DEBUG_REASON_* * * If it's not something we handled, we return ..._UNKNOWN to * tell the caller to keep trying to figure out what to do. * * If something went horribly wrong, we return ..._ERROR; * * NOTE: This API was added in Linux 2.5.46 */ RDebugReasonType linux_ptrace_event (RDebug *dbg, int pid, int status) { ut32 pt_evt; #if __powerpc64__ || __arm64__ || __aarch64__ || __x86_64__ ut64 data; #else ut32 data; #endif /* we only handle stops with SIGTRAP here */ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) { return R_DEBUG_REASON_UNKNOWN; } pt_evt = status >> 16; switch (pt_evt) { case 0: /* NOTE: this case is handled by linux_handle_signals */ break; case PTRACE_EVENT_CLONE: if (dbg->trace_clone) { if (ptrace (PTRACE_GETEVENTMSG, pid, 0, &data) == -1) { r_sys_perror ("ptrace GETEVENTMSG"); return R_DEBUG_REASON_ERROR; } // eprintf ("PTRACE_EVENT_CLONE new_thread=%"PFMT64d"\n", (ut64)data); linux_add_and_attach_new_thread (dbg, (int)data); return R_DEBUG_REASON_NEW_TID; } break; case PTRACE_EVENT_FORK: if (dbg->trace_forks) { if (ptrace (PTRACE_GETEVENTMSG, pid, 0, &data) == -1) { r_sys_perror ("ptrace GETEVENTMSG"); return R_DEBUG_REASON_ERROR; } // eprintf ("PTRACE_EVENT_FORK new_pid=%"PFMT64d"\n", (ut64)data); dbg->forked_pid = data; // TODO: more handling here? /* we have a new process that we are already tracing */ return R_DEBUG_REASON_NEW_PID; } break; case PTRACE_EVENT_EXIT: if (ptrace (PTRACE_GETEVENTMSG, pid, 0, &data) == -1) { r_sys_perror ("ptrace GETEVENTMSG"); return R_DEBUG_REASON_ERROR; } //eprintf ("PTRACE_EVENT_EXIT pid=%d, status=0x%"PFMT64x"\n", pid, (ut64)data); return pid != dbg->pid ? R_DEBUG_REASON_EXIT_TID : R_DEBUG_REASON_EXIT_PID; default: eprintf ("Unknown PTRACE_EVENT encountered: %d\n", pt_evt); break; } return R_DEBUG_REASON_UNKNOWN; } #endif int linux_step(RDebug *dbg) { int ret = false; ut64 addr = r_debug_reg_get (dbg, "PC"); ret = ptrace (PTRACE_SINGLESTEP, dbg->pid, (void*)(size_t)addr, 0); //XXX(jjd): why?? //linux_handle_signals (dbg); if (ret == -1) { perror ("native-singlestep"); ret = false; } else { ret = true; } return ret; } bool linux_set_options(RDebug *dbg, int pid) { int traceflags = 0; if (dbg->trace_forks) { traceflags |= PTRACE_O_TRACEFORK; traceflags |= PTRACE_O_TRACEVFORK; traceflags |= PTRACE_O_TRACEVFORKDONE; } if (dbg->trace_clone) { traceflags |= PTRACE_O_TRACECLONE; } if (dbg->trace_execs) { traceflags |= PTRACE_O_TRACEEXEC; } if (dbg->trace_aftersyscall) { traceflags |= PTRACE_O_TRACEEXIT; } /* SIGTRAP | 0x80 on signal handler .. not supported on all archs */ traceflags |= PTRACE_O_TRACESYSGOOD; if (ptrace (PTRACE_SETOPTIONS, pid, 0, traceflags) == -1) { return false; } return true; } static void linux_detach_all (RDebug *dbg) { RList *th_list = dbg->threads; if (th_list) { RDebugPid *th; RListIter *it; r_list_foreach (th_list, it, th) { if (th->pid != dbg->main_pid) { if (ptrace (PTRACE_DETACH, th->pid, NULL, NULL) == -1) { perror ("PTRACE_DETACH"); } } } } // Detaching from main proc if (ptrace (PTRACE_DETACH, dbg->main_pid, NULL, NULL) == -1) { perror ("PTRACE_DETACH"); } } static void linux_remove_thread (RDebug *dbg, int pid) { RList *th_list = dbg->threads; if (th_list) { RDebugPid *th; RListIter *it; r_list_foreach (th_list, it, th) { if (th->pid == pid) { r_list_delete_data (th_list, th); dbg->n_threads--; } } } } void linux_attach_new_process (RDebug *dbg) { linux_detach_all (dbg); if (dbg->threads) { r_list_free (dbg->threads); dbg->threads = NULL; } int stopped = linux_stop_process (dbg->forked_pid); if (!stopped) { eprintf ("Could not stop pid (%d)\n", dbg->forked_pid); } linux_attach (dbg, dbg->forked_pid); r_debug_select (dbg, dbg->forked_pid, dbg->forked_pid); } RDebugReasonType linux_dbg_wait(RDebug *dbg, int my_pid) { RDebugReasonType reason = R_DEBUG_REASON_UNKNOWN; int pid = (dbg->continue_all_threads && dbg->n_threads) ? -1 : dbg->main_pid; int status, flags = __WALL; if (pid == -1) { flags |= WNOHANG; } repeat: for (;;) { int ret = waitpid (pid, &status, flags); if (ret < 0) { perror ("waitpid"); break; } else if (!ret) { flags &= ~WNOHANG; } else { int pid = ret; reason = linux_ptrace_event (dbg, pid, status); if (reason == R_DEBUG_REASON_EXIT_TID) { ptrace (PTRACE_CONT, pid, NULL, 0); goto repeat; } if (reason != R_DEBUG_REASON_UNKNOWN) { break; } if (WIFEXITED (status)) { eprintf ("child exited with status %d\n", WEXITSTATUS (status)); if (pid == dbg->main_pid) { reason = R_DEBUG_REASON_DEAD; } else { reason = R_DEBUG_REASON_EXIT_TID; linux_remove_thread (dbg, pid); } } else if (WIFSIGNALED (status)) { eprintf ("child received signal %d\n", WTERMSIG (status)); reason = R_DEBUG_REASON_SIGNAL; } else if (WIFSTOPPED (status)) { if (WSTOPSIG (status) != SIGTRAP && WSTOPSIG (status) != SIGSTOP) { eprintf ("child stopped with signal %d\n", WSTOPSIG (status)); reason = R_DEBUG_REASON_DEAD; } if (!linux_handle_signals (dbg)) { eprintf ("can't handle signals\n"); return R_DEBUG_REASON_ERROR; } reason = dbg->reason.type; #ifdef WIFCONTINUED } else if (WIFCONTINUED (status)) { eprintf ("child continued...\n"); reason = R_DEBUG_REASON_NONE; #endif } else if (status == 1) { eprintf ("EEK DEAD DEBUGEE!\n"); reason = R_DEBUG_REASON_DEAD; } else if (status == 0) { eprintf ("STATUS=0?!?!?!?\n"); reason = R_DEBUG_REASON_DEAD; } else { if (ret != pid) { reason = R_DEBUG_REASON_NEW_PID; } else { eprintf ("CRAP. returning from wait without knowing why...\n"); } } if (reason != R_DEBUG_REASON_UNKNOWN) { break; } } } return reason; } int match_pid(const void *pid_o, const void *th_o) { int pid = *(int *)pid_o; RDebug *th = (RDebug *)th_o; return pid == th->pid; } static void linux_add_and_attach_new_thread(RDebug *dbg, int tid) { int uid = getuid(); // XXX char info[1024] = {0}; RDebugPid *tid_info; if (!procfs_pid_slurp (tid, "status", info, sizeof (info))) { tid_info = fill_pid_info (info, NULL, tid); } else { tid_info = r_debug_pid_new ("new_path", tid, uid, 's', 0); } (void) linux_attach (dbg, tid); r_list_append (dbg->threads, tid_info); dbg->tid = tid; dbg->n_threads++; } static int linux_stop_process(int pid) { int status; int ret = syscall (__NR_tkill, pid, SIGSTOP); if (ret != -1) { ret = waitpid (pid, &status, __WALL); } return ret == pid; } static int linux_attach_single_pid(RDebug *dbg, int ptid) { int ret = 0; linux_set_options (dbg, ptid); ret = ptrace (PTRACE_ATTACH, ptid, NULL, NULL); return ret; } static RList *get_pid_thread_list (RDebug *dbg, int main_pid) { RList *list = r_list_new (); if (list) { list = linux_thread_list (main_pid, list); dbg->main_pid = main_pid; } return list; } static void linux_attach_all (RDebug *dbg) { int ret = linux_attach_single_pid (dbg, dbg->main_pid); if (ret != -1) { perror ("ptrace (PT_ATTACH)"); } RList *list = dbg->threads; if (list) { RDebugPid *th; RListIter *it; r_list_foreach (list, it, th) { if (th->pid && th->pid != dbg->main_pid) { ret = linux_attach_single_pid (dbg, th->pid); if (ret == -1) { eprintf ("PID %d\n", th->pid); perror ("ptrace (PT_ATTACH)"); } } } } } int linux_attach(RDebug *dbg, int pid) { // First time we run: We try to attach to all "possible" threads and to the main pid if (!dbg->threads) { dbg->threads = get_pid_thread_list (dbg, pid); linux_attach_all (dbg); } else { // This means we did a first run, so we probably attached to all possible threads already. // So check if the requested thread is being traced already. If yes: skip if (dbg->threads && !r_list_find (dbg->threads, &pid, &match_pid)) { goto out; } int ret = linux_attach_single_pid (dbg, pid); if (ret == -1) { // ignore perror ("ptrace (PT_ATTACH)"); } } out: return pid; } static char *read_link(int pid, const char *file) { char path[1024] = {0}; char buf[1024] = {0}; snprintf (path, sizeof (path), "/proc/%d/%s", pid, file); int ret = readlink (path, buf, sizeof (buf)); if (ret > 0) { buf[sizeof (buf) - 1] = '\0'; return strdup (buf); } return NULL; } RDebugInfo *linux_info(RDebug *dbg, const char *arg) { char proc_buff[1024]; RDebugInfo *rdi = R_NEW0 (RDebugInfo); if (!rdi) { return NULL; } RList *th_list; bool list_alloc = false; if (dbg->threads) { th_list = dbg->threads; } else { th_list = r_list_new (); list_alloc = true; if (th_list) { th_list = linux_thread_list (dbg->pid, th_list); } } RDebugPid *th; RListIter *it; bool found = false; r_list_foreach (th_list, it, th) { if (th->pid == dbg->pid) { found = true; break; } } rdi->pid = dbg->pid; rdi->tid = dbg->tid; rdi->uid = found ? th->uid : -1; rdi->gid = found ? th->gid : -1; rdi->cwd = read_link (rdi->pid, "cwd"); rdi->exe = read_link (rdi->pid, "exe"); snprintf (proc_buff, sizeof (proc_buff), "/proc/%d/cmdline", rdi->pid); rdi->cmdline = r_file_slurp (proc_buff, NULL); snprintf (proc_buff, sizeof (proc_buff), "/proc/%d/stack", rdi->pid); rdi->kernel_stack = r_file_slurp (proc_buff, NULL); rdi->status = found ? th->status : R_DBG_PROC_STOP; if (list_alloc) { r_list_free (th_list); } return rdi; } RDebugPid *fill_pid_info(const char *info, const char *path, int tid) { RDebugPid *pid_info = R_NEW0 (RDebugPid); if (!pid_info) { return NULL; } char *ptr = strstr (info, "State:"); if (ptr) { switch (*(ptr + 7)) { case 'R': pid_info->status = R_DBG_PROC_RUN; break; case 'S': pid_info->status = R_DBG_PROC_SLEEP; break; case 'T': case 't': pid_info->status = R_DBG_PROC_STOP; break; case 'Z': pid_info->status = R_DBG_PROC_ZOMBIE; break; case 'X': pid_info->status = R_DBG_PROC_DEAD; break; default: pid_info->status = R_DBG_PROC_SLEEP; break; } } ptr = strstr (info, "Uid:"); if (ptr) { pid_info->uid = atoi (ptr + 5); } ptr = strstr (info, "Gid:"); if (ptr) { pid_info->gid = atoi (ptr + 5); } pid_info->pid = tid; pid_info->path = path ? strdup (path) : NULL; pid_info->runnable = true; pid_info->pc = 0; return pid_info; } RList *linux_thread_list(int pid, RList *list) { int i, thid = 0; char *ptr, buf[1024]; if (!pid) { r_list_free (list); return NULL; } list->free = (RListFree)&r_debug_pid_free; /* if this process has a task directory, use that */ snprintf (buf, sizeof (buf), "/proc/%d/task", pid); if (r_file_is_directory (buf)) { struct dirent *de; DIR *dh = opendir (buf); while ((de = readdir (dh))) { if (!strcmp (de->d_name, ".") || !strcmp (de->d_name, "..")) { continue; } int tid = atoi (de->d_name); char info[1024]; int uid = 0; if (!procfs_pid_slurp (tid, "status", info, sizeof (info))) { ptr = strstr (info, "Uid:"); if (ptr) { uid = atoi (ptr + 4); } ptr = strstr (info, "Tgid:"); if (ptr) { int tgid = atoi (ptr + 5); if (tgid != pid) { // If we want to attach to just one thread, don't attach to the parent continue; } } } if (procfs_pid_slurp (tid, "comm", buf, sizeof (buf)) == -1) { /* fall back to auto-id */ snprintf (buf, sizeof (buf), "thread_%d %s", thid++, pid == tid ? "(current)" : NULL); buf[sizeof (buf) - 1] = 0; } RDebugPid *pid_info; if (!procfs_pid_slurp (tid, "status", info, sizeof (info))) { // Get information about pid (status, pc, etc.) pid_info = fill_pid_info (info, buf, tid); } else { pid_info = r_debug_pid_new (buf, tid, uid, 's', 0); } r_list_append (list, pid_info); } closedir (dh); } else { /* LOL! linux hides threads from /proc, but they are accessible!! HAHAHA */ #undef MAXPID #define MAXPID 99999 /* otherwise, brute force the pids */ for (i = pid; i < MAXPID; i++) { // XXX if (procfs_pid_slurp (i, "status", buf, sizeof(buf)) == -1) { continue; } int uid = 0; /* look for a thread group id */ ptr = strstr (buf, "Uid:"); if (ptr) { uid = atoi (ptr + 4); } ptr = strstr (buf, "Tgid:"); if (ptr) { int tgid = atoi (ptr + 5); /* if it is not in our thread group, we don't want it */ if (tgid != pid) continue; if (procfs_pid_slurp (i, "comm", buf, sizeof(buf)) == -1) { /* fall back to auto-id */ snprintf (buf, sizeof(buf), "thread_%d", thid++); } r_list_append (list, r_debug_pid_new (buf, i, uid, 's', 0)); } } } return list; } #define PRINT_FPU(fpregs) \ eprintf ("cwd = 0x%04x ; control ", (fpregs).cwd);\ eprintf ("swd = 0x%04x ; status\n", (fpregs).swd);\ eprintf ("ftw = 0x%04x ", (fpregs).ftw);\ eprintf ("fop = 0x%04x\n", fpregs.fop);\ eprintf ("rip = 0x%016"PFMT64x" ", (ut64)(fpregs).rip);\ eprintf ("rdp = 0x%016"PFMT64x"\n", (ut64)(fpregs).rdp);\ eprintf ("mxcsr = 0x%08x ", (fpregs).mxcsr);\ eprintf ("mxcr_mask = 0x%08x\n", (fpregs).mxcr_mask)\ #define PRINT_FPU_NOXMM(fpregs) \ eprintf ("cwd = 0x%04lx ; control ", (fpregs).cwd);\ eprintf ("swd = 0x%04lx ; status\n", (fpregs).swd);\ eprintf ("twd = 0x%04lx ", (fpregs).twd);\ eprintf ("fip = 0x%04lx \n", (fpregs).fip);\ eprintf ("fcs = 0x%04lx ", (fpregs).fcs);\ eprintf ("foo = 0x%04lx \n", (fpregs).foo);\ eprintf ("fos = 0x%04lx ", (fpregs).fos) void print_fpu (void *f, int r){ #if __x86_64__ || __i386__ int i; struct user_fpregs_struct fpregs = *(struct user_fpregs_struct*)f; #if __x86_64__ #if !__ANDROID__ eprintf ("---- x86-64 ----\n"); PRINT_FPU (fpregs); eprintf ("size = 0x%08x\n", (ut32)sizeof (fpregs)); for (i = 0; i < 16; i++) { ut32 *a = (ut32*)&fpregs.xmm_space; a = a + (i * 4); eprintf ("xmm%d = %08x %08x %08x %08x ", i, (int)a[0], (int)a[1], (int)a[2], (int)a[3] ); if (i < 8) { ut64 *b = (ut64*)&fpregs.st_space[i * 4]; ut32 *c = (ut32*)&fpregs.st_space; float *f = (float *)&fpregs.st_space; double *d = (double *)&fpregs.st_space[i*4]; c = c + (i * 4); f = f + (i * 4); eprintf ("st%d = %0.3lg (0x%016"PFMT64x") | %0.3f (%08x) |\ %0.3f (%08x) \n", i, *d, *b, (float)f[0], c[0], (float)f[1], c[1]); } else { eprintf ("\n"); } } #else PRINT_FPU (fpregs); for (i = 0;i < 8; i++) { ut64 *b = (ut64 *)&fpregs.st_space[i*4]; ut32 *c = (ut32*)&fpregs.st_space; float *f = (float *)&fpregs.st_space; c = c + (i * 4); f = f + (i * 4); eprintf ("st%d =%0.3lg (0x%016"PFMT64x") | %0.3f (%08x) | \ %0.3f (%08x) \n", i, (double)*((double*)&fpregs.st_space[i*4]), *b, (float) f[0], c[0], (float) f[1], c[1]); } #endif // !__ANDROID__ #elif __i386__ if (!r) { #if !__ANDROID__ struct user_fpxregs_struct fpxregs = *(struct user_fpxregs_struct*)f; eprintf ("---- x86-32 ----\n"); eprintf ("cwd = 0x%04x ; control ", fpxregs.cwd); eprintf ("swd = 0x%04x ; status\n", fpxregs.swd); eprintf ("twd = 0x%04x ", fpxregs.twd); eprintf ("fop = 0x%04x\n", fpxregs.fop); eprintf ("fip = 0x%08x\n", fpxregs.fip); eprintf ("fcs = 0x%08x\n", fpxregs.fcs); eprintf ("foo = 0x%08x\n", fpxregs.foo); eprintf ("fos = 0x%08x\n", fpxregs.fos); eprintf ("mxcsr = 0x%08x\n", fpxregs.mxcsr); for(i = 0; i < 8; i++) { ut32 *a = (ut32*)(&fpxregs.xmm_space); ut64 *b = (ut64 *)(&fpxregs.st_space[i * 4]); ut32 *c = (ut32*)&fpxregs.st_space; float *f = (float *)&fpxregs.st_space; a = a + (i * 4); c = c + (i * 4); f = f + (i * 4); eprintf ("xmm%d = %08x %08x %08x %08x ", i, (int)a[0], (int)a[1], (int)a[2], (int)a[3] ); eprintf ("st%d = %0.3lg (0x%016"PFMT64x") | %0.3f (0x%08x) |\ %0.3f (0x%08x)\n", i, (double)*((double*)(&fpxregs.st_space[i*4])), b[0], f[0], c[0], f[1], c[1]); } #endif // !__ANDROID__ } else { eprintf ("---- x86-32-noxmm ----\n"); PRINT_FPU_NOXMM (fpregs); for(i = 0; i < 8; i++) { ut64 *b = (ut64 *)(&fpregs.st_space[i*4]); double *d = (double*)b; ut32 *c = (ut32*)&fpregs.st_space; float *f = (float *)&fpregs.st_space; c = c + (i * 4); f = f + (i * 4); eprintf ("st%d = %0.3lg (0x%016"PFMT64x") | %0.3f (0x%08x) | \ %0.3f (0x%08x)\n", i, d[0], b[0], f[0], c[0], f[1], c[1]); } } #endif #else #warning print_fpu not implemented for this platform #endif } int linux_reg_read (RDebug *dbg, int type, ut8 *buf, int size) { bool showfpu = false; int pid = dbg->pid; int ret; if (type < -1) { showfpu = true; type = -type; } switch (type) { case R_REG_TYPE_DRX: #if __POWERPC__ // no drx for powerpc return false; #elif __i386__ || __x86_64__ #if !__ANDROID__ { int i; for (i = 0; i < 8; i++) { //DR0-DR7 if (i == 4 || i == 5) continue; long ret = ptrace (PTRACE_PEEKUSER, pid, r_offsetof (struct user, u_debugreg[i]), 0); if ((i+1) * sizeof (ret) > size) { eprintf ("linux_reg_get: Buffer too small %d\n", size); break; } memcpy (buf + (i * sizeof (ret)), &ret, sizeof (ret)); } struct user a; return sizeof (a.u_debugreg); } #else #warning Android X86 does not support DRX #endif #endif return true; break; case R_REG_TYPE_FPU: case R_REG_TYPE_MMX: case R_REG_TYPE_XMM: #if __POWERPC__ return false; #elif __x86_64__ || __i386__ { int ret1 = 0; struct user_fpregs_struct fpregs; if (type == R_REG_TYPE_FPU) { #if __x86_64__ #if !__ANDROID__ ret1 = ptrace (PTRACE_GETFPREGS, pid, NULL, &fpregs); if (showfpu) print_fpu ((void *)&fpregs, 0); if (ret1 != 0) return false; if (sizeof(fpregs) < size) size = sizeof(fpregs); memcpy (buf, &fpregs, size); return sizeof(fpregs); #else ret1 = ptrace (PTRACE_GETFPREGS, pid, NULL, &fpregs); if (showfpu) print_fpu ((void *)&fpregs, 0); if (ret1 != 0) return false; if (sizeof(fpregs) < size) size = sizeof(fpregs); memcpy (buf, &fpregs, size); return sizeof(fpregs); #endif // !__ANDROID__ #elif __i386__ #if !__ANDROID__ struct user_fpxregs_struct fpxregs; ret1 = ptrace (PTRACE_GETFPXREGS, pid, NULL, &fpxregs); if (ret1 == 0) { if (showfpu) print_fpu ((void *)&fpxregs, ret1); if (sizeof(fpxregs) < size) size = sizeof(fpxregs); memcpy (buf, &fpxregs, size); return sizeof(fpxregs); } else { ret1 = ptrace (PTRACE_GETFPREGS, pid, NULL, &fpregs); if (showfpu) print_fpu ((void *)&fpregs, ret1); if (ret1 != 0) return false; if (sizeof(fpregs) < size) size = sizeof(fpregs); memcpy (buf, &fpregs, size); return sizeof(fpregs); } #else ret1 = ptrace (PTRACE_GETFPREGS, pid, NULL, &fpregs); if (showfpu) print_fpu ((void *)&fpregs, 1); if (ret1 != 0) return false; if (sizeof (fpregs) < size) size = sizeof(fpregs); memcpy (buf, &fpregs, size); return sizeof(fpregs); #endif // !__ANDROID__ #endif // __i386__ } } #else #warning getfpregs not implemented for this platform #endif break; case R_REG_TYPE_SEG: case R_REG_TYPE_FLG: case R_REG_TYPE_GPR: { R_DEBUG_REG_T regs; memset (®s, 0, sizeof (regs)); memset (buf, 0, size); #if __arm64__ || __aarch64__ { struct iovec io = { .iov_base = ®s, .iov_len = sizeof (regs) }; ret = ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS, &io); } #elif __BSD__ && __POWERPC__ || __sparc__ ret = ptrace (PTRACE_GETREGS, pid, ®s, NULL); #else /* linux -{arm/x86/x86_64} */ ret = ptrace (PTRACE_GETREGS, pid, NULL, ®s); #endif /* * if perror here says 'no such process' and the * process exists still.. is because there's a missing call * to 'wait'. and the process is not yet available to accept * more ptrace queries. */ if (ret != 0) return false; if (sizeof (regs) < size) size = sizeof(regs); memcpy (buf, ®s, size); return sizeof (regs); } break; } return true; } int linux_reg_write (RDebug *dbg, int type, const ut8 *buf, int size) { if (type == R_REG_TYPE_DRX) { #if !__ANDROID__ && (__i386__ || __x86_64__) int i; long *val = (long*)buf; for (i = 0; i < 8; i++) { // DR0-DR7 if (i == 4 || i == 5) continue; if (ptrace (PTRACE_POKEUSER, dbg->pid, r_offsetof ( struct user, u_debugreg[i]), val[i])) { eprintf ("ptrace error for dr %d\n", i); r_sys_perror ("ptrace POKEUSER"); } } return sizeof (R_DEBUG_REG_T); #else return false; #endif } if (type == R_REG_TYPE_GPR) { #if __arm64__ || __aarch64__ struct iovec io = { .iov_base = buf, .iov_len = sizeof (R_DEBUG_REG_T) }; int ret = ptrace (PTRACE_SETREGSET, dbg->pid, NT_PRSTATUS, &io); #elif __POWERPC__ || __sparc__ int ret = ptrace (PTRACE_SETREGS, dbg->pid, buf, NULL); #else int ret = ptrace (PTRACE_SETREGS, dbg->pid, 0, (void*)buf); #endif #if DEAD_CODE if (size > sizeof (R_DEBUG_REG_T)) { size = sizeof (R_DEBUG_REG_T); } #endif return (ret != 0) ? false : true; } return false; } RList *linux_desc_list (int pid) { RList *ret = NULL; char path[512], file[512], buf[512]; struct dirent *de; RDebugDesc *desc; int type, perm; int len, len2; struct stat st; DIR *dd = NULL; snprintf (path, sizeof (path), "/proc/%i/fd/", pid); if (!(dd = opendir (path))) { r_sys_perror ("opendir /proc/x/fd"); return NULL; } ret = r_list_new (); if (!ret) { closedir (dd); return NULL; } ret->free = (RListFree)r_debug_desc_free; while ((de = (struct dirent *)readdir(dd))) { if (de->d_name[0] == '.') continue; len = strlen (path); len2 = strlen (de->d_name); if (len + len2 + 1 >= sizeof(file)) { r_list_free (ret); closedir (dd); eprintf ("Filename is too long"); return NULL; } memcpy (file, path, len); memcpy (file + len, de->d_name, len2 + 1); memset (buf, 0, sizeof(buf)); readlink (file, buf, sizeof (buf) - 1); buf[sizeof (buf)-1] = 0; type = perm = 0; if (stat (file, &st) != -1) { type = st.st_mode & S_IFIFO ? 'P': #ifdef S_IFSOCK st.st_mode & S_IFSOCK ? 'S': #endif st.st_mode & S_IFCHR ? 'C':'-'; } if (lstat(path, &st) != -1) { if (st.st_mode & S_IRUSR) perm |= R_IO_READ; if (st.st_mode & S_IWUSR) perm |= R_IO_WRITE; } //TODO: Offset desc = r_debug_desc_new (atoi (de->d_name), buf, perm, type, 0); if (!desc) break; r_list_append (ret, desc); } closedir (dd); return ret; } #endif