亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 913 | 回復(fù): 0
打印 上一主題 下一主題

玩轉(zhuǎn)ptrace(二) [z] [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2007-08-18 17:33 |只看該作者 |倒序瀏覽

by Pradeep Padala
Created 2002-11-01 02:00
翻譯: Magic.D

在第一部分中我們已經(jīng)看到ptrace怎么獲取子進(jìn)程的系統(tǒng)調(diào)用以及改變系統(tǒng)調(diào)用的參數(shù)。在這篇文章中,我們將要研究如何在子進(jìn)程中設(shè)置斷點和往運行中的程序里插入代碼。實際上調(diào)試器就是用這種方法來設(shè)置斷點和執(zhí)行調(diào)試句柄。與前面一樣,這里的所有代碼都是針對i386平臺的。

附著在進(jìn)程上

在第一部分鐘,我們使用ptrace(PTRACE_TRACEME, …)來跟蹤一個子進(jìn)程,如果你只是想要看進(jìn)程是怎么進(jìn)行系統(tǒng)調(diào)用和跟蹤程序的,這個做法是不錯的。但如果你要對運行中的進(jìn)程進(jìn)行調(diào)試,則需要使用 ptrace( PTRACE_ATTACH, ….)

當(dāng) ptrace( PTRACE_ATTACH, …)在被調(diào)用的時候傳入了子進(jìn)程的pid時, 它大體是與ptrace( PTRACE_TRACEME, …)的行為相同的,它會向子進(jìn)程發(fā)送SIGSTOP信號,于是我們可以察看和修改子進(jìn)程,然后使用 ptrace( PTRACE_DETACH, …)來使子進(jìn)程繼續(xù)運行下去。

下面是調(diào)試程序的一個簡單例子
int main()
{
   int i;
    for(i = 0;i  10; ++i) {
        printf("My counter: %d ", i);
        sleep(2);
    }
    return 0;
}
將上面的代碼保存為dummy2.c。按下面的方法編譯運行:
gcc -o dummy2 dummy2.c
./dummy2 &

現(xiàn)在我們可以用下面的代碼來附著到dummy2上。

#include sys/ptrace.h>
#include sys/types.h>
#include sys/wait.h>
#include unistd.h>
#include linux/user.h> /**//* For user_regs_struct
                             etc. */
int main(int argc, char *argv[])
{
    pid_t traced_process;
    struct user_regs_struct regs;
    long ins;
    if(argc != 2) ...{
        printf("Usage: %s  ",
               argv[0], argv[1]);
        exit(1);
    }
    traced_process = atoi(argv[1]);
    ptrace(PTRACE_ATTACH, traced_process,
           NULL, NULL);
    wait(NULL);
    ptrace(PTRACE_GETREGS, traced_process,
           NULL, &regs);
    ins = ptrace(PTRACE_PEEKTEXT, traced_process,
                 regs.eip, NULL);
    printf("EIP: %lx Instruction executed: %lx ",
           regs.eip, ins);
    ptrace(PTRACE_DETACH, traced_process,
           NULL, NULL);
    return 0;
}
上面的程序僅僅是附著在子進(jìn)程上,等待它結(jié)束,并測量它的eip( 指令指針)然后釋放子進(jìn)程。
設(shè)置斷點
調(diào)試器是怎么設(shè)置斷點的呢?通常是將當(dāng)前將要執(zhí)行的指令替換成trap指令,于是被調(diào)試的程序就會在這里停滯,這時調(diào)試器就可以察看被調(diào)試程序的信息了。被調(diào)試程序恢復(fù)運行以后調(diào)試器會把原指令再放回來。這里是一個例子:
#include sys/ptrace.h>
#include sys/types.h>
#include sys/wait.h>
#include unistd.h>
#include linux/user.h>
const int long_size = sizeof(long);
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] = '';
}
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(int argc, char *argv[])
{
    pid_t traced_process;
    struct user_regs_struct regs, newregs;
    long ins;
    /**//* int 0x80, int3 */
    char code[] = ...{0xcd,0x80,0xcc,0};
    char backup[4];
    if(argc != 2) ...{
        printf("Usage: %s  ",
               argv[0], argv[1]);
        exit(1);
    }
    traced_process = atoi(argv[1]);
    ptrace(PTRACE_ATTACH, traced_process,
           NULL, NULL);
    wait(NULL);
    ptrace(PTRACE_GETREGS, traced_process,
           NULL, &regs);
    /**//* Copy instructions into a backup variable */
    getdata(traced_process, regs.eip, backup, 3);
    /**//* Put the breakpoint */
    putdata(traced_process, regs.eip, code, 3);
    /**//* Let the process continue and execute
       the int 3 instruction */
    ptrace(PTRACE_CONT, traced_process, NULL, NULL);
    wait(NULL);
    printf("The process stopped, putting back "
           "the original instructions ");
    printf("Press  to continue ");
    getchar();
    putdata(traced_process, regs.eip, backup, 3);
    /**//* Setting the eip back to the original
       instruction to let the process continue */
    ptrace(PTRACE_SETREGS, traced_process,
           NULL, &regs);
    ptrace(PTRACE_DETACH, traced_process,
           NULL, NULL);
    return 0;
}
上面的程序?qū)讶齻byte的內(nèi)容進(jìn)行替換以執(zhí)行trap指令,等被調(diào)試進(jìn)程停滯以后,我們把原指令再替換回來并把eip修改為原來的值。下面的圖中演示了指令的執(zhí)行過程




