以下記錄自己的解答方式。本篇僅紀錄 part1 的解法,簡單來說就是使用gdb找出正確輸入值,避免程式進入 explode_bomb()。
一、拆引線之前的準備動作
1. 先將程式反組譯為組語程式碼,下列所有分析需對照組語才能了解。
$ arm-linux-gnueabihf-objdump -xDs bomb > code.s
2. 執行程式,指定port,等待gdb連結 ( -g: wait gdb connection to 'port'):
$ qemu-arm -L /usr/arm-linux-gnueabihf -g 1234 bomb
3. 使用 gdb 追蹤程式流程
$ arm-none-eabi-gdb bomb
進入之後要輸入port ( target remote : 1234 )
4. 設定中斷點
(gdb) b *phase_1
(gdb) b *phase_2
(gdb) b *phase_3
(gdb) b *phase_4
(gdb) continue
二、開始拆引線,此遊戲 Part1 共設計五個關卡
phase_0:
答案:help
解法:題目說明直接提示此答案
phase_1:
答案:Yea, though I walk through the valley of the shadow of death, I will fear no evil; for thou art with me: thy rod and thy staff, they comfort me.
解法:如下
1. 將使用者輸入的參數所對應的位址,放入[r7, #4]
880a: 6078 str r0, [r7, #4]
2. 取出預設字串對應的位址,並將此位址複製到 [r7, #12]
880c: f648 3334 movw r3, #35636 ; 0x8b34
8810: f2c0 0300 movt r3, #0
8814: 60fb str r3, [r7, #12]
3. 取出兩個字串並呼叫 strcmp 進行比較,將比較結果放入 r3,確認結果是否為0
8816: 6878 ldr r0, [r7, #4]
8818: 68f9 ldr r1, [r7, #12]
881a: f7ff ee56 blx 84c8
881e: 4603 mov r3, r0
8820: 2b00 cmp r3, #0
8822: d001 beq.n 8828
4. 如果兩個字串相等時,跳到下下一行執行程式,否則引爆炸彈
8822: d001 beq.n 8828
8824: f7ff ffcc bl 87c0
8828: 3710 adds r7, #16
5. 因此可知解答為一字串,放在 0x8b34,使用下列指令檢視字串可確認答案
(gdb) x/s 0x8b34
答案:5 6 16 7
解法:如下
1. phase_2 會有兩次引爆炸彈的時機。分別是 0x8860 與 0x8884
8860: f7ff ffae bl 87c0
8884: f7ff ff9c bl 87c0
2. 0x8860 檢查 scanf 輸入的參數,此時會確認是否 scanf 讀入四個值
884e: f648 31c8 movw r1, #35784 ; 0x8bc8
8852: f2c0 0100 movt r1, #0
8856: f7ff ee74 blx 8540 <__isoc99_sscanf plt="">
885a: 4603 mov r3, r0
885c: 2b04 cmp r3, #4
885e: d001 beq.n 8864
3. ARM組合語言中,呼叫 scanf 時,會將欲讀取的資料格式放在 r1,此處可由 0x8bc8 得知,輸入的四個值都是整數
(gdb) x/s 0x8bc8
0x8bc8: "%d %d %d %d"
4. 確認四個值分別存放的位置,以及數值之間的關係。由於後續組語都直接使用r7做計算,因此須先搞清楚這個值。
8834: af02 add r7, sp, #8
此表示 r7 存放的值為 sp + #8,可檢視記憶體得知 r7+n 放入哪些值,
假設輸入的值為 4, 5, 6, 7。(註:此組合會引爆炸彈)
(gdb) x/40d $r7
0x407ffe00: 0 0 0 0 52 -2 127 64
0x407ffe08: -36 0 0 0 4 0 0 0
0x407ffe10: 5 0 0 0 6 0 0 0
0x407ffe18: 7 0 0 0 52 -2 127 64
0x407ffe20: 40 -2 127 64 69 -121 0 0
可以得知 r7+#12, r7+#16, r7+#20, r7+#24 分別依順序存入scanf輸入的四個值,由於 r7 = sp + #8,所以四個值的位址分別是 sp +#20, sp +#24, sp +#28, sp +#32
5. 0x8864 ~ 0x887a 為一個迴圈,會將第二個數加10,還原為C語言如下
for(i = 0 ; i <= 9 ; i++)
d1++; // [r7, #16],此為第二個數
6. 四個整數的關係為 “第二個數加10 等於第三個數”
887c: 693a ldr r2, [r7, #16]
887e: 697b ldr r3, [r7, #20]
8880: 429a cmp r2, r3
8882: d001 beq.n 8888
8884: f7ff ff9c bl 87c0
7. 還原為 C 語言
if(sscanf(str,"%d %d %d %d",&d0,&d1,&d2,&d3) != 4)
explode_bomb();
for(int i = 0 ; i <= 9 ; i++)
d1++;
if(d1 != d2)
explode_bomb();
8. 因此答案是任意四個整數,其中第二個數加10 等於第三個數,此處我取 5 6 16 7
phase_3:
答案:40 2 2
解法:如下
答案:40 2 2
解法:如下
1. phase_3 會有兩次引爆炸彈的時機。分別是 0x88ba 與 0x88d8
88ba: f7ff ff81 bl 87c0
88d8: f7ff ff72 bl 87c0
2. 0x88ba 檢查 scanf 輸入的參數,此時會確認是否 scanf 讀入三個值
88a8: f648 31d4 movw r1, #35796 ; 0x8bd4
88ac: f2c0 0100 movt r1, #0
88b0: f7ff ee46 blx 8540 <__isoc99_sscanf plt="">
88b4: 4603 mov r3, r0
88b6: 2b03 cmp r3, #3
88b8: d001 beq.n 88be
88ba: f7ff ff81 bl 87c0
3. 確認 Scanf參數型態
(gdb) x/s 0x8bd4
0x8bd4: "%d %d %d"
4. 三個變數分別放在 [r7, #12], [r7, #16], [r7, #20],若變數一等於 40, 跳至 0x88ce,否則 [r7, #16] = [r7, #16] + [r7, #20]
88be: 68fb ldr r3, [r7, #12]
88c0: 2b28 cmp r3, #40 ; 0x28
88c2: d004 beq.n 88ce
88c4: 693a ldr r2, [r7, #16]
88c6: 697b ldr r3, [r7, #20]
88c8: 4413 add r3, r2
88ca: 613b str r3, [r7, #16]
88cc: e000 b.n 88d0
88ce: bf00 nop
88d0: 693a ldr r2, [r7, #16]
88d2: 697b ldr r3, [r7, #20]
88d4: 429a cmp r2, r3
88d6: d001 beq.n 88dc
88d8: f7ff ff72 bl 87c0
5. 還原為 C 語言
if(sscanf(str,"%d %d %d",&d0,&d1,&d2) != 3)
explode_bomb();
if(d0 != 40)
d1 += d2;
if(d1 != d2)
explode_bomb();
phase_4:
答案:10
解法:如下
答案:10
解法:如下
1. phase_4 會有兩次引爆炸彈的時機。分別是 0x892e 與 0x8942
892e: f7ff ff47 bl 87c0
8942: f7ff ff3d bl 87c0
2. 0x892e 檢查 scanf 輸入的參數,此時會確認是否 scanf 讀入一個值
891a: f648 31e0 movw r1, #35808 ; 0x8be0
891e: f2c0 0100 movt r1, #0
8922: 461a mov r2, r3
8924: f7ff ee0c blx 8540 <__isoc99_sscanf plt="">
8928: 4603 mov r3, r0
892a: 2b01 cmp r3, #1
892c: d001 beq.n 8932
892e: f7ff ff47 bl 87c0
3. 確認參數型態為整數
(gdb) x/s 0x8be0
0x8be0: "%d"
4. 呼叫 func4,傳入參數的位址為 [r7, #12],func4 組語如下,內部實作分析如後
000088e4:
88e4: b580 push {r7, lr}
88e6: b082 sub sp, #8
88e8: af00 add r7, sp, #0
88ea: 6078 str r0, [r7, #4]
88ec: 687b ldr r3, [r7, #4]
88ee: 2b00 cmp r3, #0
88f0: d101 bne.n 88f6
88f2: 2301 movs r3, #1
88f4: e006 b.n 8904
88f6: 687b ldr r3, [r7, #4]
88f8: 3b01 subs r3, #1
88fa: 4618 mov r0, r3
88fc: f7ff fff2 bl 88e4
8900: 4603 mov r3, r0
8902: 005b lsls r3, r3, #1
8904: 4618 mov r0, r3
8906: 3708 adds r7, #8
8908: 46bd mov sp, r7
890a: bd80 pop {r7, pc}
4.1. func4 會自行呼叫 func4, 所以是遞迴函數。
88fc: f7ff fff2 bl 88e4
4.2. 當 [r7, #4] == 0 時,程式會設定r3==1,跳至 0x8904,離開 func4,否則會跳至 0x88f6,再次呼叫 func4。
88ea: 6078 str r0, [r7, #4]
88ec: 687b ldr r3, [r7, #4]
88ee: 2b00 cmp r3, #0
88f0: d101 bne.n 88f6
88f2: 2301 movs r3, #1
88f4: e006 b.n 8904
4.3. 每次進入遞迴函數,[r7, #4] = [r7, #4] - 1。
88f6: 687b ldr r3, [r7, #4]
88f8: 3b01 subs r3, #1
88fa: 4618 mov r0, r3
4.4. 每次離開遞迴函數之前,[r7, #4] 會往左 shift 一位 (乘上2)。
8900: 4603 mov r3, r0
8902: 005b lsls r3, r3, #1 ; logical shift left
8904: 4618 mov r0, r3
4.5. func4的C語言如下
int fun4(int c){
if(!c)
return 1;
return fun4(c--)<<1 br="">} 1>
4.6. func4 執行結束後,預期回傳值須等於 1024,要使此函數返回 1024, 則 c=10。
893a: 4603 mov r3, r0
893c: f5b3 6f80 cmp.w r3, #1024 ; 0x400
8940: d001 beq.n 8946
8942: f7ff ff3d bl 87c0
5. 還原為 C 語言
int fun4(int c){
if(!c)
return 1;
return fun4(c--)<<1 br="">} 1>
int d0;
if(sscanf(str,format,&d0) != 1)
explode_bomb();
if(fun4(d0) != 1024)
explode_bomb();
$ qemu-arm -L /usr/arm-linux-gnueabihf bomb solution.txt