bool pink_util_putn_safe(pid_t pid, long addr, const char *src, size_t len) { int n, m; union { long val; char x[sizeof(long)]; } u; n = 0; m = len / sizeof(long); while (n < m) { memcpy(u.x, src, sizeof(long)); if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr + n * ADDR_MUL, NULL))) return false; if (PINK_GCC_UNLIKELY(!pink_util_pokedata(pid, addr + n * ADDR_MUL, u.val))) return false; ++n; src += sizeof(long); } m = len % sizeof(long); if (m) { memcpy(u.x, src, m); if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr + n * ADDR_MUL, NULL))) return false; if (PINK_GCC_UNLIKELY(!pink_util_pokedata(pid, addr + n * ADDR_MUL, u.val))) return false; } return true; }
bool pink_util_movestr(pid_t pid, long addr, char *dest, size_t len) { int n, m; int started = 0; union { long val; char x[sizeof(long)]; } u; if (addr & (sizeof(long) -1)) { /* addr not a multiple of sizeof(long) */ n = addr - (addr & -sizeof(long)); /* residue */ addr &= -sizeof(long); /* residue */ if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr, &u.val))) { if (PINK_GCC_LIKELY(started && (errno == EPERM || errno == EIO || errno == EFAULT))) { /* Ran into end of memory */ return true; } /* But if not started, we had a bogus address */ return false; } started = 1; memcpy(dest, &u.x[n], m = MIN(sizeof(long) - n, len)); while (n & (sizeof(long) - 1)) if (u.x[n++] == '\0') return true; addr += sizeof(long), dest += m, len -= m; } while (len > 0) { if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr, &u.val))) { if (PINK_GCC_LIKELY(started && (errno == EPERM || errno == EIO || errno == EFAULT))) { /* Ran into end of memory */ return true; } /* But if not started, we had a bogus address */ return false; } started = 1; memcpy(dest, u.x, m = MIN(sizeof(long), len)); for (unsigned int i = 0; i < sizeof(long); i++) if (u.x[i] == '\0') return true; addr += sizeof(long), dest += m, len -= m; } return true; }
long pink_name_lookup(const char *name, pink_bitness_t bitness) { int n; long scno; const char **names; if (PINK_GCC_UNLIKELY(name == NULL || name[0] == '\0')) return -1; switch (bitness) { case PINK_BITNESS_32: n = nsys32; names = sysnames32; break; case PINK_BITNESS_64: n = nsys; names = sysnames; break; default: return -1; } for (scno = 0; scno < n; scno++) { if (!strcmp(names[scno], name)) return scno; } return -1; }
char * pink_decode_string_array_member_persistent(pid_t pid, pink_bitness_t bitness, long arg, unsigned ind) { int save_errno; unsigned short wordsize; union { unsigned int p32; unsigned long p64; char data[sizeof(long)]; } cp; save_errno = errno; wordsize = pink_bitness_wordsize(bitness); arg += ind * wordsize; if (PINK_GCC_UNLIKELY(!pink_util_moven(pid, arg, cp.data, wordsize))) return NULL; if (bitness == PINK_BITNESS_32) cp.p64 = cp.p32; if (cp.p64 == 0) { /* hit NULL, end of the array */ errno = save_errno; return NULL; } return pink_util_movestr_persistent(pid, cp.p64); }
bool pink_decode_string_array_member(pid_t pid, pink_bitness_t bitness, long arg, unsigned ind, char *dest, size_t len, bool *nil) { unsigned short wordsize; union { unsigned int p32; unsigned long p64; char data[sizeof(long)]; } cp; wordsize = pink_bitness_wordsize(bitness); arg += ind * wordsize; if (PINK_GCC_UNLIKELY(!pink_util_moven(pid, arg, cp.data, wordsize))) return false; if (bitness == PINK_BITNESS_32) cp.p64 = cp.p32; if (cp.p64 == 0) { /* hit NULL, end of the array */ if (nil) *nil = true; return true; } if (nil) *nil = false; return pink_util_movestr(pid, cp.p64, dest, len); }
bool pink_util_get_syscall(pid_t pid, pink_bitness_t bitness, long *res) { long parm_offset; struct reg r; assert(bitness == PINK_BITNESS_32 || bitness == PINK_BITNESS_64); if (PINK_GCC_UNLIKELY(!pink_util_get_regs(pid, &r))) return false; /* * FreeBSD has two special kinds of system call redirections -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basicly; the latter is for quad-aligned arguments. */ *res = r.r_rax; switch (*res) { case SYS_syscall: case SYS___syscall: if (bitness == PINK_BITNESS_32) { parm_offset = r.r_rsp + sizeof(int); if (!pink_util_peekdata(pid, parm_offset, res)) return false; } else *res = r.r_rdi; return true; default: return true; } }
long pink_name_lookup_with_length(const char *name, size_t length, pink_bitness_t bitness) { long scno; if (PINK_GCC_UNLIKELY(bitness != PINK_BITNESS_32)) return -1; if (PINK_GCC_UNLIKELY(name == NULL || name[0] == '\0')) return -1; for (scno = 0; scno < nsys; scno++) { if (!strncmp(sysnames[scno], name, length)) return scno; } return -1; }
const char * pink_name_syscall(long scno, pink_bitness_t bitness) { int n; const char **names; if (PINK_GCC_UNLIKELY(bitness != PINK_BITNESS_32)) return NULL; if (scno < 0) { /* Architecture specific system call */ scno = -scno; n = nsys_arch; names = sysnames_arch; } else { /* Thumb-mode system call */ n = nsys; names = sysnames; } if (PINK_GCC_UNLIKELY(scno < 0 || scno >= n)) return NULL; return names[scno]; }
bool pink_util_peekdata(pid_t pid, long off, long *res) { long val; errno = 0; val = ptrace(PTRACE_PEEKDATA, pid, off, NULL); if (PINK_GCC_UNLIKELY(val == -1 && errno != 0)) return false; if (res) *res = val; return true; }
const char * pink_name_syscall(long scno, pink_bitness_t bitness) { int n; const char **names; switch (bitness) { case PINK_BITNESS_32: n = nsys32; names = sysnames32; break; case PINK_BITNESS_64: n = nsys; names = sysnames; break; default: return NULL; } if (PINK_GCC_UNLIKELY(scno < 0 || scno >= n)) return NULL; return names[scno]; }
pink_bitness_t pink_bitness_get(pid_t pid) { char progt[32]; size_t len = sizeof(progt); int mib[4]; struct bitness_types *walk; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_SV_NAME; mib[3] = pid; if (PINK_GCC_UNLIKELY(sysctl(mib, 4, progt, &len, NULL, 0) < 0)) return PINK_BITNESS_UNKNOWN; for (walk = bitness_types; walk->type; walk++) { if (!strcmp(walk->type, progt)) return walk->bitness; } return PINK_BITNESS_UNKNOWN; }
pink_bitness_t pink_bitness_get(pid_t pid) { long cs; /* * Check CS register value, * On x86-64 linux this is: * 0x33 for long mode (64 bit) * 0x23 for compatibility mode (32 bit) */ if (PINK_GCC_UNLIKELY(!pink_util_peek(pid, 8 * CS, &cs))) return PINK_BITNESS_UNKNOWN; switch (cs) { case 0x33: return PINK_BITNESS_64; case 0x23: return PINK_BITNESS_32; default: return PINK_BITNESS_UNKNOWN; } }
char * pink_util_movestr_persistent(pid_t pid, long addr) { int n, m, sum, save_errno; bool started; union { long val; char x[sizeof(long)]; } u; char *res, *res_ptr; #define XFREE(x) \ do { \ if ((x)) { \ free((x)); \ } \ } while (0) save_errno = errno; started = false; res = res_ptr = NULL; sum = 0; if (addr & (sizeof(long) -1)) { /* addr not a multiple of sizeof(long) */ n = addr - (addr & -sizeof(long)); /* residue */ addr &= -sizeof(long); /* residue */ if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr, &u.val))) { if (PINK_GCC_LIKELY(started && (errno == EPERM || errno == EIO || errno == EFAULT))) { /* Ran into end of memory */ return res; } /* But if not started, we had a bogus address */ XFREE(res); if (PINK_GCC_UNLIKELY(!started)) { /* NULL */ errno = save_errno; } return NULL; } m = sizeof(long) - n; sum += m; if ((res = realloc(res, sum)) == NULL) return NULL; res_ptr = started ? res + (sum - m) : res; started = true; memcpy(res_ptr, &u.x[n], m); while (n & (sizeof(long) - 1)) if (u.x[n++] == '\0') return res; addr += sizeof(long); } for (;;) { if (PINK_GCC_UNLIKELY(!pink_util_peekdata(pid, addr, &u.val))) { if (PINK_GCC_LIKELY(started && (errno == EPERM || errno == EIO || errno == EFAULT))) { /* Ran into end of memory */ return res; } /* But if not started, we had a bogus address */ XFREE(res); if (PINK_GCC_UNLIKELY(!started)) { /* NULL */ errno = save_errno; } return NULL; } sum += sizeof(long); if ((res = realloc(res, sum)) == NULL) return NULL; res_ptr = started ? res + (sum - sizeof(long)) : res; started = true; memcpy(res_ptr, u.x, sizeof(long)); for (unsigned int i = 0; i < sizeof(long); i++) if (u.x[i] == '\0') return res; addr += sizeof(long); } /* never reached */ assert(false); #undef XFREE }