青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

jeromewen

努力中
隨筆 - 9, 文章 - 0, 評論 - 22, 引用 - 0
數據加載中……

Playing with ptrace, Part I

來自http://www.linuxjournal.com/article/6100
SysAdmin

Using ptrace allows you to set up system call interception and modification at the user level.

Have you ever wondered how system calls can be intercepted? Have you ever tried fooling the kernel by changing system call arguments? Have you ever wondered how debuggers stop a running process and let you take control of the process?

If you are thinking of using complex kernel programming to accomplish tasks, think again. Linux provides an elegant mechanism to achieve all of these things: the ptrace (Process Trace) system call. ptrace provides a mechanism by which a parent process may observe and control the execution of another process. It can examine and change its core image and registers and is used primarily to implement breakpoint debugging and system call tracing.

In this article, we learn how to intercept a system call and change its arguments. In Part II of the article we will study advanced techniques--setting breakpoints and injecting code into a running program. We will peek into the child process' registers and data segment and modify the contents. We will also describe a way to inject code so the process can be stopped and execute arbitrary instructions.

Basics

Operating systems offer services through a standard mechanism called system calls. They provide a standard API for accessing the underlying hardware and low-level services, such as the filesystems. When a process wants to invoke a system call, it puts the arguments to system calls in registers and calls soft interrupt 0x80. This soft interrupt is like a gate to the kernel mode, and the kernel will execute the system call after examining the arguments.

On the i386 architecture (all the code in this article is i386-specific), the system call number is put in the register %eax. The arguments to this system call are put into registers %ebx, %ecx, %edx, %esi and %edi, in that order. For example, the call:

write(2, "Hello", 5)

roughly would translate into

movl   $4, %eax
movl   $2, %ebx
movl   $hello,%ecx
movl   $5, %edx
int    $0x80
where $hello points to a literal string "Hello".

So where does ptrace come into picture? Before executing the system call, the kernel checks whether the process is being traced. If it is, the kernel stops the process and gives control to the tracking process so it can examine and modify the traced process' registers.

Let's clarify this explanation with an example of how the process works:

#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;
}

When run, this program prints:

The child made a system call 11
along with the output of ls. System call number 11 is execve, and it's the first system call executed by the child. For reference, system call numbers can be found in /usr/include/asm/unistd.h.

As you can see in the example, a process forks a child and the child executes the process we want to trace. Before running exec, the child calls ptrace with the first argument, equal to PTRACE_TRACEME. This tells the kernel that the process is being traced, and when the child executes the execve system call, it hands over control to its parent. The parent waits for notification from the kernel with a wait() call. Then the parent can check the arguments of the system call or do other things, such as looking into the registers.

When the system call occurs, the kernel saves the original contents of the eax register, which contains the system call number. We can read this value from child's USER segment by calling ptrace with the first argument PTRACE_PEEKUSER, shown as above.

After we are done examining the system call, the child can continue with a call to ptrace with the first argument PTRACE_CONT, which lets the system call continue.

ptrace Parameters

ptrace is called with four arguments:

long ptrace(enum __ptrace_request request,
            pid_t pid,
            void *addr,
            void *data);

The first argument determines the behaviour of ptrace and how other arguments are used. The value of request should be one of PTRACE_TRACEME, PTRACE_PEEKTEXT, PTRACE_PEEKDATA, PTRACE_PEEKUSER, PTRACE_POKETEXT, PTRACE_POKEDATA, PTRACE_POKEUSER, PTRACE_GETREGS, PTRACE_GETFPREGS, PTRACE_SETREGS, PTRACE_SETFPREGS, PTRACE_CONT, PTRACE_SYSCALL, PTRACE_SINGLESTEP, PTRACE_DETACH. The significance of each of these requests will be explained in the rest of the article.

Reading System Call Parameters

By calling ptrace with PTRACE_PEEKUSER as the first argument, we can examine the contents of the USER area where register contents and other information is stored. The kernel stores the contents of registers in this area for the parent process to examine through ptrace.

Let's show this with an example:

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>
#include <sys/syscall.h>   /* For SYS_write etc */
int main()
{   pid_t child;
    long orig_eax, eax;
    long params[3];
    int status;
    int insyscall = 0;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/bin/ls", "ls", NULL);
    }
    else {
       while(1) {
          wait(&status);
          if(WIFEXITED(status))
              break;
          orig_eax = ptrace(PTRACE_PEEKUSER, 
                     child, 4 * ORIG_EAX, NULL);
          if(orig_eax == SYS_write) {
             if(insyscall == 0) {    
                /* Syscall entry */
                insyscall = 1;
                params[0] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * EBX, 
                                   NULL);
                params[1] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * ECX, 
                                   NULL);
                params[2] = ptrace(PTRACE_PEEKUSER,
                                   child, 4 * EDX, 
                                   NULL);
                printf("Write called with "
                       "%ld, %ld, %ld\n",
                       params[0], params[1],
                       params[2]);
                }
          else { /* Syscall exit */
                eax = ptrace(PTRACE_PEEKUSER, 
                             child, 4 * EAX, NULL);
                    printf("Write returned "
                           "with %ld\n", eax);
                    insyscall = 0;
                }
            }
            ptrace(PTRACE_SYSCALL, 
                   child, NULL, NULL);
        }
    }
    return 0;
}

This program should print an output similar to the following:

ppadala@linux:~/ptrace > ls
a.out        dummy.s      ptrace.txt   
libgpm.html  registers.c  syscallparams.c
dummy        ptrace.html  simple.c
ppadala@linux:~/ptrace > ./a.out
Write called with 1, 1075154944, 48
a.out        dummy.s      ptrace.txt
Write returned with 48
Write called with 1, 1075154944, 59
libgpm.html  registers.c  syscallparams.c
Write returned with 59
Write called with 1, 1075154944, 30
dummy        ptrace.html  simple.c
Write returned with 30
Here we are tracing the write system calls, and ls makes three write system calls. The call to ptrace, with a first argument of PTRACE_SYSCALL, makes the kernel stop the child process whenever a system call entry or exit is made. It's equivalent to doing a PTRACE_CONT and stopping at the next system call entry/exit.

In the previous example, we used PTRACE_PEEKUSER to look into the arguments of the write system call. When a system call returns, the return value is placed in %eax, and it can be read as shown in that example.

The status variable in the wait call is used to check whether the child has exited. This is the typical way to check whether the child has been stopped by ptrace or was able to exit. For more details on macros like WIFEXITED, see the wait(2) man page.

Reading Register Values

If you want to read register values at the time of a syscall entry or exit, the procedure shown above can be cumbersome. Calling ptrace with a first argument of PTRACE_GETREGS will place all the registers in a single call.

The code to fetch register values looks like this:

#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;
    long orig_eax, eax;
    long params[3];
    int status;
    int insyscall = 0;
    struct user_regs_struct regs;
    child = fork();
    if(child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execl("/bin/ls", "ls", NULL);
    }
    else {
       while(1) {
          wait(&status);
          if(WIFEXITED(status))
              break;
          orig_eax = ptrace(PTRACE_PEEKUSER, 
                            child, 4 * ORIG_EAX, 
                            NULL);
          if(orig_eax == SYS_write) {
              if(insyscall == 0) {
                 /* Syscall entry */
                 insyscall = 1;
                 ptrace(PTRACE_GETREGS, child, 
                        NULL, &regs);
                 printf("Write called with "
                        "%ld, %ld, %ld\n",
                        regs.ebx, regs.ecx, 
                        regs.edx);
             }
             else { /* Syscall exit */
                 eax = ptrace(PTRACE_PEEKUSER, 
                              child, 4 * EAX, 
                              NULL);
                 printf("Write returned "
                        "with %ld\n", eax);
                 insyscall = 0;
             }
          }
          ptrace(PTRACE_SYSCALL, child,
                 NULL, NULL);
       }
   }
   return 0;
}

