# Bomb lab 紀錄 contributed by < `justapig9020` > ###### tags: `CSAPP` ## Link [bomb lab](http://csapp.cs.cmu.edu/3e/labs.html) ## Lab 說明 題目給予一支已編譯好的 binary 檔案 `bomb` ,`bomb` 總共有 6 個關卡,每個階段會從 stdin 讀取一個字串,若輸入的字串正確,則成功解除該關卡,否則炸彈引爆。 ## Lab 實做 ### 工具 在實做 lab 時,使用到的工具有: - objdump: 用以反組譯 `bomb` - gdb: 用以觀察 `bomb` 的行為,以及阻止其爆炸 - [peda](https://github.com/longld/peda): 更棒的 gdb 使用者體驗 - [pwntools](https://docs.pwntools.com/en/stable/): 用以實做腳本,執行 `bomb` 並餵入字串 ===============防雷頁=============== ===============防雷頁=============== ===============防雷頁=============== ===============防雷頁=============== ===============防雷頁=============== ===============防雷頁=============== ===============防雷頁=============== ===============防雷頁=============== ### 解答 #### text ``` Border relations with Canada have never been better. 1 2 4 8 16 32 1 311 7 0 IONEFG 4 3 2 1 6 5 ``` #### pwntools ```python3 from pwn import * p = process("./bomb") sol = open("./psol.txt", "w+") def recv_msg(): msg = p.recv().decode("ascii") print(msg) def str_to_bytes(s): return bytes(s, "ascii") def phase_1(): # phase_1_cmp_addr = 0x402400 payload = "Border relations with Canada have never been better." p.sendline(str_to_bytes(payload)) sol.write(payload) sol.write('\n') def phase_2(): args = [] val = 1 for i in range(6): args.append(val) val *= 2 payload = ' '.join(str(n) for n in args) p.sendline(str_to_bytes(payload)) sol.write(payload) sol.write('\n') def phase_3(): """ 0x402470: 0x0000000000400f7c 0x0000000000400fb9 0x402480: 0x0000000000400f83 0x0000000000400f8a 0x402490: 0x0000000000400f91 0x0000000000400f98 0x4024a0: 0x0000000000400f9f 0x0000000000400fa6 """ a = 1 b = 0x137 payload = str(a) + ' ' + str(b) p.sendline(str_to_bytes(payload)) sol.write(payload) sol.write('\n') def phase_4(): a = 7 b = 0 payload = str(a) + ' ' + str(b) p.sendline(str_to_bytes(payload)) sol.write(payload) sol.write('\n') def phase_5(): """ 6d616475696572736e666f747662796c """ data = "maduiersnfotvbyl" key = "flyers" args = [] for i in key: n = data.find(i) args.append(n) payload = ''.join(chr(n + ord('@')) for n in args) print(payload) p.sendline(str_to_bytes(payload)) sol.write(payload) sol.write('\n') def phase_6(): """ 0x6032d0 <node1>: 0x000000010000014c 0x00000000006032e0 0x6032e0 <node2>: 0x00000002000000a8 0x00000000006032f0 0x6032f0 <node3>: 0x000000030000039c 0x0000000000603300 0x603300 <node4>: 0x00000004000002b3 0x0000000000603310 0x603310 <node5>: 0x00000005000001dd 0x0000000000603320 """ values = [0x0000014c, 0x000000a8, 0x0000039c, 0x000002b3, 0x000001dd, 0x000001bb] tuples = [] for i in range(len(values)): tuples.append((i + 1, values[i])) tuples.sort(key = lambda tup: tup[1]) args = [] for tup in tuples: args.append(7 - tup[0]) args.reverse() payload = ' '.join(str(n) for n in args) p.sendline(str_to_bytes(payload)) sol.write(payload) sol.write('\n') recv_msg() phase_1() recv_msg() phase_2() recv_msg() phase_3() recv_msg() phase_4() recv_msg() phase_5() recv_msg() phase_6() recv_msg() sol.close() ``` ### 炸彈引爆 ```shell $ ./bomb Welcome to my fiendish little bomb. You have 6 phases with which to blow yourself up. Have a nice day! a BOOM!!! The bomb has blown up. ``` 當輸入錯誤字串時,炸彈便會被引爆。在實際課程中,每當炸彈引爆,都會被扣分。因此,首先要避免在答錯時引爆炸彈。 在 `bomb` 中,炸彈引爆的函數為: ```asm 000000000040143a <explode_bomb>: 40143a: 48 83 ec 08 sub rsp,0x8 40143e: bf a3 25 40 00 mov edi,0x4025a3 401443: e8 c8 f6 ff ff call 400b10 <puts@plt> 401448: bf ac 25 40 00 mov edi,0x4025ac 40144d: e8 be f6 ff ff call 400b10 <puts@plt> 401452: bf 08 00 00 00 mov edi,0x8 401457: e8 c4 f7 ff ff call 400c20 <exit@plt> ``` 因此,在開始執行前,透過 gdb 在 `explode_bomb` 下中斷點即可防止炸彈引爆。一旦進入 `explode_bomb` 並出發中斷點,馬上透過 kill 指令中止 process 即可。 使用 gdb 的方法為: 1. 執行 `bomb` 後透過 `ps -a` 指令來獲得其 pid ```shell $ ps -a PID TTY TIME CMD 14815 pts/1 00:00:00 bomb 14819 pts/0 00:00:00 ps ``` 2. 在 gdb 中使用 attach $pid 指令 attach 上 bomb 的 process ```gdb gdb-peda$ attach 14815 Attaching to process 14815 ``` ### phase_1 ```asm 0000000000400ee0 <phase_1>: 400ee0: 48 83 ec 08 sub rsp,0x8 400ee4: be 00 24 40 00 mov esi,0x402400 400ee9: e8 4a 04 00 00 call 401338 <strings_not_equal> 400eee: 85 c0 test eax,eax 400ef0: 74 05 je 400ef7 <phase_1+0x17> 400ef2: e8 43 05 00 00 call 40143a <explode_bomb> 400ef7: 48 83 c4 08 add rsp,0x8 400efb: c3 ret ``` 觀察組合語言可以輕易的發現, `phase_1` 是將輸入的字串與位在 0x402400 的字串比對。 透過 gdb 可以找出該位址的字串為: ```gdb gdb-peda$ x/s 0x402400 0x402400: "Border relations with Canada have never been better." ``` 因此只要輸入該字串即可通過 `phase_1` phase_1 完整的邏輯如下: ```c= void phase_1(char *input) { const char *password = "Border relations with Canada have never been better."; if (strings_not_equal(input, password)) explode_bomb(); } ``` ### phase_2 ```asm 0000000000400efc <phase_2>: 400efc: 55 push rbp 400efd: 53 push rbx 400efe: 48 83 ec 28 sub rsp,0x28 400f02: 48 89 e6 mov rsi,rsp 400f05: e8 52 05 00 00 call 40145c <read_six_numbers> 400f0a: 83 3c 24 01 cmp DWORD PTR [rsp],0x1 400f0e: 74 20 je 400f30 <phase_2+0x34> 400f10: e8 25 05 00 00 call 40143a <explode_bomb> 400f15: eb 19 jmp 400f30 <phase_2+0x34> 400f17: 8b 43 fc mov eax,DWORD PTR [rbx-0x4] 400f1a: 01 c0 add eax,eax 400f1c: 39 03 cmp DWORD PTR [rbx],eax 400f1e: 74 05 je 400f25 <phase_2+0x29> 400f20: e8 15 05 00 00 call 40143a <explode_bomb> 400f25: 48 83 c3 04 add rbx,0x4 400f29: 48 39 eb cmp rbx,rbp 400f2c: 75 e9 jne 400f17 <phase_2+0x1b> 400f2e: eb 0c jmp 400f3c <phase_2+0x40> 400f30: 48 8d 5c 24 04 lea rbx,[rsp+0x4] 400f35: 48 8d 6c 24 18 lea rbp,[rsp+0x18] 400f3a: eb db jmp 400f17 <phase_2+0x1b> 400f3c: 48 83 c4 28 add rsp,0x28 400f40: 5b pop rbx 400f41: 5d pop rbp 400f42: c3 ret ``` #### 有註解的版本 ```asm // rdi => read_line 0000000000400efc <phase_2>: 400efc: 55 push rbp 400efd: 53 push rbx 400efe: 48 83 ec 28 sub rsp,0x28 400f02: 48 89 e6 mov rsi,rsp # void *buf = rsp // Read 6 integers into rsp[0 - 5], rsp as a integer buffer. 400f05: e8 52 05 00 00 call 40145c <read_six_numbers> 400f0a: 83 3c 24 01 cmp DWORD PTR [rsp],0x1 // rsp[0] == 1 400f0e: 74 20 je 400f30 <phase_2+0x34> 400f10: e8 25 05 00 00 call 40143a <explode_bomb> 400f15: eb 19 jmp 400f30 <phase_2+0x34> // A loop start /* rbx = &buf[1] rbp = &buf[6] while (rbx != rbp): if rbx[0] != (rbx[-1] * 2): explode_bomb() rbx += 1 */ 400f17: 8b 43 fc mov eax,DWORD PTR [rbx-0x4] // eax = rbx[-1] 400f1a: 01 c0 add eax,eax // eax *= 2 400f1c: 39 03 cmp DWORD PTR [rbx],eax // rbx[0] == rbx[-1] * 2 /* * if rbx[0] != (rbx[-1] * 2): * explode_bomb() */ 400f1e: 74 05 je 400f25 <phase_2+0x29> 400f20: e8 15 05 00 00 call 40143a <explode_bomb> 400f25: 48 83 c3 04 add rbx,0x4 // rbx += 1 400f29: 48 39 eb cmp rbx,rbp // if rbx != rbp, keep looping 400f2c: 75 e9 jne 400f17 <phase_2+0x1b> // Exit loop, defuse phase_2 400f2e: eb 0c jmp 400f3c <phase_2+0x40> 400f30: 48 8d 5c 24 04 lea rbx,[rsp+0x4] // rbx = &rsp[1] 400f35: 48 8d 6c 24 18 lea rbp,[rsp+0x18] // rbp = &rsp[6](another variable outside the buffer) // Goto a loop 400f3a: eb db jmp 400f17 <phase_2+0x1b> 400f3c: 48 83 c4 28 add rsp,0x28 400f40: 5b pop rbx 400f41: 5d pop rbp 400f42: c3 ret ``` 在 phase_2 中,首先會將輸入字串解析成六個數字,並依序存放在 rsp[0] ~ rsp[5] 中。 解析完後的邏輯如下: ```c= if (rsp[0] != 1) explode_bomb(); for (int i=1; i<6; i++) { if ((rsp[i-1] * 2) != rsp[i]) explode_bomb(); ``` 因此只要輸入的第一個數字為 1 ,且之後每個數字皆為前一位的兩倍即可。 答案為: `1 2 4 8 16 32` phase_2 完整的邏輯如下: ```c= void phase_2(char *input) { int buf[6]; read_six_numbers(input, buf); if (1 != buf[0]) explode_bomb(); for (int i=1; i<6; i++) if ((buf[i-1] * 2) != buf[i]) explode_bomb(); } ``` ### phase_3 ```asm 0000000000400f43 <phase_3>: 400f43: 48 83 ec 18 sub rsp,0x18 400f47: 48 8d 4c 24 0c lea rcx,[rsp+0xc] 400f4c: 48 8d 54 24 08 lea rdx,[rsp+0x8] 400f51: be cf 25 40 00 mov esi,0x4025cf 400f56: b8 00 00 00 00 mov eax,0x0 400f5b: e8 90 fc ff ff call 400bf0 <__isoc99_sscanf@plt> 400f60: 83 f8 01 cmp eax,0x1 400f63: 7f 05 jg 400f6a <phase_3+0x27> 400f65: e8 d0 04 00 00 call 40143a <explode_bomb> 400f6a: 83 7c 24 08 07 cmp DWORD PTR [rsp+0x8],0x7 400f6f: 77 3c ja 400fad <phase_3+0x6a> 400f71: 8b 44 24 08 mov eax,DWORD PTR [rsp+0x8] 400f75: ff 24 c5 70 24 40 00 jmp QWORD PTR [rax*8+0x402470] 400f7c: b8 cf 00 00 00 mov eax,0xcf 400f81: eb 3b jmp 400fbe <phase_3+0x7b> 400f83: b8 c3 02 00 00 mov eax,0x2c3 400f88: eb 34 jmp 400fbe <phase_3+0x7b> 400f8a: b8 00 01 00 00 mov eax,0x100 400f8f: eb 2d jmp 400fbe <phase_3+0x7b> 400f91: b8 85 01 00 00 mov eax,0x185 400f96: eb 26 jmp 400fbe <phase_3+0x7b> 400f98: b8 ce 00 00 00 mov eax,0xce 400f9d: eb 1f jmp 400fbe <phase_3+0x7b> 400f9f: b8 aa 02 00 00 mov eax,0x2aa 400fa4: eb 18 jmp 400fbe <phase_3+0x7b> 400fa6: b8 47 01 00 00 mov eax,0x147 400fab: eb 11 jmp 400fbe <phase_3+0x7b> 400fad: e8 88 04 00 00 call 40143a <explode_bomb> 400fb2: b8 00 00 00 00 mov eax,0x0 400fb7: eb 05 jmp 400fbe <phase_3+0x7b> 400fb9: b8 37 01 00 00 mov eax,0x137 400fbe: 3b 44 24 0c cmp eax,DWORD PTR [rsp+0xc] 400fc2: 74 05 je 400fc9 <phase_3+0x86> 400fc4: e8 71 04 00 00 call 40143a <explode_bomb> 400fc9: 48 83 c4 18 add rsp,0x18 400fcd: c3 ret ``` #### 有註解的版本 ```asm 0000000000400f43 <phase_3>: 400f43: 48 83 ec 18 sub rsp,0x18 400f47: 48 8d 4c 24 0c lea rcx,[rsp+0xc] 400f4c: 48 8d 54 24 08 lea rdx,[rsp+0x8] 400f51: be cf 25 40 00 mov esi,0x4025cf // %d %d 400f56: b8 00 00 00 00 mov eax,0x0 // int a @ rsp + 0x8 // int b @ rsp + 0xc // sscanf(str, "%d %d", &a, &b) 400f5b: e8 90 fc ff ff call 400bf0 <__isoc99_sscanf@plt> 400f60: 83 f8 01 cmp eax,0x1 400f63: 7f 05 jg 400f6a <phase_3+0x27> 400f65: e8 d0 04 00 00 call 40143a <explode_bomb> // if a > 7: // explode_bomb() 400f6a: 83 7c 24 08 07 cmp DWORD PTR [rsp+0x8],0x7 400f6f: 77 3c ja 400fad <phase_3+0x6a> 400f71: 8b 44 24 08 mov eax,DWORD PTR [rsp+0x8] // eax = a 400f75: ff 24 c5 70 24 40 00 jmp QWORD PTR [rax*8+0x402470] 400f7c: b8 cf 00 00 00 mov eax,0xcf 400f81: eb 3b jmp 400fbe <phase_3+0x7b> 400f83: b8 c3 02 00 00 mov eax,0x2c3 400f88: eb 34 jmp 400fbe <phase_3+0x7b> 400f8a: b8 00 01 00 00 mov eax,0x100 400f8f: eb 2d jmp 400fbe <phase_3+0x7b> 400f91: b8 85 01 00 00 mov eax,0x185 400f96: eb 26 jmp 400fbe <phase_3+0x7b> 400f98: b8 ce 00 00 00 mov eax,0xce 400f9d: eb 1f jmp 400fbe <phase_3+0x7b> 400f9f: b8 aa 02 00 00 mov eax,0x2aa 400fa4: eb 18 jmp 400fbe <phase_3+0x7b> 400fa6: b8 47 01 00 00 mov eax,0x147 400fab: eb 11 jmp 400fbe <phase_3+0x7b> 400fad: e8 88 04 00 00 call 40143a <explode_bomb> 400fb2: b8 00 00 00 00 mov eax,0x0 400fb7: eb 05 jmp 400fbe <phase_3+0x7b> 400fb9: b8 37 01 00 00 mov eax,0x137 // 1 // if eax != b: // explode_bomb() 400fbe: 3b 44 24 0c cmp eax,DWORD PTR [rsp+0xc] // eax == b 400fc2: 74 05 je 400fc9 <phase_3+0x86> 400fc4: e8 71 04 00 00 call 40143a <explode_bomb> 400fc9: 48 83 c4 18 add rsp,0x18 400fcd: c3 ret ``` 首先,透過 sscanf 將輸入字串解析成兩個 int ,分別存放至 rsp + 0x8 , rsp + 0xc 。分別令兩 int 為 `a` , `b` 。 接著,檢查 `a` 是否大於 7 ,若大於則引爆炸彈。這邊需要注意的是 `ga` 這個指令比較時是將兩數視為 `unsigned` ,因此,比較後 $0\le$ `a` $\le 7$ 。 接著在 0x400f71 , 0x400f75 的指令,首先將 `a` 載入至 `eax` ,並將 `eax` 作為位址的 offset 來執行 `jmp` 。 透過 gdb 檢視 jump table 的 base address `0x402470` 。 ```gdb gdb-peda$ x/8gx 0x402470 0x402470: 0x0000000000400f7c 0x0000000000400fb9 0x402480: 0x0000000000400f83 0x0000000000400f8a 0x402490: 0x0000000000400f91 0x0000000000400f98 0x4024a0: 0x0000000000400f9f 0x0000000000400fa6 ``` 可以發現與 `phase_2` 中的這段指令位址具有對應關係 ```asm 400f7c: b8 cf 00 00 00 mov eax,0xcf 400f81: eb 3b jmp 400fbe <phase_3+0x7b> 400f83: b8 c3 02 00 00 mov eax,0x2c3 400f88: eb 34 jmp 400fbe <phase_3+0x7b> 400f8a: b8 00 01 00 00 mov eax,0x100 400f8f: eb 2d jmp 400fbe <phase_3+0x7b> 400f91: b8 85 01 00 00 mov eax,0x185 400f96: eb 26 jmp 400fbe <phase_3+0x7b> 400f98: b8 ce 00 00 00 mov eax,0xce 400f9d: eb 1f jmp 400fbe <phase_3+0x7b> 400f9f: b8 aa 02 00 00 mov eax,0x2aa 400fa4: eb 18 jmp 400fbe <phase_3+0x7b> 400fa6: b8 47 01 00 00 mov eax,0x147 400fab: eb 11 jmp 400fbe <phase_3+0x7b> 400fad: e8 88 04 00 00 call 40143a <explode_bomb> 400fb2: b8 00 00 00 00 mov eax,0x0 400fb7: eb 05 jmp 400fbe <phase_3+0x7b> 400fb9: b8 37 01 00 00 mov eax,0x137 ``` 因此,實際上邏輯為(為省空間以及有助閱讀, switch 中省略 `break`): ```c= int eax = a; switch(eax) { case 0: eax = 0xcf; case 1: eax = 0x137; case 2: eax = 0x2c3; case 3: eax = 0x100; case 4: eax = 0x185; case 5: eax = 0xce; case 6: eax = 0x2aa; case 7: eax = 0x147; default: explode_bomb() } ``` 接著,比較 eax 與 `b` ,若不相等則引爆炸彈。 因此,只須任意選定 `a` ,並輸入對應的 `b` 即可。 參考答案: `1 311` phase_3 完整的邏輯如下: ```c= void phase_3(char *input) { int a, b; sprintf(input, "%d %d", &a, &b); int eax = a; switch(eax) { case 0: eax = 0xcf; break; case 1: eax = 0x137; break; case 2: eax = 0x2c3; break; case 3: eax = 0x100; break; case 4: eax = 0x185; break; case 5: eax = 0xce; break; case 6: eax = 0x2aa; break; case 7: eax = 0x147; break; default: explode_bomb(); } if (eax != b) explode_bomb(); } ``` ### phase_4 ```asm 0000000000400fce <func4>: 400fce: 48 83 ec 08 sub rsp,0x8 400fd2: 89 d0 mov eax,edx 400fd4: 29 f0 sub eax,esi 400fd6: 89 c1 mov ecx,eax 400fd8: c1 e9 1f shr ecx,0x1f 400fdb: 01 c8 add eax,ecx 400fdd: d1 f8 sar eax,1 400fdf: 8d 0c 30 lea ecx,[rax+rsi*1] 400fe2: 39 f9 cmp ecx,edi 400fe4: 7e 0c jle 400ff2 <func4+0x24> 400fe6: 8d 51 ff lea edx,[rcx-0x1] 400fe9: e8 e0 ff ff ff call 400fce <func4> 400fee: 01 c0 add eax,eax 400ff0: eb 15 jmp 401007 <func4+0x39> 400ff2: b8 00 00 00 00 mov eax,0x0 400ff7: 39 f9 cmp ecx,edi 400ff9: 7d 0c jge 401007 <func4+0x39> 400ffb: 8d 71 01 lea esi,[rcx+0x1] 400ffe: e8 cb ff ff ff call 400fce <func4> 401003: 8d 44 00 01 lea eax,[rax+rax*1+0x1] 401007: 48 83 c4 08 add rsp,0x8 40100b: c3 ret 000000000040100c <phase_4>: 40100c: 48 83 ec 18 sub rsp,0x18 401010: 48 8d 4c 24 0c lea rcx,[rsp+0xc] 401015: 48 8d 54 24 08 lea rdx,[rsp+0x8] 40101a: be cf 25 40 00 mov esi,0x4025cf 40101f: b8 00 00 00 00 mov eax,0x0 401024: e8 c7 fb ff ff call 400bf0 <__isoc99_sscanf@plt> 401029: 83 f8 02 cmp eax,0x2 40102c: 75 07 jne 401035 <phase_4+0x29> 40102e: 83 7c 24 08 0e cmp DWORD PTR [rsp+0x8],0xe 401033: 76 05 jbe 40103a <phase_4+0x2e> 401035: e8 00 04 00 00 call 40143a <explode_bomb> 40103a: ba 0e 00 00 00 mov edx,0xe 40103f: be 00 00 00 00 mov esi,0x0 401044: 8b 7c 24 08 mov edi,DWORD PTR [rsp+0x8] 401048: e8 81 ff ff ff call 400fce <func4> 40104d: 85 c0 test eax,eax 40104f: 75 07 jne 401058 <phase_4+0x4c> 401051: 83 7c 24 0c 00 cmp DWORD PTR [rsp+0xc],0x0 401056: 74 05 je 40105d <phase_4+0x51> 401058: e8 dd 03 00 00 call 40143a <explode_bomb> 40105d: 48 83 c4 18 add rsp,0x18 401061: c3 ret ``` #### 有註解的版本 ```asm 0000000000400fce <func4>: /* func4(a, rsi, rdx): buf = edx - esi // e tmp = a >>> 0x1f // 16 + 15 = 31 buf += tmp // e + 0 = e buf >>= 1 // e >> 1 = 7 n = buf + rsi // buf + 0 = 7 // n == a -> return 0 if n <= a : if n >= a: return 0 ret = func(a, n + 1, rdx); ret = ret * 2 + 1 else: ret = func4(a, rsi, n - 1) ret *= 2 return ret */ 400fce: 48 83 ec 08 sub rsp,0x8 400fd2: 89 d0 mov eax,edx // eax = edx 400fd4: 29 f0 sub eax,esi // eax -= esi 400fd6: 89 c1 mov ecx,eax // ecx = eax 400fd8: c1 e9 1f shr ecx,0x1f // ecx >>>= 0x1f 400fdb: 01 c8 add eax,ecx // eax += ecx 400fdd: d1 f8 sar eax,1 // eax >>= 1 400fdf: 8d 0c 30 lea ecx,[rax+rsi*1] 400fe2: 39 f9 cmp ecx,edi 400fe4: 7e 0c jle 400ff2 <func4+0x24> 400fe6: 8d 51 ff lea edx,[rcx-0x1] 400fe9: e8 e0 ff ff ff call 400fce <func4> 400fee: 01 c0 add eax,eax 400ff0: eb 15 jmp 401007 <func4+0x39> // end func 400ff2: b8 00 00 00 00 mov eax,0x0 400ff7: 39 f9 cmp ecx,edi 400ff9: 7d 0c jge 401007 <func4+0x39> // end func 400ffb: 8d 71 01 lea esi,[rcx+0x1] 400ffe: e8 cb ff ff ff call 400fce <func4> 401003: 8d 44 00 01 lea eax,[rax+rax*1+0x1] 401007: 48 83 c4 08 add rsp,0x8 40100b: c3 ret 000000000040100c <phase_4>: 40100c: 48 83 ec 18 sub rsp,0x18 401010: 48 8d 4c 24 0c lea rcx,[rsp+0xc] 401015: 48 8d 54 24 08 lea rdx,[rsp+0x8] 40101a: be cf 25 40 00 mov esi,0x4025cf // %d %d 40101f: b8 00 00 00 00 mov eax,0x0 // int a @ rsp + 0x8 // int b @ rsp + 0xc // if sscanf(str, "%d %d", &a, &b) != 2: // explode_bomb() 401024: e8 c7 fb ff ff call 400bf0 <__isoc99_sscanf@plt> 401029: 83 f8 02 cmp eax,0x2 40102c: 75 07 jne 401035 <phase_4+0x29> 40102e: 83 7c 24 08 0e cmp DWORD PTR [rsp+0x8],0xe 401033: 76 05 jbe 40103a <phase_4+0x2e> 401035: e8 00 04 00 00 call 40143a <explode_bomb> 40103a: ba 0e 00 00 00 mov edx,0xe 40103f: be 00 00 00 00 mov esi,0x0 401044: 8b 7c 24 08 mov edi,DWORD PTR [rsp+0x8] // a // if a > 0xe: // explode_bomb() // ret = func4(a, 0, 0xe); // return 0 if a == 7 // if ret != 0: // explode_bomb() // if b != 0: // explode_bomb() 401048: e8 81 ff ff ff call 400fce <func4> 40104d: 85 c0 test eax,eax 40104f: 75 07 jne 401058 <phase_4+0x4c> 401051: 83 7c 24 0c 00 cmp DWORD PTR [rsp+0xc],0x0 401056: 74 05 je 40105d <phase_4+0x51> 401058: e8 dd 03 00 00 call 40143a <explode_bomb> 40105d: 48 83 c4 18 add rsp,0x18 401061: c3 ret ``` 首先,輸入的字串透過 sscanf 解析成兩個 int 。令他們分別為 `a` , `b` 。 接著,執行 `func4(a, 0, 0xe)` ,若返回值不為 0 則爆炸。此外若 `b` 不為 0 也會爆炸。 `func4` 的邏輯如下: ```python= def func4(a, rsi, rdx): buf = edx - esi # e tmp = a >>> 0x1f # logic right shift, 16 + 15 = 31 buf += tmp # e + 0 = e buf >>= 1 # e >> 1 = 7 n = buf + rsi # buf + 0 = 7 # n == a -> return 0 if n <= a : if n >= a: return 0 ret = func(a, n + 1, rdx); ret = ret * 2 + 1 else: ret = func4(a, rsi, n - 1) ret *= 2 return ret ``` 可以發現只要在開始時使 `a` = 7 即可避免遞迴,並直接返回 0 。 因此 phase_4 的解答為: `7 0` phase_4 的完整邏輯為: ```c= int func4(int a, int rsi, int rdx) { int buf = rdx - rsi; int tmp = ((unsigned int)a) >> 0x1f; // logical shift buf += tmp; buf >>= 1; int n = buf + rsi; if (n <= a) { if (n >= a) return 0; int ret = func4(a, n + 1, rdx); return ret * 2 + 1; } else { int ret = func4(a, rsi, n - 1); return ret * 2; } } void phase_4(char *input) { int a, b; int n = sscanf(input, "%d %d", &a, &b); if (n != 2) explode_bomb(); if (func4(a, 0, 0xe) != 0) explode_bomb(); if (b != 0) explode_bomb(); } ``` ### phase_5 ```asm 0000000000401062 <phase_5>: 401062: 53 push rbx 401063: 48 83 ec 20 sub rsp,0x20 401067: 48 89 fb mov rbx,rdi 40106a: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28 401071: 00 00 401073: 48 89 44 24 18 mov QWORD PTR [rsp+0x18],rax 401078: 31 c0 xor eax,eax 40107a: e8 9c 02 00 00 call 40131b <string_length> 40107f: 83 f8 06 cmp eax,0x6 401082: 74 4e je 4010d2 <phase_5+0x70> 401084: e8 b1 03 00 00 call 40143a <explode_bomb> 401089: eb 47 jmp 4010d2 <phase_5+0x70> 40108b: 0f b6 0c 03 movzx ecx,BYTE PTR [rbx+rax*1] 40108f: 88 0c 24 mov BYTE PTR [rsp],cl 401092: 48 8b 14 24 mov rdx,QWORD PTR [rsp] 401096: 83 e2 0f and edx,0xf 401099: 0f b6 92 b0 24 40 00 movzx edx,BYTE PTR [rdx+0x4024b0] 4010a0: 88 54 04 10 mov BYTE PTR [rsp+rax*1+0x10],dl 4010a4: 48 83 c0 01 add rax,0x1 4010a8: 48 83 f8 06 cmp rax,0x6 4010ac: 75 dd jne 40108b <phase_5+0x29> 4010ae: c6 44 24 16 00 mov BYTE PTR [rsp+0x16],0x0 4010b3: be 5e 24 40 00 mov esi,0x40245e 4010b8: 48 8d 7c 24 10 lea rdi,[rsp+0x10] 4010bd: e8 76 02 00 00 call 401338 <strings_not_equal> 4010c2: 85 c0 test eax,eax 4010c4: 74 13 je 4010d9 <phase_5+0x77> 4010c6: e8 6f 03 00 00 call 40143a <explode_bomb> 4010cb: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0] 4010d0: eb 07 jmp 4010d9 <phase_5+0x77> 4010d2: b8 00 00 00 00 mov eax,0x0 4010d7: eb b2 jmp 40108b <phase_5+0x29> 4010d9: 48 8b 44 24 18 mov rax,QWORD PTR [rsp+0x18] 4010de: 64 48 33 04 25 28 00 xor rax,QWORD PTR fs:0x28 4010e5: 00 00 4010e7: 74 05 je 4010ee <phase_5+0x8c> 4010e9: e8 42 fa ff ff call 400b30 <__stack_chk_fail@plt> 4010ee: 48 83 c4 20 add rsp,0x20 4010f2: 5b pop rbx 4010f3: c3 ret ``` #### 有註解的版本 ```asm 0000000000401062 <phase_5>: 401062: 53 push rbx 401063: 48 83 ec 20 sub rsp,0x20 401067: 48 89 fb mov rbx,rdi // rbx = str 40106a: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28 401071: 00 00 401073: 48 89 44 24 18 mov QWORD PTR [rsp+0x18],rax // canary 401078: 31 c0 xor eax,eax // len = string_length(str) // if len != 6: // explode_bomb() 40107a: e8 9c 02 00 00 call 40131b <string_length> 40107f: 83 f8 06 cmp eax,0x6 401082: 74 4e je 4010d2 <phase_5+0x70> 401084: e8 b1 03 00 00 call 40143a <explode_bomb> 401089: eb 47 jmp 4010d2 <phase_5+0x70> // loop start // for (i = 0; i != 6; i ++): // int rdx = str[i] // rdx &= 0xf // edx = data[rdx] // data = " // buf[i] = edx 40108b: 0f b6 0c 03 movzx ecx,BYTE PTR [rbx+rax*1] // move with zero extend 40108f: 88 0c 24 mov BYTE PTR [rsp],cl 401092: 48 8b 14 24 mov rdx,QWORD PTR [rsp] 401096: 83 e2 0f and edx,0xf 401099: 0f b6 92 b0 24 40 00 movzx edx,BYTE PTR [rdx+0x4024b0] 4010a0: 88 54 04 10 mov BYTE PTR [rsp+rax*1+0x10],dl 4010a4: 48 83 c0 01 add rax,0x1 4010a8: 48 83 f8 06 cmp rax,0x6 4010ac: 75 dd jne 40108b <phase_5+0x29> 4010ae: c6 44 24 16 00 mov BYTE PTR [rsp+0x16],0x0 4010b3: be 5e 24 40 00 mov esi,0x40245e 4010b8: 48 8d 7c 24 10 lea rdi,[rsp+0x10] 4010bd: e8 76 02 00 00 call 401338 <strings_not_equal> 4010c2: 85 c0 test eax,eax 4010c4: 74 13 je 4010d9 <phase_5+0x77> 4010c6: e8 6f 03 00 00 call 40143a <explode_bomb> 4010cb: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0] 4010d0: eb 07 jmp 4010d9 <phase_5+0x77> 4010d2: b8 00 00 00 00 mov eax,0x0 4010d7: eb b2 jmp 40108b <phase_5+0x29> // canary check 4010d9: 48 8b 44 24 18 mov rax,QWORD PTR [rsp+0x18] 4010de: 64 48 33 04 25 28 00 xor rax,QWORD PTR fs:0x28 4010e5: 00 00 4010e7: 74 05 je 4010ee <phase_5+0x8c> 4010e9: e8 42 fa ff ff call 400b30 <__stack_chk_fail@plt> 4010ee: 48 83 c4 20 add rsp,0x20 4010f2: 5b pop rbx 4010f3: c3 ret ``` 首先判斷輸入字串的長度是否為 6 。 接著 0x401089 ~ 0x4010ac 為一迴圈,迴圈歷遍輸入字串,並且取當前字元的低 4 bits 作為 offset 。接著從位於 0x4024b0 的陣列中,取出對應 offset 的字元,存入另一字串 `key` 中。 最後比對 `key` 與目標字串(位於 0x40245e )是否相同。若不相同則引爆炸彈。 首先利用 gdb 便可找出 0x4024b0 與目標字串分別為: - 0x4024b0 ```gdb gdb-peda$ x/s 0x4024b0 0x4024b0 <array.3449>: "maduiersnfotvbyl(後略)" ``` - 0x40245e ```gdb gdb-peda$ x/s 0x40245e 0x40245e: "flyers" ``` 接著只要透過簡單的腳本,從 0x4024b0 字串中找出對應 "flyers" 的字元的 offset 即可。 這邊需要注意的是,由於是取每一字元的低 4 bits 。因此在轉成可視字元時,只要找到一個低 4 bits 為 0x0 的基底即可。(我這邊使用的是 '@' 其 ascii code 為 0x40)。 對應的腳本為 ```python= data = "maduiersnfotvbyl" password = "flyers" args = [] for i in password: n = data.find(i) args.append(n) payload = ''.join(chr(n + ord('@')) for n in args) ``` 參考解答為: `IONEFG` phase_5 的邏輯如下: ```c= void phase_5(char *input) { if (string_length(input) != 6) explode_bomb() static const char *words = "maduiersnfotvbyl"; char key[6]; for (int i=0; i<6; i++) { unsigned int offset = input[i] & 0xf; key[i] = words[offset]; } static const char *password = "flyers"; if (string_not_equal(key, password)) explode_bomb(); } ``` ### phase_6 ```asm 00000000004010f4 <phase_6>: 4010f4: 41 56 push r14 4010f6: 41 55 push r13 4010f8: 41 54 push r12 4010fa: 55 push rbp 4010fb: 53 push rbx 4010fc: 48 83 ec 50 sub rsp,0x50 401100: 49 89 e5 mov r13,rsp 401103: 48 89 e6 mov rsi,rsp 401106: e8 51 03 00 00 call 40145c <read_six_numbers> 40110b: 49 89 e6 mov r14,rsp 40110e: 41 bc 00 00 00 00 mov r12d,0x0 401114: 4c 89 ed mov rbp,r13 401117: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] 40111b: 83 e8 01 sub eax,0x1 40111e: 83 f8 05 cmp eax,0x5 401121: 76 05 jbe 401128 <phase_6+0x34> 401123: e8 12 03 00 00 call 40143a <explode_bomb> 401128: 41 83 c4 01 add r12d,0x1 40112c: 41 83 fc 06 cmp r12d,0x6 401130: 74 21 je 401153 <phase_6+0x5f> 401132: 44 89 e3 mov ebx,r12d 401135: 48 63 c3 movsxd rax,ebx 401138: 8b 04 84 mov eax,DWORD PTR [rsp+rax*4] 40113b: 39 45 00 cmp DWORD PTR [rbp+0x0],eax 40113e: 75 05 jne 401145 <phase_6+0x51> 401140: e8 f5 02 00 00 call 40143a <explode_bomb> 401145: 83 c3 01 add ebx,0x1 401148: 83 fb 05 cmp ebx,0x5 40114b: 7e e8 jle 401135 <phase_6+0x41> 40114d: 49 83 c5 04 add r13,0x4 401151: eb c1 jmp 401114 <phase_6+0x20> 401153: 48 8d 74 24 18 lea rsi,[rsp+0x18] 401158: 4c 89 f0 mov rax,r14 40115b: b9 07 00 00 00 mov ecx,0x7 401160: 89 ca mov edx,ecx 401162: 2b 10 sub edx,DWORD PTR [rax] 401164: 89 10 mov DWORD PTR [rax],edx 401166: 48 83 c0 04 add rax,0x4 40116a: 48 39 f0 cmp rax,rsi 40116d: 75 f1 jne 401160 <phase_6+0x6c> 40116f: be 00 00 00 00 mov esi,0x0 401174: eb 21 jmp 401197 <phase_6+0xa3> 401176: 48 8b 52 08 mov rdx,QWORD PTR [rdx+0x8] 40117a: 83 c0 01 add eax,0x1 40117d: 39 c8 cmp eax,ecx 40117f: 75 f5 jne 401176 <phase_6+0x82> 401181: eb 05 jmp 401188 <phase_6+0x94> 401183: ba d0 32 60 00 mov edx,0x6032d0 401188: 48 89 54 74 20 mov QWORD PTR [rsp+rsi*2+0x20],rdx 40118d: 48 83 c6 04 add rsi,0x4 401191: 48 83 fe 18 cmp rsi,0x18 401195: 74 14 je 4011ab <phase_6+0xb7> 401197: 8b 0c 34 mov ecx,DWORD PTR [rsp+rsi*1] 40119a: 83 f9 01 cmp ecx,0x1 40119d: 7e e4 jle 401183 <phase_6+0x8f> 40119f: b8 01 00 00 00 mov eax,0x1 4011a4: ba d0 32 60 00 mov edx,0x6032d0 4011a9: eb cb jmp 401176 <phase_6+0x82> 4011ab: 48 8b 5c 24 20 mov rbx,QWORD PTR [rsp+0x20] 4011b0: 48 8d 44 24 28 lea rax,[rsp+0x28] 4011b5: 48 8d 74 24 50 lea rsi,[rsp+0x50] 4011ba: 48 89 d9 mov rcx,rbx 4011bd: 48 8b 10 mov rdx,QWORD PTR [rax] 4011c0: 48 89 51 08 mov QWORD PTR [rcx+0x8],rdx 4011c4: 48 83 c0 08 add rax,0x8 4011c8: 48 39 f0 cmp rax,rsi 4011cb: 74 05 je 4011d2 <phase_6+0xde> 4011cd: 48 89 d1 mov rcx,rdx 4011d0: eb eb jmp 4011bd <phase_6+0xc9> 4011d2: 48 c7 42 08 00 00 00 mov QWORD PTR [rdx+0x8],0x0 4011d9: 00 4011da: bd 05 00 00 00 mov ebp,0x5 4011df: 48 8b 43 08 mov rax,QWORD PTR [rbx+0x8] 4011e3: 8b 00 mov eax,DWORD PTR [rax] 4011e5: 39 03 cmp DWORD PTR [rbx],eax 4011e7: 7d 05 jge 4011ee <phase_6+0xfa> 4011e9: e8 4c 02 00 00 call 40143a <explode_bomb> 4011ee: 48 8b 5b 08 mov rbx,QWORD PTR [rbx+0x8] 4011f2: 83 ed 01 sub ebp,0x1 4011f5: 75 e8 jne 4011df <phase_6+0xeb> 4011f7: 48 83 c4 50 add rsp,0x50 4011fb: 5b pop rbx 4011fc: 5d pop rbp 4011fd: 41 5c pop r12 4011ff: 41 5d pop r13 401201: 41 5e pop r14 401203: c3 ret ``` #### 有註解的版本 ```asm 00000000004010f4 <phase_6>: 4010f4: 41 56 push r14 4010f6: 41 55 push r13 4010f8: 41 54 push r12 4010fa: 55 push rbp 4010fb: 53 push rbx 4010fc: 48 83 ec 50 sub rsp,0x50 401100: 49 89 e5 mov r13,rsp // r13 = rsp 401103: 48 89 e6 mov rsi,rsp 401106: e8 51 03 00 00 call 40145c <read_six_numbers> 40110b: 49 89 e6 mov r14,rsp // void *r14 = rsp 40110e: 41 bc 00 00 00 00 mov r12d,0x0 // r12d = 0 (r12d = r12 lower 32 bits) /* // 1. arg[i] - 1 <= 5, arg[i] <= 6, unsigned cmp // 2. arg[i] != arg[j] for all i != j r13 = buf rsi = buf read_six_numbers(str, buf) r14 = buf i = 0 int *ptr = buf // r13 do { if (ptr[0] - 1) > 5: // ptr[0] = arg[i], unsigned explode_bomb i += 1 if i == 6: break outer ebx = i do { long rax = ebx // sign extend eax = buf[rax] if eax == ptr[0]: explode_bomb ebx += 1 } while ebx <= 5 ptr += 1 } */ 401114: 4c 89 ed mov rbp,r13 // rbp = r13 401117: 41 8b 45 00 mov eax,DWORD PTR [r13+0x0] // eax = rsp[0] 40111b: 83 e8 01 sub eax,0x1 // eax - 1 40111e: 83 f8 05 cmp eax,0x5 401121: 76 05 jbe 401128 <phase_6+0x34> 401123: e8 12 03 00 00 call 40143a <explode_bomb> 401128: 41 83 c4 01 add r12d,0x1 40112c: 41 83 fc 06 cmp r12d,0x6 401130: 74 21 je 401153 <phase_6+0x5f> 401132: 44 89 e3 mov ebx,r12d 401135: 48 63 c3 movsxd rax,ebx 401138: 8b 04 84 mov eax,DWORD PTR [rsp+rax*4] 40113b: 39 45 00 cmp DWORD PTR [rbp+0x0],eax 40113e: 75 05 jne 401145 <phase_6+0x51> 401140: e8 f5 02 00 00 call 40143a <explode_bomb> 401145: 83 c3 01 add ebx,0x1 401148: 83 fb 05 cmp ebx,0x5 40114b: 7e e8 jle 401135 <phase_6+0x41> 40114d: 49 83 c5 04 add r13,0x4 401151: eb c1 jmp 401114 <phase_6+0x20> // End /* // n = 7 - i // i = 7 - n // buf = buf.map(i -> 7-i) int *bound = rsp + 0x18 // &rsp[6] = &buf[0] int *ptr = &buf[0] ecx = 7 do { *ptr = 7 - *ptr ptr += 1 } while ptr != bound */ 401153: 48 8d 74 24 18 lea rsi,[rsp+0x18] 401158: 4c 89 f0 mov rax,r14 // r14 = rsp 40115b: b9 07 00 00 00 mov ecx,0x7 // loop start 401160: 89 ca mov edx,ecx 401162: 2b 10 sub edx,DWORD PTR [rax] // edx = edx - *rax 401164: 89 10 mov DWORD PTR [rax],edx 401166: 48 83 c0 04 add rax,0x4 40116a: 48 39 f0 cmp rax,rsi 40116d: 75 f1 jne 401160 <phase_6+0x6c> // loop end /* // long *nodes = rsp + 0x20 // n = buf[i] // nodes[i] = node[n], if n > 1 // node[1], if n <= 1 i = 0 // i = esi do { n = buf[i] if n <= 1: tmp = node1 // 0x6032d0 else: eax = 1 tmp = node1 // 0x6032d0 do tmp = tmp.next // tmp.next @ tmp[1] eax += 1 while eax != n // long *nodes = rsp + 0x20 nodes[i] = tmp i += 1 } while i != 6 */ 40116f: be 00 00 00 00 mov esi,0x0 401174: eb 21 jmp 401197 <phase_6+0xa3> 401176: 48 8b 52 08 mov rdx,QWORD PTR [rdx+0x8] 40117a: 83 c0 01 add eax,0x1 40117d: 39 c8 cmp eax,ecx 40117f: 75 f5 jne 401176 <phase_6+0x82> 401181: eb 05 jmp 401188 <phase_6+0x94> 401183: ba d0 32 60 00 mov edx,0x6032d0 401188: 48 89 54 74 20 mov QWORD PTR [rsp+rsi*2+0x20],rdx 40118d: 48 83 c6 04 add rsi,0x4 401191: 48 83 fe 18 cmp rsi,0x18 401195: 74 14 je 4011ab <phase_6+0xb7> // break 401197: 8b 0c 34 mov ecx,DWORD PTR [rsp+rsi*1] 40119a: 83 f9 01 cmp ecx,0x1 40119d: 7e e4 jle 401183 <phase_6+0x8f> // backward 40119f: b8 01 00 00 00 mov eax,0x1 4011a4: ba d0 32 60 00 mov edx,0x6032d0 4011a9: eb cb jmp 401176 <phase_6+0x82> // end // rsp + 0x20 is nodes /* // nodes[0] -> nodes[1] .... // nodes[i] -> nodes[i + 1] struct node *rbx = nodes[0] struct node **ptr = &nodes[1] struct node **bound = &nodes[6] struct node *prev = rbx loop { struct node *curr = *ptr prev.next = curr ptr += 1 if ptr == bound: break prev = curr } curr.next = NULL */ 4011ab: 48 8b 5c 24 20 mov rbx,QWORD PTR [rsp+0x20] // rbx = nodes[0] 4011b0: 48 8d 44 24 28 lea rax,[rsp+0x28] // rax = &buf[1] 4011b5: 48 8d 74 24 50 lea rsi,[rsp+0x50] // rsi = &buf[6] 4011ba: 48 89 d9 mov rcx,rbx // rcx = nodes[0] 4011bd: 48 8b 10 mov rdx,QWORD PTR [rax] 4011c0: 48 89 51 08 mov QWORD PTR [rcx+0x8],rdx 4011c4: 48 83 c0 08 add rax,0x8 4011c8: 48 39 f0 cmp rax,rsi 4011cb: 74 05 je 4011d2 <phase_6+0xde> // break 4011cd: 48 89 d1 mov rcx,rdx 4011d0: eb eb jmp 4011bd <phase_6+0xc9> // loop 4011d2: 48 c7 42 08 00 00 00 mov QWORD PTR [rdx+0x8],0x0 // end 4011d9: 00 // rbx = nodes[0] // A pointer to first node /* // Check whether the list is sorted in decreasing order or not. i = 5 // i = ebp curr = nodes[0] do { next = curr.next if curr.value < next.value: explode_bomb curr = curr.next i -= 1 } while i >= 0 */ 4011da: bd 05 00 00 00 mov ebp,0x5 // ebp = 5 4011df: 48 8b 43 08 mov rax,QWORD PTR [rbx+0x8] // rax = node[1] 4011e3: 8b 00 mov eax,DWORD PTR [rax] // eax = rax.value 4011e5: 39 03 cmp DWORD PTR [rbx],eax 4011e7: 7d 05 jge 4011ee <phase_6+0xfa> 4011e9: e8 4c 02 00 00 call 40143a <explode_bomb> 4011ee: 48 8b 5b 08 mov rbx,QWORD PTR [rbx+0x8] 4011f2: 83 ed 01 sub ebp,0x1 4011f5: 75 e8 jne 4011df <phase_6+0xeb> // End 4011f7: 48 83 c4 50 add rsp,0x50 4011fb: 5b pop rbx 4011fc: 5d pop rbp 4011fd: 41 5c pop r12 4011ff: 41 5d pop r13 401201: 41 5e pop r14 401203: c3 ret ``` 首先呼叫 `read_six_numbers` 將 input 解析成 6 個 int 並放於 buf 中。 接著對 buf 內的數字做檢查。規則如下: 1. $1 \le buf \le 6$ 2. buf[i] $\ne$ buf[j], $\forall i \ne j$ 接著將 buf 中的每一數字變為其 mod 7 的加法反元素。 為了解析接下來的程式,必須先定義一個 struct `node` ```c= struct node { int value; int index; struct node *next; }; ``` 可以發現此 struct 構成 linked list 節點。 接下來的程式將會圍繞著一個 struct node 陣列 `node_arr` 展開。該陣列中具有 6 的元素,並且位於 0x6032d0 。 首先透過 gdb 觀察該陣列: ```gdb gdb-peda$ x/12gx 0x6032d0 0x6032d0 <node1>: 0x000000010000014c 0x00000000006032e0 0x6032e0 <node2>: 0x00000002000000a8 0x00000000006032f0 0x6032f0 <node3>: 0x000000030000039c 0x0000000000603300 0x603300 <node4>: 0x00000004000002b3 0x0000000000603310 0x603310 <node5>: 0x00000005000001dd 0x0000000000603320 0x603320 <node6>: 0x00000006000001bb 0x0000000000000000 ``` 在初始狀態 linked list 的狀態為: node1 -> node2 -> node3 -> node4 -> node5 -> node6 -> NULL 接下來,為了方便解釋,會先將接下來的程式翻譯成 c 語言來解釋。 ```c= struct node *nodes[6]; // nodes[i] = node_arr[buf[i]]; for (int i=0; i<6; i++) { struct node *tmp = &node_arr[1]; n = buf[i]; if (n > 1) { for (int j=0; j<n; j++) { tmp = tmp->next; } } nodes[i] = tmp; } // link the list by the order of "nodes" struct node *prev = nodes[0]; struct node *curr; for (int i=1; i<6; i++) { curr = nodes[i]; prev->next = curr; prev = curr; } curr->next = NULL; // Check wheither the linked list is in non-increasing order curr = nodes[0]; struct node *next; for (int i=5; i>=0; i++) { next = curr->next; if (curr->value < next->value) explode_bomb(); curr = curr->next; } ``` 首先,將 nodes[i] 指向 linked list 的第 buf[i] 個節點。接著將 nodes[i] 的節點的 next 指向 nodes[i+1] 。也就是在執行結束後, linked list 會是: nodes[0] -> nodes[1] -> nodes[2] ->nodes[3] ->nodes[4] ->nodes[5] -> NULL 。 最後在檢查整個 linked list 中的值是否按照非遞增排列。 了解邏輯後,便知道我們需要按照需求找出節點間非遞增排列的順序,並依序輸入他們的 mod 7 的加法反元素。 ```python= values = [0x0000014c, 0x000000a8, 0x0000039c, 0x000002b3, 0x000001dd, 0x000001bb] tuples = [] for i in range(len(values)): tuples.append((i + 1, values[i])) tuples.sort(key = lambda tup: tup[1]) args = [] for tup in tuples: args.append(7 - tup[0]) args.reverse() payload = ' '.join(str(n) for n in args) ``` 透過一個簡單的腳本我們可以得出答案為: `4 3 2 1 6 5` 而執行後 linked list 的狀態為: ```gdb gdb-peda$ x/12gx 0x6032d0 0x6032d0 <node1>: 0x000000010000014c 0x00000000006032e0 0x6032e0 <node2>: 0x00000002000000a8 0x0000000000000000 0x6032f0 <node3>: 0x000000030000039c 0x0000000000603300 0x603300 <node4>: 0x00000004000002b3 0x0000000000603310 0x603310 <node5>: 0x00000005000001dd 0x0000000000603320 0x603320 <node6>: 0x00000006000001bb 0x00000000006032d0 ``` 邏輯上的順序為: node3 -> node4 -> node5 -> node6 -> node1 -> node2 -> NULL phase_6 的邏輯如下: ```c= void phase_6(char *input) { int buf[6]; read_six_numbers(input, buf); for (int i=0; i<6; i++) { if (!(buf[i] >= 1 && buf[i] <=6)) explode_bomb(); for (int j=i+1; j<6; j++) { if (buf[i] == buf[j]) explode_bomb(); } } for (int i=0; i<6; i++) { buf[i] = 7 - buf[i]; } static struct node_arr[] = { // init omit }; struct node *nodes[6]; // nodes[i] = node_arr[buf[i]]; for (int i=0; i<6; i++) { struct node *tmp = &node_arr[1]; n = buf[i]; if (n > 1) { for (int j=0; j<n; j++) { tmp = tmp->next; } } nodes[i] = tmp; } // link the list by the order of "nodes" struct node *prev = nodes[0]; struct node *curr; for (int i=1; i<6; i++) { curr = nodes[i]; prev->next = curr; prev = curr; } curr->next = NULL; // Check wheither the linked list is in non-increasing order curr = nodes[0]; struct node *next; for (int i=5; i>=0; i++) { next = curr->next; if (curr->value < next->value) explode_bomb(); curr = curr->next; } } ``` ## TODO - [ ] secret_phase