分析
题目说是arm相关,并给出了源代码与程序,我们来看一看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <stdio.h> #include <fcntl.h> int key1(){ asm("mov r3, pc\n"); } int key2(){ asm( "push {r6}\n" "add r6, pc, $1\n" "bx r6\n" ".code 16\n" "mov r3, pc\n" "add r3, $0x4\n" "push {r3}\n" "pop {pc}\n" ".code 32\n" "pop {r6}\n" ); } int key3(){ asm("mov r3, lr\n"); } int main(){ int key=0; printf("Daddy has very strong arm! : "); scanf("%d", &key); if( (key1()+key2()+key3()) == key ){ printf("Congratz!\n"); int fd = open("flag", O_RDONLY); char buf[100]; int r = read(fd, buf, 100); write(0, buf, r); } else{ printf("I have strong leg :P\n"); } return 0; }
|
逻辑很简单,就是三个函数的返回值相加要等于我们输入的key,使用了c语言内联arm汇编,同时题目给出了用gdb整出来的反汇编文件http://pwnable.kr/bin/leg.asm
基础知识可以参考ARM架构简介
key1
1 2 3 4 5 6 7 8 9 10
| (gdb) disass key1 Dump of assembler code for function key1: 0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008cd8 <+4>: add r11, sp, #0 0x00008cdc <+8>: mov r3, pc 0x00008ce0 <+12>: mov r0, r3 0x00008ce4 <+16>: sub sp, r11, #0 0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4) 0x00008cec <+24>: bx lr End of assembler dump.
|
这里我们可以发现光有基础知识还不够,所以补充知识
寄存器pc(r15)在arm状态下(指令长度为4,RISC等长指令集),总是指向当前指令(正在执行)+8处
故分析可得,执行 0x00008cdc 后,r3内的值为 0x00008ce4
可得返回值r0的值为 0x00008ce4
key2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| (gdb) disass key2 Dump of assembler code for function key2: 0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008cf4 <+4>: add r11, sp, #0 0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!) 0x00008cfc <+12>: add r6, pc, #1 0x00008d00 <+16>: bx r6 0x00008d04 <+20>: mov r3, pc 0x00008d06 <+22>: adds r3, #4 0x00008d08 <+24>: push {r3} 0x00008d0a <+26>: pop {pc} 0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4) 0x00008d10 <+32>: mov r0, r3 0x00008d14 <+36>: sub sp, r11, #0 0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4) 0x00008d1c <+44>: bx lr End of assembler dump.
|
这里 +8 的地方熟悉保存一波 r6,然后 +12 指令实际上是 r6 = pc+1,需要注意的是bx跳转的时候是跳转到 +20,因为指令地址会先和0xFFFFFFFE进行按位与,因为最后一位肯定是0,因此最后一位用于做标志位,bx执行时如果地址最后一位是0,表示跳到arm状态,1则跳到thumb态,而在thumb态下,每条指令2字节长,故 +20 执行后,r3的值为 0x00008d08 ,然后 r3+=4 并传值给r0
故返回值r0 = 0x00008d0c
key3
1 2 3 4 5 6 7 8 9 10
| (gdb) disass key3 Dump of assembler code for function key3: 0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!) 0x00008d24 <+4>: add r11, sp, #0 0x00008d28 <+8>: mov r3, lr 0x00008d2c <+12>: mov r0, r3 0x00008d30 <+16>: sub sp, r11, #0 0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4) 0x00008d38 <+24>: bx lr End of assembler dump.
|
这里需要关注的是lr寄存器,而lr用于保存子程序的返回地址
1 2
| 0x00008d7c <+64>: bl 0x8d20 <key3> 0x00008d80 <+68>: mov r3, r0
|
故此时 lr 的值为 0x00008d80
可得返回值r0 = 0x00008d80
exp
可得 0x00008ce4 + 0x00008d0c + 0x00008d80 = 0x1a770 = 108400
直接输入即可
参考: