Fix ARM ptrace compatibility and improve remote call safety (#94)
- Implement fallbacks to `PTRACE_GETREGS` and `PTRACE_SETREGS` for 32-bit ARM (`__arm__`). Some kernels return `EIO` or `EINVAL` when attempting to access `NT_PRSTATUS` via `PTRACE_GETREGSET`/`PTRACE_SETREGSET`. - Update `transfer_fd_to_remote` to use `libc_return_addr` instead of `0` as the return address during the `recvmsg` split-call. This ensures the remote process stops predictably at a known non-executable location rather than relying on a potentially unsafe jump to `0x0`. - Clarify comments regarding i386 argument passing in `utils.cpp`. Correctly note that a linear `write_proc` starting at the new SP matches the `cdecl` Right-to-Left memory layout (since stacks grow downwards while memory writes move upwards), removing the suggestion that arguments needed reversing.
This commit is contained in:
@@ -374,7 +374,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
|
|||||||
|
|
||||||
// 12. Initiate the remote recvmsg call. This will block the remote process.
|
// 12. Initiate the remote recvmsg call. This will block the remote process.
|
||||||
args = {static_cast<uintptr_t>(remote_fd), remote_hdr, MSG_WAITALL};
|
args = {static_cast<uintptr_t>(remote_fd), remote_hdr, MSG_WAITALL};
|
||||||
if (!remote_pre_call(pid, regs, reinterpret_cast<uintptr_t>(funcs.recvmsg_addr), 0, args)) {
|
if (!remote_pre_call(pid, regs, reinterpret_cast<uintptr_t>(funcs.recvmsg_addr), libc_return_addr, args)) {
|
||||||
LOGE("Failed to initiate remote recvmsg call.");
|
LOGE("Failed to initiate remote recvmsg call.");
|
||||||
close_remote(remote_fd);
|
close_remote(remote_fd);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -414,7 +414,7 @@ static std::optional<int> transfer_fd_to_remote(int pid, const char *lib_path, s
|
|||||||
|
|
||||||
// 15. Complete the remote recvmsg call. This will retrieve the return value.
|
// 15. Complete the remote recvmsg call. This will retrieve the return value.
|
||||||
auto recvmsg_result =
|
auto recvmsg_result =
|
||||||
static_cast<ssize_t>(remote_post_call(pid, regs, 0)); // No specific expected return address for recvmsg
|
static_cast<ssize_t>(remote_post_call(pid, regs, libc_return_addr));
|
||||||
if (recvmsg_result == -1) {
|
if (recvmsg_result == -1) {
|
||||||
errno = get_remote_errno();
|
errno = get_remote_errno();
|
||||||
PLOGE("Remote recvmsg call failed.");
|
PLOGE("Remote recvmsg call failed.");
|
||||||
|
|||||||
@@ -263,8 +263,15 @@ bool get_regs(int pid, struct user_regs_struct ®s) {
|
|||||||
struct iovec reg_iov = {.iov_base = ®s, .iov_len = sizeof(struct user_regs_struct)};
|
struct iovec reg_iov = {.iov_base = ®s, .iov_len = sizeof(struct user_regs_struct)};
|
||||||
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ®_iov) == -1) {
|
if (ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, ®_iov) == -1) {
|
||||||
PLOGE("Failed to get register set for PID %d.", pid);
|
PLOGE("Failed to get register set for PID %d.", pid);
|
||||||
|
#if defined(__arm__)
|
||||||
|
if (ptrace(PTRACE_GETREGS, pid, 0, ®s) == -1) {
|
||||||
|
PLOGE("Fallback to PTRACE_GETREGS failed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
# error "Unsupported architecture for register access in get_regs."
|
# error "Unsupported architecture for register access in get_regs."
|
||||||
#endif
|
#endif
|
||||||
@@ -296,8 +303,15 @@ bool set_regs(int pid, struct user_regs_struct ®s) {
|
|||||||
struct iovec reg_iov = {.iov_base = ®s, .iov_len = sizeof(struct user_regs_struct)};
|
struct iovec reg_iov = {.iov_base = ®s, .iov_len = sizeof(struct user_regs_struct)};
|
||||||
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ®_iov) == -1) {
|
if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, ®_iov) == -1) {
|
||||||
PLOGE("Failed to set register set for PID %d.", pid);
|
PLOGE("Failed to set register set for PID %d.", pid);
|
||||||
|
#if defined(__arm__)
|
||||||
|
if (ptrace(PTRACE_SETREGS, pid, 0, ®s) == -1) {
|
||||||
|
PLOGE("Fallback to PTRACE_SETREGS failed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
# error "Unsupported architecture for register access in set_regs."
|
# error "Unsupported architecture for register access in set_regs."
|
||||||
#endif
|
#endif
|
||||||
@@ -588,17 +602,10 @@ bool remote_pre_call(int pid, struct user_regs_struct ®s, uintptr_t func_addr
|
|||||||
size_t stack_args_size = args.size() * sizeof(uintptr_t);
|
size_t stack_args_size = args.size() * sizeof(uintptr_t);
|
||||||
align_stack(regs, stack_args_size);
|
align_stack(regs, stack_args_size);
|
||||||
|
|
||||||
// Push all arguments onto the stack (order is important if ABI is right-to-left push).
|
// i386 cdecl expects arguments pushed Right-to-Left (stack grows down).
|
||||||
// The current implementation writes args.data() directly,
|
// Since `write_proc` writes to increasing addresses (up), a linear write
|
||||||
// assuming it's already in the correct order for push.
|
// starting at the new SP places the first argument at the lowest address.
|
||||||
// For cdecl, arguments are pushed right-to-left.
|
// This matches the ABI memory layout without needing to reverse the vector.
|
||||||
// A vector `args = {A, B, C}` means A is arg1, B is arg2 etc.
|
|
||||||
// So, `C` should be pushed first, then `B`, then `A`.
|
|
||||||
// `write_proc` copies linearly.
|
|
||||||
// This implies `args` should be pre-reversed for cdecl.
|
|
||||||
// For simplicity, we assume the remote function is compatible with how it's pushed,
|
|
||||||
// or that it's variadic where order doesn't matter for first args.
|
|
||||||
// A robust i386 implementation would need to push args in reverse order.
|
|
||||||
if (write_proc(pid, static_cast<uintptr_t>(regs.REG_SP), args.data(), stack_args_size) !=
|
if (write_proc(pid, static_cast<uintptr_t>(regs.REG_SP), args.data(), stack_args_size) !=
|
||||||
static_cast<ssize_t>(stack_args_size)) {
|
static_cast<ssize_t>(stack_args_size)) {
|
||||||
LOGE("Failed to push arguments for i386 remote call.");
|
LOGE("Failed to push arguments for i386 remote call.");
|
||||||
|
|||||||
Reference in New Issue
Block a user