My goal is to use ptrace
to set the program counter (and other registers) of a process running a 32-bit ARM executable on a 64-bit processor.
If this was a 64-bit executable, then I could use the pc
field of struct user_pt_regs
(defined in asm/ptrace.h).
Which structure would I use for a 32-bit executable? I see struct user_regs
(in sys/user.h) defined as
struct user_regs {
unsigned long uregs[18];
};
I read the answer for this question (though the author admitted that this was an educated guess) which suggests the use of uregs[15]
from struct user_regs
. However, would that still work in my case since I actually have a 64-bit system?
Even when running on a 64-bit processor, ptrace
hands back the effective 32-bit registers. To confirm this, consider the following two executables (for brevity's sake, I've omitted the header files and error checking):
test.c
int main()
{
printf("PID = %li\n", (long)getpid());
pause();
return 0;
}
attacher.c
int main(int argc, char **argv)
{
unsigned int expected_size;
pid_t pid;
struct {
uint32_t r[18];
} regs;
struct iovec iov;
pid = atoi(argv[1]); // Yes, I know I should use strtol. However, this suffices for this example.
ptrace(PTRACE_ATTACH, pid, NULL, NULL);
iov.iov_base = ®s;
iov.iov_len = expected_size = sizeof(regs);
ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
if ( iov.iov_len != expected_size ) {
fprintf(stderr, "Unexpected size (%zu instead of %u)\n", iov.iov_len, expected_size);
// abort
}
printf("PC is 0x%x\n", regs.r[15]);
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return 0;
}
I compiled the first into a 32-bit executable and the second into a 64-bit executable.
Taking the output from the attacher, I looked up the address in /proc/<pid>/maps
and saw that it lands in the executable section of libc (corresponding to pause
).