真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

怎么利用Ptrace攔截和模擬Linux系統(tǒng)調(diào)用

本篇內(nèi)容介紹了“怎么利用Ptrace攔截和模擬Linux系統(tǒng)調(diào)用”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

創(chuàng)新互聯(lián)是一家專業(yè)提供蕪湖縣企業(yè)網(wǎng)站建設(shè),專注與做網(wǎng)站、成都做網(wǎng)站、H5響應(yīng)式網(wǎng)站、小程序制作等業(yè)務(wù)。10年已為蕪湖縣眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)的建站公司優(yōu)惠進(jìn)行中。

寫在前面的話

ptrace(2)這個(gè)系統(tǒng)調(diào)用一般都跟調(diào)試離不開關(guān)系,它不僅是類Unix系統(tǒng)中本地調(diào)試器監(jiān)控實(shí)現(xiàn)的主要機(jī)制,而且它還是strace系統(tǒng)調(diào)用常用的實(shí)現(xiàn)方法。ptrace()系統(tǒng)調(diào)用函數(shù)提供了一個(gè)進(jìn)程(the “tracer”)監(jiān)察和控制另一個(gè)進(jìn)程(the “tracee”)的方法,它不僅可以監(jiān)控系統(tǒng)調(diào)用,而且還能夠檢查和改變“tracee”進(jìn)程的內(nèi)存和寄存器里的數(shù)據(jù),甚至它還可以攔截系統(tǒng)調(diào)用。

這里的“攔截”我指的是tracer能夠改變系統(tǒng)調(diào)用參數(shù),改變系統(tǒng)調(diào)用的返回值,甚至屏蔽特定的系統(tǒng)調(diào)用。這也就意味著,一個(gè)tracer將能夠完全實(shí)現(xiàn)自己的系統(tǒng)調(diào)用,這就非常有趣了,也就是說,一個(gè)tracer將可以模擬出一整套操作系統(tǒng)機(jī)制,而且這一切都不需要內(nèi)核提供任何其他幫助。

但問題在于,一個(gè)進(jìn)程一次只能夠綁定一個(gè)tracer,因此我們無法在調(diào)試進(jìn)程(GDB)的過程中模擬出一套外部操作系統(tǒng),而另一個(gè)問題就是模擬系統(tǒng)調(diào)用將耗費(fèi)更多的資源開銷。

在這篇文章中,我將主要討論x86-64架構(gòu)下的Linux Ptrace,并且我還會(huì)使用到一些特定的Linux擴(kuò)展。除此之外,我可能會(huì)忽略錯(cuò)誤檢查,但最終發(fā)布的完整源碼將會(huì)解決這些問題。

strace

在開始之前,我們先看一看strace的實(shí)現(xiàn)骨架。Ptrace一直都沒有相應(yīng)的使用標(biāo)準(zhǔn),但在不同的操作系統(tǒng)中它的接口都是類似的,尤其是它的核心功能,但多多少少都會(huì)有一些細(xì)微的差別。Ptrace(2)的原型類似如下:

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

pid是tracee的進(jìn)程ID,一個(gè)tracee一次只能綁定一個(gè)tracer,但一個(gè)tracer可以綁定多個(gè)tracee。

request域負(fù)責(zé)選擇一個(gè)指定的Ptrace函數(shù),例如ioctl(2)接口。對(duì)于strace來說,只有下面是必須的:

PTRACE_TRACEME:它的父進(jìn)程必須跟蹤這個(gè)進(jìn)程。

PTRACE_SYSCALL:繼續(xù)運(yùn)行,但是會(huì)在下一個(gè)系統(tǒng)調(diào)用入口暫停運(yùn)行。

PTRACE_GETREGS:獲取tracee的寄存器備份。

另外兩個(gè)數(shù)據(jù)域,即addr和data,它們負(fù)責(zé)給選定的Ptrace函數(shù)提供參數(shù),一般這兩個(gè)數(shù)據(jù)都可以忽略,這里我選擇傳入0。

strace接口本質(zhì)上是其他命令的前綴:

$strace [strace options] program [arguments]

我的最小化配置不包含任何參數(shù),所以要做的第一件事就是假設(shè)它至少包含一個(gè)參數(shù)(fork(2)),通過argv傳遞。在加載目標(biāo)程序之前,新的進(jìn)程會(huì)告知內(nèi)核它的父進(jìn)程將會(huì)對(duì)它進(jìn)行跟蹤監(jiān)視,tracee將會(huì)被這個(gè)Ptrace系統(tǒng)調(diào)用掛起:

