--- tags: jserv --- # Bomb Lab 實作紀錄 本次實驗中,參考資料有 * 以往同學在 ARM 環境下已 qemu 模擬的[紀錄](https://hackmd.io/s/HJe2aUvFf) * [如何以 qemu 模擬 AArch64環境](https://blahcat.github.io/2018/01/07/building-a-debian-stretch-qemu-image-for-aarch64/) ## 實驗目的 1. 了解 Aarch64 及 arm 中的[差異](https://en.wikipedia.org/wiki/ARM_architecture#AArch64_features),AArch64 為後來提出的指令集架構,熟悉AArch64 指令集,並分析 assembly code 實作 bomb lab 需求 2. 以 qemu 模擬 AArch64 硬體平台,進一步以 GDB 分析 register 和 assembly code ## 以 qemu 模擬 AArch64 硬體平台 1. 我們一開始參考老師給我們的[連結](https://blahcat.github.io/2018/01/07/building-a-debian-stretch-qemu-image-for-aarch64/),試著一步一步照著上面給定的步驟去走,不過最後失敗了,我們最後是以[這個連結](http://www.cnblogs.com/wangaohui/p/5184476.html)中的教學,改裝 ubuntu image 才順利成功。 2. 我們有嘗試過在 VM 內,實際編譯程式碼,並以 GDB debug 但 GCC 和 GDB 在 VM 內都裝不起來,後來有想那是不是可以從 VM 內啟動一個 debug server 再從 host 去連,但依然失敗。 3. 最後我們效法之前同學做的作法不在 VM 內,而是以 `qemu-aarch64 -L /usr/aarch64-linux-gnu/ my_elf` 實際 run 起 process 等待連接,並以 `aarch64-gdb my_elf` 連接我們想要監控的進程。 ## aarch64-gdb, aarch64-gcc 我們一開始參考了[這個網站](https://www.archlinux.org/packages/community/x86_64/aarch64-linux-gnu-gdb/)想要安裝 aarch64-linux-gnu-gdb,但是 apt install 時卻找不到這包 package,於是後來在[這裡](https://releases.linaro.org/components/toolchain/binaries/latest-5/aarch64-linux-gnu/)的[這個連結](https://releases.linaro.org/components/toolchain/binaries/latest-5/aarch64-linux-gnu/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu.tar.xz)中下載解壓縮後在 `gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin` 中找到了 `aarch64-linux-gnu-gdb`並且將它軟連結至 `/usr/bin` ```shell $ cd /usr/bin $ ln -s ~/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gdb aarch-gdb ``` 這樣的話之後想要開啟 aarch64 gdb 就只要 ```shell $ aarch-gdb my_elf ``` 同理我們的 aarch-gcc 也是相同的做法 ``` $ ln -s ~/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc aarch-gcc $ aarch-gcc my_file.c ``` ## 利用 AArch64 gdb 監控執行 bomb 我們設計的 bomb.c 有兩個 phase: - phase 1 為字串比對,如果輸入字串為 ubuntu 的話,就通過 phase 1,反之則爆炸 - phase 2 為數字的比對,如果輸入的數字符合我們設計的數字 0xAAAAAAAAAAAAAAAA ,也就是奇數位皆是 1 的話就通過 phase 2 並結束程式,反之則會爆炸 ```C= #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> void phase_1(char *user_input) { char *ans = "ubuntu"; if(strcmp(user_input, ans)) { printf("Bomb explode !!!!!! hahahaha\n"); exit(1); } else { printf("phase_1 pass!!\n"); } } void phase_2(uint64_t user_input) { if(!(user_input ^ 0xAAAAAAAAAAAAAAAA)) printf("phase_2 pass !!\n"); else { printf("Bomb explode !!!!!! hahahaha\n"); exit(1); } } int main() { printf("Start to defuse bomb!!!\n"); char p1_input[100]; printf("enter a string\n"); scanf("%s", p1_input); phase_1(p1_input); printf("phase 1 defuse !!!\n"); uint64_t p2_input; printf("enter a long unsigned integer\n"); scanf("%lu", &p2_input); phase_2(p2_input); printf("phase 2 defuse !!!\n"); return 0; } ``` 使用 aarch-gcc 來編譯上面這段程式碼 ```shell $ aarch-gcc bomb.c ``` 先背景執行程式在 port 1234 ```shell $ qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu/ a.out ``` 開啟 aarch-gdb 來觀察執行時期的行為 ```shell $ aarch-gdb ./a.out ``` 進入 gdb,連上 port 1234 ```shell $ target remote :1234 ``` 成功訊息 ![](https://i.imgur.com/XfPYSFv.png) 設定 break point ``` shell (gdb) break phase_1 Breakpoint 1 at 0x400798 (gdb) break phase_2 Breakpoint 2 at 0x4007ec ``` 再輸入 ```shell (gdb) continue ``` 在另外一個 terminal 輸入 'test' ```shell $ qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu/ a.out Start to defuse bomb!!! enter a string test ``` 接著雖然 'test' 跟我們設定的 'ubuntu' 不一樣,但因為進入了 break point 所以還沒爆炸,這時候我們可以去看這時候的 assemble code ```shell (gdb) disassemble Dump of assembler code for function phase_1: 0x0000000000400790 <+0>: stp x29, x30, [sp, #-48]! 0x0000000000400794 <+4>: mov x29, sp => 0x0000000000400798 <+8>: str x0, [x29, #24] 0x000000000040079c <+12>: adrp x0, 0x400000 0x00000000004007a0 <+16>: add x0, x0, #0x970 0x00000000004007a4 <+20>: str x0, [x29, #40] 0x00000000004007a8 <+24>: ldr x0, [x29, #24] 0x00000000004007ac <+28>: ldr x1, [x29, #40] 0x00000000004007b0 <+32>: bl 0x400610 <strcmp@plt> 0x00000000004007b4 <+36>: cmp w0, wzr 0x00000000004007b8 <+40>: b.eq 0x4007d0 <phase_1+64> // b.none 0x00000000004007bc <+44>: adrp x0, 0x400000 0x00000000004007c0 <+48>: add x0, x0, #0x978 0x00000000004007c4 <+52>: bl 0x400600 <puts@plt> 0x00000000004007c8 <+56>: mov w0, #0x1 // #1 0x00000000004007cc <+60>: bl 0x4005b0 <exit@plt> 0x00000000004007d0 <+64>: adrp x0, 0x400000 0x00000000004007d4 <+68>: add x0, x0, #0x998 0x00000000004007d8 <+72>: bl 0x400600 <puts@plt> 0x00000000004007dc <+76>: ldp x29, x30, [sp], #48 0x00000000004007e0 <+80>: ret End of assembler dump. ``` 執行 stepi 直到 0x00000000004007b0 這個位址 ```shell (gdb) stepi ``` 這時候去看 x0 跟 x1 這兩個暫存器 ```shell (gdb) x/s $x0 0x40007ffc40: "test" (gdb) x/s $x1 0x400970: "ubuntu" ``` 如果直接將 $x1 的值 assign 給 $x0 並執行 strcmp 的話就能順利通過 phase 1 ```shell (gdb) set $x0 = $x1 (gdb) continue ``` 在另外一個 terminal 就能看到成功的指示 ```shell $ qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu/ a.out Start to defuse bomb!!! enter a string test phase_1 pass!! phase 1 defuse !!! enter a long unsigned integer ``` 接著進入 phase 2 ```shell $ qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu/ a.out Start to defuse bomb!!! enter a string test phase_1 pass!! phase 1 defuse !!! enter a long unsigned integer 55 ``` 並在另外一邊觀察 phase_2 的 assembly code ```shell (gdb) disassemble Dump of assembler code for function phase_2: 0x00000000004007e4 <+0>: stp x29, x30, [sp, #-32]! 0x00000000004007e8 <+4>: mov x29, sp => 0x00000000004007ec <+8>: str x0, [x29, #24] 0x00000000004007f0 <+12>: ldr x1, [x29, #24] 0x00000000004007f4 <+16>: mov x0, #0xaaaaaaaaaaaaaaaa // #-6148914691236517206 0x00000000004007f8 <+20>: cmp x1, x0 0x00000000004007fc <+24>: b.ne 0x400810 <phase_2+44> // b.any 0x0000000000400800 <+28>: adrp x0, 0x400000 0x0000000000400804 <+32>: add x0, x0, #0x9a8 0x0000000000400808 <+36>: bl 0x400600 <puts@plt> 0x000000000040080c <+40>: b 0x400824 <phase_2+64> 0x0000000000400810 <+44>: adrp x0, 0x400000 0x0000000000400814 <+48>: add x0, x0, #0x978 0x0000000000400818 <+52>: bl 0x400600 <puts@plt> 0x000000000040081c <+56>: mov w0, #0x1 // #1 0x0000000000400820 <+60>: bl 0x4005b0 <exit@plt> 0x0000000000400824 <+64>: ldp x29, x30, [sp], #32 0x0000000000400828 <+68>: ret End of assembler dump. ``` 在以上這邊觀察到如果輸入的數字為 0xaaaaaaaaaaaaaaaa ,則會執行 0x0000000000400800 ~ 0x000000000040080c 這段並且跳過 exit 直接到 0x0000000000400824;如果輸入的數字錯誤的話,則會在 0x00000000004007fc 這裡直接跳到 0x0000000000400810 ~ 0x0000000000400820 並離開這支程式。 先執行幾次 stepi 直到 0x00000000004007f8 ```shell (gdb) stepi ``` 並觀察 x0 及 x1 ``` (gdb) x/s $x0 0xaaaaaaaaaaaaaaaa: <error: Cannot access memory at address 0xaaaaaaaaaaaaaaaa> (gdb) x/s $x1 0x37: <error: Cannot access memory at address 0x37> ``` 一樣直接將 $x0 的值 assign 給 $x1 ```shell (gdb) set $x1 = $x0 ``` 就會在另外一邊看到 phase 2 成功的指示 ```shell $ qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu/ a.out Start to defuse bomb!!! enter a string test phase_1 pass!! phase 1 defuse !!! enter a long unsigned integer 55 phase_2 pass !! phase 2 defuse !!! ```