1. 進(jìn)程停滯后
2. 替換入trap指令




3.斷點成功,控制權(quán)交給了調(diào)試器
4. 繼續(xù)運行,將原指令替換回來并將eip復(fù)原
在了解了斷點的機(jī)制以后,往運行中的程序里面添加指令也不再是難事了,下面的代碼會使原程序多出一個”hello world”的輸出
這時一個簡單的”hello world”程序,當(dāng)然為了我們的特殊需要作了點修改:
void main()
{
__asm__("
         jmp forward
backward:
         popl %esi # Get the address of
                          # hello world string
         movl $4, %eax # Do write system call
         movl $2, %ebx
         movl %esi, %ecx
         movl $12, %edx
         int $0x80
         int3 # Breakpoint. Here the
                          # program will stop and
                          # give control back to
                          # the parent
forward:
         call backward
         .string "Hello World\n""
       );
}
使用 gcc –o hello hello.c來編譯它。
在backward和forward之間的跳轉(zhuǎn)是為了使程序能夠找到”hello world” 字符串的地址。
使用GDB我們可以得到上面那段程序的機(jī)器碼。啟動GDB,然后對程序進(jìn)行反匯編:
(gdb) disassemble main
Dump of assembler code for function main:
0x80483e0 main>: push %ebp
0x80483e1 main+1>: mov %esp,%ebp
0x80483e3 main+3>: jmp 0x80483fa forward>
End of assembler dump.
(gdb) disassemble forward
Dump of assembler code for function forward:
0x80483fa forward>: call 0x80483e5 backward>
0x80483ff forward+5>: dec %eax
0x8048400 forward+6>: gs
0x8048401 forward+7>: insb (%dx),%es:(%edi)
0x8048402 forward+8>: insb (%dx),%es:(%edi)
0x8048403 forward+9>: outsl %ds:(%esi),(%dx)
0x8048404 forward+10>: and %dl,0x6f(%edi)
0x8048407 forward+13>: jb 0x8048475
0x8048409 forward+15>: or %fs:(%eax),%al
0x804840c forward+18>: mov %ebp,%esp
0x804840e forward+20>: pop %ebp
0x804840f forward+21>: ret
End of assembler dump.
(gdb) disassemble backward
Dump of assembler code for function backward:
0x80483e5 backward>: pop %esi
0x80483e6 backward+1>: mov $0x4,%eax
0x80483eb backward+6>: mov $0x2,%ebx
0x80483f0 backward+11>: mov %esi,%ecx
0x80483f2 backward+13>: mov $0xc,%edx
0x80483f7 backward+18>: int $0x80
0x80483f9 backward+20>: int3
End of assembler dump.
我們需要使用從man+3到backward+20之間的字節(jié)碼,總共41字節(jié)。使用GDB中的x命令來察看機(jī)器碼。