pid_tpid = fork(); switch(pid) {
    case -1: /* error */         FATAL("%s", strerror(errno));
    case 0: /* child */         ptrace(PTRACE_TRACEME, 0, 0, 0);
        execvp(argv[1], argv + 1);
        FATAL("%s", strerror(errno));
}

父進(jìn)程將使用wait(2)來等待子進(jìn)程的PTRACE_TRACEME,當(dāng)wait(2)返回值之后,子進(jìn)程將會(huì)被掛起:

wait pid(pid,0, 0);

在允許子進(jìn)程繼續(xù)運(yùn)行之前,我們將告訴操作系統(tǒng)tracee應(yīng)該跟它的父進(jìn)程一起終止。真實(shí)場(chǎng)景下的strace實(shí)現(xiàn)還需要設(shè)置其他的參數(shù),例如PTRACE_O_TRACEFORK:

ptrace(PTRACE_SETOPTIONS,pid, 0, PTRACE_O_EXITKILL);

捕捉系統(tǒng)調(diào)用的循環(huán)步驟如下:

1.   等待進(jìn)程進(jìn)入下一次系統(tǒng)調(diào)用。

2.   打印系統(tǒng)調(diào)用信息。

3.   允許系統(tǒng)調(diào)用執(zhí)行,并等待返回結(jié)果。

4.   打印系統(tǒng)調(diào)用的返回值。

PTRACE_SYSCALL請(qǐng)求可以完成等待下一個(gè)系統(tǒng)調(diào)用以及等待系統(tǒng)調(diào)用結(jié)束這兩個(gè)任務(wù),跟之前一樣,這里也需要使用wait(2)來等待tracee進(jìn)入特定狀態(tài)。

ptrace(PTRACE_SYSCALL,pid, 0, 0); waitpid(pid,0, 0);

wait(2)返回后,線程寄存器中將存儲(chǔ)有系統(tǒng)調(diào)用號(hào)和相應(yīng)參數(shù)。下一步就是收集系統(tǒng)調(diào)用信息,在不同的系統(tǒng)架構(gòu)中這一步的實(shí)現(xiàn)方式也不同。在x86-64中,系統(tǒng)調(diào)用號(hào)是通過rax傳遞的,參數(shù)(最大為6)將傳遞給rdi、rsi、rdx、r10、r8和r9。讀取寄存器還需要其他的Ptrace調(diào)用,但這里就不需要wait(2)了,因?yàn)閠racee并不會(huì)改變狀態(tài)。

struct user_regs_struct regs;
ptrace(PTRACE_GETREGS,pid, 0, ®s);
longsyscall = regs.orig_rax;
  fprintf(stderr,"%ld(%ld, %ld, %ld, %ld, %ld, %ld)",
        syscall,
        (long)regs.rdi, (long)regs.rsi,(long)regs.rdx,
        (long)regs.r10, (long)regs.r8,  (long)regs.r9);

接下來就是另一個(gè)PTRACE_SYSCALL和wait(2),然后利用PTRACE_GETREGS獲取結(jié)果,結(jié)果將存儲(chǔ)在rax中:

ptrace(PTRACE_GETREGS,pid, 0, ®s); fprintf(stderr," = %ld\n", (long)regs.rax);

這個(gè)樣本程序的輸出結(jié)果還是比較簡(jiǎn)陋的,其中沒有包含系統(tǒng)調(diào)用的符號(hào)名,并且每一個(gè)參數(shù)都是按數(shù)字形式打印的,不過這已經(jīng)足夠奠定系統(tǒng)調(diào)用攔截的基礎(chǔ)了。

系統(tǒng)調(diào)用攔截

假設(shè)我們想利用Ptrace去實(shí)現(xiàn)一個(gè)類似OpenBSD的pledge(2)這樣的東西?;舅悸啡缦拢汉芏喑绦蛞话愣加幸粋€(gè)初始化過程,這個(gè)過程需要涉及到很多系統(tǒng)訪問權(quán)限,例如打開文件和綁定套接字等等。初始化完成之后,它們會(huì)進(jìn)入主循環(huán),并處理輸入數(shù)據(jù),這里只需要使用到少量系統(tǒng)調(diào)用。