This code is similar to the previous example except for the call to ptrace with PTRACE_GETREGS. Here we have made use of the user_regs_struct defined in <linux/user.h> to read the register values.

Doing Funny Things

Now it's time for some fun. In the following example, we will reverse the string passed to the write system call:

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <linux/user.h>
#include <sys/syscall.h>
const int long_size = sizeof(long);
void reverse(char *str)
{   int i, j;
    char temp;
    for(i = 0, j = strlen(str) - 2; 
        i <= j; ++i, --j) {
        temp = str[i];
        str[i] = str[j];
        str[j] = temp;
    }
}
void getdata(pid_t child, long addr, 
             char *str, int len)
{   char *laddr;
    int i, j;
    union u {
            long val;
            char chars[long_size];
    }data;
    i = 0;
    j = len / long_size;
    laddr = str;
    while(i < j) {
        data.val = ptrace(PTRACE_PEEKDATA, 
                          child, addr + i * 4, 
                          NULL);
        memcpy(laddr, data.chars, long_size);
        ++i;
        laddr += long_size;
    }
    j = len % long_size;
    if(j != 0) {
        data.val = ptrace(PTRACE_PEEKDATA, 
                          child, addr + i * 4, 
                          NULL);
        memcpy(laddr, data.chars, j);
    }
    str[len] = '\0';
}
void putdata(pid_t child, long addr, 
             char *str, int len)
{   char *laddr;
    int i, j;
    union u {
            long val;
            char chars[long_size];
    }data;
    i = 0;
    j = len / long_size;
    laddr = str;
    while(i < j) {
        memcpy(data.chars, laddr, long_size);
        ptrace(PTRACE_POKEDATA, child, 
               addr + i * 4, data.val);
        ++i;
        laddr += long_size;
    }
    j = len % long_size;
    if(j != 0) {
        memcpy(data.chars, laddr, j);
        ptrace(PTRACE_POKEDATA, child, 
               addr + i * 4, data.val);
    }
}
int main()
{   
   pid_t child;
   child = fork();
   if(child == 0) {
      ptrace(PTRACE_TRACEME, 0, NULL, NULL);
      execl("/bin/ls", "ls", NULL);
   }
   else {
      long orig_eax;
      long params[3];
      int status;
      char *str, *laddr;
      int toggle = 0;
      while(1) {
         wait(&status);
         if(WIFEXITED(status))
             break;
         orig_eax = ptrace(PTRACE_PEEKUSER, 
                           child, 4 * ORIG_EAX, 
                           NULL);
         if(orig_eax == SYS_write) {
            if(toggle == 0) {
               toggle = 1;
               params[0] = ptrace(PTRACE_PEEKUSER, 
                                  child, 4 * EBX, 
                                  NULL);
               params[1] = ptrace(PTRACE_PEEKUSER, 
                                  child, 4 * ECX, 
                                  NULL);
               params[2] = ptrace(PTRACE_PEEKUSER,
                                  child, 4 * EDX, 
                                  NULL);
               str = (char *)calloc((params[2]+1)
                                 * sizeof(char));
               getdata(child, params[1], str, 
                       params[2]);
               reverse(str);
               putdata(child, params[1], str, 
                       params[2]);
            }
            else {
               toggle = 0;
            }
         }
      ptrace(PTRACE_SYSCALL, child, NULL, NULL);
      }
   }
   return 0;
}

The output looks like this:

ppadala@linux:~/ptrace > ls
a.out        dummy.s      ptrace.txt
libgpm.html  registers.c  syscallparams.c
dummy        ptrace.html  simple.c
ppadala@linux:~/ptrace > ./a.out
txt.ecartp      s.ymmud      tuo.a
c.sretsiger     lmth.mpgbil  c.llacys_egnahc
c.elpmis        lmth.ecartp  ymmud
This example makes use of all the concepts previously discussed, plus a few more. In it, we use calls to ptrace with PTRACE_POKEDATA to change the data values. It works exactly the same way as PTRACE_PEEKDATA, except it both reads and writes the data thatt the child passes in arguments to the system call whereas PEEKDATA only reads the data.

Single-Stepping

ptrace provides features to single-step through the child's code. The call to ptrace(PTRACE_SINGLESTEP,..) tells the kernel to stop the child at each instruction and let the parent take control. The following example shows a way of reading the instruction being executed when a system call is executed. I have created a small dummy executable for you to understand what is happening instead of bothering with the calls made by libc.

Here's the listing for dummy1.s. It's written in assembly language and compiled as 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

The example program that single-steps through the above code is:

#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, &regs);
            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;
}
This program prints:
hello world
EIP: 8049478 Instruction executed: 80cddb31
EIP: 804947c Instruction executed: c3
You might have to look at Intel's manuals to make sense out of those instruction bytes. Using single stepping for more complex processes, such as setting breakpoints, requires careful design and more complex code.

In Part II, we will see how breakpoints can be inserted and code can be injected into a running program.

posted on 2006-10-10 14:48 JeromeWen 閱讀(666) 評論(1)  編輯 收藏 引用 所屬分類: C++

評論

# re: Playing with ptrace, Part I  回復  更多評論   

so good! 3ks
2008-08-15 17:16 | Greentime
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一区二区三区高清视频在线观看| 美女黄毛**国产精品啪啪| 欧美猛交免费看| 老妇喷水一区二区三区| 久久九九精品99国产精品| 久久精品中文| 久久综合99re88久久爱| 欧美成人精品| 国产精品成av人在线视午夜片| 国产精品理论片在线观看| 国内精品久久久久影院 日本资源| 欧美性大战久久久久久久蜜臀| 国产精品多人| 一区二区三区在线观看国产| 亚洲免费高清视频| 久久国产加勒比精品无码| 蜜桃av一区二区| 亚洲盗摄视频| 亚洲精品综合| 欧美资源在线观看| 欧美美女福利视频| 精品成人在线观看| 亚洲一区3d动漫同人无遮挡| 久久久久国产精品午夜一区| 欧美激情1区2区3区| aa成人免费视频| 久久米奇亚洲| 国产精品视频导航| 亚洲三级性片| 久久夜色精品国产噜噜av| 一本色道久久综合亚洲精品按摩| 久久久精品一区| 国产精品欧美一区二区三区奶水| 亚洲欧洲日产国产网站| 久久久高清一区二区三区| 一区二区精品国产| 欧美激情1区2区| 永久久久久久| 欧美中在线观看| 开元免费观看欧美电视剧网站| 亚洲第一中文字幕在线观看| 亚洲影院在线| 最新日韩精品| 久久亚洲春色中文字幕| 国产喷白浆一区二区三区| 亚洲一区二区免费看| 亚洲国产合集| 麻豆国产精品一区二区三区| 国模大胆一区二区三区| 性欧美8khd高清极品| 一区二区欧美国产| 欧美视频在线免费看| 一区二区三区黄色| 日韩一级黄色av| 欧美人与禽性xxxxx杂性| 亚洲黄色免费网站| 欧美电影在线免费观看网站| 久久久午夜电影| 在线播放豆国产99亚洲| 久久躁狠狠躁夜夜爽| 久久久精品五月天| 亚洲高清视频的网址| 免费观看不卡av| 巨乳诱惑日韩免费av| 亚洲经典视频在线观看| 亚洲第一黄网| 欧美精品一区二区三区视频| 日韩午夜一区| 99精品国产在热久久婷婷| 欧美日产在线观看| 亚洲综合日韩中文字幕v在线| 亚洲一二三区精品| 国产伦精品一区二区三区照片91| 欧美在线综合| 久久亚洲视频| 99re热这里只有精品视频| 亚洲精品五月天| 国产精品少妇自拍| 久久躁日日躁aaaaxxxx| 欧美极品一区| 先锋影音一区二区三区| 久久九九国产| 一区二区日韩免费看| 午夜精品国产| 亚洲激情网站| 亚洲一区二区三区色| 伊人天天综合| 一区二区欧美激情| 一区二区在线观看av| 亚洲欧洲在线播放| 国产偷国产偷亚洲高清97cao| 欧美粗暴jizz性欧美20| 国产精品高潮呻吟| 欧美69视频| 国产精品日日摸夜夜摸av| 蜜臀av性久久久久蜜臀aⅴ四虎| 欧美国产激情二区三区| 香港久久久电影| 欧美激情视频一区二区三区免费| 欧美自拍偷拍| 欧美日韩精品国产| 国内欧美视频一区二区| 99在线热播精品免费| 午夜视频在线观看一区二区| 亚洲日韩欧美视频| 午夜精品www| 一区二区三区四区在线| 久久精品国产77777蜜臀| 亚洲图片欧洲图片日韩av| 久久婷婷国产综合精品青草| 先锋影音国产精品| 欧美日韩国产首页在线观看| 免费成人网www| 国产亚洲一区二区精品| 夜夜嗨av一区二区三区中文字幕| 18成人免费观看视频| 午夜久久美女| 先锋影音国产精品| 欧美性一二三区| 亚洲精品综合久久中文字幕| 91久久精品国产91性色tv| 久久激情视频久久| 久久成人久久爱| 国产精品爽爽爽| 在线亚洲伦理| 亚洲欧美另类国产| 欧美三级在线播放| 一本大道久久a久久综合婷婷| 亚洲精品小视频在线观看| 免费国产自线拍一欧美视频| 美女主播精品视频一二三四| 精品动漫3d一区二区三区| 久久激情网站| 免费高清在线一区| 亚洲激情一区| 欧美激情一区二区三区四区| 亚洲高清网站| 这里只有精品视频在线| 欧美日韩国产综合视频在线观看中文| 亚洲第一福利视频| 99在线热播精品免费| 国产精品成人一区| 午夜亚洲视频| 免费欧美电影| 99国产精品视频免费观看| 欧美日韩国产综合视频在线观看| 日韩午夜在线电影| 欧美一区二区女人| 伊人成人网在线看| 欧美激情一区二区三区高清视频| 99综合精品| 久久精品国产亚洲一区二区三区| 国产一区二区三区免费不卡| 久久久美女艺术照精彩视频福利播放| 欧美高清日韩| 亚洲先锋成人| 韩国成人精品a∨在线观看| 免费日本视频一区| 99国内精品久久| 久久精品国亚洲| 亚洲三级免费电影| 国产精品人成在线观看免费 | 亚洲欧美韩国| 久久亚洲国产精品一区二区| 亚洲黄色成人| 国产精品成人一区二区网站软件| 午夜伦欧美伦电影理论片| 国产精品va在线播放| 亚洲日本中文字幕| 香蕉成人啪国产精品视频综合网| 国产一区二区三区四区三区四| 久久综合综合久久综合| 日韩视频在线免费| 久久久久成人精品免费播放动漫| 亚洲激情综合| 国产日韩一级二级三级| 欧美精品国产精品| 久久国产精品一区二区三区四区| 亚洲精品视频中文字幕| 可以免费看不卡的av网站| 亚洲一区在线播放| 亚洲黄色av| 国产在线观看一区| 欧美激情一区二区三级高清视频 | 久久九九99视频| 妖精视频成人观看www| 国产自产v一区二区三区c| 欧美日韩福利在线观看| 久久婷婷综合激情| 午夜精品一区二区三区电影天堂| 亚洲精品精选| 欧美二区不卡| 麻豆精品精品国产自在97香蕉| 亚洲一区二区三区免费观看| 亚洲人成在线观看一区二区| 一区二区亚洲欧洲国产日韩| 国产自产2019最新不卡| 国产精品尤物| 国产精品腿扒开做爽爽爽挤奶网站| 欧美另类高清视频在线|