(gdb) x/40bx main+3
main+3>: eb 15 5e b8 04 00 00 00
backward+6>: bb 02 00 00 00 89 f1 ba
backward+14>: 0c 00 00 00 cd 80 cc
forward+1>: e6 ff ff ff 48 65 6c 6c
forward+9>: 6f 20 57 6f 72 6c 64 0a
已經(jīng)有了我們想要執(zhí)行的指令,還等什么呢?只管把它們根前面那個例子一樣插入到被調(diào)試程序中去!
代碼:
int main(int argc, char *argv[])
{
pid_t traced_process;
    struct user_regs_struct regs, newregs;
    long ins;
    int len = 41;
    char insertcode[] =
        "\xeb\x15\x5e\xb8\x04\x00"
        "\x00\x00\xbb\x02\x00\x00\x00\x89\xf1\xba"
        "\x0c\x00\x00\x00\xcd\x80\xcc\xe8\xe6\xff"
        "\xff\xff\x48\x65\x6c\x6c\x6f\x20\x57\x6f"
        "\x72\x6c\x64\x0a\x00";
    char backup[len];
    if(argc != 2) ...{
        printf("Usage: %s  ",
               argv[0], argv[1]);
        exit(1);
    }
    traced_process = atoi(argv[1]);
    ptrace(PTRACE_ATTACH, traced_process,
           NULL, NULL);
    wait(NULL);
    ptrace(PTRACE_GETREGS, traced_process,
           NULL, &regs);
    getdata(traced_process, regs.eip, backup, len);
    putdata(traced_process, regs.eip,
            insertcode, len);
    ptrace(PTRACE_SETREGS, traced_process,
           NULL, &regs);
    ptrace(PTRACE_CONT, traced_process,
           NULL, NULL);
    wait(NULL);
    printf("The process stopped, Putting back "
           "the original instructions ");
    putdata(traced_process, regs.eip, backup, len);
    ptrace(PTRACE_SETREGS, traced_process,
           NULL, &regs);
    printf("Letting it continue with "
           "original flow ");
    ptrace(PTRACE_DETACH, traced_process,
           NULL, NULL);
    return 0;
}
將代碼插入到自由空間
在前面的例子中我們將代碼直接插入到了正在執(zhí)行的指令流中,然而,調(diào)試器可能會被這種行為弄糊涂,所以我們決定把指令插入到進(jìn)程中的自由空間中去。通過察看/proc/pid/maps可以知道這個進(jìn)程中自由空間的分布。接下來這個函數(shù)可以找到這個內(nèi)存映射的起始點:
long freespaceaddr(pid_t pid)
{
    FILE *fp;
    char filename[30];
    char line[85];
    long addr;
    char str[20];
    sprintf(filename, "/proc/%d/maps", pid);
    fp = fopen(filename, "r");
    if(fp == NULL)
        exit(1);
    while(fgets(line, 85, fp) != NULL) ...{
        sscanf(line, "%lx-%*lx %*s %*s %s", &addr,
               str, str, str, str);
        if(strcmp(str, "00:00") == 0)
            break;
    }
    fclose(fp);
    return addr;
}
在/proc/pid/maps中的每一行都對應(yīng)了進(jìn)程中一段內(nèi)存區(qū)域。主函數(shù)的代碼如下:
int main(int argc, char *argv[])
{
    pid_t traced_process;
    struct user_regs_struct oldregs, regs;
    long ins;
    int len = 41;
    char insertcode[] =
        "\xeb\x15\x5e\xb8\x04\x00"
        "\x00\x00\xbb\x02\x00\x00\x00\x89\xf1\xba"
        "\x0c\x00\x00\x00\xcd\x80\xcc\xe8\xe6\xff"
        "\xff\xff\x48\x65\x6c\x6c\x6f\x20\x57\x6f"
        "\x72\x6c\x64\x0a\x00";
    char backup[len];
    long addr;
    if(argc != 2) ...{
        printf("Usage: %s  ",
               argv[0], argv[1]);
        exit(1);
    }
    traced_process = atoi(argv[1]);
    ptrace(PTRACE_ATTACH, traced_process,
           NULL, NULL);
    wait(NULL);
    ptrace(PTRACE_GETREGS, traced_process,
           NULL, &regs);
    addr = freespaceaddr(traced_process);
    getdata(traced_process, addr, backup, len);
    putdata(traced_process, addr, insertcode, len);
    memcpy(&oldregs, &regs, sizeof(regs));
    regs.eip = addr;
    ptrace(PTRACE_SETREGS, traced_process,
           NULL, &regs);
    ptrace(PTRACE_CONT, traced_process,
           NULL, NULL);
    wait(NULL);
    printf("The process stopped, Putting back "
           "the original instructions ");
    putdata(traced_process, addr, backup, len);
    ptrace(PTRACE_SETREGS, traced_process,
           NULL, &oldregs);
    printf("Letting it continue with "
           "original flow ");
    ptrace(PTRACE_DETACH, traced_process,
           NULL, NULL);
    return 0;
}
ptrace的幕后工作
那么,在使用ptrace的時候,內(nèi)核里發(fā)生了聲么呢?這里有一段簡要的說明:當(dāng)一個進(jìn)程調(diào)用了 ptrace( PTRACE_TRACEME, …)之后,內(nèi)核為該進(jìn)程設(shè)置了一個標(biāo)記,注明該進(jìn)程將被跟蹤。內(nèi)核中的相關(guān)原代碼如下:
Source: arch/i386/kernel/ptrace.c
if (request == PTRACE_TRACEME) {
    /* are we already being traced? */
    if (current->ptrace & PT_PTRACED)
        goto out;
    /* set the ptrace bit in the process flags. */
    current->ptrace |= PT_PTRACED;
    ret = 0;
    goto out;
}
一次系統(tǒng)調(diào)用完成之后,內(nèi)核察看那個標(biāo)記,然后執(zhí)行trace系統(tǒng)調(diào)用(如果這個進(jìn)程正處于被跟蹤狀態(tài)的話)。其匯編的細(xì)節(jié)可以在 arh/i386/kernel/entry.S中找到。
現(xiàn)在讓我們來看看這個sys_trace()函數(shù)(位于 arch/i386/kernel/ptrace.c )。它停止子進(jìn)程,然后發(fā)送一個信號給父進(jìn)程,告訴它子進(jìn)程已經(jīng)停滯,這個信號會激活正處于等待狀態(tài)的父進(jìn)程,讓父進(jìn)程進(jìn)行相關(guān)處理。父進(jìn)程在完成相關(guān)操作以后就調(diào)用ptrace( PTRACE_CONT, …)或者 ptrace( PTRACE_SYSCALL, …), 這將喚醒子進(jìn)程,內(nèi)核此時所作的是調(diào)用一個叫wake_up_process() 的進(jìn)程調(diào)度函數(shù)。其他的一些系統(tǒng)架構(gòu)可能會通過發(fā)送SIGCHLD給子進(jìn)程來達(dá)到這個目的。
小結(jié):
ptrace函數(shù)可能會讓人們覺得很奇特,因為它居然可以檢測和修改一個運行中的程序。這種技術(shù)主要是在調(diào)試器和系統(tǒng)調(diào)用跟蹤程序中使用。它使程序員可以在用戶級別做更多有意思的事情。已經(jīng)有過很多在用戶級別下擴(kuò)展操作系統(tǒng)得嘗試,比如UFO,一個用戶級別的文件系統(tǒng)擴(kuò)展,它使用ptrace來實現(xiàn)一些安全機(jī)制。
作者: Pradeep Padala,

p_padala@yahoo.com
http://www.cise.ufl.edu/~ppadala


本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u/19651/showart_362921.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP