昨天水過了,今天大概或許會認真一點。事實證明沒有,我很抱歉。
今天會接續著 Day6 大雜燴之紅燒雜燴 - Pwn:Basic(4)的那題講解一下是怎麼做的。
附上題目的程式碼:
#include <stdio.h>
#include <stdlib.h>
void game() {
long val = 0xdeadbeef;
char buf[4] = "abcd";
printf("Input: ");
scanf("%4s",&buf);
printf("buf: %s\n",buf);
printf("val: 0x%08x\n",val);
if(val == 0xdeadbe00) {
printf("GOOD!!\n");
} else {
printf("BAD!!\n");
}
}
int main() {
game();
return 0;
}
我們使用 s 進入 game
可以看到在 game+18 以及 game+25 的位置,分別在 ebp-0xc 以及 ebp-0x10 的位置設置了兩個變數。
對應到高階語言就會是:
long val = 0xdeadbeef;
char buf[4] = "abcd";
大家可能會疑惑為什麼我是設 abcd(0x61626364),但實際上卻是將 dcba(0x64636261)放入到 ebp-0x10 呢?
這是因為在不同架構下位元組的儲存順序不同,以本次實驗為例,我們使用的是 Little-Endian,意思是它會把最高位的位元組放在最高的記憶體位址上,下面會有具體的例子。
low address
===========================================
ebp-0x10 (0xffffd0e8) |_____0x61_____|
(0xffffd0e9) |_____0x62_____|
(0xffffd0ea) |_____0x63_____|
(0xffffd0eb) |_____0x64_____|
ebp-0xc (0xffffd0ec) |_____0xef_____|
(0xffffd0ed) |_____0xbe_____|
(0xffffd0ee) |_____0xad_____|
(0xffffd0ef) |_____0xde_____|
: | : |
ebp (0xffffd0f8) |______________|
===========================================
high address
還記得之前說過一個函數的 stack frame 會由 ebp 當底,所以記憶體內容會長上面這樣,可以看到 dcba(0x64636261)最高位元 d(0x64)放在記憶體位置最高位,這就是 Little-Endian。
將 abcd 以 dcba 順序存到記憶體的結果就是我們很直觀的看到我們 abcd 是由低到高這樣排下去。
從 gdb 觀察記憶體,也同樣會長這樣
補充:要看到記憶體存甚麼,可以在 gdb 下 x 指令,具體怎麼用可以用 help x 查看。
我們繼續往下看
printf("Input: ");
scanf("%4s",&buf);
由於我們是設 %4s,因此最多會讀取 4 個 bytes 進來,而因為是字串(s)因此會在尾端補上 '\0' (null 字元)。
那會造成什麼問題呢?還記得我們是怎麼宣告的嗎?
char buf[4] = "abcd";
我們當初只設了 4 個 bytes 的大小,而我們若輸入 aaaa 會在尾端被多加一個 byte,超出我們原先設置的 buffer 大小,會往下覆蓋下去,因此就會變這樣:
low address
===========================================
ebp-0x10 (0xffffd0e8) |_____0x61_____|
(0xffffd0e9) |_____0x61_____|
(0xffffd0ea) |_____0x61_____|
(0xffffd0eb) |_____0x61_____|
ebp-0xc (0xffffd0ec) |_____0x00_____| <=
(0xffffd0ed) |_____0xbe_____|
(0xffffd0ee) |_____0xad_____|
(0xffffd0ef) |_____0xde_____|
: | : |
ebp (0xffffd0f8) |______________|
===========================================
high address
因此我們就能成功印出 GOOD!
反正就是清蒸的雜燴,應該不需要圖了吧。