這是CSAPP
官網上的著名實驗,二進制炸彈,
經過gdb
和反彙編
猜想程序意圖,共有6關和一個隱藏關卡,
只有輸入正確的字符串才能過關,不然會程序會bomb
終止運行,
隱藏關卡須要輸入特定字符串方會開啓git
實驗材料可到個人github
倉庫 https://github.com/Cheukyin/C... 下載github
反彙編:objdump -d bomb > bomb_assembly_32.S
app
打開bomb_assembly_32.S
,定位到·<phase_1>
函數,能夠看到如下代碼:函數
8048b26: 8b 45 08 mov 0x8(%ebp),%eax 8048b29: 83 c4 f8 add $0xfffffff8,%esp 8048b2c: 68 c0 97 04 08 push $0x80497c0 8048b31: 50 push %eax 8048b32: e8 f9 04 00 00 call 8049030 <strings_not_equal> 8048b37: 83 c4 10 add $0x10,%esp 8048b3a: 85 c0 test %eax,%eax 8048b3c: 74 05 je 8048b43 <phase_1+0x23> 8048b3e: e8 b9 09 00 00 call 80494fc <explode_bomb>
能夠看出,用戶輸入字串指針保存在0x8(%ebp)
, <phase_1>
此指針放入eax
,
而後把$0x80497c0
壓棧,再把eax
也就是用戶字串指針壓棧,
而後調用<strings_not_equal>
待<strings_not_equal>
返回後,測試返回值,
若equal
則進入下一phase
,不然<explode_bomb>
oop
從<strings_not_equal>
可知該函數用於比較兩函數的值,所以須要兩個字串做爲輸入,
上面代碼中,push %eax
用於傳遞用戶字串指針,
則push $0x80497c0
天然是傳遞比較字串的指針了.測試
打開gdb,x/s 0x80497c0
, 能夠直接查看到該指針指向的子符串:Public speaking is very easy.
this
打開bomb_assembly_32.S
,定位到<phase_2>
函數,留意如下幾行:3d
8048b50: 8b 55 08 mov 0x8(%ebp),%edx 8048b53: 83 c4 f8 add $0xfffffff8,%esp 8048b56: 8d 45 e8 lea -0x18(%ebp),%eax 8048b59: 50 push %eax 8048b5a: 52 push %edx 8048b5b: e8 78 04 00 00 call 8048fd8 <read_six_numbers>
mov 0x8(%ebp),%edx
將用戶字串指針存入edx
,指針
lea -0x18(%ebp),%eax
把ebp-0x18這個地址存入eax
,code
則最後三句
push %eax push %edx call 8048fd8 <read_six_numbers>
至關於read_six_numbers
( 用戶字串指針地址, ebp-0x18
)
如今咱們切換到<read_six_numbers>
,看看這個函數是幹什麼的:
先來看下面2行:
8048fde: 8b 4d 08 mov 0x8(%ebp),%ecx 8048fe1: 8b 55 0c mov 0xc(%ebp),%edx
把用戶字串指針存入ecx
, ebp-0x18
存入edx
往下看:
8048fe4: 8d 42 14 lea 0x14(%edx),%eax
eax
存入了 edx+0x14
這個值
再往下:
8048fe7: 50 push %eax 8048fe8: 8d 42 10 lea 0x10(%edx),%eax 8048feb: 50 push %eax 8048fec: 8d 42 0c lea 0xc(%edx),%eax 8048fef: 50 push %eax 8048ff0: 8d 42 08 lea 0x8(%edx),%eax 8048ff3: 50 push %eax 8048ff4: 8d 42 04 lea 0x4(%edx),%eax 8048ff7: 50 push %eax 8048ff8: 52 push %edx
上面幾行依次把 edx+0x14
, edx+0x10
, edx+0xc
, edx+0x8
, edx+4
, edx
這6
個地址值壓棧
注意edx
是<phase_2>
的stack frame
的 ebp-0x18
這個地址值
8048ff9: 68 1b 9b 04 08 push $0x8049b1b 8048ffe: 51 push %ecx 8048fff: e8 5c f8 ff ff call 8048860 <sscanf@plt>
前2行把 $0x8049b1b
和 ecx
(用戶字串指針) 壓棧, 而後調用sscanf
sscanf
的原型是int sscanf(const char *str, const char *format, ...);
按format
的格式解釋str
,而後把獲得的值放入後面省略號所表明的變量中
所以, 按剛纔壓棧的順序, str
是用戶輸入字串, $0x8049b1b
是format
的地址,edx
, edx+4
, ...
, edx+0x14
是對應的變量.
先用gdb
查看format
, x/s $0x8049b1b, "%d %d %d %d %d %d"
.
可知,須要從用戶字串中提取6
個整數,存入(edx)--(edx+0x14)
中.
綜上, <read_six_numbers>
做用就是從用戶字串中提取6
個數字, 存入<phase_2>
stack frame
中的(ebp-0x18)
中
回到<phase_2>
接着看:
8048b63: 83 7d e8 01 cmpl $0x1,-0x18(%ebp) 8048b67: 74 05 je 8048b6e <phase_2+0x26> 8048b69: e8 8e 09 00 00 call 80494fc <explode_bomb>
測試(ebp-0x18)
是否等於1
, 不等則bomb
, 所以用戶輸入的第一個數字應爲1
.
8048b6e: bb 01 00 00 00 mov $0x1,%ebx 8048b73: 8d 75 e8 lea -0x18(%ebp),%esi
令ebx=1
, esi = ebp-18
8048b76: 8d 43 01 lea 0x1(%ebx),%eax 8048b79: 0f af 44 9e fc imul -0x4(%esi,%ebx,4),%eax 8048b7e: 39 04 9e cmp %eax,(%esi,%ebx,4) 8048b81: 74 05 je 8048b88 <phase_2+0x40> 8048b83: e8 74 09 00 00 call 80494fc <explode_bomb> 8048b88: 43 inc %ebx 8048b89: 83 fb 05 cmp $0x5,%ebx 8048b8c: 7e e8 jle 8048b76 <phase_2+0x2e>
注意, esi
是存放6
個數字中第1
數字的地址,
所以 -0x4(%esi,%ebx,4)
表示第ebx
個數字,(%esi,ebx,4)
表示第ebx+1
個數字
所以上面第3-6
行代碼檢查 a[ebx]*(ebx+1) == a[ebx+1]
, 其中a[n]
表示第n
個數字
若不等則bomb
,不然ebx
增1
並循環
所以<phase_2>
須要輸入一個數列, a[1]=1
, a[n+1] = a[n]*(n+1)
, n<=6
1, 2, 6, 24, 120, 720
打開bomb_assembly_32.S
,定位到<phase_3>
函數,能夠看到如下代碼:
;; edx stores pointer of user input 8048b9f: 8b 55 08 mov 0x8(%ebp),%edx 8048ba2: 83 c4 f4 add $0xfffffff4,%esp ;; push ebp-4 onto stack 8048ba5: 8d 45 fc lea -0x4(%ebp),%eax 8048ba8: 50 push %eax ;; push ebp-5 onto stack 8048ba9: 8d 45 fb lea -0x5(%ebp),%eax 8048bac: 50 push %eax ;; push ebp-12 onto stack 8048bad: 8d 45 f4 lea -0xc(%ebp),%eax 8048bb0: 50 push %eax ;; push $0x80497de onto stack ;; gdb x/s 0x80497de: "%d %c %d" 8048bb1: 68 de 97 04 08 push $0x80497de ;; push pointer of user input onto stack 8048bb6: 52 push %edx 8048bb7: e8 a4 fc ff ff call 8048860 <sscanf@plt>
具體代碼請看註釋,一開始主要是sscanf(用戶字串指針, "%d %c %d", ebp-12, ebp-5, ebp-4)
繼續看下去:
;; (ebp-12) stores the first int, compare to 7 ;; cmpl takes (ebp-12) as unsigned int 8048bc9: 83 7d f4 07 cmpl $0x7,-0xc(%ebp) ;; (unsigned)(ebp-12) > 7, jump to 0x8048c88, which will bomb 8048bcd: 0f 87 b5 00 00 00 ja 8048c88 <phase_3+0xf0> ;; jump to *( 0x80497e8 + 4*(the first int) ) 8048bd3: 8b 45 f4 mov -0xc(%ebp),%eax 8048bd6: ff 24 85 e8 97 04 08 jmp *0x80497e8(,%eax,4)
關鍵在於最後的跳轉,根據輸入的第一個整數肯定跳轉地址,
地址存儲在(0x80497e8 + 4*(the first int))
.
容易聯想到(0x80497e8)
存儲着一個跳轉表
,用gdb
查看之,x/10wx 0x80497e8
:
0x80497e8: 0x08048be0 0x08048c00 0x08048c16 0x08048c28 0x80497f8: 0x08048c40 0x08048c52 0x08048c64 0x08048c76 0x8049808: 0x67006425 0x746e6169
能夠看到表中有不少個地址,先來看第一個地址指向的語句(對應的輸入整數爲0
):
;; bl = 0x71 8048be0: b3 71 mov $0x71,%bl ;; if 0x309==777==the last int, ;; jump to 0x8048c8f, which will compare the char 8048be2: 81 7d fc 09 03 00 00 cmpl $0x309,-0x4(%ebp) 8048be9: 0f 84 a0 00 00 00 je 8048c8f <phase_3+0xf7> 8048bef: e8 08 09 00 00 call 80494fc <explode_bomb>
能夠看出,先把0x71
存入bl
,
而後若輸入的最後一個整數==777
的話,則跳轉到0x8048c8f
;; after compare the last int, jump here ;; bl = 0x71 = 'q', compare to the char ;; if ==, jump to 0x8048c99, and leave this function 8048c8f: 3a 5d fb cmp -0x5(%ebp),%bl 8048c92: 74 05 je 8048c99 <phase_3+0x101> 8048c94: e8 63 08 00 00 call 80494fc <explode_bomb>
比較輸入的字符是否等於'q'
,若等於則defuse
成功
所以,輸入應爲: "0 q 777"
固然此題應該有不止一個答案,選擇跳轉表中不一樣的地址會致使不一樣的輸入.
打開bomb_assembly_32.S
,定位到<phase_4>
函數,能夠看到如下代碼:
;; edx = pointer of input string 8048ce6: 8b 55 08 mov 0x8(%ebp),%edx 8048ce9: 83 c4 fc add $0xfffffffc,%esp ;; eax = ebp-4 8048cec: 8d 45 fc lea -0x4(%ebp),%eax ;; push ebp-4 8048cef: 50 push %eax ;; push $0x8049808 ;; x/s 0x804980: "%d" 8048cf0: 68 08 98 04 08 push $0x8049808 ;; push pointer of input string 8048cf5: 52 push %edx 8048cf6: e8 65 fb ff ff call 8048860 <sscanf@plt>
就是讀入一個整數,存入ebp-4
;; func4( input_number ) 8048d11: 8b 45 fc mov -0x4(%ebp),%eax 8048d14: 50 push %eax 8048d15: e8 86 ff ff ff call 8048ca0 <func4> 8048d1a: 83 c4 10 add $0x10,%esp ;; eax should contain the return value of <func4> ;; if eax == 0x37 == 55, defused 8048d1d: 83 f8 37 cmp $0x37,%eax 8048d20: 74 05 je 8048d27 <phase_4+0x47> 8048d22: e8 d5 07 00 00 call 80494fc <explode_bomb>
而後比較func4( input_number )==55
, 若等於則成功defuse
.
接下來看看<func4>
:
;; ebx = input_number 8048ca8: 8b 5d 08 mov 0x8(%ebp),%ebx ;; if input_number<=1, <func4> return 1 8048cab: 83 fb 01 cmp $0x1,%ebx 8048cae: 7e 20 jle 8048cd0 <func4+0x30> 8048cb0: 83 c4 f4 add $0xfffffff4,%esp ;; esi == func4( input_number-1 ) 8048cb3: 8d 43 ff lea -0x1(%ebx),%eax 8048cb6: 50 push %eax 8048cb7: e8 e4 ff ff ff call 8048ca0 <func4> 8048cbc: 89 c6 mov %eax,%esi 8048cbe: 83 c4 f4 add $0xfffffff4,%esp ;; esi += func4( input_number-2 ) 8048cc1: 8d 43 fe lea -0x2(%ebx),%eax 8048cc4: 50 push %eax 8048cc5: e8 d6 ff ff ff call 8048ca0 <func4> 8048cca: 01 f0 add %esi,%eax
很明顯是Fibonacci
數列, func4(n) = func4(n-1) + func4(n-2)
注意f(0)=f(1)=1
, 經過簡單計算知f(9)=55
所以輸入應爲55
打開bomb_assembly_32.S
,定位到<phase_5>
函數,能夠看到如下代碼:
;; ebx = pointer of input ;; push ebx onto stack ;; call string_length 8048d34: 8b 5d 08 mov 0x8(%ebp),%ebx 8048d37: 83 c4 f4 add $0xfffffff4,%esp 8048d3a: 53 push %ebx 8048d3b: e8 d8 02 00 00 call 8049018 <string_length> 8048d40: 83 c4 10 add $0x10,%esp ;; eax stores the return value of string_length ;; if eax == 6, jump to 0x8048d4d 8048d43: 83 f8 06 cmp $0x6,%eax 8048d46: 74 05 je 8048d4d <phase_5+0x21> 8048d48: e8 af 07 00 00 call 80494fc <explode_bomb>
從上面代碼可知,輸入須要6
個字符.
;; edx = 0 8048d4d: 31 d2 xor %edx,%edx ;; ecx = ebp-8 8048d4f: 8d 4d f8 lea -0x8(%ebp),%ecx ;; esi = 0x804b220 8048d52: be 20 b2 04 08 mov $0x804b220,%esi ;; edx is a counter from 0 to 5 ;; al = (edx + ebx), then al reads a char each time 8048d57: 8a 04 1a mov (%edx,%ebx,1),%al ;; extract the low 4 bit of al 8048d5a: 24 0f and $0xf,%al ;; sign-extend al to eax 8048d5c: 0f be c0 movsbl %al,%eax ;; al = ( eax + 0x804b220 ) ;; x/16c 0x804b220: ;; 0x804b220: 105 'i' 115 's' 114 'r' 118 'v' 101 'e' 97 'a' 119 'w' 104 'h' ;; 0x804b228: 111 'o' 98 'b' 112 'p' 110 'n' 117 'u' 116 't' 102 'f' 103 'g' 8048d5f: 8a 04 30 mov (%eax,%esi,1),%al ;; edx + ecx = al, ;; notice that, ecx = ebp-8 ;; and edx is a counter from 0 to 5 8048d62: 88 04 0a mov %al,(%edx,%ecx,1) 8048d65: 42 inc %edx ;; loop 8048d66: 83 fa 05 cmp $0x5,%edx 8048d69: 7e ec jle 8048d57 <phase_5+0x2b> ;; ebp-2 = 0, a terminal of string started from ebp-8 8048d6b: c6 45 fe 00 movb $0x0,-0x2(%ebp) 8048d6f: 83 c4 f8 add $0xfffffff8,%esp
上面代碼的做用是循環讀取6個輸入字符中的每一字符input[k]
,
提取input[k]
的低四位,把這四位構成的整數index
看成索引,
查找0x804b220
開始16
個字節中存儲的字符.
用gdb
查看, x/16c 0x804b220
:
0x804b220: 105 'i' 115 's' 114 'r' 118 'v' 101 'e' 97 'a' 119 'w' 104 'h' 0x804b228: 111 'o' 98 'b' 112 'p' 110 'n' 117 'u' 116 't' 102 'f' 103 'g'
獲取0x804b220[ input[k] & 0xf ]
後,將之copy
至 (ebp-8)[k]
繼續看:
;; x/s 0x804980b: "giants" ;; push "giants" 8048d72: 68 0b 98 04 08 push $0x804980b ;; push ebp-8 8048d77: 8d 45 f8 lea -0x8(%ebp),%eax 8048d7a: 50 push %eax ;; compare "giants" and the string started from ebp-8 8048d7b: e8 b0 02 00 00 call 8049030 <strings_not_equal> 8048d80: 83 c4 10 add $0x10,%esp 8048d83: 85 c0 test %eax,%eax ;; if two strings equal to each other, defused 8048d85: 74 05 je 8048d8c <phase_5+0x60> 8048d87: e8 70 07 00 00 call 80494fc <explode_bomb>
上面代碼即是將ebp-18
開始的字串和giants
比較,若相等,則defused
.
注意到 (ebp-18)[k] = 0x804b220[ input[k] & 0xf ]
0x804b220: 105 'i' 115 's' 114 'r' 118 'v' 101 'e' 97 'a' 119 'w' 104 'h' 0x804b228: 111 'o' 98 'b' 112 'p' 110 'n' 117 'u' 116 't' 102 'f' 103 'g'
所以,
input[0]&0xf = 0xf, input[1]&0xf = 0x0, input[2]&0xf = 0x5, input[3]&0xf = 0xb, input[4]&0xf = 0xd, input[5]&0xf = 0x1,
只要輸入的各個字符的低四位符合上面就好,我我的選取了opekma
寫得太複雜了,各類內外循環,各類跳轉,看得頭暈,往後有閒再看.
如今先把能看懂的部份寫出來:
;; edx = pointer of input 8048da1: 8b 55 08 mov 0x8(%ebp),%edx ;; (ebp-0x34) = $0x804b26c 8048da4: c7 45 cc 6c b2 04 08 movl $0x804b26c,-0x34(%ebp) 8048dab: 83 c4 f8 add $0xfffffff8,%esp ;; read six numbers from input, ;; and storse in the area started from ebp-18 8048dae: 8d 45 e8 lea -0x18(%ebp),%eax 8048db1: 50 push %eax 8048db2: 52 push %edx 8048db3: e8 20 02 00 00 call 8048fd8 <read_six_numbers>
上面代碼就是從輸入讀入6
個整數,存入ebp-0x18
,
初步懷疑0x804b26c
地址存放着一個鏈表.
;; edi = 0 8048db8: 31 ff xor %edi,%edi 8048dba: 83 c4 10 add $0x10,%esp 8048dbd: 8d 76 00 lea 0x0(%esi),%esi ;; eax = (ebp-0x18 + 4*edi) = six-number[edi] ;; ebp-0x18 = the beginning address of the six numbers ;; edi is a counter from 0 to 5 8048dc0: 8d 45 e8 lea -0x18(%ebp),%eax 8048dc3: 8b 04 b8 mov (%eax,%edi,4),%eax ;; eax = six-number[edi]-1 8048dc6: 48 dec %eax ;; if eax <= 5 , continue 8048dc7: 83 f8 05 cmp $0x5,%eax 8048dca: 76 05 jbe 8048dd1 <phase_6+0x39> 8048dcc: e8 2b 07 00 00 call 80494fc <explode_bomb> ;; if edi+1 > 5, finish edi loop 8048dd1: 8d 5f 01 lea 0x1(%edi),%ebx 8048dd4: 83 fb 05 cmp $0x5,%ebx 8048dd7: 7f 23 jg 8048dfc <phase_6+0x64> ;; (ebp-0x38) = edi*4 8048dd9: 8d 04 bd 00 00 00 00 lea 0x0(,%edi,4),%eax 8048de0: 89 45 c8 mov %eax,-0x38(%ebp) ;; esi = ebp-18 = the beginning address of the six numbers 8048de3: 8d 75 e8 lea -0x18(%ebp),%esi ;; edx = (ebp-0x38) = edi*4 ;; inner loops, ;; ebx is the counter from edi+1 to 5 8048de6: 8b 55 c8 mov -0x38(%ebp),%edx ;; eax = edx + esi = six-number[edi] 8048de9: 8b 04 32 mov (%edx,%esi,1),%eax ;; compare six-number[edi] and six-number[edi+ebx] 8048dec: 3b 04 9e cmp (%esi,%ebx,4),%eax ;; if six-number[edi] != six-number[edi+1], continue 8048def: 75 05 jne 8048df6 <phase_6+0x5e> 8048df1: e8 06 07 00 00 call 80494fc <explode_bomb> ;; ebx++ ;; if ebx<=5, jump to 0x8048de6, ebx loops ;; else , finish ebx loop 8048df6: 43 inc %ebx 8048df7: 83 fb 05 cmp $0x5,%ebx 8048dfa: 7e ea jle 8048de6 <phase_6+0x4e>
內外兩層循環,外層用edi
計數,確保輸入的6
個整數不大於6
,
內層用ebx
計數,保證全部數字兩兩不相等.
再日後的代碼異常混亂,各類鏈表離歷,沒空看....
先從網上得到答案:4 2 6 3 1 5
首先要找到<secret_phase>
的入口,經搜索發現入口是在<phase_defused>
裏面.
先來看看<phase_defused>
:
;; every time call read_line, ( 0x804b480 )++ ;; only with 6 correct answer given ,will the secret phase appear 8049533: 83 3d 80 b4 04 08 06 cmpl $0x6,0x804b480 804953a: 75 63 jne 804959f <phase_defused+0x73>
(0x804b480)
是一個計數器,每當調用一次<read_line>
每自增1
,所以只有6
關全通才能打開隱藏關卡.
;; push ebp-0x50 804953c: 8d 5d b0 lea -0x50(%ebp),%ebx 804953f: 53 push %ebx ;; push ebp-0x54 8049540: 8d 45 ac lea -0x54(%ebp),%eax 8049543: 50 push %eax ;; (gdb) x/s 0x8049d03 ;; 0x8049d03: "%d %s" 8049544: 68 03 9d 04 08 push $0x8049d03 ;; push the string stores in 0x804b770 ;; the address of input of phase 4 8049549: 68 70 b7 04 08 push $0x804b770 804954e: e8 0d f3 ff ff call 8048860 <sscanf@plt> .... ;; (gdb) x/s 0x8049d09 ;; 0x8049d09: "austinpowers" 804955e: 68 09 9d 04 08 push $0x8049d09 ;; push the %s 8049563: 53 push %ebx 8049564: e8 c7 fa ff ff call 8049030 <strings_not_equal>
省略號上方的代碼調用sscanf( (char *)0x804b770, "%d %s", (int *)(ebp-0x54), (char *)ebp-0x50 )
即從0x804b770
讀入一個整數和字串.
再看省略號下方的代碼,比較讀入的字串和austinpowers
, 若相等,則打開<secret_phase>
好了,如今問題是,如何把一個整數和austinpowers
寫入地址0x804b770
?
回想前幾關,寫入字串都是經過read_line
,因此猜測多是在某一關的輸入中多輸入些內容以寫入地址0x804b770
.
用gdb
查看前幾關輸入字串的指針,發現第4關的輸入恰好是在地址0x804b770
,而Phase 4
只需輸入一個數字,所以只需
在第4
關的輸入中多輸入一個austinpowers
便可進入<secret_phase>
.
如今看看<secret_phase>
:
8048eef: e8 08 03 00 00 call 80491fc <read_line> 8048ef4: 6a 00 push $0x0 ;; strtol( user input string, 0, 10) ;; long int strtol(const char *nptr, char **endptr, int base); ;; converts the initial part of the string in nptr to a long integer value according to the given base 8048ef6: 6a 0a push $0xa 8048ef8: 6a 00 push $0x0 8048efa: 50 push %eax 8048efb: e8 f0 f8 ff ff call 80487f0 <__strtol_internal@plt>
首先,讀入一個字串,並用strtol
將之轉換爲long int
.
;; if fun7( 0x804b320, the input long int ) ;; x/d 0x804b320: (0x804b320) = 36 8048f17: 53 push %ebx 8048f18: 68 20 b3 04 08 push $0x804b320 8048f1d: e8 72 ff ff ff call 8048e94 <fun7> 8048f22: 83 c4 10 add $0x10,%esp ;; if fun7(0x804b320, the input long int) == 7, defused 8048f25: 83 f8 07 cmp $0x7,%eax 8048f28: 74 05 je 8048f2f <secret_phase+0x47> 8048f2a: e8 cd 05 00 00 call 80494fc <explode_bomb>
代碼很簡單,調用fun7( (void *)0x804b320, 輸入的整數 )
,若返回值==7
, 則成功defused
.
如今看看<fun7>
:
;; edx = the first parameter, an address 8048e9a: 8b 55 08 mov 0x8(%ebp),%edx ;; eax = the input long int 8048e9d: 8b 45 0c mov 0xc(%ebp),%eax ;; if edx != 0 8048ea0: 85 d2 test %edx,%edx 8048ea2: 75 0c jne 8048eb0 <fun7+0x1c> 8048ea4: b8 ff ff ff ff mov $0xffffffff,%eax 8048ea9: eb 37 jmp 8048ee2 <fun7+0x4e> 8048eab: 90 nop 8048eac: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi ;; if the input number >= (edx), jump to 0x8048ec5 8048eb0: 3b 02 cmp (%edx),%eax 8048eb2: 7d 11 jge 8048ec5 <fun7+0x31> ;; eax > (edx) 8048eb4: 83 c4 f8 add $0xfffffff8,%esp ;; <func7>( (edx+4) ,the input long int ) 8048eb7: 50 push %eax 8048eb8: 8b 42 04 mov 0x4(%edx),%eax 8048ebb: 50 push %eax 8048ebc: e8 d3 ff ff ff call 8048e94 <fun7> ;; return eax *= 2, exit 8048ec1: 01 c0 add %eax,%eax 8048ec3: eb 1d jmp 8048ee2 <fun7+0x4e> ;; the input number >= (edx) ;; if eax == (edx), return eax=0 8048ec5: 3b 02 cmp (%edx),%eax 8048ec7: 74 17 je 8048ee0 <fun7+0x4c> ;; the input number > (edx) 8048ec9: 83 c4 f8 add $0xfffffff8,%esp ;; <fun7>( (edx+8) ,the input long int ) 8048ecc: 50 push %eax 8048ecd: 8b 42 08 mov 0x8(%edx),%eax 8048ed0: 50 push %eax 8048ed1: e8 be ff ff ff call 8048e94 <fun7> ;; fun7 return 2*eax + 1 8048ed6: 01 c0 add %eax,%eax 8048ed8: 40 inc %eax 8048ed9: eb 07 jmp 8048ee2 <fun7+0x4e> 8048edb: 90 nop 8048edc: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi 8048ee0: 31 c0 xor %eax,%eax
從上面代碼可看出函數原型是:fun7( void *address, long int number )
.
當 number == *(int*)address
, fun7( address, number) = 0
當 number > *(int*)address
, fun7( address, number) = 2*fun7( address+8, number ) + 1
當 number < *(int*)address
, fun7( address, number) = 2*fun7( address+4, number )
從上面能夠看出, 上面的address
表示的是棵二叉樹
(左子樹的值<父節點的值, 右子樹的值>父節點的值):
struct BST { int num; struct BST *left; struct BST *right; } *bst;
則上面的遞推式可表示爲:
當 number == bst->num
, fun7( bst, number ) = 0
;
當 number > bst->num
, fun7( bst, number ) = 2*fun7( bst->right, number ) + 1
;
當 number < bst->num
, fun7( bst, number ) = 2*fun7( bst->left, number )
;
鑑於<secret_phase>
須要fun7( (struct BST *)0x804b320, number )
返回7
, 一個奇數, 因此第一步應該執行第二鍾狀況,
又經觀察發現如下遞推規律:
fun7( (struct BST *)0x804b320, number ) = 2 * fun7( (struct BST *)0x804b320->right, number ) + 1 = 2 * (2 * fun7( (struct BST *)0x804b320->right->right, number ) + 1) + 1 = 4 * fun7( (struct BST *)0x804b320->right->right, number ) + 3 = 4 * (2 * fun7( (struct BST *)0x804b320->right->right->right, number ) + 1) + 3 = 8 * fun7( (struct BST *)0x804b320->right->right->right, number ) + 7
所以當 number == (struct BST *)0x804b320->right->right->right->num
, fun7
即可返回7
用gdb
查看,
x/wx 0x804b320+8 ==> 0x0804b308 x/wx 0x804b308+8 ==> 0x0804b2d8 x/wx 0x804b2d8+8 ==> 0x0804b278 x/d 0x0804b278 ==> 1001
所以應輸入1001