1. 程式人生 > >如何特意製造棧緩衝區溢位?(x86 & ARM)

如何特意製造棧緩衝區溢位?(x86 & ARM)

看了之後,覺得還是蠻有趣的,貌似老早以前的蠕蟲病毒就是利用C語言的邊界檢查的的缺點(現在也存在啊,C語言的哲學思想:相信程式設計師),使客戶端的棧緩衝區溢位,使客戶的子程式返回時,返回到自己的病毒或者木馬程式上去,而不是原來進去的地方。這在MCU程式設計裡面叫“程式跑飛了”。 不過貌似蠻難發生的,那就構造一個,看看這發生的過程。這樣以後如果發生了,心裡有個譜。 我們知道,在作業系統中,每個執行緒都有自己的stack,當然,對RTOS來說,那就是任務。這裡為了方便說明,僅僅以裸機程式,即相當於一個執行緒,整個程式只有一個stack。 先看看MSVC下面的過程:  visual studio 2008
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>

static void my_func(void)
{
    printf("stack overflow success!");
}

void fun(int a, int b, int c) {
    char buffer1[5];
    int *ret;

    ret = ( int* )(buffer1 + 20);   //找到返回地址ret(ret is short for return)
    *ret = (int)my_func;            //跳過x = 1指令,即使ret返回的是我指定的地址程式
}

void main() {
    int x;

    x = 0;
    fun(1,2,3);
    x = 1;
    printf("%d\n",x);

    getchar();
}
這裡的結果就是在fun函式退出的時候,不返回到“x=1”這條指令,而是去執行我指定的程式my_func。
並且因為沒有進入此函式的入棧儲存,故返回時發生如下錯誤。
先看看從主函式到進入子函式的過程中,哪些東西需要儲存到stack中,以便子函式結束之後可以回到原來的軌道上來。直接上彙編程式碼看看就曉得了。 Step 1.
 再進入子函式之前,先把函式的引數從後至前push到stack中
此時,stack如下所示:              1     2     3
<------ [    ][    ][    ]
堆疊頂部           堆疊底部 Step 2. 然後去呼叫函式符號“_fun”,也就是41128Fh這個地址,這個地址上面是一條jmp指令,走你--->
此時,執行跳轉指令jmp時,ret地址自動push到stack中,此時,stack如下所示:             0x00416510 1     2     3
<------ [                ][    ][    ][    ]

堆疊頂部                堆疊底部 Step 3. 再將子函式的區域性變數push到stack中(因為編譯器就是那麼做的),那麼我們通過記憶體看看buffer1存在哪?
我們現在知道的是,返回的地址ret在buffer1之前被放入stack中的,但是具體多少呢?看程式碼應該可以看出來,這裡可以通過直接看記憶體也可以。先看看buffer1在記憶體的哪個位置。buffer1的地址是0x0012FE6C,並且在其附近找到了1,2,3,以及0x00416510 ,這裡就是stack的區域沒錯了。
此時的stack如下所示:              others buffer1  others  0x00416510 1     2     3
<------ [          ][           ][            ][                ][    ][    ][    ]
堆疊頂部                                         ret地址                    堆疊底部 Step 4. 數數吧,差多少,20個!然後就是改變這個地址中的值。也就是下面那兩條語句了。
本文木有講清楚的地方:對x86的彙編不熟悉,step3的那一大段不瞭解(下面看看ARM的,這個還稍微瞭解點,而且二者的區別還是蠻大的);還有,為啥叫做棧溢位呢?感覺有點名不副實啊,這裡只是通過指標干擾了原來應該是編譯器該乾的事而已。 再看看ARM MDK的過程有和不同: 程式碼與MSVC的類似,只需要將20改成12即可。 Step 1. 看看進入子函式之前做些什麼?
ARM是直接將子函式的引數儲存在暫存器r0,r1,r2中。 Step 2.而後,在進入子函式之後,再將暫存器r2,r3,r4和lr push到stack中。這裡的r2-r4將會用來儲存區域性變數在操作過程中的使用。
比較下push {r2-r4,lr}(先壓lr)之後,stack中發生的變化,其中0x20000730是buffer1的地址。
找到返回地址類似的值“0x080003E5”,而返回地址是“ 0x080003E4”,稍微有點不同,咋會多1了呢?原因可能如下: “儘管PC的LSB總是0(因為程式碼至少是字對齊的),LR的LSB確實可讀可寫的。這是歷史遺留的產物。在以前,由位0來指示ARM/Thumb狀態。因為其他有些ARM處理器支援ARM和Thumb狀態並存,為了方便彙編程式移植,CM3需要允許LSB可讀可寫“——《P28》 ”然而,在分支時,無論是直接寫PC的值還是使用分支指令,都必須保證載入到PC的數值是奇數(即LSB=1),用以表明這是在Thumb狀態下執行。“——《P29》 看看此時暫存器的值,也是如此。
當然,PC值還是字對齊的,只是在PC進入分支的時候,LSB為1且儲存在LR中,等待出來的時候返回,並且後面的實驗表明,返回的PC也同樣是字對齊的。 總結:對於如何管理函式呼叫時,出棧入棧的引數以及返回地址,沒有統一的規定,交給編譯器廠商來實現,而且實現方式也大不相同,比如ARM具有較多的通用暫存器(14個),所以ARM的預設函式呼叫的前4個引數由硬體幫你入棧,可以減少函式呼叫時的時間。但是我們可以通過看內容的內容和彙編程式碼可以哨探各個不同編譯器的實現方式,我就看看麼,O(∩_∩)O~~~ 最後還有一個現象,就是在vs2008下回不來,回來的時候就會報錯,ARM下可以回來,那是因為ARM的自動入棧保護,它會由硬體來自動保護R0-R3,LR,PSR這些個暫存器,所以可以返回。 參考資料: 《ARM Cortex-M3權威指南》

