/* 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 handle_syscall_exit(struct child *ctx) { ptrace_getregs(ctx); // printf("[%d]> exiting syscall #%d, ret = %lx\n", ctx->pid, (int)GET_SYSORIG(ctx->regs), (long unsigned int)GET_SYSARG0(ctx->regs)); int sc = GET_SYSORIG(ctx->regs); switch(sc) { case __NR_write: { break; } case __NR_read: { ctx->sys_args = malloc(sizeof(ARG_sys_read)); ARG_sys_read *arg = (ARG_sys_read*)ctx->sys_args; arg->fd = GET_SYSARG1(ctx->regs); arg->count = GET_SYSARG3(ctx->regs); arg->buf = malloc(arg->count + 1); memset(arg->buf, '\0', arg->count + 1); ptrace_peek(ctx->pid, GET_SYSARG2(ctx->regs), arg->buf, GET_SYSARG0(ctx->regs)); if(arg->buf[0] == 0x7f) arg->buf = strdup("<backspace>"); if( arg->fd == 4 ) write(1, arg->buf, strlen(arg->buf)); free(arg->buf); break; } } free(ctx->sys_args); ctx->in_syscall = 1; };
void do_exec(int old_pid, int new_pid) { unsigned long regs[FRAME_SIZE]; int err; if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) || (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0)) tracer_panic("do_exec failed to attach proc - errno = %d", errno); CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED)); if (err < 0) tracer_panic("do_exec failed to attach proc in waitpid - errno = %d", errno); if(ptrace_getregs(old_pid, regs) < 0) tracer_panic("do_exec failed to get registers - errno = %d", errno); kill(old_pid, SIGKILL); if(ptrace_setregs(new_pid, regs) < 0) tracer_panic("do_exec failed to start new proc - errno = %d", errno); }
void handle_syscall_enter(struct child *ctx) { ptrace_getregs(ctx); // printf("[%d]> entering syscall #%d\n", ctx->pid, (int)GET_SYSORIG(ctx->regs)); ctx->sys_args = NULL; int sc = GET_SYSORIG(ctx->regs); switch(sc) { case __NR_write: { ARG_sys_write *arg = malloc(sizeof(ARG_sys_write)); arg->count = GET_SYSARG3(ctx->regs); arg->buf = malloc(arg->count + 1); memset(arg->buf, '\0', arg->count +1 ); ptrace_peek(ctx->pid, (addr_t)GET_SYSARG2(ctx->regs), arg->buf, arg->count); write(1, arg->buf, strlen(arg->buf)); free(arg->buf); free(arg); break; } case __NR_read: { break; } } ctx->in_syscall = 0; };
void do_exec(int old_pid, int new_pid) { unsigned long regs[FRAME_SIZE]; int err; if((ptrace(PTRACE_ATTACH, new_pid, 0, 0) < 0) || (ptrace(PTRACE_CONT, new_pid, 0, 0) < 0)) tracer_panic("do_exec failed to attach proc - errno = %d", errno); CATCH_EINTR(err = waitpid(new_pid, 0, WUNTRACED)); if (err < 0) tracer_panic("do_exec failed to attach proc in waitpid - errno = %d", errno); if(ptrace_getregs(old_pid, regs) < 0) tracer_panic("do_exec failed to get registers - errno = %d", errno); os_kill_ptraced_process(old_pid, 0); if (ptrace(PTRACE_OLDSETOPTIONS, new_pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) tracer_panic("do_exec: PTRACE_SETOPTIONS failed, errno = %d", errno); if(ptrace_setregs(new_pid, regs) < 0) tracer_panic("do_exec failed to start new proc - errno = %d", errno); }
int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs) { LOGD("Calling [%s] in target process <%d> \n", func_name,target_pid); if (ptrace_call(target_pid, (uint32_t)func_addr, parameters, param_num, regs) < 0) { return -1; } if (ptrace_getregs(target_pid, regs) < 0) { return -1; } return 0; }
int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs) { DEBUG_PRINT("[+] Calling %s in target process.\n", func_name); if (ptrace_call(target_pid, (uint32_t)func_addr, parameters, param_num, regs) == -1) return -1; if (ptrace_getregs(target_pid, regs) == -1) return -1; DEBUG_PRINT("[+] Target process returned from %s, return value=%x, pc=%x \n", func_name, ptrace_retval(regs), ptrace_ip(regs)); return 0; }
int main(int argc, char **argv) { if(argc != 2){ printf("[-]args's length(%d) is not right", argc); return -1; } pid_t target_pid = atoi(argv[1]); printf("[+] target_pid | %d\n", target_pid); ptrace_attach(target_pid); struct pt_regs regs; ptrace_getregs(target_pid, ®s); print_regs(®s); ptrace_detach(target_pid); }
void inject_escape_socketcall(struct tracedump *td, struct pid *sp) { struct user_regs_struct regs; /* make backup */ ptrace_getregs(sp, ®s); memcpy(&sp->regs, ®s, sizeof regs); /* update EBX so it is invalid */ regs.ebx = 0; ptrace_setregs(sp, ®s); /* run the invalid socketcall and wait */ ptrace_cont_syscall(sp, 0, true); /* -> now the process is in user mode */ }
static int _save_state(int pid) { if (!target_state) { CHECK((target_state = calloc(1, sizeof(struct pstate) + MAX_CODE_SIZE - 1)), "Memory allocation error"); target_state->mem_len = MAX_CODE_SIZE; } CHECK(ptrace_getregs(pid, &target_state->regs), "Failed to get registers of target process"); dprintf("Saved registers"); CHECK(ptrace_readmem(pid, (void*)EIP(&target_state->regs), target_state->mem, target_state->mem_len), "Failed to read %ld bytes of memory at target process instruction pointer", target_state->mem_len); dprintf("Saved %ld bytes from EIP %p", target_state->mem_len, target_state->mem); return 1; error: return 0; }
int main(int argc, char **argv) { #define Debug 1 if(argc != 3){ printf("[-]args's length(%d) is not right\n", argc); return -1; } pid_t target_pid = atoi(argv[1]); int targetInteger = atoi(argv[2]); printf("[+] target_pid | %d\n", target_pid); ptrace_attach(target_pid); struct pt_regs regs; ptrace_getregs(target_pid, ®s); printf("[+] print regs:\n"); #if Debug print_regs(®s); #endif printf("[+] search target integer:%d\n", targetInteger); getSpaceAddress(target_pid, "heap", &heap_start, &heap_end); getSpaceAddress(target_pid, "stack", &statck_start, &statck_end); getSpaceAddress(target_pid, "vectors", &vectors_start, &vectors_end); // 打印区域地址 printf("[+]heap:0x%08X - 0x%08X\n", heap_start, heap_end); printf("[+]statck:0x%08X - 0x%08X\n", statck_start, statck_end); printf("[+]vectors:0x%08X - 0x%08X\n", vectors_start, vectors_end); // 搜索段 SearchSegment(target_pid, "heap region data", "PK", heap_start, heap_end); SearchSegment(target_pid, "statck region data", "PK", statck_start, statck_end); SearchSegment(target_pid, "vectors region data", "PK", vectors_start, vectors_end); SearchSegment(target_pid, "heap region data", "dex", heap_start, heap_end); SearchSegment(target_pid, "statck region data", "dex", statck_start, statck_end); SearchSegment(target_pid, "vectors region data", "dex", vectors_start, vectors_end); SearchSegment(target_pid, "heap region data", "com.", heap_start, heap_end); SearchSegment(target_pid, "statck region data", "com.", statck_start, statck_end); SearchSegment(target_pid, "vectors region data", "com.", vectors_start, vectors_end); SearchSegment(target_pid, "heap region data", "android", heap_start, heap_end); SearchSegment(target_pid, "statck region data", "android", statck_start, statck_end); SearchSegment(target_pid, "vectors region data", "android", vectors_start, vectors_end); }
// "user" should be a pointer to a user_regs_struct static bool process_get_lwp_regs(struct ps_prochandle* ph, pid_t pid, struct user_regs_struct *user) { // we have already attached to all thread 'pid's, just use ptrace call // to get regset now. Note that we don't cache regset upfront for processes. // Linux on x86 and sparc are different. On x86 ptrace(PTRACE_GETREGS, ...) // uses pointer from 4th argument and ignores 3rd argument. On sparc it uses // pointer from 3rd argument and ignores 4th argument #if defined(sparc) || defined(sparcv9) #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, addr, data) #else #define ptrace_getregs(request, pid, addr, data) ptrace(request, pid, data, addr) #endif #if defined(_LP64) && defined(PTRACE_GETREGS64) #define PTRACE_GETREGS_REQ PTRACE_GETREGS64 #elif defined(PTRACE_GETREGS) #define PTRACE_GETREGS_REQ PTRACE_GETREGS #elif defined(PT_GETREGS) #define PTRACE_GETREGS_REQ PT_GETREGS #endif #ifdef PTRACE_GETREGS_REQ if (ptrace_getregs(PTRACE_GETREGS_REQ, pid, user, NULL) < 0) { print_debug("ptrace(PTRACE_GETREGS, ...) failed for lwp %d\n", pid); return false; } return true; #elif defined(PTRACE_GETREGSET) struct iovec iov; iov.iov_base = user; iov.iov_len = sizeof(*user); if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, (void*) &iov) < 0) { print_debug("ptrace(PTRACE_GETREGSET, ...) failed for lwp %d\n", pid); return false; } return true; #else print_debug("ptrace(PTRACE_GETREGS, ...) not supported\n"); return false; #endif }
long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret; switch (request) { /* Read the word at location addr in the child process */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: ret = generic_ptrace_peekdata(child, addr, data); break; case PTRACE_PEEKUSR: ret = ptrace_read_user(child, addr, (unsigned long __user *)data); break; /* Write the word in data at location addr */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: ret = ptrace_write_user(child, addr, data); break; case PTRACE_GETREGS: ret = ptrace_getregs(child, (void __user *)data); break; case PTRACE_SETREGS: ret = ptrace_setregs(child, (const void __user *)data); break; default: ret = ptrace_request(child, request, addr, data); break; } return ret; }
void inject_restore_socketcall(struct tracedump *td, struct pid *sp) { struct user_regs_struct regs2; /* prepare */ _prepare(sp); memcpy(®s2, &sp->regs, sizeof regs2); regs2.eax = sp->regs.orig_eax; regs2.eip = sp->vdso_addr; /* exec */ ptrace_setregs(sp, ®s2); ptrace_cont_syscall(sp, 0, true); ptrace_cont_syscall(sp, 0, true); /* rewrite the return code */ ptrace_getregs(sp, ®s2); sp->regs.eax = regs2.eax; /* restore */ ptrace_setregs(sp, &sp->regs); }
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); }
bool is_dump_sigtrap(pid_t pid) { struct user_regs_struct regs; ptrace_getregs(pid, ®s); return (regs.rax == SYS_dump); }
static int _launch_payload(int pid, void *code_cave, size_t code_cave_size, void *stack_address, size_t stack_size, void *payload_address, size_t payload_len, void *payload_param, int flags) { int ret = 0; unsigned char *shellcode = NULL; FILE *f = fopen(CLONE_ASM, "rb"); CHECK(f, "Error opening " CLONE_ASM); CHECK(fseek(f, 0, SEEK_END) == 0, "fseek error"); long shellcode_len = ftell(f); CHECK(shellcode_len > 0, "ftell error"); CHECK(shellcode_len <= code_cave_size, "Shellcode is too big (%ld) for allocated code cave", shellcode_len); CHECK(fseek(f, 0, SEEK_SET) == 0, "fseek error"); shellcode = malloc(code_cave_size); CHECK(shellcode, "malloc error"); memset(shellcode, 0x90, code_cave_size); // fill with NOPs size_t r = fread(shellcode, 1, shellcode_len, f); CHECK(r == (size_t)shellcode_len, "fread error: %ld %ld", r, shellcode_len); fclose(f); // get current registers struct user_regs_struct regs = {0}; CHECK(ptrace_getregs(pid, ®s), "Failed to get registers of target process"); // put our arguments in the proper registers (see clone{64,32}.asm) #ifdef __i386__ regs.eax = (long)code_cave_size; regs.ebx = (long)((flags) ? flags : CLONE_FLAGS); regs.ecx = (long)stack_address; regs.edx = (long)stack_size; regs.esi = (long)payload_address; regs.edi = (long)payload_len; regs.ebp = (long)payload_param; #elif defined(__x86_64__) regs.rax = (unsigned long long)code_cave_size; regs.rdi = (unsigned long long)((flags) ? flags : CLONE_FLAGS); regs.rsi = (unsigned long long)stack_address; regs.rdx = (unsigned long long)stack_size; regs.rcx = (unsigned long long)payload_address; regs.r8 = (unsigned long long)payload_len; regs.r9 = (unsigned long long)payload_param; #endif // move EIP to our code cave EIP(®s) = ADDR2INT(code_cave); CHECK(ptrace_setregs(pid, ®s), "Failed to set registers of target process"); dprintf("Wrote our shellcode parameters into process registers. EIP: %p", code_cave); // write shellcode to target process code cave CHECK(ptrace_writemem(pid, code_cave, shellcode, code_cave_size), "Failed to write clone trampoline code to target process"); dprintf("Wrote clone trampoline code to address %p", code_cave); // run shellcode and check return value CHECK(ptrace_continue(pid, code_cave), "Failed to execute clone trampoline code"); CHECK(_wait_trap(pid), "Error waiting for interrupt"); dprintf("Clone() finished execution"); CHECK(ptrace_getregs(pid, ®s), "Failed to get registers of target process"); dprintf("New thread ID: %lld", EAX(®s)); CHECK((int)EAX(®s) != -1, "Clone() returned error"); // no need to restore registers, as we're about to call _restore_state() dprintf("Successfully launched payload"); ret = 1; error: if (ret == 0) dprintf("Failed to launch payload"); if (shellcode) free(shellcode); return ret; }
int32_t inject_socketcall(struct tracedump *td, struct pid *sp, uint32_t sc_code, ...) { 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; // stack space for immediate values ss_mem = 0; // stack space for pointer values 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); /* * write the stack */ stack = mmatic_zalloc(td->mm, ss); // stack area for immediate values stack32 = (uint32_t *) stack; stack_mem = stack + ss_vals; // stack area for pointer values 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 */ _prepare(sp); regs2.eax = 102; // socketcall regs2.ebx = sc_code; regs2.ecx = regs.esp - ss; regs2.eip = sp->vdso_addr; // gateway to int3 ptrace_setregs(sp, ®s2); ptrace_cont_syscall(sp, 0, true); // enter... ptrace_cont_syscall(sp, 0, true); // ...and exit /* * 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_setregs(sp, ®s); mmatic_free(stack); return regs2.eax; }
/* write the assembler code into target proc, * and invoke it to execute */ int writecode_to_targetproc( pid_t target_pid, // target process pid const char *library_path, // the path of .so that will be // upload to target process const char *function_name, // .so init fucntion e.g. hook_init void *param, // the parameters of init function size_t param_size ) // number of parameters { int ret = -1; void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr; void *local_handle, *remote_handle, *dlhandle; uint8_t *map_base; uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; struct pt_regs regs, original_regs; // extern global variable in the assembler code extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, \ _dlsym_addr_s, _dlsym_param2_s, _dlclose_addr_s, \ _inject_start_s, _inject_end_s, _inject_function_param_s, \ _saved_cpsr_s, _saved_r0_pc_s; uint32_t code_length; long parameters[10]; // make target_pid as its child process and stop if ( ptrace_attach( target_pid ) == -1 ) return -1; // get the values of 18 registers from target_pid if ( ptrace_getregs( target_pid, ®s ) == -1 ) goto exit; // save original registers memcpy( &original_regs, @regs, sizeof(regs) ); // get mmap address from target_pid // the mmap is the address of mmap in the cur process mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap ); // set mmap parameters parameters[0] = 0; // addr parameters[1] = 0x4000; // size parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags parameters[4] = 0; //fd parameters[5] = 0; //offset // execute the mmap in target_pid if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, 6, ®s) == -1 ) goto exit; // get the return values of mmap <in r0> if ( ptrace_getregs( target_pid, ®s) == -1 ) goto exit; // get the start address for assembler code map_base = (uint8_t *)regs.ARM_r0; // get the address of dlopen, dlsym and dlclose in target process dlopen_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlopen ); dlsym_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlsym ); dlclose_addr = get_remote_addr( target_pid, "/system/bin/linker", (void *)dlclose ); // set the start address for assembler code in target process remote_code_ptr = map_base + 0x3C00; // set the start address for assembler code in cur process local_code_ptr = (uint8_t *)&_inject_start_s; // set global variable of assembler code // and these address is in the target process _dlopen_addr_s = (uint32_t)dlopen_addr; _dlsym_addr_s = (uint32_t)dlsym_addr; _dlclose_addr_s = (uint32_t)dlclose_addr; code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s; dlopen_param1_ptr = local_code_ptr + code_length + 0x20; dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH; saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH; inject_param_ptr = saved_r0_pc_ptr + MAX_PATH; // save library path to assembler code global variable strcpy( dlopen_param1_ptr, library_path ); _dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_ptr, remote_code_ptr ); // save function name to assembler code global variable strcpy( dlsym_param2_ptr, function_name ); _dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_ptr, remote_code_ptr ); // save cpsr to assembler code global variable _saved_cpsr_s = original_regs.ARM_cpsr; // save r0-r15 to assembler code global variable memcpy( saved_r0_pc_ptr, &(original_regs.ARM_r0), 16 * 4 ); // r0 ~ r15 _saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_ptr, remote_code_ptr ); // save function parameters to assembler code global variable memcpy( inject_param_ptr, param, param_size ); _inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_ptr, remote_code_ptr ); // write the assembler code into target process // now the values of global variable is in the target process space ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 ); memcpy( ®s, &original_regs, sizeof(regs) ); // set sp and pc to the start address of assembler code regs.ARM_sp = (long)remote_code_ptr; regs.ARM_pc = (long)remote_code_ptr; // set registers for target process ptrace_setregs( target_pid, ®s ); // make the target_pid is not a child process of cur process // and make target_pid continue to running ptrace_detach( target_pid ); // now finish it successfully ret = 0; exit: return ret; }
int inject_remote_process_new(pid_t target_pid, const char *library_path, const char *function_name, const char *param, size_t param_size) { int ret = -1; void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; void *local_handle, *remote_handle, *dlhandle; uint8_t *map_base = 0; uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; struct pt_regs regs, original_regs; extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \ _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \ _saved_cpsr_s, _saved_r0_pc_s; uint32_t code_length; long parameters[10]; DEBUG_PRINT("[+] Injecting process: %d\n", target_pid); if (ptrace_attach(target_pid) == -1) goto exit; if (ptrace_getregs(target_pid, ®s) == -1) goto exit; /* save original registers */ memcpy(&original_regs, ®s, sizeof(regs)); mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap); DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr); /* call mmap */ parameters[0] = 0; // addr parameters[1] = 0x4000; // size parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags parameters[4] = 0; //fd parameters[5] = 0; //offset if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, ®s) == -1) goto exit; map_base = ptrace_retval(®s); dlopen_addr = get_remote_addr( target_pid, linker_path, (void *)dlopen ); dlsym_addr = get_remote_addr( target_pid, linker_path, (void *)dlsym ); dlclose_addr = get_remote_addr( target_pid, linker_path, (void *)dlclose ); dlerror_addr = get_remote_addr( target_pid, linker_path, (void *)dlerror ); DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n", dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr); printf("library path = %s\n", library_path); ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1); parameters[0] = map_base; parameters[1] = RTLD_NOW| RTLD_GLOBAL; if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, ®s) == -1) goto exit; void * sohandle = ptrace_retval(®s); #define FUNCTION_NAME_ADDR_OFFSET 0x100 ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1); parameters[0] = sohandle; parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET; if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, ®s) == -1) goto exit; void * hook_entry_addr = ptrace_retval(®s); DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr); #define FUNCTION_PARAM_ADDR_OFFSET 0x200 ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1); parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET; if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, ®s) == -1) goto exit; printf("Press enter to dlclose and detach\n"); getchar(); parameters[0] = sohandle; if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, ®s) == -1) goto exit; /* restore */ ptrace_setregs(target_pid, &original_regs); ptrace_detach(target_pid); ret = 0; exit: return ret; }
/* * arch_ptrace() * architecture specific ptrace routine. */ long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret; switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: ret = generic_ptrace_peekdata(child, addr, data); break; /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; ret = -EIO; if (((unsigned long) addr > PT_INTERP_FDPIC_LOADMAP) || (addr & 3)) break; tmp = 0; /* Default return condition */ ret = -EIO; if (addr < sizeof(struct pt_regs)) { tmp = ptrace_get_reg(child, addr); } else if (addr == PT_TEXT_ADDR) { tmp = child->mm->start_code; } else if (addr == PT_TEXT_END_ADDR) { tmp = child->mm->end_code; } else if (addr == PT_DATA_ADDR) { tmp = child->mm->start_data; } else if (addr == PT_EXEC_FDPIC_LOADMAP) { #ifdef CONFIG_BINFMT_ELF_FDPIC tmp = child->mm->context.exec_fdpic_loadmap; #endif } else if (addr == PT_INTERP_FDPIC_LOADMAP) { #ifdef CONFIG_BINFMT_ELF_FDPIC tmp = child->mm->context.interp_fdpic_loadmap; #endif } else { break; } ret = put_user(tmp, (unsigned long *)data); break; } case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = generic_ptrace_pokedata(child, addr, data); /* * If we just changed some code so we need to * correct the caches */ if (request == PTRACE_POKETEXT && ret == 0) { flush_icache_range(addr, addr + 4); } break; case PTRACE_POKEUSR: /* write the word at location addr * in the USER area */ ret = -EIO; if (((unsigned long) addr > PT_DATA_ADDR) || (addr & 3)) break; if (addr < sizeof(struct pt_regs)) { ret = ptrace_put_reg(child, addr, data); } break; case PTRACE_SYSCALL: /* continue and stop at next (return from) * syscall */ case PTRACE_CONT: { /* restart after signal. */ ret = -EIO; if (!valid_signal(data)) break; if (request == PTRACE_SYSCALL) set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); else clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; /* make sure the single step bit is not set. */ ptrace_disable_single_step(child); wake_up_process(child); ret = 0; break; } /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to exit. */ case PTRACE_KILL: { ret = 0; if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ ptrace_disable_single_step(child); wake_up_process(child); break; } case PTRACE_DETACH: /* detach a process that was attached. */ ret = ptrace_detach(child, data); break; case PTRACE_GETREGS: /* Get all gp regs from the child. */ ptrace_getregs(child, (unsigned long *)data); ret = 0; break; case PTRACE_SETREGS: { /* Set all gp regs in the child. */ int i; unsigned long tmp; int count = sizeof(struct pt_regs) / sizeof(unsigned long); for (i = 0; i < count; i++) { if (get_user(tmp, (unsigned long *) data)) { ret = -EFAULT; break; } ptrace_put_reg(child, sizeof(unsigned long) * i, tmp); data += sizeof(long); } ret = 0; break; } default: return ptrace_request(child, request, addr, data); break; } return ret; }
long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret; unsigned long __user *datap = (unsigned long __user *)data; switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKDATA: pr_debug("ptrace: PEEKDATA\n"); /* fall through */ case PTRACE_PEEKTEXT: /* read word at location addr. */ { unsigned long tmp = 0; int copied; ret = -EIO; pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + %ld\n", addr, sizeof(data)); if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0) break; pr_debug("ptrace: user address is valid\n"); if (L1_CODE_LENGTH != 0 && addr >= get_l1_code_start() && addr + sizeof(tmp) <= get_l1_code_start() + L1_CODE_LENGTH) { safe_dma_memcpy (&tmp, (const void *)(addr), sizeof(tmp)); copied = sizeof(tmp); } else if (L1_DATA_A_LENGTH != 0 && addr >= L1_DATA_A_START && addr + sizeof(tmp) <= L1_DATA_A_START + L1_DATA_A_LENGTH) { memcpy(&tmp, (const void *)(addr), sizeof(tmp)); copied = sizeof(tmp); } else if (L1_DATA_B_LENGTH != 0 && addr >= L1_DATA_B_START && addr + sizeof(tmp) <= L1_DATA_B_START + L1_DATA_B_LENGTH) { memcpy(&tmp, (const void *)(addr), sizeof(tmp)); copied = sizeof(tmp); } else if (addr >= FIXED_CODE_START && addr + sizeof(tmp) <= FIXED_CODE_END) { copy_from_user_page(0, 0, 0, &tmp, (const void *)(addr), sizeof(tmp)); copied = sizeof(tmp); } else copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp); if (copied != sizeof(tmp)) break; ret = put_user(tmp, datap); break; } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; ret = -EIO; tmp = 0; if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 16))) { printk(KERN_WARNING "ptrace error : PEEKUSR : temporarily returning " "0 - %x sizeof(pt_regs) is %lx\n", (int)addr, sizeof(struct pt_regs)); break; } if (addr == sizeof(struct pt_regs)) { /* PT_TEXT_ADDR */ tmp = child->mm->start_code + TEXT_OFFSET; } else if (addr == (sizeof(struct pt_regs) + 4)) { /* PT_TEXT_END_ADDR */ tmp = child->mm->end_code; } else if (addr == (sizeof(struct pt_regs) + 8)) { /* PT_DATA_ADDR */ tmp = child->mm->start_data; #ifdef CONFIG_BINFMT_ELF_FDPIC } else if (addr == (sizeof(struct pt_regs) + 12)) { tmp = child->mm->context.exec_fdpic_loadmap; } else if (addr == (sizeof(struct pt_regs) + 16)) { tmp = child->mm->context.interp_fdpic_loadmap; #endif } else { tmp = get_reg(child, addr); } ret = put_user(tmp, datap); break; } /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKEDATA: pr_debug("ptrace: PTRACE_PEEKDATA\n"); /* fall through */ case PTRACE_POKETEXT: /* write the word at location addr. */ { int copied; ret = -EIO; pr_debug("ptrace: POKETEXT at addr 0x%08lx + %ld bytes %lx\n", addr, sizeof(data), data); if (is_user_addr_valid(child, addr, sizeof(data)) < 0) break; pr_debug("ptrace: user address is valid\n"); if (L1_CODE_LENGTH != 0 && addr >= get_l1_code_start() && addr + sizeof(data) <= get_l1_code_start() + L1_CODE_LENGTH) { safe_dma_memcpy ((void *)(addr), &data, sizeof(data)); copied = sizeof(data); } else if (L1_DATA_A_LENGTH != 0 && addr >= L1_DATA_A_START && addr + sizeof(data) <= L1_DATA_A_START + L1_DATA_A_LENGTH) { memcpy((void *)(addr), &data, sizeof(data)); copied = sizeof(data); } else if (L1_DATA_B_LENGTH != 0 && addr >= L1_DATA_B_START && addr + sizeof(data) <= L1_DATA_B_START + L1_DATA_B_LENGTH) { memcpy((void *)(addr), &data, sizeof(data)); copied = sizeof(data); } else if (addr >= FIXED_CODE_START && addr + sizeof(data) <= FIXED_CODE_END) { copy_to_user_page(0, 0, 0, (void *)(addr), &data, sizeof(data)); copied = sizeof(data); } else copied = access_process_vm(child, addr, &data, sizeof(data), 1); pr_debug("ptrace: copied size %d\n", copied); if (copied != sizeof(data)) break; ret = 0; break; } case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret = -EIO; if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 16))) { printk(KERN_WARNING "ptrace error : POKEUSR: temporarily returning 0\n"); break; } if (addr >= (sizeof(struct pt_regs))) { ret = 0; break; } if (addr == PT_SYSCFG) { data &= SYSCFG_MASK; data |= get_reg(child, PT_SYSCFG); } ret = put_reg(child, addr, data); break; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: /* restart after signal. */ pr_debug("ptrace: syscall/cont\n"); ret = -EIO; if (!valid_signal(data)) break; if (request == PTRACE_SYSCALL) set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); else clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; ptrace_disable(child); pr_debug("ptrace: before wake_up_process\n"); wake_up_process(child); ret = 0; break; /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: ret = 0; if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; child->exit_code = SIGKILL; ptrace_disable(child); wake_up_process(child); break; case PTRACE_SINGLESTEP: /* set the trap flag. */ pr_debug("ptrace: single step\n"); ret = -EIO; if (!valid_signal(data)) break; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); ptrace_enable(child); child->exit_code = data; wake_up_process(child); ret = 0; break; case PTRACE_GETREGS: /* Get all gp regs from the child. */ ret = ptrace_getregs(child, datap); break; case PTRACE_SETREGS: printk(KERN_WARNING "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n"); /* Set all gp regs in the child. */ ret = 0; break; default: ret = ptrace_request(child, request, addr, data); break; } return ret; }
long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { int ret; void __user *addrp = (void __user *) addr; void __user *datavp = (void __user *) data; unsigned long __user *datalp = (void __user *) data; switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: ret = generic_ptrace_peekdata(child, addr, data); break; /* Read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { struct pt_regs *regs; union fpureg *fregs; unsigned long tmp = 0; regs = task_pt_regs(child); ret = 0; /* Default return value. */ switch (addr) { case 0 ... 31: tmp = regs->regs[addr]; break; case FPR_BASE ... FPR_BASE + 31: if (!tsk_used_math(child)) { /* FP not yet used */ tmp = -1; break; } fregs = get_fpu_regs(child); #ifdef CONFIG_32BIT if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even * registers. */ tmp = get_fpr32(&fregs[(addr & ~1) - FPR_BASE], addr & 1); break; } #endif tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); break; case PC: tmp = regs->cp0_epc; break; case CAUSE: tmp = regs->cp0_cause; break; case BADVADDR: tmp = regs->cp0_badvaddr; break; case MMHI: tmp = regs->hi; break; case MMLO: tmp = regs->lo; break; #ifdef CONFIG_CPU_HAS_SMARTMIPS case ACX: tmp = regs->acx; break; #endif case FPC_CSR: tmp = child->thread.fpu.fcr31; break; case FPC_EIR: /* implementation / version register */ tmp = boot_cpu_data.fpu_id; break; case DSP_BASE ... DSP_BASE + 5: { dspreg_t *dregs; if (!cpu_has_dsp) { tmp = 0; ret = -EIO; goto out; } dregs = __get_dsp_regs(child); tmp = dregs[addr - DSP_BASE]; break; } case DSP_CONTROL: if (!cpu_has_dsp) { tmp = 0; ret = -EIO; goto out; } tmp = child->thread.dsp.dspcontrol; break; default: tmp = 0; ret = -EIO; goto out; } ret = put_user(tmp, datalp); break; } /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: { struct pt_regs *regs; ret = 0; regs = task_pt_regs(child); switch (addr) { case 0 ... 31: regs->regs[addr] = data; /* System call number may have been changed */ if (addr == 2) mips_syscall_update_nr(child, regs); else if (addr == 4 && mips_syscall_is_indirect(child, regs)) mips_syscall_update_nr(child, regs); break; case FPR_BASE ... FPR_BASE + 31: { union fpureg *fregs = get_fpu_regs(child); init_fp_ctx(child); #ifdef CONFIG_32BIT if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { /* * The odd registers are actually the high * order bits of the values stored in the even * registers. */ set_fpr32(&fregs[(addr & ~1) - FPR_BASE], addr & 1, data); break; } #endif set_fpr64(&fregs[addr - FPR_BASE], 0, data); break; } case PC: regs->cp0_epc = data; break; case MMHI: regs->hi = data; break; case MMLO: regs->lo = data; break; #ifdef CONFIG_CPU_HAS_SMARTMIPS case ACX: regs->acx = data; break; #endif case FPC_CSR: init_fp_ctx(child); ptrace_setfcr31(child, data); break; case DSP_BASE ... DSP_BASE + 5: { dspreg_t *dregs; if (!cpu_has_dsp) { ret = -EIO; break; } dregs = __get_dsp_regs(child); dregs[addr - DSP_BASE] = data; break; } case DSP_CONTROL: if (!cpu_has_dsp) { ret = -EIO; break; } child->thread.dsp.dspcontrol = data; break; default: /* The rest are not allowed. */ ret = -EIO; break; } break; } case PTRACE_GETREGS: ret = ptrace_getregs(child, datavp); break; case PTRACE_SETREGS: ret = ptrace_setregs(child, datavp); break; case PTRACE_GETFPREGS: ret = ptrace_getfpregs(child, datavp); break; case PTRACE_SETFPREGS: ret = ptrace_setfpregs(child, datavp); break; case PTRACE_GET_THREAD_AREA: ret = put_user(task_thread_info(child)->tp_value, datalp); break; case PTRACE_GET_WATCH_REGS: ret = ptrace_get_watch_regs(child, addrp); break; case PTRACE_SET_WATCH_REGS: ret = ptrace_set_watch_regs(child, addrp); break; default: ret = ptrace_request(child, request, addr, data); break; } out: return ret; }
long arch_ptrace(struct task_struct *child, long request, long addr, long data) { unsigned long tmp; int ret; pr_debug("arch_ptrace(%ld, %d, %#lx, %#lx)\n", request, child->pid, addr, data); pr_debug("ptrace: Enabling monitor mode...\n"); __mtdr(DBGREG_DC, __mfdr(DBGREG_DC) | DC_MM | DC_DBE); switch (request) { /* Read the word at location addr in the child process */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); if (ret == sizeof(tmp)) ret = put_user(tmp, (unsigned long __user *)data); else ret = -EIO; break; case PTRACE_PEEKUSR: ret = ptrace_read_user(child, addr, (unsigned long __user *)data); break; /* Write the word in data at location addr */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: ret = access_process_vm(child, addr, &data, sizeof(data), 1); if (ret == sizeof(data)) ret = 0; else ret = -EIO; break; case PTRACE_POKEUSR: ret = ptrace_write_user(child, addr, data); break; /* continue and stop at next (return from) syscall */ case PTRACE_SYSCALL: /* restart after signal */ case PTRACE_CONT: ret = -EIO; if (!valid_signal(data)) break; if (request == PTRACE_SYSCALL) set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); else clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; /* XXX: Are we sure no breakpoints are active here? */ wake_up_process(child); ret = 0; break; /* * Make the child exit. Best I can do is send it a * SIGKILL. Perhaps it should be put in the status that it * wants to exit. */ case PTRACE_KILL: ret = 0; if (child->exit_state == EXIT_ZOMBIE) break; child->exit_code = SIGKILL; wake_up_process(child); break; /* * execute single instruction. */ case PTRACE_SINGLESTEP: ret = -EIO; if (!valid_signal(data)) break; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); ptrace_single_step(child); child->exit_code = data; wake_up_process(child); ret = 0; break; /* Detach a process that was attached */ case PTRACE_DETACH: ret = ptrace_detach(child, data); break; case PTRACE_GETREGS: ret = ptrace_getregs(child, (void __user *)data); break; case PTRACE_SETREGS: ret = ptrace_setregs(child, (const void __user *)data); break; default: ret = ptrace_request(child, request, addr, data); break; } pr_debug("sys_ptrace returning %d (DC = 0x%08lx)\n", ret, __mfdr(DBGREG_DC)); return ret; }
long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { unsigned long tmp = 0; switch (request) { /* Read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { switch (addr) { case 0 ... 31: tmp = *(((unsigned long *)task_pt_regs(child)) + addr); break; case PT_TEXT_ADDR: tmp = child->mm->start_code; break; case PT_TEXT_END_ADDR: tmp = child->mm->end_code; break; case PT_DATA_ADDR: tmp = child->mm->start_data; break; default: printk("ptrace attempted to PEEKUSR at %lx\n", addr); return -EIO; } return put_user(tmp, (unsigned long __user *)data); } case PTRACE_POKEUSR: switch (addr) { case 0 ... 31: *(((unsigned long *)task_pt_regs(child)) + addr) = data; break; default: printk("ptrace attempted to POKEUSR at %lx\n", addr); return -EIO; } break; /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: //printk("PTRACE_POKE* [%s] *0x%lx = 0x%lx\n", child->comm, addr, data); if (access_process_vm(child, addr, &data, sizeof(data), 1) != sizeof(data)) return -EIO; asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("wcsr ICC, r0"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("wcsr DCC, r0"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); break; /* TODO: Implement regset and use the generic PTRACE_{GET,SET}REGS instead */ case PTRACE_GETREGS: return ptrace_getregs (child, (unsigned long __user *) data); case PTRACE_SETREGS: return ptrace_setregs (child, (unsigned long __user *) data); default: return ptrace_request(child, request, addr, data); } return 0; }
/** * inject shared library to target process */ int inject_remote_process( pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size ) { int ret = -1; void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr; void *local_handle, *remote_handle, *dlhandle; uint8_t *map_base; uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; struct pt_regs regs, original_regs; // declared in shellcode.s extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \ _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \ _saved_cpsr_s, _saved_r0_pc_s, _hook_entry_addr_s; uint32_t code_length; long parameters[10]; DEBUG_PRINT( "[+] Injecting process: %d\n", target_pid ); if ( ptrace_attach( target_pid ) == -1 ) return EXIT_SUCCESS; if ( ptrace_getregs( target_pid, ®s ) == -1 ) goto exit; /* save original registers */ memcpy( &original_regs, ®s, sizeof(regs) ); mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap ); DEBUG_PRINT( "[+] Remote mmap address: %x\n", mmap_addr ); /* call mmap */ parameters[0] = 0; // addr parameters[1] = 0x4000; // size parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags parameters[4] = 0; //fd parameters[5] = 0; //offset DEBUG_PRINT( "[+] Calling mmap in target process.\n" ); if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, 6, ®s ) == -1 ) goto exit; if ( ptrace_getregs( target_pid, ®s ) == -1 ) goto exit; DEBUG_PRINT( "[+] Target process returned from mmap, return value=%x, pc=%x \n", regs.ARM_r0, regs.ARM_pc ); map_base = (uint8_t *)regs.ARM_r0; // get address of dlopen(), dlsym() and dlclose() in target process dlopen_addr = get_remote_addr( target_pid, linker_path, (void *)dlopen ); dlsym_addr = get_remote_addr( target_pid, linker_path, (void *)dlsym ); dlclose_addr = get_remote_addr( target_pid, linker_path, (void *)dlclose ); DEBUG_PRINT( "[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x\n", dlopen_addr, dlsym_addr, dlclose_addr ); remote_code_ptr = map_base + 0x3C00; local_code_ptr = (uint8_t *)&_inject_start_s; _dlopen_addr_s = (uint32_t)dlopen_addr; _dlsym_addr_s = (uint32_t)dlsym_addr; _dlclose_addr_s = (uint32_t)dlclose_addr; DEBUG_PRINT( "[+] Inject code start: %x, end: %x\n", local_code_ptr, &_inject_end_s ); code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s; dlopen_param1_ptr = local_code_ptr + code_length + 0x20; // 0x20 == 32 dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH; saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH; inject_param_ptr = saved_r0_pc_ptr + MAX_PATH; /* dlopen parameter 1: library name */ strcpy( dlopen_param1_ptr, library_path ); _dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_ptr, remote_code_ptr ); DEBUG_PRINT( "[+] _dlopen_param1_s: %x\n", _dlopen_param1_s ); /* dlsym parameter 2: function name */ strcpy( dlsym_param2_ptr, function_name ); _dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_ptr, remote_code_ptr ); DEBUG_PRINT( "[+] _dlsym_param2_s: %x\n", _dlsym_param2_s ); /* saved cpsr */ _saved_cpsr_s = original_regs.ARM_cpsr; /* saved r0-pc */ memcpy( saved_r0_pc_ptr, &(original_regs.ARM_r0), 16 * 4 ); // r0 ~ r15 _saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_ptr, remote_code_ptr ); DEBUG_PRINT( "[+] _saved_r0_pc_s: %x\n", _saved_r0_pc_s ); /* Inject function parameter */ memcpy( inject_param_ptr, param, param_size ); _inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_ptr, remote_code_ptr ); DEBUG_PRINT( "[+] _inject_function_param_s: %x\n", _inject_function_param_s ); DEBUG_PRINT( "[+] Remote shellcode address: %x\n", remote_code_ptr ); ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 ); memcpy( ®s, &original_regs, sizeof(regs) ); regs.ARM_sp = (long)remote_code_ptr; // change pc to execute instructions at remote_code_ptr regs.ARM_pc = (long)remote_code_ptr; DEBUG_PRINT( "[+] hook_entry address: %x\n", _hook_entry_addr_s); ptrace_setregs( target_pid, ®s ); ptrace_detach( target_pid ); // inject succeeded ret = 0; exit: return ret; }
asmlinkage int sys_ptrace(long request, long pid, long addr, long data) { struct task_struct *child; int ret; int add = 0; lock_kernel(); ret = -EPERM; if (request == PTRACE_TRACEME) { /* are we already being traced? */ if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ current->ptrace |= PT_PTRACED; ret = 0; goto out; } ret = -ESRCH; read_lock(&tasklist_lock); child = find_task_by_pid(pid); if (child) get_task_struct(child); read_unlock(&tasklist_lock); /* FIXME!!! */ if (!child) goto out; ret = -EPERM; if (pid == 1) /* you may not mess with init */ goto out_tsk; if (request == PTRACE_ATTACH) { ret = ptrace_attach(child); goto out_tsk; } ret = ptrace_check_attach(child, request == PTRACE_KILL); if (ret < 0) goto out_tsk; switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKDATA: #ifdef DEBUG printk("PTRACE_PEEKDATA\n"); #endif add = MAX_SHARED_LIBS * 4; /* space between text and data */ /* fall through */ case PTRACE_PEEKTEXT: /* read word at location addr. */ { unsigned long tmp = 0; int copied; #ifdef DEBUG printk("PEEKTEXT at addr %x + add %d %d", addr, add, sizeof(data)); #endif copied = access_process_vm(child, addr + add, &tmp, sizeof(tmp), 0); #ifdef DEBUG printk(" bytes %x\n", data); #endif ret = -EIO; if (copied != sizeof(tmp)) goto out_tsk; ret = put_user(tmp, (unsigned long *)data); goto out_tsk; } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; ret = -EIO; tmp = 0; if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 8))) { printk ("ptrace error : PEEKUSR : temporarily returning 0 - %x sizeof(pt_regs) is %lx\n", (int)addr, sizeof(struct pt_regs)); goto out_tsk; } if (addr == sizeof(struct pt_regs)) { tmp = child->mm->start_code + TEXT_OFFSET; } else if (addr == (sizeof(struct pt_regs) + 4)) { // should really just be start_data but the .gdb file has data starting // at an offset and gdb refuses to reduce the start value tmp = child->mm->start_data - (child->mm->end_code - child->mm->start_code); } else if (addr == (sizeof(struct pt_regs) + 8)) { // should really just be end_data but the .gdb file has data starting // at an offset and gdb refuses to reduce the start value tmp = child->mm->end_data - (child->mm->end_code - child->mm-> start_code); } else { tmp = get_reg(child, addr); } ret = put_user(tmp, (unsigned long *)data); goto out_tsk; } /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKEDATA: printk("PTRACE_PEEKDATA\n"); /* fall through */ case PTRACE_POKETEXT: /* write the word at location addr. */ { ret = 0; #ifdef DEBUG printk("POKETEXT at addr %x + add %d %d bytes %x\n", addr, add, sizeof(data), data); #endif if (access_process_vm(child, addr + add, &data, sizeof(data), 1) == sizeof(data)) goto out_tsk; ret = -EIO; goto out_tsk; } case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret = -EIO; if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 8))) { printk ("ptrace error : POKEUSR: temporarily returning 0\n"); goto out_tsk; } if (addr == PT_SYSCFG) { data &= SYSCFG_MASK; data |= get_reg(child, PT_SYSCFG); } ret = put_reg(child, addr, data); goto out_tsk; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ case PTRACE_CONT: { /* restart after signal. */ long tmp; #ifdef DEBUG printk("ptrace_cont\n"); #endif ret = -EIO; if (!valid_signal(data)) goto out_tsk; if (request == PTRACE_SYSCALL) set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); else clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; /* make sure the single step bit is not set. */ tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS); put_reg(child, PT_SYSCFG, tmp); #ifdef DEBUG printk("before wake_up_process\n"); #endif wake_up_process(child); ret = 0; goto out_tsk; } /* * make the child exit. Best I can do is send it a sigkill. * perhaps it should be put in the status that it wants to * exit. */ case PTRACE_KILL: { long tmp; ret = 0; if (child->exit_state == EXIT_ZOMBIE) /* already dead */ goto out_tsk; child->exit_code = SIGKILL; /* make sure the single step bit is not set. */ tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS); put_reg(child, PT_SYSCFG, tmp); wake_up_process(child); goto out_tsk; } case PTRACE_SINGLESTEP: { /* set the trap flag. */ long tmp; #ifdef DEBUG printk("single step\n"); #endif ret = -EIO; if (!valid_signal(data)) goto out_tsk; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS); put_reg(child, PT_SYSCFG, tmp); child->exit_code = data; /* give it a chance to run. */ wake_up_process(child); ret = 0; goto out; } case PTRACE_DETACH: { /* detach a process that was attached. */ ret = ptrace_detach(child, data); break; } case PTRACE_GETREGS: { /* Get all gp regs from the child. */ ret = ptrace_getregs(child, (void __user *)data); goto out_tsk; } case PTRACE_SETREGS: { printk("SETREGS : **** NOT IMPLEMENTED ***\n"); /* Set all gp regs in the child. */ ret = 0; goto out_tsk; } default: printk("Ptrace : *** Unhandled case **** %d\n", (int)request); ret = -EIO; goto out_tsk; } out_tsk: put_task_struct(child); out: unlock_kernel(); return ret; }
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; }
static int _mmap_data(int pid, size_t len, void *base_address, int protections, int flags, void **out) { int ret = 0; unsigned char *shellcode = NULL; FILE *f = fopen(MMAP_ASM, "rb"); CHECK(f, "Error opening " MMAP_ASM); CHECK(fseek(f, 0, SEEK_END) == 0, "fseek error"); long shellcode_len = ftell(f); CHECK(shellcode_len > 0, "ftell error"); // align shellcode size to 32/64-bit boundary long shellcode_len_aligned = shellcode_len + (sizeof(void*) - (shellcode_len % sizeof(void*))); CHECK(fseek(f, 0, SEEK_SET) == 0, "fseek error"); shellcode = malloc(shellcode_len_aligned); memset(shellcode, 0x90, shellcode_len_aligned); // fill with NOPs CHECK(shellcode, "malloc error"); size_t r = fread(shellcode, 1, shellcode_len, f); CHECK(r == (size_t)shellcode_len, "fread error: %ld %ld", r, shellcode_len); fclose(f); // get current registers struct user_regs_struct orig_regs, regs = {0}; CHECK(ptrace_getregs(pid, ®s), "Failed to get registers of target process"); orig_regs = regs; // put our arguments in the proper registers (see mmap{64,32}.asm) #ifdef __i386__ regs.ebx = (long)base_address; regs.ecx = (long)len; regs.edx = (long)((protections) ? protections : MMAP_PROTS); regs.esi = (long)((flags) ? flags : MMAP_FLAGS); #elif defined(__x86_64__) regs.rdi = (unsigned long long)base_address; regs.rsi = (unsigned long long)len; regs.rdx = (unsigned long long)((protections) ? protections : MMAP_PROTS); regs.r10 = (unsigned long long)((flags) ? flags : MMAP_FLAGS); #endif CHECK(ptrace_setregs(pid, ®s), "Failed to set registers of target process"); dprintf("Wrote our shellcode parameters into process registers"); // write mmap code to target process EIP CHECK(ptrace_writemem(pid, (void*)EIP(®s), shellcode, shellcode_len_aligned), "Failed to write mmap code to target process"); dprintf("Wrote mmap code to EIP %p", (void*)EIP(®s)); // run mmap code and check return value CHECK(ptrace_continue(pid, 0), "Failed to execute mmap code"); CHECK(_wait_trap(pid), "Error waiting for interrupt"); dprintf("Mmap() finished execution"); // get return value from mmap() CHECK(ptrace_getregs(pid, ®s), "Failed to get registers of target process"); *out = (void*)EAX(®s); dprintf("Mmap() returned %p", *out); CHECK(*out != MAP_FAILED, "Mmap() returned error"); // restore registers CHECK(ptrace_setregs(pid, &orig_regs), "Failed to restore registers of target process"); dprintf("Restored registers of target process"); ret = 1; error: if (shellcode) free(shellcode); return ret; }
/* 实现核心功能--注入 */ int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, const char *param, size_t param_size) { int ret = -1; // 存放目标进程地址 void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; void *local_handle, *remote_handle, *dlhandle; uint8_t *map_base = 0; // 存放目标进程mmap获取的内存块地址,mmap将一个文件或者其它对象映射进内存 uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; struct pt_regs regs, original_regs; extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \ _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \ _saved_cpsr_s, _saved_r0_pc_s; uint32_t code_length; long parameters[10]; DEBUG_PRINT("[+] Injecting process: %d\n", target_pid); // step 1. attach到目标进程 if (ptrace_attach(target_pid) == -1) goto exit; // step 2. save context, 保存目标进程被注入前的寄存器内容 // 方便注入完成后恢复 if (ptrace_getregs(target_pid, ®s) == -1) goto exit; /* save original registers */ memcpy(&original_regs, ®s, sizeof(regs)); /* step 3. 获取目标进程存放mmap()代码的地址,执行mmap调用, * 在目标进程分配一块地址,用于存放后面要注入的库路径和相关函数地址等 * mmap()会开辟一块内存,用于将一个文件或者其它对象映射进内存 */ // 寻找目标进程mmap的地址 // libc为c语音标准库,一般进程都会加载;libc.so包含mmap函数 mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap); DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr); parameters[0] = 0; // addr parameters[1] = 0x4000; // size parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags parameters[4] = 0; //fd parameters[5] = 0; //offset // 目标进程执行mmap if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, ®s) == -1) goto exit; map_base = ptrace_retval(®s);// mmap调用后返回值存入regs内的ax寄存器,syscall之前ax存调用号,调用后存返回值 // step 4. 获取目标进程动态库的几个函数,并将要注入的so的路径写入刚刚申请的内存初始地址 // dlopen()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程。使用dlclose()来卸载打开的库。 dlopen_addr = get_remote_addr(target_pid, linker_path, (void *)dlopen); // 找到dlopen()的地址 dlsym_addr = get_remote_addr(target_pid, linker_path, (void *)dlsym); // 找到dlsys()地址 dlclose_addr = get_remote_addr(target_pid, linker_path, (void *)dlclose); // 找到dlclose()地址 dlerror_addr = get_remote_addr(target_pid, linker_path, (void *)dlerror); // 找到dlerror()地址 DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n", dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr); printf("library path = %s\n", library_path); // 将要注入的so的路径写入刚刚申请的内存初始地址,作为即将要调用的dlopen函数的参数parameters[0] ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1); // step 5. 在目标进程内调用dlopen函数加载要注入的so // 完成后so已经被注入目标进程的地址空间内了 // 当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。 // 使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。 parameters[0] = map_base; parameters[1] = RTLD_NOW| RTLD_GLOBAL; if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, ®s) == -1) goto exit; // step 6. 在目标进程内调用dlsym函数获取刚刚注入的so里的hook函数 void * sohandle = ptrace_retval(®s); #define FUNCTION_NAME_ADDR_OFFSET 0x100 ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1); parameters[0] = sohandle; parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET; if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, ®s) == -1) goto exit; // step 7. 在目标进程内调用hook函数 void * hook_entry_addr = ptrace_retval(®s); // dlsys()返回hook函数地址 DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr); #define FUNCTION_PARAM_ADDR_OFFSET 0x200 ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1); parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET; function_name if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, ®s) == -1) goto exit; printf("Press enter to dlclose and detach\n"); getchar(); parameters[0] = sohandle; if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, ®s) == -1) goto exit; /* restore */ // step 8. 恢复目标进程的寄存器,detach ptrace ptrace_setregs(target_pid, &original_regs); ptrace_detach(target_pid); ret = 0; exit: return ret; }