在進(jìn)入主循環(huán)之前,進(jìn)程可以限制自身只進(jìn)行少量操作,如果程序存在漏洞的話,pledge還可以限制漏洞利用代碼所能完成的事情。當(dāng)然了,我們不僅可以篡改系統(tǒng)調(diào)用參數(shù),而且還可以修改系統(tǒng)調(diào)用號(hào),并將其轉(zhuǎn)換成一個(gè)不存在的系統(tǒng)調(diào)用,然后在errno中報(bào)告一個(gè)EPERM錯(cuò)誤信息:

for(;;) {
    /* Enter next system call */     ptrace(PTRACE_SYSCALL, pid, 0, 0);
    waitpid(pid, 0, 0);
    struct user_regs_struct regs;
    ptrace(PTRACE_GETREGS, pid, 0, ®s);
    /* Is this system call permitted? */     int blocked = 0;
    if (is_syscall_blocked(regs.orig_rax)) {
        blocked = 1;
        regs.orig_rax = -1; // set to invalidsyscall         ptrace(PTRACE_SETREGS, pid, 0,®s);
    }
 
    /* Run system call and stop on exit */     ptrace(PTRACE_SYSCALL, pid, 0, 0);
    waitpid(pid, 0, 0);
    if (blocked) {
        /* errno = EPERM */         regs.rax = -EPERM; // Operation notpermitted         ptrace(PTRACE_SETREGS, pid, 0,®s);
    }
}

創(chuàng)建自定義的系統(tǒng)調(diào)用

我將我新創(chuàng)建的模仿pledge的系統(tǒng)調(diào)用稱為xpledge(),我選擇的系統(tǒng)調(diào)用號(hào)是10000:

#define SYS_xpledge 10000

下面是這個(gè)針對(duì)tracee的系統(tǒng)調(diào)用完整接口實(shí)現(xiàn):

#define_GNU_SOURCE #include #defineXPLEDGE_RDWR  (1 << 0) #defineXPLEDGE_OPEN  (1 << 1) #definexpledge(arg) syscall(SYS_xpledge, arg)

如果傳遞的參數(shù)為0,則只允許執(zhí)行一些基本的系統(tǒng)調(diào)用,包括內(nèi)存分配等。PLEDGE_RDWR指定的是各種讀寫操作,如read(2)、readv(2)、pread(2)和preadv(2)等。

在xpledge tracer中,我只需要檢測(cè)這個(gè)系統(tǒng)調(diào)用:

/*Handle entrance */ switch(regs.orig_rax) {
    case SYS_pledge:
        register_pledge(regs.rdi);
        break;
}

操作系統(tǒng)將返回ENOSYS,因?yàn)樗皇且粋€(gè)真正的系統(tǒng)調(diào)用,所以我們需要用success(0)重寫返回結(jié)果:

/*Handle exit */ switch(regs.orig_rax) {
    case SYS_pledge:
        ptrace(PTRACE_POKEUSER, pid, RAX * 8,0);
        break;
}

樣例程序的輸出結(jié)果如下:

$./example
fread("/dev/urandom")[1]= 0xcd2508c7 XPledging...
XPledgefailed: Function not implemented
fread("/dev/urandom")[2]= 0x0be4a986 fread("/dev/urandom")[1]= 0x03147604

在tracer下運(yùn)行的結(jié)果如下:

$./xpledge ./example
fread("/dev/urandom")[1]= 0xb2ac39c4 XPledging...
fopen("/dev/urandom")[2]:Operation not permitted
fread("/dev/urandom")[1]= 0x2e1bd1c4

外部系統(tǒng)模擬

Linux下的Ptrace中有一個(gè)非常實(shí)用的函數(shù):PTRACE_SYSMU,我們可以利用這個(gè)函數(shù)來實(shí)現(xiàn)系統(tǒng)模擬:

for(;;) {
    ptrace(PTRACE_SYSEMU, pid, 0, 0);
    waitpid(pid, 0, 0);
    struct user_regs_struct regs;
    ptrace(PTRACE_GETREGS, pid, 0, ®s);
    switch (regs.orig_rax) {
        case OS_read:
            /* ... */         case OS_write:
            /* ... */         case OS_open:
            /* ... */         case OS_exit:
            /* ... */         /* ... and so on ... */     }
}

此代碼框架在相同系統(tǒng)架構(gòu)中的測(cè)試結(jié)果都是能夠穩(wěn)定運(yùn)行的,大家可以根據(jù)自己的需要來修改代碼。

“怎么利用Ptrace攔截和模擬Linux系統(tǒng)調(diào)用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!


當(dāng)前題目:怎么利用Ptrace攔截和模擬Linux系統(tǒng)調(diào)用
本文URL:http://weahome.cn/article/pcdodg.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部