void *ptrace_dlsym(pid_t pid, void *handle, const char *symbol) { #ifdef ANDROID regs_t regs; //int stat; ptrace_readreg(pid, ®s); ptrace_dump_regs(®s, "before call to ptrace_dlsym\n"); #ifdef THUMB regs.ARM_lr = 1; #else regs.ARM_lr= 0; #endif regs.ARM_r0= (long)handle; regs.ARM_r1= (long)ptrace_push(pid,®s, (void*)symbol,strlen(symbol)+1); regs.ARM_pc= ldl.l_dlsym; ptrace_writereg(pid, ®s); ptrace_cont(pid); ptrace_wait_for_signal(pid, SIGSEGV); ptrace_readreg(pid, ®s); ptrace_dump_regs(®s, "before return ptrace_dlsym\n"); return (void*) regs.ARM_r0; #endif }
void *ptrace_dlopen(pid_t pid, const char *filename, int flag) { #ifdef ANDROID regs_t regs; //int stat; ptrace_readreg(pid, ®s); ptrace_dump_regs(®s, "before call to ptrace_dlopen\n"); #ifdef THUMB regs.ARM_lr = 1; #else regs.ARM_lr= 0; #endif regs.ARM_r0= (long)ptrace_push(pid,®s, (void*)filename,strlen(filename)+1); regs.ARM_r1= flag; regs.ARM_pc= ldl.l_dlopen; ptrace_writereg(pid, ®s); ptrace_cont(pid); ptrace_wait_for_signal(pid, SIGSEGV); ptrace_readreg(pid, ®s); ptrace_dump_regs(®s, "before return ptrace_call\n"); return (void*) regs.ARM_r0; #endif }
void call_shit(struct elf_info *einfo) { unsigned long addr2 = 0; unsigned long rel_addr = find_sym_in_rel(einfo, "math_shit"); regs_t regs; ptrace_read(einfo->pid, rel_addr, &addr2, sizeof(long)); printf("math_shit rel addr\t %lx\n", rel_addr); printf("addr2 is \t %lx\n", addr2); ptrace_readreg(einfo->pid, ®s); ptrace_dump_regs(®s,"before call to call_shit\n"); #ifdef THUMB regs.ARM_lr = 1; #else regs.ARM_lr = 0; #endif regs.ARM_r0 = 5; regs.ARM_r1 = 6; regs.ARM_r2 = 7; regs.ARM_r3 = 8; { int a5 = 9; ptrace_push(einfo->pid, ®s, &a5, 4); ptrace_push(einfo->pid, ®s, &a5, 4); ptrace_push(einfo->pid, ®s, &a5, 4); ptrace_push(einfo->pid, ®s, &a5, 4); ptrace_push(einfo->pid, ®s, &a5, 4); ptrace_push(einfo->pid, ®s, &a5, 4); ptrace_push(einfo->pid, ®s, &a5, 4); ptrace_push(einfo->pid, ®s, &a5, 4); ptrace_push(einfo->pid, ®s, &a5, 4); a5 = 10; ptrace_push(einfo->pid, ®s, &a5, 4); } regs.ARM_pc = addr2; ptrace_writereg(einfo->pid, ®s); ptrace_cont(einfo->pid); printf("done %d\n", ptrace_wait_for_signal(einfo->pid,SIGSEGV)); ptrace_readreg(einfo->pid, ®s); ptrace_dump_regs(®s,"before return call_shit\n"); }
void call_dl_open (int pid, unsigned long addr, char *libname) { void *pRLibName; struct user_regs_struct regs; /* 先找个空间存放要装载的共享库名,我们可以简单的把它放入堆栈 */ pRLibName = ptrace_push (pid, libname, strlen (libname) + 1); /* 设置参数到寄存器 */ ptrace_readreg (pid, ®s); regs.eax = (unsigned long) pRLibName; regs.ecx = 0x0; regs.edx = RTLD_LAZY; ptrace_writereg (pid, ®s); /* 调用_dl_open */ ptrace_call (pid, addr); puts ("call _dl_open ok"); }
int ptrace_call(int pid, long proc, int argc, ptrace_arg *argv) { int i = 0; #define ARGS_MAX 64 regs_t regs; ptrace_readreg(pid, ®s); ptrace_dump_regs(®s, "before ptrace_call\n"); /*prepare stacks*/ for (i = 0; i < argc; i++) { ptrace_arg *arg = &argv[i]; if (arg->type == PAT_STR) { arg->_stackid = ptrace_push(pid, ®s, arg->s, strlen(arg->s) + 1); } else if (arg->type == PAT_MEM) { //LOGD("push data %p to stack[%d] :%d \n", arg->mem.addr, stackcnt, *((int*)arg->mem.addr)); arg->_stackid = ptrace_push(pid, ®s, arg->mem.addr, arg->mem.size); } } for (i = 0; (i < 4) && (i < argc); i++) { ptrace_arg *arg = &argv[i]; if (arg->type == PAT_INT) { regs.uregs[i] = arg->i; } else if (arg->type == PAT_STR) { regs.uregs[i] = arg->_stackid; } else if (arg->type == PAT_MEM) { regs.uregs[i] = arg->_stackid; } else { LOGD("unkonwn arg type\n"); } } for (i = argc - 1; i >= 4; i--) { ptrace_arg *arg = &argv[i]; if (arg->type == PAT_INT) { ptrace_push(pid, ®s, &arg->i, sizeof(int)); } else if (arg->type == PAT_STR) { ptrace_push(pid, ®s, &arg->_stackid, sizeof(unsigned long)); } else if (arg->type == PAT_MEM) { ptrace_push(pid, ®s, &arg->_stackid, sizeof(unsigned long)); } else { LOGD("unkonwn arg type\n"); } } #ifdef THUMB regs.ARM_lr = 1; #else regs.ARM_lr= 0; #endif regs.ARM_pc= proc; ptrace_writereg(pid, ®s); ptrace_cont(pid); ptrace_wait_for_signal(pid, SIGSEGV); ptrace_readreg(pid, ®s); ptrace_dump_regs(®s, "before return ptrace_call\n"); //sync memory for (i = 0; i < argc; i++) { ptrace_arg *arg = &argv[i]; if (arg->type == PAT_STR) { } else if (arg->type == PAT_MEM) { ptrace_read(pid, arg->_stackid, arg->mem.addr, arg->mem.size); } } return regs.ARM_r0; }
static void inject_memory(void) { struct mem_region * stack_r = NULL; SYS_TRACE("nr_regions=%d\n", cf->nr_regions); /* for each mem region in ckpt file */ for (int i = 0; i < cf->nr_regions; i++) { struct mem_region * r = cf->regions[i]; SYS_TRACE("range %d: 0x%x--0x%x (0x%x:0x%x): %s\n", i, r->start, r->end, r->prot, r->offset, r->fn); /* find the region already mapped */ struct proc_entry e; bool_t res; uint32_t start, end; start = r->start; end = r->end; do { res = proc_find_in_range(&e, child_pid, start, end); if (res) { start = e.end; SYS_TRACE("\talready mapped in target: 0x%x--0x%x (0x%x:0x%x): %s\n", e.start, e.end, e.prot, e.offset, e.fn); /* check if 2 maps are same */ if ((e.start != r->start) || (e.end != r->end) || (e.prot != r->prot) || (strcmp(e.fn, r->fn) != 0)) { /* different */ /* special treat [heap] and [stack] */ if (strcmp(e.fn, "[heap]") == 0) { /* heap size is different */ /* that shouldn't happen, we restore heap address before * calling this function*/ THROW(EXCEPTION_FATAL, "heap address inconsistent"); } else if (strcmp(e.fn, "[stack]") == 0) { if (strcmp(r->fn, "[stack]") != 0) { /* that shouldn't happen... */ THROW(EXCEPTION_FATAL, "stack inconsistent"); } /* stack can auto expand */ } else if (strcmp(e.fn, "[vdso]") == 0) { THROW(EXCEPTION_FATAL, "vdso inconsistent"); } else { /* unmap the already mapped file */ /* first, check eip */ struct user_regs_struct regs = ptrace_peekuser(); if ((regs.eip >= e.start) && (regs.eip < e.end)) { /* target ckpt is special: it unmap itself... */ /* currently we don't support it */ THROW(EXCEPTION_FATAL, "eip (0x%x) inconsistent", (uint32_t)regs.eip); } /* now we can safely unmap that file or memregion */ int err; SYS_TRACE("\tunmap 0x%x--0x%x\n", e.start, e.end); err = ptrace_syscall(munmap, 2, e.start, e.end - e.start); if (err < 0) THROW(EXCEPTION_FATAL, "unmap memrigon 0x%x--0x%x failed: %d", e.start, e.end, err); } } } else { start = r->end; } } while (start < end); /* now the region has been cleaned up, there may be 2 situations: * 1. the desired region is empty; * 2. the desired region is mapped, but the same as ckpt. * here we use procutils to check it again. */ res = proc_find_in_range(&e, child_pid, r->start, r->end); if (res) { SYS_TRACE("\tdesired region is mapped\n"); if ((e.start != r->start) || (e.end != r->end) || (e.prot != r->prot) || (strcmp(e.fn, r->fn) != 0)) { /* if the lower end of stack inconsistent, don't care. * stack is auto expandable. */ /* NOT(e.fn same as r->fn and e.fn is "[stack]") */ if (!(!strcmp(e.fn, r->fn) && !strncmp(e.fn, "[stack]", 7))) { INJ_FATAL("this region is not cleaned up:\n"); INJ_FATAL("\tstart : 0x%x 0x%x\n", e.start, r->start); INJ_FATAL("\tend : 0x%x 0x%x\n", e.end, r->end); INJ_FATAL("\tprot : 0x%x 0x%x\n", e.prot, r->prot); INJ_FATAL("\tfn: : \"%s\" \"%s\"\n", e.fn, r->fn); THROW(EXCEPTION_FATAL, "Shouldn't happed...\n"); } } } else { SYS_TRACE("\tdesired region is unmapped\n"); /* from the ckpt, find the file and do the map */ uint32_t map_addr = r->start; uint32_t size = r->end - r->start; uint32_t prot = r->prot; uint32_t flags = MAP_FIXED | MAP_EXECUTABLE | MAP_PRIVATE; if ((r->fn_len <= 1) || (r->fn[0] == '\0')) { /* this is not a file map */ uint32_t ret_addr; flags |= MAP_ANONYMOUS; SYS_TRACE("\tdo the anonymouse map\n"); ret_addr = ptrace_syscall(mmap2, 6, map_addr, size, prot, flags, 0, 0); CTHROW(map_addr == ret_addr, "mmap2 failed, return 0x%x, not 0x%x", ret_addr, map_addr); } else { /* this is a file map */ uint32_t fn_pos; /* push the filename */ fn_pos = ptrace_push(r->fn, strlen(r->fn) + 1, TRUE); int fd = ptrace_syscall(open, 3, fn_pos, O_RDONLY, 0); CTHROW(fd >= 0, "open file %s failed: %d", r->fn, fd); SYS_TRACE("\tdo the map\n"); uint32_t off = r->offset; uint32_t ret_addr = ptrace_syscall(mmap2, 6, map_addr, size, prot, flags, fd, off >> PAGE_SHIFT); CTHROW(ret_addr == map_addr, "mmap2 file %s failed: return 0x%x", r->fn, ret_addr); ptrace_syscall(close, 1, fd); } } /* now the memory region has been built up, we then poke * memory into it */ /* don't update stack here. although in most case the stack * is the last region we meet, there are some special situations. * for example in compat memlayout. the stack may be polluted by * pervious ptrace_push operation. */ if (strcmp(r->fn, "[stack]") == 0) stack_r = r; else if (strcmp(r->fn, "[vdso]") != 0) /* chkp don't contain vdso */ ptrace_updmem(r->f_pos + cf->ckpt_img, r->start, r->end - r->start); } CTHROW(stack_r != NULL, "no \"[stack]\" found"); /* we poke stack at last */ ptrace_updmem(stack_r->f_pos + cf->ckpt_img, stack_r->start, stack_r->end - stack_r->start); }
static void gdbloader_main(const char * target_fn) { /* check: target_fn should be same as argv[0] */ if (strcmp(target_fn, cf->cmdline[0]) != 0) { SYS_FATAL("target should be %s, not %s\n", cf->cmdline[0], target_fn); THROW(EXCEPTION_FATAL, "cmdline error"); } /* execve child */ child_pid = ptrace_execve(target_fn, cf->cmdline, cf->environ); /* inject memory */ /* before we inject memory, we need to restore heap */ uint32_t heap_end; heap_end = ptrace_syscall(brk, 1, cf->state->brk); CTHROW(heap_end == cf->state->brk, "restore heap failed: %d", heap_end); SYS_TRACE("restore heap to 0x%x\n", heap_end); inject_memory(); /* then, we retrive the inject so file, enter from * __debug_entry. we need to push: * nothing. * process can retrive all from state vector. * and we cannot use stack now * */ /* NOTICE: the state_vector should be saved in the ckpt memory, * we needn't restore them in ptrace process. let the inject so * to do it. */ /* from the opts get the so-file bias */ uint32_t inj_bias = opts->inj_bias; /* use procutils to get the file */ struct proc_entry e; e.start = inj_bias; e.bits = PE_START; proc_fill_entry(&e, child_pid); SYS_TRACE("inject so is %s\n", e.fn); /* use elfutils to retrive the symbol */ void * img = load_file(e.fn); struct elf_handler * inj_so = elf_init(img, inj_bias); uintptr_t debug_entry = elf_get_symbol_address(inj_so, opts->entry); SYS_TRACE("symbol %s at 0x%x\n", opts->entry, debug_entry); /* inject the injector opts */ inject_injopts(inj_so); elf_cleanup(inj_so); free(img); /* we have to restore register here... */ SYS_FORCE("pid=%d\n", child_pid); SYS_FORCE("eip=0x%x\n", cf->state->regs.eip); ptrace_pokeuser(cf->state->regs); SYS_TRACE("eax=0x%x\n", cf->state->regs.eax); SYS_TRACE("ebx=0x%x\n", cf->state->regs.ebx); SYS_TRACE("ecx=0x%x\n", cf->state->regs.ecx); SYS_TRACE("edx=0x%x\n", cf->state->regs.edx); SYS_TRACE("esi=0x%x\n", cf->state->regs.esi); SYS_TRACE("edi=0x%x\n", cf->state->regs.edi); SYS_TRACE("ebp=0x%x\n", cf->state->regs.ebp); SYS_TRACE("esp=0x%x\n", cf->state->regs.esp); // SYS_TRACE("gs=0x%x\n", cf->state->regs.gs); // SYS_TRACE("es=0x%x\n", cf->state->regs.es); /* we push eip at the top of the new stack */ ptrace_push(&cf->state->regs.eip, sizeof(uint32_t), FALSE); /* fix libpthread problem: * * when gdb attaches to target, if it find libpthread, gdb * will try to use libthread_db to retrive thread-local info. * some data, like `errno', is TLS and need those info. * * When gdb does the work, it use ptrace to peek memory from target image. * so gdb will see the original thread info, the tid is different from * current pid, therefore gdb will think there are at least 2 threads and * then it will try to attach to the 'old' one and definitely fail. When this * failure occures, gdb print a warning message. * * We have 2 ways to solve this problem: * * 1. add a syscall into kernel's code, change its pid. it is simple. * 2. change the image when gdb attach. * * We choose the 2nd one because we prefer user space solution. * * */ uint32_t sym_stack_used = 0, sym_stack_user = 0; if (opts->fix_pthread_tid) { fix_libpthread(&sym_stack_used, &sym_stack_user); SYS_WARNING("sym_stack_used=0x%x, sym_stack_user=0x%x\n", sym_stack_used, sym_stack_user); } /* we push those 2 addresses onto the stack */ ptrace_push(&sym_stack_used, sizeof(uint32_t), FALSE); ptrace_push(&sym_stack_user, sizeof(uint32_t), FALSE); /* move eip and detach, let the target process to run */ ptrace_goto(debug_entry); /* detach in main */ return; }
static void map_wrap_so(const char * so_file, uintptr_t load_bias, uint32_t * pvdso_entrance, uint32_t * pvdso_ehdr) { uint32_t name_pos; int fd, err; struct stat s; err = stat(so_file, &s); assert_errno_throw("stat file %s failed", so_file); assert_throw(S_ISREG(s.st_mode), "file %s not a regular file", so_file); /* don't use off_t, it may not be a 32 bit word! */ int32_t fsize = s.st_size; SYS_TRACE("desired so file length is %d\n", fsize); /* elf operations */ void * so_image = load_file(so_file); struct elf_handler * h = elf_init(so_image, load_bias); /* load program headers */ int nr_phdr = 0; struct elf32_phdr * phdr = elf_get_phdr_table(h, &nr_phdr); assert_throw(((phdr != NULL) && (nr_phdr != 0)), "load phdr of file %s failed\n", so_file); /* find the entry symbol */ uintptr_t entry_addr = elf_get_symbol_address(h, "syscall_wrapper_entrace"); SYS_TRACE("wrapper func address will be 0x%x\n", entry_addr); name_pos = ptrace_push(so_file, strlen(so_file), TRUE); fd = ptrace_syscall(open, 3, name_pos, O_RDONLY, 0); assert_throw(fd >= 0, "open sofile for child failed, return %d", fd); SYS_TRACE("open so file for child, fd=%d\n", fd); /* for each program header */ for (int i = 0; i < nr_phdr; i++, phdr ++) { SYS_FORCE("phdr %d, type=0x%x, flag=0x%x\n", i, phdr->p_type, phdr->p_flags); if (phdr->p_type != PT_LOAD) continue; int elf_prot = 0, elf_flags = 0; if (phdr->p_flags & PF_R) elf_prot |= PROT_READ; if (phdr->p_flags & PF_W) elf_prot |= PROT_WRITE; if (phdr->p_flags & PF_X) elf_prot |= PROT_EXEC; elf_flags = MAP_PRIVATE | MAP_EXECUTABLE; unsigned long size = phdr->p_filesz + ELF_PAGEOFFSET(phdr->p_vaddr); unsigned long off = phdr->p_offset - ELF_PAGEOFFSET(phdr->p_vaddr); int32_t map_addr = load_bias + phdr->p_vaddr - ELF_PAGEOFFSET(phdr->p_vaddr); map_addr = ptrace_syscall(mmap2, 6, map_addr, size, elf_prot, elf_flags | MAP_FIXED, fd, off); assert_throw(map_addr != 0xffffffff, "map wrap so failed, return 0x%x", map_addr); } elf_cleanup(h); free(so_image); if (pvdso_ehdr) *pvdso_ehdr = load_bias; if (pvdso_entrance) *pvdso_entrance = entry_addr; }