相關推薦

如何特意製造緩衝區溢位x86 & ARM

看了之後,覺得還是蠻有趣的,貌似老早以前的蠕蟲病毒就是利用C語言的邊界檢查的的缺點(現在也存在啊,C語言的哲學思想:相信程式設計師),使客戶端的棧緩衝區溢位,使客戶的子程式返回時,返回到自己的病毒或者木馬程式上去,而不是原來進去的地方。這在MCU程式設計裡面叫“程式跑飛了”

緩衝區溢位buffer overflow避免方法

什麼是緩衝區溢位? copy資料進buffer時,資料長度超過buffer中的剩餘空間。 緩衝區溢位的危害? 緩衝區溢位,結果隨機,可能會導致程式功能不正常,也可能導致程式崩潰。如果受到影響的是其它功能,因為故障現象隨機,所以問題通常很難定位。別有用心的攻擊者還會利用緩衝區溢位缺陷,覆蓋

e2fsprogs 編譯安裝x86/ARM[2013-10-21更新]

# tar -zxvf e2fsprogs-1.41.14.tar.gz # cd e2fsprogs-1.41.14 # ./configure --prefix=$PWD/build # make # make install --------------------------------------

緩衝區溢位溢位

前言 在現在的網路攻擊中,緩衝區溢位方式的攻擊佔據了很大一部分,緩衝區溢位是一種非常普遍的漏洞,但同時,它也是非常危險的一種漏洞,輕則導致系統宕機,重則可導致攻擊者獲取系統許可權,進而盜取資料,為所欲為。 其實緩衝區攻擊說來也簡單,請看下面一段程式碼: void main(int argc, char *ar

簡單嘗試利用維控LeviStudioU的一緩衝區溢位漏洞

  這是別人給我發的,讓我分析一下,看能否寫出exp。只怪自己水平不夠,最後沒能寫出exp,以下為自己的分析思路   環境為win10 pro x64 英文版(10.0.16299) 預設安全配置 一、漏洞分析   此漏洞是由於LeviStudioU在處理.G_Picture.xml檔案的szFilenam

爬蟲問題一:溢位stack overflow問題解決方案

在爬取某個網頁的時候遇到了這個問題: Fatal Python error: Cannot recover from stack overflow 我問題所在:使用函式時遞迴呼叫次數過多(800左右會出現),導致棧溢位。 在Python中,函式呼叫是通過棧(s

緩衝區溢位

快取區溢位 1:概要 緩衝區:使用者提交的資料放在緩衝區裡(記憶體的片段),程式對緩衝區的大小沒有進行限制,可能造成使用者提交的資料溢位預設的大小以外,覆蓋了相鄰記憶體的資料,造成程序劫持,執行惡意程式碼,獲取伺服器控制權等後果。 2:漏洞原理

數制轉換-的應用C++實現

技術分享 ont while namespace 不同 hit enter rac content 本程序實現的是十進制與不同進制之間的的數據轉換,利用的數據結構是棧,基本數學方法輾轉相除法。 conversion.h #include<stack>

習題3.9 堆操作合法性20 分浙大版《數據結構第2版》題目集

ram xxxxx text -html base logs main 格式 using 假設以S和X分別表示入棧和出棧操作。如果根據一個僅由S和X構成的序列,對一個空堆棧進行操作,相應操作均可行(如沒有出現刪除時棧空)且最後狀態也是棧空,則稱該序列是合法的堆棧操作

的運用 Valid Parentheses

valid empty turn bool == sed null ont ets Given a string containing just the characters ‘(‘, ‘)‘, ‘{‘, ‘}‘, ‘[‘ and ‘]‘, determine if the

leetcode_的應用括號匹配

20. 有效的括號 給定一個只包括 '(',')','{','}','[',']' 的字串,判斷字串是否有效。 有效字串需滿足: 左括號必須用相同型別的右括號閉合。 左括號必須以正確的順序閉合。 注意空字串可被認為

堆和的區別非常經典

 一、預備知識—程式的記憶體分配    一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分    1、棧區(stack)—   由編譯器自動分配釋放   ,存放函式的引數值,區域性變數的值等。其&

的實現基於陣列

(基於已寫好的動態陣列實現的棧) 首先寫一個介面 public interface Stack<E> { int getSize(); boolean isEmpty(); boolean contains(E e); //入棧 void push(

【資料結構】鏈式的實現C語言

棧的鏈式儲存稱為鏈式棧,鏈式棧是一種特殊的單鏈表,它的插入和刪除規定在單鏈表的同一端進行。鏈式棧的棧頂指標一般用top表示。(個人理解:相當於只對單鏈表的第一個結點進行操作) 鏈式棧要掌握以下基本操作: 1、建立一個空鏈式棧 2、判斷鏈式棧是否為空 3、讀鏈式棧的

KeilMDK-ARM系列教程_工程目標選項配置

Ⅰ、寫在前面 Options for Target目標選項其完全意思是工程目標選項配置,或許有些人分不清工作空間、工程、目標這三個名詞的意思和區別,下面章節將簡述工作空間、工程、目標他們的意思。 由於目標配置的選項比較多,我將其分為工程目標選項配置(Ⅰ)和工程目標

gcc 預定義巨集x86-64

列印方法: touch test.c gcc -E -dM -c test.c >> marco_x86_64.txt #define __DBL_MIN_EXP__ (-1021) #define __UINT_LEAST16_MAX__ 65535 #def

堆和的區別非常詳細

堆和棧的區別(轉載) 1.全域性區(靜態區),全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域,未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。-程式結束釋放   2.另外還有一個專門放常量的地方。-程式結束釋放   3.程式程式碼區,存放2進位制程式碼。   在函

堆和的區別經典轉載

 一、預備知識—程式的記憶體分配   一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分   1、棧區(stack)—   由編譯器自動分配釋放   ,存放函式的引數值,區域性變數的值等。其   操作方式類似於資料結構中的棧。   2、堆區(heap)   —   一般由程式設計師分配釋放,   若程式設

堆和的區別經典乾貨

一、預備知識—程式的記憶體分配    一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分    1、棧區(stack)—   由編譯器自動分配釋放   ,存放函式的引數值,區域性變數的值等。其    操作方式類似於資料結構中的棧。    2、堆區(heap)   —  

堆與的區別經典講解

摘要:對於堆和棧,很多朋友都是不怎麼理解的,就算是開發了程式多年的朋友都會容易混淆。其實要區分它們並不難,但是怎樣使自己永久不會忘記哪得有技巧了。我相信,通過下面經典的講解,您一定不會再忘記堆和棧的區別了。 對於堆和棧,很多朋友都是不怎麼理解的,就算是開發了程式多年的朋友都會容易混淆。其實要區分它們並不難,