long ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct user_regs_struct * regs) { regs->esp -= (num_params) * sizeof(long) ; ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long)); long tmp_addr = 0x00; regs->esp -= sizeof(long); ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr)); regs->eip = addr; if (ptrace_setregs(pid, regs) == -1 || ptrace_continue( pid) == -1) { printf("error\n"); return -1; } int stat = 0; waitpid(pid, &stat, WUNTRACED); while (stat != 0xb7f) { if (ptrace_continue(pid) == -1) { printf("error\n"); return -1; } waitpid(pid, &stat, WUNTRACED); } return 0; }
int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs) { uint32_t i; // 前面4个参数存放到寄存器里 for (i = 0; i < num_params && i < 4; i ++) { regs->uregs[i] = params[i]; } // // push remain params into stack // if (i < num_params) { // 栈顶指针sp往低地址移动,减去剩余参数的地址数(栈顶往“上”挪以容下剩余参数) regs->ARM_sp -= (num_params - i) * sizeof(long); // 往tracee的栈写入剩余参数 ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long)); } // 将PC寄存器指向目标函数, PS: 与x86不同,ARM中IP不是指令计数器,而是通用寄存器 regs->ARM_pc = addr; if (regs->ARM_pc & 1) { /* 16位的thumb格式 */ regs->ARM_pc &= (~1u); regs->ARM_cpsr |= CPSR_T_MASK; } else { /* arm格式 */ regs->ARM_cpsr &= ~CPSR_T_MASK; } regs->ARM_lr = 0; // 将构造好的寄存器内容写入目标进程寄存器 if (ptrace_setregs(pid, regs) == -1 // 恢复目标进程执行,将从调用函数地址即addr开始执行,参数为新赋值过来的寄存器内容 || ptrace_continue(pid) == -1) { printf("error\n"); return -1; } int stat = 0; waitpid(pid, &stat, WUNTRACED); while (stat != 0xb7f) { if (ptrace_continue(pid) == -1) { printf("error\n"); return -1; } waitpid(pid, &stat, WUNTRACED); } return 0; }
int ptrace_call(pid_t pid, uint32_t addr, const long *params, uint32_t num_params, struct pt_regs* regs) { uint32_t i; //前面四个参数用寄存器传递 for (i = 0; i < num_params && i < 4; i ++) { regs->uregs[i] = params[i]; } //后面参数放到栈里 if (i < num_params) { regs->ARM_sp -= (num_params - i) * sizeof(long) ; ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long)); } //PC指向要执行的函数地址 regs->ARM_pc = addr; if (regs->ARM_pc & 1) { /* thumb */ regs->ARM_pc &= (~1u); regs->ARM_cpsr |= CPSR_T_MASK; } else { /* arm */ regs->ARM_cpsr &= ~CPSR_T_MASK; } //把返回地址设为0,这样目标进程执行完返回时会出现地址错误,这样目标进程将被挂起,控制权会回到调试进程手中 regs->ARM_lr = 0; //设置目标进程的寄存器,让目标进程继续运行 if (ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1) { return -1; } //等待目标进程结束 int stat = 0; waitpid(pid, &stat, WUNTRACED); while (stat != 0xb7f) { if (ptrace_continue(pid) == -1) { return -1; } waitpid(pid, &stat, WUNTRACED); } return 0; }
int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs) { uint32_t i; for (i = 0; i < num_params && i < 4; i ++) { regs->uregs[i] = params[i]; } // // push remained params onto stack // if (i < num_params) { regs->ARM_sp -= (num_params - i) * sizeof(long) ; ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long)); } regs->ARM_pc = addr; if (regs->ARM_pc & 1) { /* thumb */ regs->ARM_pc &= (~1u); regs->ARM_cpsr |= CPSR_T_MASK; } else { /* arm */ regs->ARM_cpsr &= ~CPSR_T_MASK; } regs->ARM_lr = 0; if (ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1) { printf("error\n"); return -1; } int stat = 0; waitpid(pid, &stat, WUNTRACED); while (stat != 0xb7f) { if (ptrace_continue(pid) == -1) { printf("error\n"); return -1; } waitpid(pid, &stat, WUNTRACED); } return 0; }
static VALUE ptrace_cont(int argc, VALUE *argv, VALUE self) { VALUE data = INT2FIX(0); if (argc == 1) { data = argv[0]; } return ptrace_continue(self, PT_CONTINUE, data); }
static VALUE ptrace_singlestep(int argc, VALUE *argv, VALUE self) { VALUE data = INT2FIX(0); if (argc == 1) { data = argv[0]; } return ptrace_continue(self, PT_STEP, data); }
//------------------------------------------------------------------------------ // Name: resume // Desc: //------------------------------------------------------------------------------ void DebuggerCore::resume(edb::EVENT_STATUS status) { // TODO: assert that we are paused if(attached()) { if(status != edb::DEBUG_STOP) { const edb::tid_t tid = active_thread(); const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(threads_[tid].status) : 0; ptrace_continue(tid, code); // resume the other threads passing the signal they originally reported had for(auto it = threads_.begin(); it != threads_.end(); ++it) { if(waited_threads_.contains(it.key())) { ptrace_continue(it.key(), resume_code(it->status)); } } } } }
// waits until the ATTACH has stopped the process // by signal SIGSTOP static bool ptrace_waitpid(pid_t pid) { int ret; int status; while (true) { // Wait for debuggee to stop. ret = waitpid(pid, &status, 0); if (ret == -1 && errno == ECHILD) { // try cloned process. ret = waitpid(pid, &status, __WALL); } if (ret >= 0) { if (WIFSTOPPED(status)) { // Any signal will stop the thread, make sure it is SIGSTOP. Otherwise SIGSTOP // will still be pending and delivered when the process is DETACHED and the process // will go to sleep. if (WSTOPSIG(status) == SIGSTOP) { // Debuggee stopped by SIGSTOP. return true; } if (!ptrace_continue(pid, WSTOPSIG(status))) { print_error("Failed to correctly attach to VM. VM might HANG! [PTRACE_CONT failed, stopped by %d]\n", WSTOPSIG(status)); return false; } } else { print_debug("waitpid(): Child process exited/terminated (status = 0x%x)\n", status); return false; } } else { switch (errno) { case EINTR: continue; break; case ECHILD: print_debug("waitpid() failed. Child process pid (%d) does not exist \n", pid); break; case EINVAL: print_debug("waitpid() failed. Invalid options argument.\n"); break; default: print_debug("waitpid() failed. Unexpected error %d\n",errno); break; } return false; } } }
int ptrace_call( pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs ) { uint32_t i; // put the first 4 parameters into r0-r3 for ( i = 0; i < num_params && i < 4; i ++ ) { regs->uregs[i] = params[i]; } // push remained params into stack if ( i < num_params ) { regs->ARM_sp -= (num_params - i) * sizeof(long) ; ptrace_writedata( pid, (void *)regs->ARM_sp, (uint8_t *)params[i], (num_params - i) * sizeof(long) ); } // set the pc to func <e.g: mmap> that will be executed regs->ARM_pc = addr; if ( regs->ARM_pc & 1 ) { /* thumb */ regs->ARM_pc &= (~1u); regs->ARM_cpsr |= CPSR_T_MASK; } else { /* arm */ regs->ARM_cpsr &= ~CPSR_T_MASK; } // when finish this func, pid will stop regs->ARM_lr = 0; // set the regsister and start to execute if ( ptrace_setregs( pid, regs ) == -1 || ptrace_continue( pid ) == -1 ) { return -1; } // wait pid finish work and stop waitpid( pid, NULL, WUNTRACED ); return 0; }
/** * call function at addr in target process * pid pid of target process * addr address of the function you want to call * num_params the number of parameters * regs registers' status */ int ptrace_call( pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs ) { uint32_t i; for ( i = 0; i < num_params && i < 4; i ++ ) // ?? { regs->uregs[i] = params[i]; } // // push remained params into stack // if ( i < num_params ) { regs->ARM_sp -= (num_params - i) * sizeof(long) ; ptrace_writedata( pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long) ); } regs->ARM_pc = addr; if ( regs->ARM_pc & 1 ) // thumb { regs->ARM_pc &= (~1u); regs->ARM_cpsr |= CPSR_T_MASK; } else // arm { regs->ARM_cpsr &= ~CPSR_T_MASK; } regs->ARM_lr = 0; // ?? if ( ptrace_setregs( pid, regs ) == -1 || ptrace_continue( pid ) == -1 ) { return -1; } waitpid( pid, NULL, WUNTRACED ); return 0; }
int ptrace_call(pid_t pid, unsigned int addr, long *params, unsigned int num_params, struct pt_regs* regs){ unsigned int i; for (i = 0; i < num_params && i < 4; i++){ regs->uregs[i] = params[i]; } // // push remained params onto stack // if (i < num_params){ regs->ARM_sp-= (num_params - i)* sizeof(long); ptrace_writedata(pid, (unsigned char *)regs->ARM_sp, (unsigned char*)¶ms[i], (num_params - i)* sizeof(long)); } regs->ARM_pc= addr; if (regs->ARM_pc& 1) { /* thumb */ regs->ARM_pc &= (~1u); regs->ARM_cpsr |= CPSR_T_MASK; } else { /* arm */ regs->ARM_cpsr &= ~CPSR_T_MASK; } regs->ARM_lr= 0; if (ptrace_setregs(pid, regs)== -1 || ptrace_continue(pid)== -1){ printf("[-] failed to call function: %s", strerror(errno)); exit(Error_Ptrace_Call); } waitpid(pid, NULL, WUNTRACED); return 0; }
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; }
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; }
//------------------------------------------------------------------------------ // Name: handle_event // Desc: //------------------------------------------------------------------------------ IDebugEvent::const_pointer DebuggerCore::handle_event(edb::tid_t tid, int status) { // note that we have waited on this thread waited_threads_.insert(tid); // was it a thread exit event? if(WIFEXITED(status)) { threads_.remove(tid); waited_threads_.remove(tid); // if this was the last thread, return true // so we report it to the user. // if this wasn't, then we should silently // procceed. if(!threads_.empty()) { return nullptr; } } // was it a thread create event? if(is_clone_event(status)) { unsigned long new_tid; if(ptrace_get_event_message(tid, &new_tid) != -1) { auto newThread = std::make_shared<PlatformThread>(this, process_, new_tid); newThread->status_ = 0; newThread->state_ = PlatformThread::Stopped; threads_.insert(new_tid, newThread); int thread_status = 0; if(!waited_threads_.contains(new_tid)) { if(native::waitpid(new_tid, &thread_status, __WALL) > 0) { waited_threads_.insert(new_tid); } } if(!WIFSTOPPED(thread_status) || WSTOPSIG(thread_status) != SIGSTOP) { qDebug("[warning] new thread [%d] received an event besides SIGSTOP", static_cast<int>(new_tid)); } newThread->status_ = thread_status; // TODO: what the heck do we do if this isn't a SIGSTOP? newThread->resume(); } ptrace_continue(tid, 0); return nullptr; } // normal event auto e = std::make_shared<PlatformEvent>(); e->pid_ = pid(); e->tid_ = tid; e->status_ = status; if(ptrace_getsiginfo(tid, &e->siginfo_) == -1) { // TODO: handle no info? } active_thread_ = tid; auto it = threads_.find(tid); if(it != threads_.end()) { it.value()->status_ = status; } stop_threads(); return e; }