pwnable.kr之leg初探arm指令
27 Apr 2017
|
基础知识
-
程序计数器(PC)总是指向“正在取指”的指令
由于ARM是3级流水线的,所以当第N条指令正在执行时,实际上第N+2条指令正在取值,此时PC是指向N+2的
因为处理器处于Thumb状态时,每条指令为2字节,所以PC = 当前执行指令的位置 + 4
ARM状态下,由于每条指令是4字节(32位),所以PC = 当前执行指令的位置 + 8 - ARM7采用的是三级流水线的冯.诺伊曼结构,ARM9采用五级流水线的哈佛结构。ARM7流水线包括取值(fetch)、译码(decode)、执行(excute)。ARM7流水线在译码阶段不读取操作数寄存器,因此执行阶段的PC值和取值阶段的PC值关系为:PC(excute)= PC(fetch)+ 8。
ARM9流水线包括取值(fetch)、译码(decode)、执行(excute)、缓冲/数据(buffer/data)、回写(write-back)寄存器堆。ARM9流水线在译码阶段已经开始读取操作数寄存器,因此译码阶段的PC值和取值阶段的PC值关系为:PC(decode) = PC(fetch) + 4。因此执行阶段的PC值和译码阶段的PC值关系为:PC(excute) = PC(decode) + 4。
为了保证ARM9流水线和ARM7流水线兼容,ARM9流水线将取指阶段的PC值跨过取指和译码流水线寄存器,直接送往译码阶段寄存器,这样仍然保证执行阶段的PC值和取值阶段的PC值关系为PC(excute) = PC(fetch) + 8。 -
thumb指令
bx的语法格式
BX{} <Rm> <cond>为指令执行的条件码。当<cond>忽略时指令为无条件执行。 <Rm>该寄存器中为跳转的目标地址。当 寄存器的bit[0]为0时,目标地址处的指令为ARM指令; 当 寄存器的bit[0]为1时,目标地址处的指令为Thumb指令。 - 子程序返回的三种方法
1.MOV PC,LR
2.BL LR
3.在子程序入口处使用以下指令将R14存入堆栈,R14保存着LR的值,即保存着返回地址
leg.c源码
#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;
}
leg.asm源码
(gdb) disass main
Dump of assembler code for function main:
0x00008d3c <+0>: push {r4, r11, lr}
0x00008d40 <+4>: add r11, sp, #8
0x00008d44 <+8>: sub sp, sp, #12
0x00008d48 <+12>: mov r3, #0
0x00008d4c <+16>: str r3, [r11, #-16]
0x00008d50 <+20>: ldr r0, [pc, #104] ; 0x8dc0 <main+132>
0x00008d54 <+24>: bl 0xfb6c <printf>
0x00008d58 <+28>: sub r3, r11, #16
0x00008d5c <+32>: ldr r0, [pc, #96] ; 0x8dc4 <main+136>
0x00008d60 <+36>: mov r1, r3
0x00008d64 <+40>: bl 0xfbd8 <__isoc99_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
0x00008d88 <+76>: ldr r3, [r11, #-16]
0x00008d8c <+80>: cmp r2, r3
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>
0x00008da4 <+104>: b 0x8db0 <main+116>
0x00008da8 <+108>: ldr r0, [pc, #32] ; 0x8dd0 <main+148>
0x00008dac <+112>: bl 0x1050c <puts>
0x00008db0 <+116>: mov r3, #0
0x00008db4 <+120>: mov r0, r3
0x00008db8 <+124>: sub sp, r11, #8
0x00008dbc <+128>: pop {r4, r11, pc}
0x00008dc0 <+132>: andeq r10, r6, r12, lsl #9
0x00008dc4 <+136>: andeq r10, r6, r12, lsr #9
0x00008dc8 <+140>: ; <UNDEFINED> instruction: 0x0006a4b0
0x00008dcc <+144>: ; <UNDEFINED> instruction: 0x0006a4bc
0x00008dd0 <+148>: andeq r10, r6, r4, asr #9
End of assembler dump.
(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.
(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.
(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.
(gdb)
这里逻辑很简单,只需要key1()+key2()+key3()) == key满足这个条件就可以通过。有了上面的准备知识就可以做这题了。
key1:直接r0存的是0x8cdc的pc值,所以r0 = 0x8cdc + 8 = 0x8ce4
key2:
0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!)
0x00008cfc <+12>: add r6, pc, #1
0x00008d00 <+16>: bx r6
这里r6 = 0x8cfc + 8 + 1 = 0x8d05,此时bit[0]为1,所以接下来就是thumb指令,每个指令两个字节,所以PC=PC+4,所以r3 = r0 = 0x8d04 + 4 + 4 = 0x8d0c
然后main函数里面r2 = r4 + r3 = 0x8ce4 + 0x8d10
key3:
r0 = r0 = lr = 0x8d80,lr存的就是返回地址
main函数里面 r2 = r4 + r3 = 0x8ce4 + 0x8d0c + 0x8d80 = 108400
flag:My daddy has a lot of ARMv5te muscle!