# ARM ## I. Build binary và run binary ### Build - 32bit : `arm-linux-gnueabi-gcc -o arm32 source.c` - 64bit : `aarch64-linux-gnu-gcc -o arm64 source.c` -> các option `PIE`, `RELRO` giống như trong AMD ### Run - 32bit : `qemu-arm -L /usr/arm-linux-gnueabi ./arm32` - 64bit : `qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./arm64` ## II. Debug binary ### Build docker ARM64 ``` sudo docker build --platform linux/arm64 -t demo . sudo docker run --rm -p 1234:1234 -it --platform linux/arm64 {ID_images} ``` ### Debug static - Terminal 1 : ``` qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu/ ./chall ``` hoặc (với 32-bit) ``` qemu-arm -g 1234 -L /usr/arm-linux-gnueabi ./arm32 ``` - Terminal 2 : ``` gdb-multiarch ./chall target remote 0:1234 ``` ### Debug động - script : ```py def main(): context.binary = exe = ELF('./chall',checksec=False) # p = process(exe.path) p = process(["qemu-aarch64", "-g", "1234", "-L", "/home/dynhlucw/pwnable/Wargame/pwnCollege/ropARM/level1.0", './chall']) GDB(p) sl(p,b'AAAA') p.interactive() ``` -> Không muốn DEBUG thì comment dòng `qemu-aarch64` -> chạy `process(exe.path)` - Terminal 1 : ``` python3 solve.py ``` -> GDB attach mở -> `target remote 0:1234` ## III. ARM 32-bit exploitation ### 1. Thanh ghi - `r1-r6` : Các thanh ghi chung - `r0` : Giống eax, lưu giá trị trả về - `r7` : lưu syscall number - `r11` : frame pointer (giống ebp) = `fp` - `r12` : intra procedural call = `ip` - `r13` : stack pointer (giống esp) = `sp` - `r14` : link register -> Chứa return address của hàm con. = `lr` - `r15` : program counter -> trỏ đên lệnh tiếp theo = `pc` - `cpsr` ### 2. Load and store instructions - `LDR R1, [R2]` - `STR R1, [R0]` ### 3. Stack - push, pop -> push {r1,r2} = push {r2} -> push {r1} ### 4. function and calling convention ```clikes! # source.c #include<stdio.h> #include<stdlib.h> void setup() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); } void callee(){ char buf1[0x10] ; printf("Enter input for callee: ") ; fgets(buf1,0x10,stdin) ; } void caller(){ char buf2[0x30] ; printf("Enter input for caller: ") ; fgets(buf2,0x10,stdin) ; callee() ; } int main(){ setup(); char buf3[0x50] ; caller() ; return 0 ; } ``` #### Debug - Hàm lấy đối số : ![image](https://hackmd.io/_uploads/ByX0JS9Kkl.png) - stack frame của main trước khi gọi `caller` ![image](https://hackmd.io/_uploads/HJQ7lHctJx.png) - các thanh ghi quan trọng ![image](https://hackmd.io/_uploads/BJnJGH5Fke.png) - sp = esp - r11 = fp = ebp - pc = eip - lr : lưu return address - Các lệnh tạo và kết thúc 1 frame ```asm 0x00010664 <+0>: push {r11, lr} 0x00010668 <+4>: add r11, sp, #4 ------------------------------------------ -> push return addr -> push frame pointer ``` ```asm 0x106c4 <caller+96> sub sp, fp, #4 SP => 0x407fff58 (0x407fff5c - 0x4) 0x106c8 <caller+100> pop {fp, pc} <main+36> ------------------------------------------- sp = fp - 4 pop fp -> pop pc ``` ### 5. ROP chain trong arm32 ## IV. ARM 64-bit exploitation ### Thanh ghi - `x0` : lưu địa chỉ trả về - `x0 - x7` : đối số cho hàm - `x8 - x18` : thanh ghi chung - `x19 - x28` : callee-saved registers - `x29` = `fp` : giống `rbp` - `x30` = `lr` : lưu `return address` - `x31` = `sp` : giống `rsp` - `pc` : giống `RIP` ### Lệnh ASM ### Function và calling convention - lệnh `ret` : nhảy tới địa chỉ được lưu trong thanh ghi `x30` (`Link Register` - `LR`) - Các lệnh gọi hàm: `BL`, `BLR`, `BR` - `BL` (Branch with Link) : nhảy đến hàm, lưu return address ở `x30` (gọi hàm có địa chỉ cố định) - `BLR` (Branch with Link to Register) : nhảy đến địa chỉ lưu ở thanh ghi (ví dụ: blr x9, ...) - `BR` (Branch to Register) : nhảy đến địa chỉ trong thanh ghi nhưng không lưu `return address` vào `x30` hay `LR` #### calling convention - `stp x29, x30, [sp, #-80]!` ![image](https://hackmd.io/_uploads/SyhcY8cKkg.png) - `stp` = store pair : `sub sp, -0x50` -> lưu `x29` vào `[sp]` -> lưu `x30`(`LR`) vào `[sp+8]` - `mov x29, sp` : mục đích để tạo frame cho 1 hàm, tuy nhiên frame trong arm64 ngược lại so với amd64 ![image](https://hackmd.io/_uploads/HyXm6IcY1g.png) ![image](https://hackmd.io/_uploads/rkHOpLcYyg.png) - `ldp x29, x30, [sp], #0x30` : khôi phục lại x30, x29 - `ldp` = load pair : load giá trị [sp+0x30] vào `x29` và [sp+0x38] vào `x30` - `ret` = `BR x30` : nhảy đến địa chỉ lưu ở `x30` ### ARM64 Rop chain #### 1. Lấy libc_base address - Sau khi leak được 1 địa chỉ từ libc, ta cần tìm offset để tìm được libc_base 1. Tắt `ASLR` ở tất cả Terminal -> để làm cho địa chỉ cố định 2. Tìm `libc_base` cố định - chạy `binary` -> chờ nhập `input` `qemu-aarch64 -L /home/dynhlucw/pwnable/CTFs/wpictf2021/strong-arm ./arm_patched` - Đọc `infor proc maps` của tiến trình này : - Lấy pid : `ps aux` - Sau đó lấy `pid` của tiến trình -> `cat /proc/{pid}/maps` ![image](https://hackmd.io/_uploads/SyHFEN191l.png) -> Từ đấy sẽ có được `libc_base` #### 2. Đặt break point khi bật PIE - Từ lấy `libc_base`, ta cũng có thể tắt `ASLR` để coi như tắt `PIE` #### 3. Tìm gadget để ROP - Vì ARM64 sẽ call `x30-lr` để nhảy vào 1 hàm khác -> được xem như return address. - Ngoài ra, để nhảy còn có các lệnh call hàm như : `bl`, `blr`, `br` - Cần tìm các gadget : `MOV`, `LDR` và `LDP` -> để load các giá trị vào các thanh ghi :::success - **Workflow để call system("/bin/sh\x00")** - load địa chỉ `/bin/sh` vào `x0` hoặc gián tiếp rồi đưa vào `x0` - load địa chỉ system vào thanh ghi nào đó để mục tiêu call thanh ghi đó - dùng `bl`, `br`, `blr` để call thanh ghi - **Tư duy ngược** - Nghĩ làm sao để call system bằng thanh ghi nào trước - Lần theo đó để load vào thanh ghi chọn để call system :::