pwnable.kr之leg
查看原题代码:
#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; }
知道我们只要使输入的值等于 key1()+key2()+key3() ,就能拿到flag
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.
核心代码为:
0x00008cdc <+8>: mov r3, pc 0x00008ce0 <+12>: mov r0, r3
意思是把pc寄存器的值传递给r3寄存器,再把r3寄存器的值传递给r0,然后r0寄存器的值作为函数的返回值
在ARM汇编指令中,寄存器pc的值为当前指令地址加8个字节,所以寄存器pc中的值为 0x00008cdc+8,则函数返回值为 0x00008cdc+8
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.
核心代码为:
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
pc寄存器的值为:0x00008cfc+8 ,则r6寄存器的值为:pc+1=0x00008cfc+8+1,第二行指令 bx 是跳转指令,并且还会根据目标地址最低位判断是否更该处理的状态,此时 r6=0x00008cfc+8+1=0x00008d05,转化为二进制为 1000110100000101,最低位为1,所以应该转换为 Thumb状态。
由于转换状态,第三行汇编代码中pc=0x00008d04+4(注意:只增加4个字节),所以 r3=pc=0x00008d04+4
第四行,r3存储的值加4,值为 0x00008d04+4+4,按顺序执行下去可得最后 key2() 的返回值为:0x00008d04+4+4
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.
核心代码为:
0x00008d28 <+8>: mov r3, lr 0x00008d2c <+12>: mov r0, r3
lr寄存器存储的是当前函数执行完毕后的返回地址,故需要去main()函数中去找:
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
观察可知 lr的值为 0x00008d80,故key3()的返回值为: 0x00008d80
那么我们最后应该输入的值为 :0x00008cdc+8+0x00008d04+4+4+0x00008d80=1A770,由于题目要求输入十进制,故转化为十进制为 108400
flag为:My daddy has a lot of ARMv5te muscle!
- 知道C语言可以内嵌汇编代码,并了解了内嵌汇编的基本语法
- 学习了ARM汇编指令,在ARM汇编中,其寄存器是r0~r15,其中r13(别名:sp),它通常指向通常指向异常模式所专用的堆栈,也就是说五种异常模式、非异常模式(用户模式和系统模式),都有各自独立的堆栈,用不同的堆栈指针来索引。这样当ARM进入异常模式的时候,程序就可以把一般通用寄存器压入堆栈,返回时再出栈,保证了各种模式下程序的状态的完整性。lr(r14),存放的是函数的返回地址,一般调用子函数用的比较多,pc(r15),值为当前操作的地址
- 放几个写得比较好的博客:https://blog.csdn.net/bytxl/article/details/49883103
https://blog.csdn.net/aguangg_6655_la/article/details/53613270