考察基本的 ARM 汇编指令
题目源码
main
函数
key1
函数
key2
函数
key3
函数
题目分析
main()
中:
0x00008d84 <+72>: add r2, r4, r3
0x00008d88 <+76>: ldr r3, [r11, #-16]
-> 0x00008d8c <+80>: cmp r2, r3
x 0x00008d90 <+84>: bne 0x8da8 <main+108>
0x00008d94 <+88>: ldr r0, [pc, #44] ; 0x8dc8 <main+140>
0x00008d98 <+92>: bl 0x1050c <puts>
0x00008d9c <+96>: ldr r0, [pc, #40] ; 0x8dcc <main+144>
-> 0x00008da0 <+100>: bl 0xf89c <system>
在0x00008d84
这里计算了r2
、r3
、r4
,三个寄存器中数值的和,把结果赋给r2
。然后把r11-16h
赋给r3
,最后比较r2
,r3
是否相等。如果相等的话0x00008d90
处的bne
(非0则跳)将不会发生,程序会继续进行下去,一直到0x00008da0 <+100>
调用system()
打印flag
。
通过分析可以发现r11-16h
的值是0x00008d64
处程序调用scanf
得到的我们所输入的值。
继续分析三个寄存器中的数值是从哪里来的:
0x00008d68 <+44>: bl 0x8cd4 <key1>
-> 0x00008d6c <+48>: mov r4, r0
0x00008d70 <+52>: bl 0x8cf0 <key2>
-> 0x00008d74 <+56>: mov r3, r0
0x00008d78 <+60>: add r4, r4, r3
0x00008d7c <+64>: bl 0x8d20 <key3>
-> 0x00008d80 <+68>: mov r3, r0
0x00008d84 <+72>: add r2, r4, r3
在main()
中可以看到这三个寄存器中的数值来自key1()
、key2()
、key3()
被调用后,寄存器r0
中的值。
所以当我们输入的数值等于key1
、key2
、key3
三个函数执行后寄存器r0
的值的总和,即可以得到flag
。
解题过程
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
key1()
中r3 = pc
, r0 = r3
(注: arm
中程序计数器pc
指向下两条指令的地址)
最终 r0 = pc = 0x8cdc + 0x8
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
key2()
中 r3 = pc
, r0 = r3
这里有一条ADDS
指令,看样子像是对r3
进行了操作
0x00008d06 <+22>: adds r3, #4
但是查一下用法,发现ADDS
指令只是把 r3 + 4h
的值存入当前状态寄存器CPSR中
,并不对r3
的值进行修改,相当于 CPSR = r3 + 4
(注: ADD r3, #4
会r3
进行修改,相当于 r3 = r3 + 4
)
所以最终 r0 = pc = 0x8d04 + 0x8
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
最后的key3()
中 r3 = lr
, r0 = r3
lr
是连接寄存器 (Link Register) 当程序通过BL
或BLX
指令调用子程序时,R14
被设置成该子程序的返回地址
0x00008d7c <+64>: bl 0x8d20 <key3>
0x00008d80 <+68>: mov r3, r0
main()
调用key3()
时,key3()
的返回地址是它的下一条指令的地址 即 lr = 0x8d80
最终 r0 = lr = 0x8d80
现在已经找到了三个r0
的值,相加即可
More
关于ARM
寄存器和指令的更多信息可以看一下我这里的总结:
http://blog.csdn.net/smalosnail/article/details/53065048
http://blog.csdn.net/smalosnail/article/details/53048784