diff options
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | arch-x86-64.h | 29 | ||||
-rw-r--r-- | common.h | 38 | ||||
-rw-r--r-- | config.mk | 5 | ||||
-rw-r--r-- | consts.c | 2 | ||||
-rw-r--r-- | linux/os.h | 32 | ||||
-rw-r--r-- | linux/x86-64.h | 86 | ||||
-rw-r--r-- | memory.c | 39 | ||||
-rw-r--r-- | print.c | 120 | ||||
-rw-r--r-- | sctrace.c | 29 |
10 files changed, 237 insertions, 152 deletions
@@ -12,7 +12,8 @@ OBJ =\ util.o HDR =\ - arch-x86-64.h\ + linux/os.h\ + linux/x86-64.h\ arg.h\ common.h\ list-errnos.h\ @@ -29,15 +30,13 @@ sctrace: $(OBJ) list-errnos.h: printf '#define LIST_ERRNOS(_)\\\n\t' > $@ - cat /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \ - | sed 's/\/\/.*$$//' | tr -d '$$' | sed 's/\*\//\$$/g' | sed 's/\/\*[^$$]*\$$//g' \ + cat $(ERRNO_HDRS) sed 's/\/\/.*$$//' | tr -d '$$' | sed 's/\*\//\$$/g' | sed 's/\/\*[^$$]*\$$//g' \ | sed -n '/^[ \t]*#[ \t]*define[ \t].*[ \t][0-9]*[ \t]*$$/s/^[ \t#]*define[ \t]*\([^ \t]*\).*$$/_(\1)/p' \ | sort | uniq | tr '\n' '#' | sed 's/#_/\\\n\t_/g' | tr '#' '\n' >> $@ list-signums.h: printf '#define LIST_SIGNUMS(_)\\\n\t' > $@ - cat /usr/include/bits/signum.h /usr/include/bits/signum-generic.h \ - | sed 's/\/\/.*$$//' | tr -d '$$' | sed 's/\*\//\$$/g' | sed 's/\/\*[^$$]*\$$//g' \ + cat $(SIGNUM_HDRS) | sed 's/\/\/.*$$//' | tr -d '$$' | sed 's/\*\//\$$/g' | sed 's/\/\*[^$$]*\$$//g' \ | sed -n '/^[ \t]*#[ \t]*define[ \t][^_]*[ \t][0-9]*[ \t]*$$/s/^[ \t#]*define[ \t]*\([^ \t]*\).*$$/_(\1)/p' \ | sort | uniq | tr '\n' '#' | sed 's/#_/\\\n\t_/g' | tr '#' '\n' >> $@ diff --git a/arch-x86-64.h b/arch-x86-64.h deleted file mode 100644 index 044b73f..0000000 --- a/arch-x86-64.h +++ /dev/null @@ -1,29 +0,0 @@ -struct i386_user_regs_struct -{ - uint32_t ebx; - uint32_t ecx; - uint32_t edx; - uint32_t esi; - uint32_t edi; - uint32_t ebp; - uint32_t eax; - uint32_t xds; - uint32_t xes; - uint32_t xfs; - uint32_t xgs; - uint32_t orig_eax; - uint32_t eip; - uint32_t xcs; - uint32_t eflags; - uint32_t esp; - uint32_t xss; -}; - -#define SYSCALL_NUM_REG orig_rax -#define SYSCALL_ARG1_REG rdi -#define SYSCALL_ARG2_REG rsi -#define SYSCALL_ARG3_REG rdx -#define SYSCALL_ARG4_REG r10 -#define SYSCALL_ARG5_REG r8 -#define SYSCALL_ARG6_REG r9 -#define SYSCALL_RET_REG rax @@ -1,10 +1,5 @@ /* See LICENSE file for copyright and license details. */ -#include <linux/elf.h> -#include <sys/ptrace.h> -#include <linux/ptrace.h> /* After <sys/ptrace.h> */ -#include <sys/syscall.h> #include <sys/uio.h> -#include <sys/user.h> #include <sys/wait.h> #include <ctype.h> #include <errno.h> @@ -17,13 +12,9 @@ #include <string.h> #include <unistd.h> -#if defined(__x86_64__) && !defined(__IPL32__) -# include "arch-x86-64.h" +#if defined(__linux__) +# include "linux/os.h" #else -# error "This program is only implemented for x86-64" -#endif - -#if !defined(__linux__) # error "This program is only implemented for Linux" #endif @@ -32,24 +23,6 @@ #include "list-signums.h" -#ifndef ERESTARTSYS -# define ERESTARTSYS 512 -# define ALSO_ERESTARTSYS -#endif -#ifndef ERESTARTNOINTR -# define ERESTARTNOINTR 513 -# define ALSO_ERESTARTNOINTR -#endif -#ifndef ERESTARTNOHAND -# define ERESTARTNOHAND 514 -# define ALSO_ERESTARTNOHAND -#endif -#ifndef ERESTART_RESTARTBLOCK -# define ERESTART_RESTARTBLOCK 516 -# define ALSO_ERESTART_RESTARTBLOCK -#endif - - struct process; enum type { @@ -103,6 +76,11 @@ struct process { unsigned long long int ret; enum type ret_type; struct output outputs[6]; + /* multiarch support */ + unsigned long long int scall_xor; + int long_is_int; + int ptr_is_int; + int mode; /* vfork(2) data */ struct process *continue_on_exit; @@ -119,6 +97,8 @@ char *get_string(pid_t pid, unsigned long int addr, size_t *lenp, const char **e int get_struct(pid_t pid, unsigned long int addr, void *out, size_t size, const char **errorp); char *get_memory(pid_t pid, unsigned long int addr, size_t n, const char **errorp); char *escape_memory(char *str, size_t m); +char *get_escaped_string(pid_t pid, unsigned long int addr, size_t *lenp, const char **errorp); +char *get_escaped_memory(pid_t pid, unsigned long int addr, size_t n, const char **errorp); /* print.c */ void print_systemcall(struct process *proc); @@ -1,6 +1,11 @@ PREFIX = /usr MANPREFIX = $(PREFIX)/share/man +CC = cc + +ERRNO_HDRS = /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h +SIGNUM_HDRS = /usr/include/bits/signum.h /usr/include/bits/signum-generic.h + CPPFLAGS = -D_XOPEN_SOURCE=700 -D_GNU_SOURCE CFLAGS = -std=c11 -Wall -Og -g LDFLAGS = @@ -9,6 +9,7 @@ get_errno_name(int err) #define X(N) if (err == N) return #N; LIST_ERRNOS(X) + #ifdef ALSO_ERESTARTSYS X(ERESTARTSYS) #endif @@ -21,6 +22,7 @@ get_errno_name(int err) #ifdef ALSO_ERESTART_RESTARTBLOCK X(ERESTART_RESTARTBLOCK) #endif + #undef X sprintf(buf, "%i", err); diff --git a/linux/os.h b/linux/os.h new file mode 100644 index 0000000..db22f10 --- /dev/null +++ b/linux/os.h @@ -0,0 +1,32 @@ +/* See LICENSE file for copyright and license details. */ +#include <linux/elf.h> +#include <sys/ptrace.h> +#include <linux/ptrace.h> /* after <sys/ptrace.h> */ +#include <sys/syscall.h> +#include <sys/user.h> + +#ifndef ERESTARTSYS +# define ERESTARTSYS 512 +# define ALSO_ERESTARTSYS +#endif +#ifndef ERESTARTNOINTR +# define ERESTARTNOINTR 513 +# define ALSO_ERESTARTNOINTR +#endif +#ifndef ERESTARTNOHAND +# define ERESTARTNOHAND 514 +# define ALSO_ERESTARTNOHAND +#endif +#ifndef ERESTART_RESTARTBLOCK +# define ERESTART_RESTARTBLOCK 516 +# define ALSO_ERESTART_RESTARTBLOCK +#endif + +#define RETURN_IS_ERROR(RET)\ + ((RET) > -(unsigned long long int)PAGE_SIZE) /* Don't know the actual limit, but this seems safe */ + +#if defined(__x86_64__) && !defined(__IPL32__) +# include "x86-64.h" +#else +# error "This program is only implemented for x86-64 on Linux" +#endif diff --git a/linux/x86-64.h b/linux/x86-64.h new file mode 100644 index 0000000..16ff32f --- /dev/null +++ b/linux/x86-64.h @@ -0,0 +1,86 @@ +/* See LICENSE file for copyright and license details. */ +struct i386_user_regs_struct +{ + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint32_t xds; + uint32_t xes; + uint32_t xfs; + uint32_t xgs; + uint32_t orig_eax; + uint32_t eip; + uint32_t xcs; + uint32_t eflags; + uint32_t esp; + uint32_t xss; +}; + +#define SYSCALL_NUM_REG orig_rax +#define SYSCALL_RET_REG rax + +enum { + x86_64 = 0, + x32, + i386 +}; + +#define CHECK_ARCHITECTURE(proc, regsp)\ + do {\ + struct iovec iov = {\ + .iov_base = (regsp),\ + .iov_len = sizeof(*(regsp)),\ + };\ + if (ptrace(PTRACE_GETREGSET, (proc)->pid, NT_PRSTATUS, &iov)) {\ + eprintf("ptrace PTRACE_GETREGSET %ju NT_PRSTATUS {.iov_base=<buffer>, .iov_len=%zu}:",\ + (uintmax_t)(proc)->pid, sizeof(*(regsp)));\ + } else if (iov.iov_len != sizeof(*(regsp))) {\ + if ((proc)->mode != i386) {\ + (proc)->mode = i386;\ + (proc)->long_is_int = 1;\ + (proc)->ptr_is_int = 1;\ + (proc)->scall_xor = 0;\ + tprintf(proc, "Process is running in i386 mode, this is not yet supported\n"); /* TODO */\ + exit(1);\ + }\ + } else if ((proc)->scall & __X32_SYSCALL_BIT) {\ + if ((proc)->mode != x32) {\ + (proc)->mode = x32;\ + (proc)->long_is_int = 0;\ + (proc)->ptr_is_int = 1;\ + (proc)->scall_xor = __X32_SYSCALL_BIT;\ + tprintf(proc, "Process is running in x32 mode (support is untested)\n");\ + }\ + } else {\ + if ((proc)->mode != x86_64) {\ + (proc)->mode = x86_64;\ + (proc)->long_is_int = 0;\ + (proc)->ptr_is_int = 0;\ + (proc)->scall_xor = 0;\ + tprintf(proc, "Process is running in x86-64 mode\n");\ + }\ + }\ + } while (0) + +#define GET_SYSCALL_ARGUMENTS(proc, regsp)\ + do {\ + if ((proc)->mode != i386) {\ + (proc)->args[0] = (regsp)->rdi;\ + (proc)->args[1] = (regsp)->rsi;\ + (proc)->args[2] = (regsp)->rdx;\ + (proc)->args[3] = (regsp)->r10;\ + (proc)->args[4] = (regsp)->r8;\ + (proc)->args[5] = (regsp)->r9;\ + } else {\ + (proc)->args[0] = ((const struct i386_user_regs_struct *)(regsp))->ebx;\ + (proc)->args[1] = ((const struct i386_user_regs_struct *)(regsp))->ecx;\ + (proc)->args[2] = ((const struct i386_user_regs_struct *)(regsp))->edx;\ + (proc)->args[3] = ((const struct i386_user_regs_struct *)(regsp))->esi;\ + (proc)->args[4] = ((const struct i386_user_regs_struct *)(regsp))->edi;\ + (proc)->args[5] = ((const struct i386_user_regs_struct *)(regsp))->ebp;\ + }\ + } while (0) @@ -5,6 +5,9 @@ char * get_string(pid_t pid, unsigned long int addr, size_t *lenp, const char **errorp) { +#if defined(__x86_64__) && defined(__IPL32__) +# error "x32 is not supported, would not be able to read memory from 64-bit applications with current method" +#endif struct iovec inv, outv; size_t off = 0, size = 0, page_off, read_size; char *out = NULL, *in = (char *)addr, *p; @@ -39,14 +42,21 @@ int get_struct(pid_t pid, unsigned long int addr, void *out, size_t size, const char **errorp) { struct iovec inv, outv; + if (!addr) { + *errorp = "NULL"; + return -1; + } *errorp = NULL; +#if defined(__x86_64__) && defined(__IPL32__) +# error "x32 is not supported, would not be able to read memory from 64-bit applications with current method" +#endif inv.iov_base = (void *)addr; inv.iov_len = size; outv.iov_base = out; outv.iov_len = size; if (process_vm_readv(pid, &outv, 1, &inv, 1, 0) == (ssize_t)size) return 0; - *errorp = errno == EFAULT ? "<invalid address>" : "<an error occured during reading of string>"; + *errorp = errno == EFAULT ? "<invalid address>" : "<an error occured during reading of memory>"; return -1; } @@ -65,7 +75,6 @@ get_memory(pid_t pid, unsigned long int addr, size_t n, const char **errorp) } - static void add_char(char **strp, size_t *sizep, size_t *lenp, char c) { @@ -188,3 +197,29 @@ escape_memory(char *str, size_t m) free(str); return ret; } + + +char * +get_escaped_string(pid_t pid, unsigned long int addr, size_t *lenp, const char **errorp) +{ + char *r; + if (!addr) { + *errorp = "NULL"; + return NULL; + } + r = get_string(pid, addr, lenp, errorp); + return escape_memory(r, *lenp); +} + + +char * +get_escaped_memory(pid_t pid, unsigned long int addr, size_t n, const char **errorp) +{ + char *r; + if (!addr) { + *errorp = "NULL"; + return NULL; + } + r = get_memory(pid, addr, n, errorp); + return escape_memory(r, n); +} @@ -13,48 +13,15 @@ #include <sched.h> #include <time.h> -#ifndef CLONE_NEWTIME -# define CLONE_NEWTIME 0x00000080 +#if defined(__linux__) +# ifndef CLONE_NEWTIME +# define CLONE_NEWTIME 0x00000080 +# endif #endif -static char * -get_escaped_string_or_null(pid_t pid, unsigned long int addr, size_t *lenp, const char **errorp) -{ - char *r; - if (!addr) { - *errorp = "NULL"; - return NULL; - } - r = get_string(pid, addr, lenp, errorp); - return escape_memory(r, *lenp); -} - -static char * -get_escaped_memory_or_null(pid_t pid, unsigned long int addr, size_t n, const char **errorp) -{ - char *r; - if (!addr) { - *errorp = "NULL"; - return NULL; - } - r = get_memory(pid, addr, n, errorp); - return escape_memory(r, n); -} - -static int -get_struct_or_null(pid_t pid, unsigned long int addr, void *out, size_t size, const char **errorp) -{ - if (!addr) { - *errorp = "NULL"; - return -1; - } - return get_struct(pid, addr, out, size, errorp); -} - - - -#define CASE(N) if (proc->args[arg_index] == N) return tprintf(proc, "%s", #N) +#define CASE(N)\ + if (proc->args[arg_index] == N) return tprintf(proc, "%s", #N) #define FLAGS_BEGIN\ do {\ @@ -146,7 +113,7 @@ print_timespec(struct process *proc, size_t arg_index) { struct timespec ts; const char *err; - if (get_struct_or_null(proc->pid, proc->args[arg_index], &ts, sizeof(ts), &err)) { + if (get_struct(proc->pid, proc->args[arg_index], &ts, sizeof(ts), &err)) { tprintf(proc, "%s", err); return; } @@ -270,7 +237,7 @@ print_int_pair(struct process *proc, size_t arg_index) { int pair[2]; const char *err; - if (get_struct_or_null(proc->pid, proc->args[arg_index], pair, sizeof(pair), &err)) { + if (get_struct(proc->pid, proc->args[arg_index], pair, sizeof(pair), &err)) { tprintf(proc, "%s", err); return; } @@ -473,15 +440,26 @@ printf_systemcall(struct process *proc, const char *scall, const char *fmt, ...) if (*fmt == 'p') { p_fmt: + if (proc->ptr_is_int) + arg = (unsigned int)arg; if (input) { - if (get_struct_or_null(proc->pid, arg, &arg, sizeof(void *), &err)) { - tprintf(proc, "%s", err); - goto next; + if (proc->ptr_is_int) { + if (get_struct(proc->pid, arg, &arg, sizeof(int), &err)) { + tprintf(proc, "%s", err); + goto next; + } + arg = *(unsigned int *)&arg; + } else { + if (get_struct(proc->pid, arg, &arg, sizeof(long int), &err)) { + tprintf(proc, "%s", err); + goto next; + } + arg = *(unsigned long int *)&arg; } tprintf(proc, "&"); } if (arg) - tprintf(proc, "%p", (void *)arg); + tprintf(proc, "%#llu", arg); else tprintf(proc, "NULL"); } else if (*fmt >= '1' && *fmt <= '6') { @@ -490,16 +468,16 @@ printf_systemcall(struct process *proc, const char *scall, const char *fmt, ...) funcs[nfuncs++] = va_arg(ap, Function); funcs[func - 1](proc, i); } else if (*fmt == 's') { - str = get_escaped_string_or_null(proc->pid, arg, &len, &err); + str = get_escaped_string(proc->pid, arg, &len, &err); tprintf(proc, "%s", str ? str : err); free(str); } else if (*fmt == 'm') { - str = get_escaped_memory_or_null(proc->pid, arg, (size_t)args[i + 1], &err); + str = get_escaped_memory(proc->pid, arg, (size_t)args[i + 1], &err); tprintf(proc, "%s", str ? str : err); free(str); } else if (*fmt == 'F') { if (input) { - if (get_struct_or_null(proc->pid, arg, &arg, sizeof(int), &err)) { + if (get_struct(proc->pid, arg, &arg, sizeof(int), &err)) { tprintf(proc, "%s", err); goto next; } @@ -510,6 +488,8 @@ printf_systemcall(struct process *proc, const char *scall, const char *fmt, ...) else tprintf(proc, "%i", (int)arg); } else { + if (ells == 1 && proc->long_is_int) + ells = 0; if (ells == 1) size = sizeof(long int); else if (ells > 1) @@ -521,7 +501,7 @@ printf_systemcall(struct process *proc, const char *scall, const char *fmt, ...) else size = sizeof(int); if (input) { - if (get_struct_or_null(proc->pid, arg, &arg, size, &err)) { + if (get_struct(proc->pid, arg, &arg, size, &err)) { tprintf(proc, "%s", err); goto next; } @@ -942,13 +922,13 @@ print_systemcall_exit(struct process *proc) char *str, buf[32]; const char *err; - if (proc->ret_type == Int) + if (proc->ret_type == Int || (proc->long_is_int && proc->ret_type == Long)) tprintf(proc, "= %i", (int)proc->ret); - else if (proc->ret_type == UInt) + else if (proc->ret_type == UInt || (proc->long_is_int && proc->ret_type == ULong)) tprintf(proc, "= %u", (unsigned int)proc->ret); - else if (proc->ret_type == OInt) + else if (proc->ret_type == OInt || (proc->long_is_int && proc->ret_type == OLong)) tprintf(proc, "= %#o", (unsigned int)proc->ret); - else if (proc->ret_type == XInt) + else if (proc->ret_type == XInt || (proc->long_is_int && proc->ret_type == XLong)) tprintf(proc, "= %#x", (unsigned int)proc->ret); else if (proc->ret_type == Long) tprintf(proc, "= %li", (long int)proc->ret); @@ -966,12 +946,14 @@ print_systemcall_exit(struct process *proc) tprintf(proc, "= %#llo", (unsigned long long int)proc->ret); else if (proc->ret_type == XLLong) tprintf(proc, "= %#llx", (unsigned long long int)proc->ret); + else if (proc->ret_type == Ptr && (long long int)proc->ret >= 0 && proc->ptr_is_int) + tprintf(proc, "= %#u", (unsigned int)proc->ret); else if (proc->ret_type == Ptr && (long long int)proc->ret >= 0) - tprintf(proc, "= %p", (void *)proc->ret); + tprintf(proc, "= %#llu", proc->ret); else - tprintf(proc, "= %li", (long int)proc->ret); + tprintf(proc, "= %lli", (long long int)proc->ret); - if ((unsigned long long int)proc->ret > -(unsigned long long int)PAGE_SIZE) { + if (RETURN_IS_ERROR(proc->ret)) { tprintf(proc, " (%s: %s)", get_errno_name(-(int)proc->ret), strerror(-(int)proc->ret)); tprintf(proc, "\n"); @@ -984,17 +966,26 @@ print_systemcall_exit(struct process *proc) switch (proc->outputs[i].fmt) { case 'p': - if (get_struct_or_null(proc->pid, proc->args[i], buf, sizeof(void *), &err)) - tprintf(proc, "%s\n", err); - else if (*(void **)buf) - tprintf(proc, "%p\n", *(void **)buf); - else - tprintf(proc, "NULL\n"); + if (proc->ptr_is_int) { + if (get_struct(proc->pid, proc->args[i], buf, sizeof(int), &err)) + tprintf(proc, "%s\n", err); + else if (*(unsigned int *)buf) + tprintf(proc, "%#u\n", *(unsigned int *)buf); + else + tprintf(proc, "NULL\n"); + } else { + if (get_struct(proc->pid, proc->args[i], buf, sizeof(long int), &err)) + tprintf(proc, "%s\n", err); + else if (*(unsigned long int *)buf) + tprintf(proc, "%#lu\n", *(unsigned long int *)buf); + else + tprintf(proc, "NULL\n"); + } break; case 'm': value = proc->args[i + 1] < proc->ret ? proc->args[i + 1] : proc->ret; - str = get_escaped_memory_or_null(proc->pid, proc->args[i], (size_t)value, &err); + str = get_escaped_memory(proc->pid, proc->args[i], (size_t)value, &err); tprintf(proc, "%s\n", str ? str : err); free(str); break; @@ -1005,6 +996,7 @@ print_systemcall_exit(struct process *proc) break; default: + /* .ells is adjust for .long_is_int when set */ if (proc->outputs[i].ells == 1) size = sizeof(unsigned long int); else if (proc->outputs[i].ells > 1) @@ -1015,7 +1007,7 @@ print_systemcall_exit(struct process *proc) size = sizeof(unsigned char); else size = sizeof(unsigned int); - if (get_struct_or_null(proc->pid, proc->args[i], buf, size, &err)) { + if (get_struct(proc->pid, proc->args[i], buf, size, &err)) { tprintf(proc, "%s\n", err); break; } @@ -18,10 +18,6 @@ static void handle_syscall(struct process *proc) { struct user_regs_struct regs; - struct iovec iov = { - .iov_base = ®s, - .iov_len = sizeof(regs), - }; switch ((int)proc->state) { default: @@ -29,24 +25,11 @@ handle_syscall(struct process *proc) if (ptrace(PTRACE_GETREGS, proc->pid, NULL, ®s)) eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid); proc->scall = regs.SYSCALL_NUM_REG; - proc->args[0] = regs.SYSCALL_ARG1_REG; - proc->args[1] = regs.SYSCALL_ARG2_REG; - proc->args[2] = regs.SYSCALL_ARG3_REG; - proc->args[3] = regs.SYSCALL_ARG4_REG; - proc->args[4] = regs.SYSCALL_ARG5_REG; - proc->args[5] = regs.SYSCALL_ARG6_REG; - - /* Check architecture */ - if (ptrace(PTRACE_GETREGSET, proc->pid, NT_PRSTATUS, &iov)) { - eprintf("ptrace PTRACE_GETREGSET %ju NT_PRSTATUS {.iov_base=<buffer>, .iov_len=%zu}:", - (uintmax_t)proc->pid, sizeof(regs)); - } else if (iov.iov_len != sizeof(regs)) { - tprintf(proc, "Process is running as i386, this is not yet supported\n"); - exit(1); - } else if (proc->scall & __X32_SYSCALL_BIT) { - tprintf(proc, "Process is running as x32, this is not yet supported\n"); - exit(1); - } +#ifdef CHECK_ARCHITECTURE + CHECK_ARCHITECTURE(proc, ®s); + proc->scall ^= proc->scall_xor; +#endif + GET_SYSCALL_ARGUMENTS(proc, ®s); /* Print system call */ print_systemcall(proc); @@ -115,7 +98,7 @@ handle_event(struct process *proc, int status) struct process *proc2; sig = WSTOPSIG(status); - trace_event = ((status >> 8) ^ SIGTRAP) >> 8; + trace_event = status >> 16; switch (trace_event) { case PTRACE_EVENT_VFORK: |