日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線(xiàn)咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

什么是棧

  • 簡(jiǎn)單來(lái)說(shuō),棧 是一種 LIFO(Last In Frist Out,后進(jìn)先出) 形式的數(shù)據(jù)結(jié)構(gòu)。棧一般是從高地址向低地址增長(zhǎng),并且棧支持 push(入棧) 和 pop(出棧) 兩個(gè)操作。如下圖所示:

 

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

 

 

  • push 操作先將 棧頂(sp指針) 向下移動(dòng)一個(gè)位置,然后將數(shù)據(jù)寫(xiě)入到新的棧頂;而 pop 操作會(huì)從 棧頂 讀取數(shù)據(jù),并且將 棧頂(sp指針) 向上移動(dòng)一個(gè)位置。
  • 例如,將 0x100 壓入棧,過(guò)程如下圖所示:

 

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

?

 

  • 我們?cè)賮?lái)看看 出棧 操作,如下圖所示:

 

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

?

更多l(xiāng)inux內(nèi)核視頻教程文檔資料免費(fèi)領(lǐng)取后臺(tái)私信【內(nèi)核】自行獲取.

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

Linux內(nèi)核源碼/內(nèi)存調(diào)優(yōu)/文件系統(tǒng)/進(jìn)程管理/設(shè)備驅(qū)動(dòng)/網(wǎng)絡(luò)協(xié)議棧-學(xué)習(xí)視頻教程-騰訊課堂

棧幀

  • 棧幀,也就是 Sack Frame,其本質(zhì)就是一種棧,只是這種棧專(zhuān)門(mén)用于保存函數(shù)調(diào)用過(guò)程中的各種信息(參數(shù),返回地址,本地變量等)。
  • 棧幀 有 棧頂 和 棧底 之分,其中棧頂?shù)牡刂纷畹停瑮5椎牡刂纷罡摺P(棧指針) 就是一直指向棧頂?shù)摹T?x86 的 32 位 CPU 中,我們用 %ebp 寄存器指向棧底,也就是基址指針;用 %esp 寄存器指向棧頂,也就是棧指針。下面是一個(gè)棧幀的示意圖:

 

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

?

 

  • 一般來(lái)說(shuō),我們將 %ebp 到 %esp 之間區(qū)域當(dāng)做棧幀。并不是整個(gè)棧空間只有一個(gè)棧幀,每調(diào)用一個(gè)函數(shù),就會(huì)生成一個(gè)新的棧幀。
  • 在函數(shù)調(diào)用過(guò)程中,我們將調(diào)用函數(shù)的函數(shù)稱(chēng)為:調(diào)用者(caller),將被調(diào)用的函數(shù)稱(chēng)為:被調(diào)用者(callee)。在這個(gè)過(guò)程中:
  1. 調(diào)用者 需要知道在哪里獲取 被調(diào)用者 返回的值(一般存放到 %eax 寄存器)。
  2. 被調(diào)用者 需要知道傳入的參數(shù)在哪里和調(diào)用完后的返回地址在哪里。
  3. 我們需要保證在 被調(diào)用者 返回后,%ebp 和 %esp 寄存器的值應(yīng)該和調(diào)用前一致。

函數(shù)調(diào)用

  • 現(xiàn)在,我們來(lái)看看函數(shù)調(diào)用時(shí),棧幀是如何變化的。
  • 我們以一個(gè)函數(shù)調(diào)用的實(shí)例來(lái)解說(shuō),代碼如下:
// stack.c

int add_func(int a, int b)
{
    int c, d;

    c = a;
    d = b;

    return c + d;
}

int main(int argc, char *argv[])
{
    int total;

    total = add_func(1, 2);

    return 0;
}

  • 我們使用命令 gcc -S -m32 stack.c 來(lái)編譯上面的代碼,獲取的匯編代碼如下所示(去掉一些無(wú)關(guān)緊要的信息):
add_func:
    pushl   %ebp                // 保存ebp寄存器到棧
    movl    %esp, %ebp          // 把ebp進(jìn)程設(shè)置為esp的值
    subl    $16, %esp           // 為局部變量申請(qǐng)空間
    movl    8(%ebp), %eax       // 把參數(shù)a保存到eax寄存器中
    movl    %eax, -8(%ebp)      // 把eax寄存器的值保存到局部變量c中(c = a)
    movl    12(%ebp), %eax      // 把參數(shù)b保存到eax寄存器中
    movl    %eax, -4(%ebp)      // 把eax寄存器到值保存到局部變量d中(d = b)
    movl    -8(%ebp), %edx      // 把d的值保存到edx寄存器中
    movl    -4(%ebp), %eax      // 把c的值保存到eax寄存器中
    addl    %edx, %eax          // 將eax寄存器與edx寄存器的值相加,保存到eax中(返回值)
    leave
    ret                         // 函數(shù)返回
    ...

  • 可能匯編代碼比較難看懂,我們用下面的插圖來(lái)說(shuō)明這個(gè)調(diào)用過(guò)程:

 

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

