1

I use ubuntu 16.04 64bit to practice ptrace. When I used PTRACE_PEEKDATA,I'm confused.

the child process execute "ls",I want get the string pass to SYS_write. I get the string address and length in RCX,RDX with PTARECE_PEEKUSER. However when I use PTRACE_PEEKDATA to get string,the result is wrong.

here is result :

mmmmar@acer:$ ls
ptrace  ptrace_1.c  ptrace2  ptrace_2.c  ptrace3  ptrace_3.C  ptrace4  ptrace_4.C
mmmmar@acer:$ ./ptrace4
make write call params 81, 140258879076880, 81
get str: H=���s1�H����
ptrace  ptrace_1.c  ptrace2  ptrace_2.c  ptrace3  ptrace_3.C  ptrace4  ptrace_4.C

here is my code(it run on 64bit ubuntu):

#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define LONG_SIZE 8

void getdata(pid_t child,long addr,char *str,long len)
{
    char *laddr = str;
    int i = 0, j = len/LONG_SIZE;
    union u{
        long int val;
        char chars[LONG_SIZE];
    }data;
    while(i<j)
    {
        data.val = ptrace(PTRACE_PEEKDATA,child,addr + i*LONG_SIZE,NULL);
        memcpy(laddr,data.chars,LONG_SIZE);
        laddr += LONG_SIZE;
        ++i;
    }
    j = len % LONG_SIZE;
    if(j != 0)
    {
        data.val = ptrace(PTRACE_PEEKDATA,child,addr + i*LONG_SIZE,NULL);
        memcpy(laddr,data.chars,j);
    }
}

int main()
{
    pid_t child;
    child = fork();
    if(child == 0)
    {
        ptrace(PTRACE_TRACEME,0,NULL,NULL);
        execl("/bin/ls","ls",NULL);
    }
    else
    {
        long orig_rax = 0;
        long params[3] = {0};
        int status = 0;
        char *str;
        int toggle = 0;
        while(1)
        {
            wait(&status);
            if(WIFEXITED(status))
                break;
            orig_rax = ptrace(PTRACE_PEEKUSER,child,8*ORIG_RAX,NULL);
            if(orig_rax == SYS_write)
            {
                if(toggle == 0)
                {
                    toggle = 1;
                    params[0] = ptrace(PTRACE_PEEKUSER,child,8*RBX,NULL);
                    params[1] = ptrace(PTRACE_PEEKUSER,child,8*RCX,NULL);
                    params[2] = ptrace(PTRACE_PEEKUSER,child,8*RDX,NULL);
                    printf("make write call params %ld, %ld, %ld\n",params[0],params[1],params[2]);
                    str = (char*)malloc(params[2]+1);
                    memset(str,0,params[2]+1);
                    getdata(child,params[1],str,params[2]);
                    printf("get str: %s\n",str);
                    free(str);
                }
                else
                {
                    toggle = 0;
                }
            }
            ptrace(PTRACE_SYSCALL,child,NULL,NULL);
        }
    }
    return 0;
}
mmmmar
  • 31
  • 6
  • 1
    Please see [What are the calling conventions for UNIX & Linux system calls on x86-64](http://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64). The parameters are in rdi, rsi, and rdx. – Mark Plotnick Nov 01 '16 at 10:13
  • OK,thanks,it work now – mmmmar Nov 01 '16 at 11:00
  • 1
    I didn't find this comment totally clear, so could I add to this for future visitors that rdi, rsi and rdx specifically replace ebx, ecx and edx from the original x32 based code. That's not a grumble about the answers, but it might help people like me who don't know much about these things. – Iron Attorney Nov 02 '16 at 23:58

0 Answers0