Thread: Tracing execution with Ptrace

  1. #1
    Registered User
    Join Date
    May 2009
    Posts
    11

    Tracing execution with Ptrace

    I've asked a couple others places but no one yet has been able to get my program to work correctly. So please hear me out!

    I'm writing a simple linux tracing debugger that will trace a child application and catch any signals it receives. If it receives a signal and stops or crashes, I want to print out the registers of the child at that point.

    I have tried using PTRACE_TRACEME and PTRACE_ATTACH, so far PTRACE_TRACEME seems to be the one I should use.

    I can run the program in GNU debugger and see that some registers are modified if I put too much data into a variable and it changes some of the registers to match the data I use to overflow it. When I run my test program the registers look quite normal and I run it through other tracing tools such as ltrace and strace to check for things too.

    ptrace.c
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/wait.h>
    #include <sys/ptrace.h>
    #include <sys/user.h>
    
    #define BIN "/tmp/test"
    
    int main(int ac, char *ag[])
    {
    
    char buf[64];
    int eax, ebx, ecx, edx, esi, edi, esp, eip;
    pid_t child;
    struct user_regs_struct r;
    
    memset(buf, 'p', sizeof(buf));
    
    	child = fork();
    
    if(child == 0)
    {
    
            ptrace(PTRACE_TRACEME, 0, NULL, NULL);
    	execl(BIN, BIN, buf, NULL);
    
    }
    
    else
    {
    
    int sig, stat;
    
    if(WIFSIGNALED(stat))
    {
    
         wait(&stat);
    
         ptrace(PTRACE_GETREGS, child, 0, &r);
    
    eax = r.eax;
    ebx = r.ebx;
    ecx = r.ecx;
    edx = r.edx;
    esi = r.esi;
    edi = r.edi;
    esp = r.esp;
    eip = r.eip;
    
    printf("eax 0x%x\nebx 0x%x\necx 0x%x\nedx 0x%x\nesi 0x%x\nedi 0x%x\nesp 0x%x\neip 0x%x\n", eax, ebx, ecx, edx, esi, edi, esp, eip);
    
    }
    }
    
    	ptrace(PTRACE_DETACH, child, 0, 0);
            return 0;
    
    }
    ice.c
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main(int argc, char *argv[])
    {
    
    char buf[32];
    
    strcpy(buf, argv[1]);
    
    return 0;
    
    }
    gnu debugger output:
    Code:
    tommy@otto:/tmp$  gdb /tmp/test
    GNU gdb 6.8-debian
    Copyright (C) 2008 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
    and "show warranty" for details.
    This GDB was configured as "i486-linux-gnu"...
    (gdb) run `perl -e 'print "p" x 64'`
    Starting program: /tmp/test `perl -e 'print "p" x 64'`
    
    Program received signal SIGSEGV, Segmentation fault.
    0x70707070 in ?? ()
    (gdb) info registers
    eax            0x0	0
    ecx            0xbfa2470f	-1079884017
    edx            0x41	65
    ebx            0xb7ee9ff4	-1209098252
    esp            0xbfa24740	0xbfa24740
    ebp            0x70707070	0x70707070
    esi            0x80483a0	134513568
    edi            0x80482d0	134513360
    eip            0x70707070	0x70707070
    eflags         0x10246	[ PF ZF IF RF ]
    cs             0x73	115
    ss             0x7b	123
    ds             0x7b	123
    es             0x7b	123
    fs             0x0	0
    gs             0x33	51
    (gdb) q
    The program is running.  Exit anyway? (y or n) y
    tommy@otto:/tmp$
    ptrace program output:
    Code:
    tommy@otto:/tmp$ ./pt
    eax 0x0
    ebx 0x0
    ecx 0x0
    edx 0x0
    esi 0x0
    edi 0x0
    esp 0xbffd9580
    eip 0xb7fbc810
    tommy@otto:/tmp$
    Now you see what I'm looking for as output and you see what I've got?

    I've tried using PTRACE_SINGLESTEP but maybe I'm just not putting it in the right places in the code.. that is why I defiantly need help with this code.

    I am trying to...

    1) Execute a program (fork() and make it a child) and trace it with ptrace()
    2) Catch any signals the child receives
    3) Display the extended registers of the child process when the signal is received

    I have read all the documentation supplied but if you all know how to do this I would appreciate a clear cut answer for myself and everyone else.. I'm sure we're not the only ones even to attempt this small feat.

    Much appreciated all, TIA!

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    I don't understand what you are trying to do there with perl. The gdb output is garbage, the result of perl itself crashing.

    The output of your ./pt program looks correct. Upon the call to exec(), the registers should be cleared. Everything looks fine (except your indentation).
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > The gdb output is garbage, the result of perl itself crashing.
    It's the buffer overflow.
    Check all those 0x70 (ie, lower case "p") which have found their way into EBP and EIP
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Salem View Post
    > The gdb output is garbage, the result of perl itself crashing.
    It's the buffer overflow.
    Check all those 0x70 (ie, lower case "p") which have found their way into EBP and EIP
    I'm obviously missing some context here. What buffer overflow?

    EDIT: Nevermind, I see it now. I have no idea why the OP would expect the registers to look the same in those two cases.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  5. #5
    Registered User
    Join Date
    May 2009
    Posts
    11
    The problem was putting PTRACE_CONT in the right spot, right after the fork() statement.. so thats fixed. Now its not catching the signals of the child, bleh

    Anyone got any ideas?

  6. #6
    Registered User
    Join Date
    May 2009
    Posts
    11
    Does anyone here know how to achieve my goal (which you can find if you read above)?

  7. #7
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    So, you are definitely achieving:
    1. fork, exec and ptrace.
    2. catch all signals the child receives - well, the first signal (according to here: ptrace(2): process trace - Linux man page) is a sigtrap, since the child process performs execl.
    3. Display extended registers.
    Not sure what you mean by extended registers, but you are certainly displaying the registers you have a printf for.

    So the problem is probably that you didn't RTFM, and as a consequence, you are catching "the wrong signal". You may want to use PTRACE_CONT to continue the child process after the SIG_TRAP, rather than detach and exit.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  8. #8
    Registered User
    Join Date
    May 2009
    Posts
    11
    Thanks. I'm catching the signals now, but I'm crashing the program before the signal is caught? Is there a way to catch the signal before I crash it with the overflow?

  9. #9
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by tommyb05 View Post
    Thanks. I'm catching the signals now, but I'm crashing the program before the signal is caught? Is there a way to catch the signal before I crash it with the overflow?
    Huh? The crash happens in the child process, so you should receive the signal in the parent process (as well as in the child process).

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  10. #10
    Registered User
    Join Date
    May 2009
    Posts
    11
    If happens as soon as I run the child process as the code is wrote. But I found if I use WIFSTOPPED instead of WIFSIGNALED to see the current signal, things work out.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Execution Time - Rijandael encryption
    By gamer4life687 in forum C++ Programming
    Replies: 5
    Last Post: 09-20-2008, 09:25 PM
  2. Replies: 0
    Last Post: 10-06-2007, 01:25 PM
  3. Arbitrary Code Execution
    By CrazyNorman in forum C++ Programming
    Replies: 3
    Last Post: 04-10-2007, 02:33 PM
  4. What is the best way to record a process execution time?
    By hanash in forum Linux Programming
    Replies: 7
    Last Post: 03-15-2006, 07:17 AM
  5. Initiating command prompt execution
    By bluecoder in forum Windows Programming
    Replies: 2
    Last Post: 07-26-2002, 07:00 PM

Tags for this Thread