11

I was going through an article here and was trying out the code snippet I have copied out below :-

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>   /* For constants
                                   ORIG_EAX etc */
int main()
{   pid_t child;
    long orig_eax;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/bin/ls", "ls", NULL);
    }
    else {
        wait(NULL);
        orig_eax = ptrace(PTRACE_PEEKUSER,
                          child, 4 * ORIG_EAX,
                          NULL);
        printf("The child made a "
               "system call %ld\n", orig_eax);
        ptrace(PTRACE_CONT, child, NULL, NULL);
    }
    return 0;
}

I have a doubt regarding what ORIG_EAX is exactly and why 4*ORIG_EAX is passed onto the ptrace call. I initially assumed that ORIG_EAX, EBX, ECX etc would be the offsets into a particular structure where the values of the registers would be stored.

So I decided to print the value of ORIG_EAX just after the wait by using printf("origeax = %ld\n", ORIG_EAX);. The value was 11. So, my earlier assumption regarding the offsets was wrong.

I understand that the wait call is terminated when the child has a state change(in this case, issues a system call) and that ORIG_EAX would contain the system call number.

However, why is ORIG_EAX * 4 passed onto the ptrace call?

1 Answers1

13

The parameter is an offset into the user_regs_struct. Note that each of these is an unsigned long, so to get the 11th entry (orig_eax) the offset in bytes is 44, (provided you're on an x86 machine of course).

Mathew Hall
  • 994
  • 9
  • 18
  • 2
    What about dfferences between 32 and 64-bit? Shouldn't we compile-time-case on that? – Nordlöw Jan 14 '13 at 19:23
  • Further..., is this ptrace x86 only? If not we need some more general target-independent way of doing this! – Nordlöw Jan 14 '13 at 19:27
  • There's definitely a definition for an AMD64 `user_regs_struct`, but I'm not sure if you can mix and match architectures. IIRC the comments in the `ptrace` mentioned the separate headers were a problem. Since `ptrace` is a syscall it should work (you just might have to switch the offsets for each architecture manually) but I can't say I've ever tried it. – Mathew Hall Jan 18 '13 at 13:48
  • 1
    That seems to unnecessarily expose an implementation detail; why doesn't ptrace do *4 itself? – allyourcode Aug 01 '14 at 01:28
  • on Ubuntu 18.0.4, the path to "sys/user.h" is "/usr/include/x86_64-linux-gnu/sys/user.h" – hoymkot Mar 13 '20 at 20:40