ErrorCode PTrace::resume(ProcessThreadId const &ptid, ProcessInfo const &pinfo, int signal, Address const &address) { pid_t pid; caddr_t addr; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } // // Continuation from address? // if (address.valid()) { addr = (caddr_t)address.value(); } else { // (caddr_t)1 indicate that execution is to pick up where it left off. addr = (caddr_t)1; } if (wrapPtrace(PT_CONTINUE, pid, addr, signal) < 0) return Platform::TranslateError(); return kSuccess; }
ErrorCode PTrace::resume(ProcessThreadId const &ptid, ProcessInfo const &pinfo, int signal, Address const &address) { pid_t pid; caddr_t addr = (caddr_t)1; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } // // Continuation from address? // if (address.valid()) { addr = (caddr_t)address.value(); } if (wrapPtrace(PT_SYSCALL, pid, addr, signal) < 0) return Platform::TranslateError(); return kSuccess; }
ErrorCode PTrace::step(ProcessThreadId const &ptid, ProcessInfo const &pinfo, int signal, Address const &address) { pid_t pid; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } // // Continuation from address? // if (address.valid()) { Architecture::CPUState state; ErrorCode error = readCPUState(ptid, pinfo, state); if (error != kSuccess) return error; state.setPC(address); error = writeCPUState(ptid, pinfo, state); if (error != kSuccess) return error; } // (caddr_t)1 indicate that execution is to pick up where it left off. if (wrapPtrace(PT_STEP, pid, (caddr_t)1, signal) < 0) return Platform::TranslateError(); return kSuccess; }
ErrorCode PTrace::writeRegisterSet(ProcessThreadId const &ptid, int regSetCode, void const *buffer, size_t length) { struct iovec iov = {const_cast<void *>(buffer), length}; if (wrapPtrace(PTRACE_SETREGSET, ptid.validTid() ? ptid.tid : ptid.pid, regSetCode, &iov) < 0) return Platform::TranslateError(); return kSuccess; }
// // Execute will execute the code in the target process/thread, // the techinique for PTRACE is to read CPU state, rewrite the // code portion pointed by PC with the code, resuming the process/thread, // wait for the completion, read the CPU state back to grab return // values, then restoring the previous code. // ErrorCode PTrace::execute(ProcessThreadId const &ptid, ProcessInfo const &pinfo, void const *code, size_t length, uint64_t &result) { Architecture::CPUState savedState, resultState; std::string savedCode; if (!ptid.valid() || code == nullptr || length == 0) return kErrorInvalidArgument; // 1. Read and save the CPU state ErrorCode error = readCPUState(ptid, pinfo, savedState); if (error != kSuccess) return error; // 2. Copy the code at PC savedCode.resize(length); error = readMemory(ptid, savedState.pc(), &savedCode[0], length); if (error != kSuccess) return error; // 3. Write the code to execute at PC error = writeMemory(ptid, savedState.pc(), code, length); if (error != kSuccess) goto fail; // 3. Resume and wait error = resume(ptid, pinfo); if (error == kSuccess) { error = wait(ptid); } if (error == kSuccess) { // 4. Read back the CPU state error = readCPUState(ptid, pinfo, resultState); if (error == kSuccess) { // 5. Save the result result = resultState.retval(); } } // 6. Write back the old code error = writeMemory(ptid, savedState.pc(), &savedCode[0], length); if (error != kSuccess) goto fail; // 7. Restore CPU state error = writeCPUState(ptid, pinfo, savedState); if (error != kSuccess) goto fail; // Success!! We injected and executed code! return kSuccess; fail: return kill(ptid, SIGKILL); // we can't really do much at this point :( }
ErrorCode PTrace::writeCPUState(ProcessThreadId const &ptid, ProcessInfo const &pinfo, Architecture::CPUState const &state) { pid_t pid; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } // // Initialize the CPU state, just in case. // initCPUState(pid); if (pinfo.pointerSize == sizeof(uint32_t) && !state.is32) return kErrorInvalidArgument; else if (pinfo.pointerSize != sizeof(uint32_t) && state.is32) return kErrorInvalidArgument; // // Write GPRs // user_regs_struct gprs; if (state.is32) { state32_to_user(gprs, state.state32); } else { state64_to_user(gprs, state.state64); } if (wrapPtrace(PTRACE_SETREGS, pid, nullptr, &gprs) < 0) return Platform::TranslateError(); // // Write X87 and SSE state // user_fpregs_struct fprs; if (state.is32) { state32_to_user(fprs, state.state32); } else { state64_to_user(fprs, state.state64); } wrapPtrace(PTRACE_SETFPREGS, pid, nullptr, &fprs); return kSuccess; }
ErrorCode PTrace::readRegisterSet(ProcessThreadId const &ptid, int regSetCode, void *buffer, size_t length) { struct iovec iov = {buffer, length}; if (wrapPtrace(PTRACE_GETREGSET, ptid.validTid() ? ptid.tid : ptid.pid, regSetCode, &iov) < 0) return Platform::TranslateError(); // On return, the kernel modifies iov.len to return the number of bytes read. // This should be exactly equal to the number of bytes requested. DS2ASSERT(iov.iov_len == length); return kSuccess; }
ErrorCode PTrace::readCPUState(ProcessThreadId const &ptid, ProcessInfo const &pinfo, Architecture::CPUState &state) { pid_t pid; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } // // Initialize the CPU state, just in case. // initCPUState(pid); // // Read GPRs // user_regs_struct gprs; if (wrapPtrace(PTRACE_GETREGS, pid, nullptr, &gprs) < 0) return Platform::TranslateError(); if (pinfo.pointerSize == sizeof(uint32_t)) { state.is32 = true; user_to_state32(state.state32, gprs); } else { state.is32 = false; user_to_state64(state.state64, gprs); } // // Read X87 and SSE state // user_fpregs_struct fprs; if (wrapPtrace(PTRACE_GETFPREGS, pid, nullptr, &fprs) == 0) { if (pinfo.pointerSize == sizeof(uint32_t)) { user_to_state32(state.state32, fprs); } else { user_to_state64(state.state64, fprs); } } return kSuccess; }
ErrorCode PTrace::kill(ProcessThreadId const &ptid, int signal) { if (!ptid.valid()) return kErrorInvalidArgument; int rc; if (!(ptid.tid <= kAnyThreadId)) { DS2BUG("not implemented"); } else { rc = ::kill(ptid.pid, signal); } if (rc < 0) return Platform::TranslateError(); return kSuccess; }
ErrorCode PTrace::suspend(ProcessThreadId const &ptid) { pid_t pid; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } if (kill(pid, SIGSTOP) < 0) return Platform::TranslateError(); return kSuccess; }
ErrorCode PTrace::getLwpInfo(ProcessThreadId const &ptid, struct ptrace_lwpinfo *lwpinfo) { pid_t pid; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } if (wrapPtrace(PT_LWPINFO, pid, lwpinfo, sizeof(struct ptrace_lwpinfo)) < 0) return Platform::TranslateError(); return kSuccess; }
ErrorCode PTrace::getSigInfo(ProcessThreadId const &ptid, siginfo_t &si) { struct ptrace_lwpinfo lwpinfo; pid_t pid; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } if (wrapPtrace(PT_LWPINFO, pid, &lwpinfo, sizeof lwpinfo) < 0) return Platform::TranslateError(); si = lwpinfo.pl_siginfo; return kSuccess; }
ErrorCode PTrace::writeMemory(ProcessThreadId const &ptid, Address const &address, void const *buffer, size_t length, size_t *count) { pid_t pid; if (!ptid.valid() || !address.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } uintptr_t base = address; struct ptrace_io_desc desc; if (length == 0 || buffer == nullptr) { if (count != nullptr) *count = 0; return kSuccess; } desc.piod_op = PIOD_WRITE_D; desc.piod_offs = (void *)base; desc.piod_addr = (void *)buffer; desc.piod_len = length; if (wrapPtrace(PT_IO, pid, &desc, nullptr) < 0) return Platform::TranslateError(); if (count != nullptr) *count = desc.piod_len; return kSuccess; }