先歡迎各位來到整體難度最高的領域
在聽這裡之前
前面的逆向組語要先學好
還有對 OS 以及計算機概論要有一些認識會比較好
Pwn 念作胖或碰 (?
是一個駭客界用語
意思為完全擊敗對方、完全佔領對方擁有的東西這樣的意思
那在這個類型中的行為也差不多
最後的目的就是奪得對方主機的控制權
而 pwn 的攻擊方式
通常是針對程式中
對於記憶體使用或是分配不當所產生的漏洞
該漏洞可以讓攻擊者利用精心設計的一串字串
使得本應該產生程式崩潰的行為
卻變成了可任意對系統執行指令
在打 pwn 之前
我們需要先對記憶體有些認識
當一支程式被運行起來時
作業系統會分配一塊記憶體供其使用
而這塊記憶體有著一個固定的結構
這邊注意一下
越往下記憶體位置越多
往上越少
+------------------+ 0x00000000
| |
+------------------+
| |
| Text | >存放程式指令碼
| |
+------------------+
| |
| Data | >存放已經初始化過的變數
| |
+------------------+
| |
| BSS | >存放未初始化的變數 or 全域變數 or const 變數
| |
+------------------+
| |
| Heap | >當使用到 malloc 時會使用到
| | heap 會往 stack 的方向長
+------------------+
| . |
| . |
| . |
+------------------+
| |
| Stack | >所有區域變數、參數都會存在於此,也包括函數返回地址
| | stack 會往 heap 方向長
+------------------+
| |
| OS Kernal | >作業系統的區域
| |
+------------------+ 0x80000000
就拿昨天逆向那題 CTF 當例子吧~
昨天本來直接說從第四行開始
那前面兩行語法是在做什麼的呢
其實在組語中
對於函數有兩個重要的區塊
push ebp
mov ebp, esp
在每次進入一個 function 之前
都會先對 stack 重新劃區
上面有說到 stack 是用來放區域變數或是一些參數的地方
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+ < 前一個 func 的 ESP
| ret |
+---------+
從上限可以看到前一個 func 的 ESP 以下都是前一個 function 的範圍
我們現在要新進到一個 function 了
所以先將舊的 EBP push 到 stack 中存起來變成
push 完成後 ESP 會往上一格
這邊還有一件事就是那個 ret
在我們 call 這個 func 時
其實會先將當時下一行指令的位置先 push 進來
以便之後該函數執行完成時
要跳回原本的位置繼續執行
這個 ret 位置非常重要是 stack pwn 的精隨
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+ < 前一個 func 的 ESP
| 舊EBP |
+---------+
| ret |
+---------+
接著將 esp 給到 ebp
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+ < EBP, ESP
| 舊EBP |
+---------+
| ret |
+---------+
從這裡可以看到 ESP 跟 EBP 都在同一個位置上了
EBP 以上就代表著新的 function 的區塊
所以對新的 function 來說ret
跟 舊 EBP
永遠都會在 EBP
的前兩個位置上
接著來看到程式碼最後兩行
pop ebp
ret
到了這邊
一個函數的執行也到了尾聲
因此在離開前
要先清理自己剛剛的 stack frame
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+ < EBP, ESP
| 舊EBP |
+---------+
| ret |
+---------+
會先將舊的EBP pop 回去原本的 EBP 中
讓程式知道自己現在的 stack frame 回到了原本那個函數中
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+
| |
+---------+ < ESP
| ret |
+---------+
接著上面有說到這個 stack 中 ret
的功能
也會透過 ret
這個指令將這個值放回 EIP
讓程式知道接下來該去哪裡執行