diff --git a/command.c b/command.c index db02d17..e2dc883 100644 --- a/command.c +++ b/command.c @@ -30,25 +30,8 @@ static void command_start(struct mi_process proc) { u64 main_address = proc.base_address + proc.main_address; - - long saved_instruction = ptrace(PTRACE_PEEKDATA, proc.pid, main_address, NULL); - long int3_byte = 0x000000000000cc; - long int3_word = (saved_instruction & 0xFFFFFFFFFFFFFF00) | int3_byte; - - /* write 0xcc */ - ptrace(PTRACE_POKEDATA, proc.pid, main_address, int3_word); - - /* wait till child hits the interrupt */ - ptrace(PTRACE_CONT, proc.pid, 0, 0); - waitpid(proc.pid, 0, 0); - - /* restore original instrucion */ - ptrace(PTRACE_POKEDATA, proc.pid, main_address, saved_instruction); - - /* step back to original instruction */ - struct mi_registers regs = get_process_registers(proc); - regs.rip -= 1; - set_process_registers(proc, regs); + long saved_instruction = place_breakpoint_at(proc, main_address); + run_until_breakpoint_and_restore(proc, main_address, saved_instruction); } static void @@ -63,9 +46,28 @@ command_next(struct mi_process proc) ptrace(PTRACE_SINGLESTEP, proc.pid, 0, 0); waitpid(proc.pid, 0, 0); regs = get_process_registers(proc); + + long instruction = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rip, NULL); + if (is_call_instruction(instruction)) { + u64 call_at = regs.rip; + + // Do single step, get return address from stack, place breakpoint there, continue + ptrace(PTRACE_SINGLESTEP, proc.pid, 0, 0); + waitpid(proc.pid, 0, 0); + regs = get_process_registers(proc); + + long return_address = ptrace(PTRACE_PEEKDATA, proc.pid, regs.rsp, NULL); + long saved_instruction = place_breakpoint_at(proc, return_address); + run_until_breakpoint_and_restore(proc, return_address, saved_instruction); + // TODO: disambiguate recursive calls + + regs = get_process_registers(proc); + } + // TODO: skip call, repXXX + //printf("%#lx\n", regs.rip - proc.base_address); next_sp = pc_to_sourcepoint(proc, regs.rip - proc.base_address); - // TODO: skip call, repXXX, etc + if (!next_sp) break; } while (next_sp->line == sp->line); diff --git a/traceme.c b/traceme.c index 0f67353..503f53d 100644 --- a/traceme.c +++ b/traceme.c @@ -19,5 +19,8 @@ int main() foo(3); + pipa += 4; + boba = foo(2) - foo(1); + return 0; } diff --git a/util.c b/util.c index f05dc16..ddd9340 100644 --- a/util.c +++ b/util.c @@ -1,3 +1,97 @@ + +static struct mi_registers +get_process_registers(struct mi_process proc) +{ + struct user_regs_struct regs = { 0 }; + struct iovec iov = { ®s, sizeof(regs) }; + struct mi_registers result = { 0 }; + + ptrace(PTRACE_GETREGSET, proc.pid, NT_PRSTATUS, &iov); + + result.rax = regs.rax; + result.rip = regs.rip; + result.rbp = regs.rbp; + result.rbx = regs.rbx; + result.rcx = regs.rcx; + result.rdx = regs.rdx; + result.rsi = regs.rsi; + result.rdi = regs.rdi; + result.rsp = regs.rsp; + + result._sys = regs; + + return(result); +} + +static void +set_process_registers(struct mi_process proc, struct mi_registers regs) +{ + regs._sys.rax = regs.rax; + regs._sys.rip = regs.rip; + regs._sys.rbp = regs.rbp; + regs._sys.rbx = regs.rbx; + regs._sys.rcx = regs.rcx; + regs._sys.rdx = regs.rdx; + regs._sys.rsi = regs.rsi; + regs._sys.rdi = regs.rdi; + regs._sys.rsp = regs.rsp; + + struct iovec iov = { ®s._sys, sizeof(regs._sys) }; + ptrace(PTRACE_SETREGSET, proc.pid, NT_PRSTATUS, &iov); +} + + +static int +is_call_instruction(long word) +{ + // It's a call if: + // first byte == 0xe8 + // first byte == 0xff AND "reg field" (bit 3-5 in next byte) == 2 or 3 + // REX.W + 0xff/3 ????? TODO TODO TODO + + u8 first_byte = word & 0xFF; + if (first_byte == 0xE8) { + return 1; + } else if (first_byte == 0xFF) { + u8 second_byte = word & 0xFF00UL; + u8 reg = second_byte & 0x38; // 111000 (to extract bits 3-5) + if (reg == 2 || reg == 3) { + return 1; + } + } + + return 0; +} + +static long +place_breakpoint_at(struct mi_process proc, u64 address) +{ + long saved_instruction = ptrace(PTRACE_PEEKDATA, proc.pid, address, NULL); + long int3_byte = 0x000000000000CC; + long int3_word = (saved_instruction & 0xFFFFFFFFFFFFFF00) | int3_byte; + + /* write 0xcc */ + ptrace(PTRACE_POKEDATA, proc.pid, address, int3_word); + + return(saved_instruction); +} + +static void +run_until_breakpoint_and_restore(struct mi_process proc, u64 address, long saved_instruction) +{ + /* wait till child hits the interrupt */ + ptrace(PTRACE_CONT, proc.pid, 0, 0); + waitpid(proc.pid, 0, 0); + + /* restore original instrucion */ + ptrace(PTRACE_POKEDATA, proc.pid, address, saved_instruction); + + /* step back to original instruction */ + struct mi_registers regs = get_process_registers(proc); + regs.rip -= 1; + set_process_registers(proc, regs); +} + static u64 get_address_of_subroutine(struct mi_process proc, char *sr) { @@ -42,7 +136,12 @@ read_file_mmap(char *path) struct stat s; int fd = open(path, O_RDONLY); - assert(fd != -1); + + if (fd == -1) { + printf("File not found: %s\n", path); + DIE("open() failed\n"); + } + fstat(fd, &s); result.data = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0); @@ -146,47 +245,6 @@ process_create(char *path) return(result); } -static struct mi_registers -get_process_registers(struct mi_process proc) -{ - struct user_regs_struct regs = { 0 }; - struct iovec iov = { ®s, sizeof(regs) }; - struct mi_registers result = { 0 }; - - ptrace(PTRACE_GETREGSET, proc.pid, NT_PRSTATUS, &iov); - - result.rax = regs.rax; - result.rip = regs.rip; - result.rbp = regs.rbp; - result.rbx = regs.rbx; - result.rcx = regs.rcx; - result.rdx = regs.rdx; - result.rsi = regs.rsi; - result.rdi = regs.rdi; - result.rsp = regs.rsp; - - result._sys = regs; - - return(result); -} - -static void -set_process_registers(struct mi_process proc, struct mi_registers regs) -{ - regs._sys.rax = regs.rax; - regs._sys.rip = regs.rip; - regs._sys.rbp = regs.rbp; - regs._sys.rbx = regs.rbx; - regs._sys.rcx = regs.rcx; - regs._sys.rdx = regs.rdx; - regs._sys.rsi = regs.rsi; - regs._sys.rdi = regs.rdi; - regs._sys.rsp = regs.rsp; - - struct iovec iov = { ®s._sys, sizeof(regs._sys) }; - ptrace(PTRACE_SETREGSET, proc.pid, NT_PRSTATUS, &iov); -} - static void print_current_instruction(struct mi_process proc) {