?

 

  • 如上圖所示,調(diào)用過(guò)程如下:
  1. 在 main() 函數(shù)調(diào)用 add_func() 函數(shù)前,先將調(diào)用 add_func() 函數(shù)的參數(shù)壓棧。
  2. 在調(diào)用 add_func() 函數(shù)時(shí),會(huì)將 返回地址 壓棧,接著進(jìn)入 add_func() 函數(shù)。
  3. add_func() 函數(shù)執(zhí)行時(shí),會(huì)將原來(lái)的 ebp寄存器 的值壓棧,然后把 ebp寄存器 的設(shè)置為 esp寄存器 的值。
  4. 接著 add_func() 函數(shù)會(huì)為局部變量申請(qǐng)空間,也就是將 esp寄存器 向下移動(dòng)。
  5. 然后把局部變量 c 設(shè)置為參數(shù) a 的值,局部變量 d 設(shè)置為 參數(shù) b 的值。
  6. 最后將局部變量 c 和 d 的值相加,放置到 eax寄存器 中(C語(yǔ)言規(guī)定以 eax寄存器 傳遞返回值),然后調(diào)用 ret 指令返回到 main() 函數(shù)。

函數(shù)返回

  • 上面介紹了 函數(shù)調(diào)用 的過(guò)程,現(xiàn)在我們來(lái)介紹一下函數(shù)調(diào)用完畢后,從被調(diào)用函數(shù)返回到原來(lái)的函數(shù)過(guò)程是如何處理的。
  • 從 add_func() 函數(shù)的匯編代碼可以看到,當(dāng)被調(diào)用函數(shù)執(zhí)行完畢返回到調(diào)用函數(shù)前,會(huì)執(zhí)行 leave 指令,這條指令等價(jià)于:
movl %ebp, %esp
popl %ebp

  • 這兩條匯編指令的意思是,將 esp寄存器 和 ebp寄存器 恢復(fù)到調(diào)用函數(shù)前的值。
  • 然后,調(diào)用 ret 指令返回到原來(lái)的函數(shù)。ret 指令會(huì)從棧頂獲取 返回地址,然后跳轉(zhuǎn)到(jmp指令)此地址繼續(xù)執(zhí)行。這時(shí)的 棧幀 的結(jié)構(gòu)如下圖所示:

 

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

?

 

棧溢出攻擊

  • 前面說(shuō)了那么,都是為了 棧溢出攻擊 這節(jié)作鋪墊的。通過(guò)前面的學(xué)習(xí),我們知道調(diào)用函數(shù)的 參數(shù) 、執(zhí)行完函數(shù)后的 返回地址 和被調(diào)用函數(shù)的 局部變量 都是存放在棧中的。
  • 如果在調(diào)用函數(shù)時(shí),不小心將 返回地址 覆蓋了,那么調(diào)用完函數(shù)后,將不會(huì)跳轉(zhuǎn)到原來(lái)的函數(shù)繼續(xù)執(zhí)行,而是跳轉(zhuǎn)到覆蓋后的地址執(zhí)行。如下圖所示:

 

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

?

 

  • 那么,怎樣才能把 返回地址 覆蓋呢?我們可以通過(guò)下面的例子來(lái)說(shuō)明:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>

#define PTR_SIZE 8   // 指針的大小
#define EBP_SIZE 8   // ebp寄存器的大小

void inject_callback()
{
    printf("inject_callback called...n");
    exit(0);
}

void func_call(char *addr, int len)
{
    char tmpBuf[16] = {0xff};

    memcpy(tmpBuf + 16 + EBP_SIZE, addr, len);

    printf("func_call called...n");
}

int main(int argc, char** argv)
{
    uint64_t injectPtr = (uint64_t)&inject_callback;

    func_call(&injectPtr, PTR_SIZE);

    printf("main exited...n");

    return 0;
}

  • 我們使用以下命令編譯上面代碼,并且執(zhí)行:
$ gcc stack-overflow.c -fno-stack-protector -o stack-overflow
$ ./stack-overflow
func_call called...
inject_callback called...

在編譯上面程序時(shí),一定要加上 -fno-stack-protector 參數(shù),否則將會(huì)觸發(fā)棧溢出保護(hù),導(dǎo)致執(zhí)行失敗。

  • 在上面的代碼中,我們并沒(méi)有直接調(diào)用 inject_callback() 函數(shù),而是通過(guò)把 inject_callback() 函數(shù)的地址復(fù)制到 func_call() 函數(shù)的局部變量 tmpBuf 中。
  • 由于局部變量 tmpBuf 的類(lèi)型為字符串?dāng)?shù)組,而且大小為 16 個(gè)字節(jié)。但我們復(fù)制數(shù)據(jù)是從 24(16 + 8)處開(kāi)始復(fù)制,已經(jīng)超出了局部變量 tmpBuf 的大小,如下圖所示:

 

如何讀懂棧溢出攻擊,從這五點(diǎn)入手

 

?

 

  • 從上圖可以看出,func_call() 函數(shù)在調(diào)用 memcpy() 函數(shù)復(fù)制數(shù)據(jù)時(shí),由于不小心用 inject_callback() 函數(shù)的地址覆蓋了返回地址,導(dǎo)致 func_call() 函數(shù)執(zhí)行完畢后,跳轉(zhuǎn)到 inject_callback() 函數(shù)處執(zhí)行。
  • 這就是 棧溢出攻擊 的原理,而導(dǎo)致 棧溢出攻擊 的原因就是:調(diào)用 memcpy()、strcpy() 等函數(shù)復(fù)制數(shù)據(jù)時(shí),沒(méi)有對(duì)數(shù)據(jù)的長(zhǎng)度進(jìn)行驗(yàn)證,從而 返回地址 被復(fù)制的數(shù)據(jù)覆蓋了。
  • 黑客可以利用 棧溢出攻擊 來(lái)把函數(shù)的返回地址修改成入侵代碼的地址,從而實(shí)現(xiàn)攻擊的目的。

分享到:
標(biāo)簽:溢出
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定