This blog is the notes during I learning the “Playing with ptrace”(http://www.linuxjournal.com/article/6100).
The original examples was using 32 bits machine, which doesn’t work on my 64 bits Ubuntu 12.10.
Let’s start from the first ptrace example:
#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 %ldn", orig_eax);
ptrace(PTRACE_CONT, child, NULL, NULL);
}
return 0;
}
The compiler shows the following error:
fatal error: 'linux/user.h' file not found #include <linux/user.h>
Something need to change because of:
- The ‘linux/user.h’ no longer exists
- The 64 bits register is R*X, so EAX changed to RAX
There are two solutions to fix this:
1. change ‘linux/user.h’ to ‘sys/reg.h’, and use:
long original_rax = ptrace(PTRACE_PEEKUSER, child, 8 * ORIG_RAX, NULL);
The addr changed from ‘4 * ORIG_EAX’ to ‘8 * ORIG_RAX’ because it’s the address to read in the user area, and the orig_rax member in user_regs_struct is the 15th member(start from 0). The definition of ORIG_RAX in file ‘sys/reg.h’ specify it’s position: # define ORIG_RAX 15. Because of the other members has size 8 on 64 bits machine, so the addr is: 8 * ORIG_RAX.
The definition of struct user_regs_struct and user in file ‘sys/user.h’:
struct user_regs_struct
{
unsigned long int r15;
unsigned long int r14;
unsigned long int r13;
unsigned long int r12;
unsigned long int rbp;
unsigned long int rbx;
unsigned long int r11;
unsigned long int r10;
unsigned long int r9;
unsigned long int r8;
unsigned long int rax;
unsigned long int rcx;
unsigned long int rdx;
unsigned long int rsi;
unsigned long int rdi;
unsigned long int orig_rax;
unsigned long int rip;
unsigned long int cs;
unsigned long int eflags;
unsigned long int rsp;
unsigned long int ss;
unsigned long int fs_base;
unsigned long int gs_base;
unsigned long int ds;
unsigned long int es;
unsigned long int fs;
unsigned long int gs;
};
struct user
{
struct user_regs_struct regs;
int u_fpvalid;
struct user_fpregs_struct i387;
unsigned long int u_tsize;
unsigned long int u_dsize;
unsigned long int u_ssize;
unsigned long int start_code;
unsigned long int start_stack;
long int signal;
int reserved;
struct user_regs_struct* u_ar0;
struct user_fpregs_struct* u_fpstate;
unsigned long int magic;
char u_comm [32];
unsigned long int u_debugreg [8];
};
- change ‘linux/user.h’ to ‘sys/user.h’, and use
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, child, NULL, ®s);
printf("The child made a system call %ldn", regs.orig_rax);
The second one is simpler because it doesn’t need to calculate the position, but it read more data than the first one.
I think it would be more clear and easier to understand if we use the address of the orig_rax field directly:
struct user* user_space = (struct user*)0; long original_rax = ptrace(PTRACE_PEEKUSER, child, &user_space->regs.orig_rax, NULL);
We can compile and run it now, but we got: ‘The child made a system call 59‘, which is different with ‘11‘ from the original post, is there anything wrong? From the file sys/syscall.h, it included file ‘asm/unistd.h’ and the comment says that file list the system calls:
/* This file should list the numbers of the system calls the system knows. But instead of duplicating this we use the information available from the kernel sources. */ #include <asm/unistd.h>
The file ‘asm/unistd.h’ include different files based on i386 and ILP32:
# ifdef __i386__ # include <asm/unistd_32.h> # elif defined(__ILP32__) # include <asm/unistd_x32.h> # else # include <asm/unistd_64.h> # endif
From the file ‘asm/unistd_64.h’ which contains the system call names for 64 bits machine, we can found that:
#define __NR_execve 59
Ok, that’s all for the first example, and after understand it, it’s easy to understand the rest parts in part I(http://www.linuxjournal.com/article/6100) and part II(http://www.linuxjournal.com/article/6210).
This is useful for me , thank you!
Got exactly the same problem following the same article (quite obviously), this helps me! Thanks
meet register problem in x64, this help me a lot
Nice piece of information, really helped me a lot
This article was really helpful. In 64 bit systems function calling convention has been changed(arguments passed to the registers). This should also be addressed in the article.
Thank you !
Inresting post… With the help of this i have ported all the example from https://www.linuxjournal.com/article/6100 to 64bit Linux https://github.com/ranjithum/PlayingWithPtrace