void ptrace_attach(int pid) { regs_t regs; int status = 0; if (ptrace(PTRACE_ATTACH, pid, NULL, NULL ) < 0) { perror("ptrace_attach"); exit(-1); } status = ptrace_wait_for_signal(pid, SIGSTOP); LOGD("ptrace_wait_for_signal: %d %d\n", __LINE__, status); //waitpid(pid, NULL, WUNTRACED); ptrace_readreg(pid, ®s); memcpy(&oldregs, ®s, sizeof(regs)); ptrace_dump_regs(&oldregs, "old regs"); #ifdef ANDROID #ifdef THUMB regs.ARM_pc = 0x11; regs.ARM_cpsr |=0x30; #else regs.ARM_pc= 0; #endif #else regs.rip = 0; #endif ptrace_writereg(pid, ®s); ptrace_cont(pid); LOGD("waiting.. sigal...\n"); status = ptrace_wait_for_signal(pid, SIGSEGV); LOGD("ptrace_wait_for_signal2: %d %d\n", __LINE__, status); }
int ptrace_mymath_add(pid_t pid, long mymath_add_addr, int a, int b) { #ifdef ANDROID regs_t regs; //int stat; ptrace_readreg(pid, ®s); ptrace_dump_regs(®s, "before call to ptrace_mymath_add\n"); #ifdef THUMB regs.ARM_lr = 1; #else regs.ARM_lr= 0; #endif regs.ARM_r0= a; regs.ARM_r1= b; regs.ARM_pc= mymath_add_addr; ptrace_writereg(pid, ®s); ptrace_cont(pid); ptrace_wait_for_signal(pid, SIGSEGV); ptrace_readreg(pid, ®s); ptrace_dump_regs(®s, "before return ptrace_mymath_add\n"); return regs.ARM_r0; #endif }
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 }
/* x86_64 rdi rsi rdx r10 r8 r9 - */ static register_t inject_syscall(pid_t pid, int nb_args, register_t syscallid, ...) { /* If we have more than 6 arguments, we must put them on the stack */ /* For the moment, we don't handle this case */ assert(NB_MAX_ARGS >= nb_args); /* We do the backup of registers */ long r; register_t ret; struct user_regs_struct regs, regs_backup; ptrace_getregs(pid, ®s); regs_backup = regs; /* We get back arguments and adequately put them in registers */ int i = 0; va_list vargs; va_start(vargs, syscallid); long long unsigned *regs_ptr[NB_MAX_ARGS] = {&(regs.rdi), &(regs.rsi), &(regs.rdx), &(regs.r10), &(regs.r8), &(regs.r9)}; regs.rip = (register_t)tracer_buff->syscall; regs.rax = syscallid; if (syscallid == SYS_unprotect_protect) { regs.rip = (register_t)tracer_buff->unprotect_protect; regs.rax = SYS_mprotect; regs.r12 = SYS_mprotect; regs_ptr[3] = &(regs.r13); regs_ptr[4] = &(regs.r14); regs_ptr[5] = &(regs.r15); } for (i = 0; i < nb_args; i++) { *(regs_ptr[i]) = va_arg(vargs, long long unsigned); } va_end(vargs); ptrace_setregs(pid, ®s); ptrace_cont(pid); wait_event(pid); ptrace_getregs(pid, ®s); ret = regs.rax; ptrace_setregs(pid, ®s_backup); return ret; }
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 inject_restore_socketcall(struct tracedump *td, struct pid *sp) { /* int 0x80, int3 */ unsigned char code[4] = { 0xcd, 0x80, 0xcc, 0 }; char backup[4]; struct user_regs_struct regs2; /* backup */ ptrace_read(sp, sp->regs.eip, backup, 4); /* exec */ sp->regs.eax = sp->regs.orig_eax; ptrace_setregs(sp, &sp->regs); ptrace_write(sp, sp->regs.eip, code, 4); ptrace_cont(sp, 0, true); /* read the return code */ ptrace_getregs(sp, ®s2); sp->regs.eax = regs2.eax; /* restore */ ptrace_setregs(sp, &sp->regs); ptrace_write(sp, sp->regs.eip, backup, 4); }
int32_t inject_socketcall(struct tracedump *td, struct pid *sp, uint32_t sc_code, ...) { /* int 0x80, int3 */ unsigned char code[4] = { 0xcd, 0x80, 0xcc, 0 }; char backup[4]; struct user_regs_struct regs, regs2; int ss_vals, ss_mem, ss; va_list vl; enum arg_type type; uint32_t sv; void *ptr; uint8_t *stack, *stack_mem; uint32_t *stack32; int i, j; /* * get the required amount of stack space */ ss_vals = 0; ss_mem = 0; va_start(vl, sc_code); do { type = va_arg(vl, enum arg_type); if (type == AT_LAST) break; sv = va_arg(vl, uint32_t); /* each socketcall argument takes 4 bytes */ ss_vals += 4; /* if its memory, it takes additional sv bytes */ if (type == AT_MEM_IN || type == AT_MEM_INOUT) { ss_mem += sv; ptr = va_arg(vl, void *); } } while (true); va_end(vl); ss = ss_vals + ss_mem; /* * backup */ ptrace_getregs(sp, ®s); memcpy(®s2, ®s, sizeof regs); ptrace_read(sp, regs.eip, backup, sizeof backup); /* * write the stack */ stack = mmatic_zalloc(td->mm, ss); stack32 = (uint32_t *) stack; stack_mem = stack + ss_vals; va_start(vl, sc_code); i = 0; j = 0; do { type = va_arg(vl, enum arg_type); if (type == AT_LAST) break; sv = va_arg(vl, uint32_t); if (type == AT_VALUE) { stack32[i++] = sv; } else { /* i.e. its a memory arg */ stack32[i++] = regs.esp - ss_mem + j; /* copy the memory */ ptr = va_arg(vl, void *); memcpy(stack_mem + j, ptr, sv); j += sv; } } while (true); va_end(vl); ptrace_write(sp, regs.esp - ss, stack, ss); /* * write the code and run */ regs2.eax = 102; // socketcall regs2.ebx = sc_code; regs2.ecx = regs.esp - ss; ptrace_write(sp, regs.eip, code, sizeof code); ptrace_setregs(sp, ®s2); ptrace_cont(sp, 0, true); /* * read back */ ptrace_getregs(sp, ®s2); ptrace_read(sp, regs.esp - ss_mem, stack_mem, ss_mem); va_start(vl, sc_code); do { type = va_arg(vl, enum arg_type); if (type == AT_LAST) break; sv = va_arg(vl, uint32_t); if (type == AT_VALUE) continue; ptr = va_arg(vl, void *); if (type == AT_MEM_IN) continue; memcpy(ptr, stack_mem, sv); stack_mem += sv; } while (true); va_end(vl); /* restore */ ptrace_write(sp, regs.eip, backup, sizeof backup); ptrace_setregs(sp, ®s); mmatic_free(stack); return regs2.eax; }
void interact( const char *const argv_0) { EditLine *const el = el_init(argv_0, stdin, stdout, stderr); el_set(el, EL_PROMPT, &prompt); el_set(el, EL_EDITOR, "emacs"); History *const hist = history_init(); if (!hist) { fprintf(stderr, "Could not initalize history\n"); exit(EXIT_FAILURE); } HistEvent ev; history(hist, &ev, H_SETSIZE, 100); el_set(el, EL_HIST, history, hist); const pid_t child_pid = _gen_child(); verbose_printf("child process is %d\n", child_pid); if (options.verbose) help(); char buf[PAGE_SIZE]; size_t buf_sz = 0; int end = 0; struct proc_info_t info = {}; ARCH_INIT_PROC_INFO(info); ptrace_launch(child_pid); ptrace_cont(child_pid, &info); ptrace_reap(child_pid, &info); display(&info); for (;;) { int count; const char *const line = el_gets(el, &count); if (count == -1) { perror("el_gets"); exit(EXIT_FAILURE); } // count is 0 == ^d if (!count || strcasestr(line, ".quit") || strcasestr(line, ".exit")) break; // We have input, add it to the our history history(hist, &ev, H_ENTER, line); // If we start with a ., we have a command if (line[0] == '.') { if (strcasestr(line, "help")) { help(); continue; } if (strcasestr(line, "info")) { display(&info); continue; } if (strcasestr(line, "showmap")) { char cmd[PATH_MAX] = { 0 }; snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", child_pid); if (system(cmd)) fprintf(stderr, "sh: %s failed\n", cmd); continue; } if (strcasestr(line, "read")) { ui_read(child_pid, line); continue; } if (strcasestr(line, "write")) { continue; } if (strcasestr(line, "begin")) { in_block = 1; continue; } // Note the lack of continue. Need to fall through... if (strcasestr(line, "end")) { in_block = 0; end = 1; } } if (buf_sz + count > sizeof(buf)) { printf("Buffer full (max: 0x%lx), please use '.end'\n", sizeof(buf)); continue; } // Since we fell through, we want to avoid adding adding .end to our buffer if (!end) { memcpy(buf + buf_sz, line, count); buf_sz += count; } if (!in_block) { verbose_printf("Trying to assemble(%zu):\n%s", buf_sz, buf); uint8_t bytecode[PAGE_SIZE]; const size_t bytecode_sz = assemble(bytecode, sizeof(bytecode), buf, buf_sz); memset(buf, 0, sizeof(buf)); buf_sz = 0; end = 0; verbose_printf("Got asm(%zu):\n", bytecode_sz); verbose_dump(bytecode, bytecode_sz, -1); if (!bytecode_sz) { fprintf(stderr, "'%s' assembled to 0 length bytecode\n", buf); continue; } ptrace_write(child_pid, (void *)options.start, bytecode, bytecode_sz); ptrace_reset(child_pid, options.start); ptrace_cont(child_pid, &info); ptrace_reap(child_pid, &info); display(&info); } } ptrace_detatch(child_pid, &info); printf("\n"); history_end(hist); el_end(el); }
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; }
void gain_code_exec(pid_t remote_pid) { malloc_stub(); ptrace_detach(remote_pid); exit(1); int status; // Find remote function adresses for malloc, free, __libc_dlopen_mode unsigned long remote_malloc_addr = find_remote_function("libc", find_libc_function("malloc"), remote_pid); unsigned long remote_free_addr = find_remote_function("libc", find_libc_function("free"), remote_pid); unsigned long remote_dlopen_addr = find_remote_function("libc", find_libc_function("__libc_dlopen_mode"), remote_pid); // Initialize global regs structs variables memset(&_regs_backup, 0, sizeof(struct user_regs_struct)); memset(&_regs_fiddle, 0, sizeof(struct user_regs_struct)); ptrace_getregs(remote_pid, &_regs_backup); reset_regs_fiddle(); // Get Addr to write our initial stub to: unsigned long free_space_addr = find_free_space_addr(remote_pid); // Set RIP to addr start (2 byte long start instruction) _regs_fiddle.rip = free_space_addr + 2; // TODO TMP // Setup parameters in registers (see malloc_stub definition) _regs_fiddle.rbx = 1073741824; // exactly one GB _regs_fiddle.rax = remote_malloc_addr; // Upload registers printf("Uploading payload regs\n"); ptrace_setregs(remote_pid, &_regs_fiddle); // Figure out stub size size_t stub_size = (intptr_t)malloc_stub_end - (intptr_t)malloc_stub; // Create buffer holdig our stub printf("Crafting payload stub\n"); char* stub_code = malloc(stub_size * sizeof(char)); memset(stub_code, 0, stub_size * sizeof(char)); memcpy(stub_code, malloc_stub, stub_size); // Backup everything that we'll overwrite in target location printf("Backing up data\n"); char* backup = malloc(stub_size * sizeof(char)); read_data(remote_pid, free_space_addr, backup, stub_size); // Write our code to target location printf("Uploading payload\n"); write_data(remote_pid, free_space_addr, stub_code, stub_size); // Continue execution and wait for int3 printf("Code injection successfull, executing\n"); ptrace_cont(remote_pid); sleep(1); printf("Pid returned ... probably\n"); // Reupload old data printf("Reuploading old data\n"); write_data(remote_pid, free_space_addr, backup, stub_size); ptrace_setregs(remote_pid, &_regs_backup); printf("Reset execution flow, continuing execution"); ptrace_cont(remote_pid); free(backup); free(stub_code); printf("TMP; HALTING FOR 30 seconds\n"); sleep(30); }