CSAPP 3e: Bomb lab (secret_phase)
這是秘密關卡,需要通過主動調用secret_phase函數才能觸發,可以通過call secret 或者jump *0x地址來調用。
貼出函數:(fun7函數部分沒有註釋,後邊續上了手寫的圖來解析這個函數了)
0000000000401204 <fun7>: 401204: 48 83 ec 08 sub $0x8,%rsp 401208: 48 85 ff test %rdi,%rdi 40120b: 74 2b je 401238 <fun7+0x34>;如果%rdi==0,ret。 40120d: 8b 17 mov (%rdi),%edx 40120f: 39 f2 cmp %esi,%edx 401211: 7e 0d jle 401220 <fun7+0x1c> 401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi 401217: e8 e8 ff ff ff callq 401204 <fun7> 40121c: 01 c0 add %eax,%eax 40121e: eb 1d jmp 40123d <fun7+0x39> 401220: b8 00 00 00 00 mov $0x0,%eax 401225: 39 f2 cmp %esi,%edx 401227: 74 14 je 40123d <fun7+0x39> 401229: 48 8b 7f 10mov 0x10(%rdi),%rdi 40122d: e8 d2 ff ff ff callq 401204 <fun7> 401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax 401236: eb 05 jmp 40123d <fun7+0x39> 401238: b8 ff ff ff ff mov $0xffffffff,%eax 40123d: 48 83 c4 08 add $0x8,%rsp 401241: c3 retq 0000000000401242 <secret_phase>: 401242: 53 push %rbx 401243: e8 56 02 00 00 callq 40149e <read_line>;返回值%rax為所輸入字符串的首地址 401248: ba 0a 00 00 00 mov $0xa,%edx ;strtol函數參數,令函數將字符串讀取為10進制 40124d: be 00 00 00 00 mov $0x0,%esi ;NULL,strtol函數參數 401252: 48 89 c7 mov %rax,%rdi ;字符串首地址,作為參數 401255: e8 76 f9 ff ff callq 400bd0 <[email protected]> 40125a: 48 89 c3 mov %rax,%rbx ;%rax為strtol函數返回值,是一個long int型數字 40125d: 8d 40 ff lea -0x1(%rax),%eax ;要求(%rax-1)值小於0x3e8 (1000)。 401260: 3d e8 03 00 00 cmp $0x3e8,%eax 401265: 76 05 jbe 40126c <secret_phase+0x2a> 401267: e8 ce 01 00 00 callq 40143a <explode_bomb> 40126c: 89 de mov %ebx,%esi ;%esi等於strtol函數返回的那個long int型數字 40126e: bf f0 30 60 00 mov $0x6030f0,%edi ;fun7函數用到的一個地址 401273: e8 8c ff ff ff callq 401204 <fun7> 401278: 83 f8 02 cmp $0x2,%eax ;要求fun7函數返回值為2,否則Bomb. 40127b: 74 05 je 401282 <secret_phase+0x40> 40127d: e8 b8 01 00 00 callq 40143a <explode_bomb> 401282: bf 38 24 40 00 mov $0x402438,%edi 401287: e8 84 f8 ff ff callq 400b10 <[email protected]> 40128c: e8 33 03 00 00 callq 4015c4 <phase_defused> 401291: 5b pop %rbx 401292: c3 retq 401293: 90 nop 401294: 90 nop 401295: 90 nop 401296: 90 nop 401297: 90 nop 401298: 90 nop 401299: 90 nop 40129a: 90 nop 40129b: 90 nop 40129c: 90 nop 40129d: 90 nop 40129e: 90 nop 40129f: 90 nop
先研究secret_phase函數,它先調用了read_line函數,通過gdb追蹤運行發現,運行read_line函數後,%rax與%rsi的值都是所輸入字符串的首地址,%rcx為所輸入字符串的長度。
在call read_line之後的三行都是對調用strtol函數進行參數賦值,mov $0xa,%edx指示strtol函數將字符串轉換成十進制值輸出。對strtol函數的用法是(具體的百度一下你就知道):
/**************************************************************/
表頭文件: #include <stdlib.h>
定義函數: long int strtol(const char *nptr, char **endptr, int base)
函數說明: strtol()會將參數nptr字符串根據參數base來轉換成長整型數。參數base範圍從2至36,或0。參數base代表采用的進制方式,如base值為10則采用10進制(字符串以10進制表示),若base值為16則采用16進制(字符串以16進制表示)。當base值為0時則是采用10進制做轉換,但遇到如‘‘0x‘‘前置字符則會使用16進制做轉換。一開始strtol()會掃描參數nptr字符串,跳過前面的空格字符,直到遇上數字或正負符號才開始做轉換,再遇到非數字或字符串結束時(‘‘\0‘‘)結束轉換,並將結果返回。若參數endptr不為NULL,則會將遇到不合條件而終止的nptr中的字符指針由endptr返回。
返回值: 返回轉換後的長整型數,否則返回ERANGE並將錯誤代碼存入errno中
比如:
char a[] = "100";
char b[] = "100";
char c[] = "ffff";
printf("a = %d\n", strtol(a, NULL, 10)); //100
printf("b = %d\n", strtol(b, NULL, 2)); //4
printf("c = %d\n", strtol(c, NULL, 16)); //65535
/*****************************************************************/
這裏將輸入的字符串轉換成了十進制值,然後在func7函數中校驗所輸入的值是否正確,具體過程則需要研究fun7函數。
註意:下圖裏路徑C中少些了一個call fun7;
在func7中,%rsi的值一直都是所輸入的被轉換之後的值。
按照圖中所示路徑b--->c--->d 計算:
第一次 b: %edx=(%rdi); %rdi的值為0x6030f0; 所以%edx的值為0x24
說明%rsi小於0x24;(%rsi的值就是輸入且被轉換之後的值)
第二次 c: %rdi=(%rdi+0x8); 得%rdi==0x603110;然後調用了fun7自身,%edx=(%rdi); 此時%edx的值為 0x8;
說明%rsi雖然小於0x24,卻不等於0x8,而大於0x8;
第三次 d: 註意此時%rdi值為0x603110, 然後%rdi=(%rdi+0x10); 新的%rdi的值為0x603150; 之後調用自身fun7,%edx=(%rdi); %edx值為0x16;
此時可以逐步返回了,說明%rsi的值就是0x16,即10進制的22;
這樣,read_line函數中輸入的字符串被轉換為10進制值後應該為22;所以,"22"就是正確答案。
"22"被轉換成十進制後值為22,即0x16,符合在fun7函數中的計算結果。
驗證:(call調用出錯,所以用jump 跳轉調用,0x401242是secret_phase函數入口)
Wow!
I‘ve defused the secret stage!
Congraduations!!!
CSAPP 3e: Bomb lab (secret_phase)