playing with ptrace(Part I) 之四 --- Single-Stepping

文章出处:http://www.linuxjournal.com/article/6100?page=0,2

in

ptrace提供了在子进程中进行单步调试的功能。通过调用ptrace(PTRACE_SINGLESTEP,..)就可以通知内核在子进程执行每一条指令时,都对其进行暂停,并将控制权交给父进程。下面的例子展示了当系统调用发生时如何读取正在执行的指令的方法。我写了一个小例子以便你能够理解到底发生了什么,而不是纠缠于libc产生的调用中。

下面是dummy1.s的程序清单。它是用汇编语言写的,使用gcc -o dummy1 dummy1.s来进行编译:

.data
hello:
    .string "hello world\n"
.globl  main
main:
    movl    $4, %eax
    movl    $2, %ebx
    movl    $hello, %ecx
    movl    $12, %edx
    int     $0x80
    movl    $1, %eax
    xorl    %ebx, %ebx
    int     $0x80
    ret
单步调试的例程如下:

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>
#include <sys/syscall.h>
int main()
{   pid_t child;
    const int long_size = sizeof(long);
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("./dummy1", "dummy1", NULL);
    }
    else {
        int status;
        union u {
            long val;
            char chars[long_size];
        }data;
        struct user_regs_struct regs;
        int start = 0;
        long ins;
        while(1) {
            wait(&status);
            if(WIFEXITED(status))
                break;
            ptrace(PTRACE_GETREGS,
                   child, NULL, ®s);
            if(start == 1) {
                ins = ptrace(PTRACE_PEEKTEXT,
                             child, regs.eip,
                             NULL);
                printf("EIP: %lx Instruction "
                       "executed: %lx\n",
                       regs.eip, ins);
            }
            if(regs.orig_eax == SYS_write) {
                start = 1;
                ptrace(PTRACE_SINGLESTEP, child,
                       NULL, NULL);
            }
            else
                ptrace(PTRACE_SYSCALL, child,
                       NULL, NULL);
        }
    }
    return 0;
}

程序输出如下:

hello world
EIP: 8049478 Instruction executed: 80cddb31
EIP: 804947c Instruction executed: c3
你可以通过查阅Inter的手册来了解具体指令的含义。使用单步实现更复杂的功能,如设置断点等,则需要更加仔细的设计和更加复杂的代码实现。

在Part II中,我们将看到断点是怎样被插入到程序中的以及如何向一个正在运行中的程序中诸如代码。



你可能感兴趣的:(struct,汇编,gcc,user,null,语言)