当前位置: 代码迷 >> 综合 >> pwnable.kr leg
  详细解决方案

pwnable.kr leg

热度:90   发布时间:2023-12-17 11:12:11.0

看一下题目
在这里插入图片描述
提供了两个文件下载,我们来直接看一下

<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, #80x00008d44 <+8>:	sub	sp, sp, #120x00008d48 <+12>:	mov	r3, #00x00008d4c <+16>:	str	r3, [r11, #-16]0x00008d50 <+20>:	ldr	r0, [pc, #104]	; 0x8dc0 <main+132>0x00008d54 <+24>:	bl	0xfb6c <printf>0x00008d58 <+28>:	sub	r3, r11, #160x00008d5c <+32>:	ldr	r0, [pc, #96]	; 0x8dc4 <main+136>0x00008d60 <+36>:	mov	r1, r30x00008d64 <+40>:	bl	0xfbd8 <__isoc99_scanf>0x00008d68 <+44>:	bl	0x8cd4 <key1>0x00008d6c <+48>:	mov	r4, r00x00008d70 <+52>:	bl	0x8cf0 <key2>0x00008d74 <+56>:	mov	r3, r00x00008d78 <+60>:	add	r4, r4, r30x00008d7c <+64>:	bl	0x8d20 <key3>0x00008d80 <+68>:	mov	r3, r00x00008d84 <+72>:	add	r2, r4, r30x00008d88 <+76>:	ldr	r3, [r11, #-16]0x00008d8c <+80>:	cmp	r2, r30x00008d90 <+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, #00x00008db4 <+120>:	mov	r0, r30x00008db8 <+124>:	sub	sp, r11, #80x00008dbc <+128>:	pop	{r4, r11, pc}0x00008dc0 <+132>:	andeq	r10, r6, r12, lsl #90x00008dc4 <+136>:	andeq	r10, r6, r12, lsr #90x00008dc8 <+140>:			; <UNDEFINED> instruction: 0x0006a4b00x00008dcc <+144>:			; <UNDEFINED> instruction: 0x0006a4bc0x00008dd0 <+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, #00x00008cdc <+8>:	mov	r3, pc0x00008ce0 <+12>:	mov	r0, r30x00008ce4 <+16>:	sub	sp, r11, #00x00008ce8 <+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, #00x00008cf8 <+8>:	push	{r6}		; (str r6, [sp, #-4]!)0x00008cfc <+12>:	add	r6, pc, #10x00008d00 <+16>:	bx	r60x00008d04 <+20>:	mov	r3, pc0x00008d06 <+22>:	adds	r3, #40x00008d08 <+24>:	push	{r3}0x00008d0a <+26>:	pop	{pc}0x00008d0c <+28>:	pop	{r6}		; (ldr r6, [sp], #4)0x00008d10 <+32>:	mov	r0, r30x00008d14 <+36>:	sub	sp, r11, #00x00008d18 <+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, #00x00008d28 <+8>:	mov	r3, lr0x00008d2c <+12>:	mov	r0, r30x00008d30 <+16>:	sub	sp, r11, #00x00008d34 <+20>:	pop	{r11}		; (ldr r11, [sp], #4)0x00008d38 <+24>:	bx	lr
End of assembler dump.
(gdb) 

<leg.c>就是主程序了
<leg.asm>就是程序内的反汇编形式
看.c文件逻辑很清楚,输入一个key==(key1()+key2()+key3())的运算,但仔细看key1/2/3函数的实现并不是我所熟悉的x86/x64汇编语法而是arm,这块基本已经忘得差不多了,所以还是回去复习了一下,不熟悉的同学也可以先熟悉一下arm的语法然后就可以上手了
直接看.asm文件直观一点
先看key1

(gdb) disass key1
Dump of assembler code for function key1:0x00008cd4 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)0x00008cd8 <+4>:	add	r11, sp, #00x00008cdc <+8>:	mov	r3, pc0x00008ce0 <+12>:	mov	r0, r30x00008ce4 <+16>:	sub	sp, r11, #00x00008ce8 <+20>:	pop	{r11}		; (ldr r11, [sp], #4)0x00008cec <+24>:	bx	lr
End of assembler dump.
   0x00008cdc <+8>:	mov	r3, pc0x00008ce0 <+12>:	mov	r0, r3

真正执行的是这两句,其他的是一些函数相关的栈操作。寄存器pc(r15)在arm状态下(指令长度为4,RISC等长指令集),总是指向当前指令(正在执行)+8处,这里执行了0x00008cdc后,r3为0x00008ce4。此后将r3给了r0作为返回值(ARM用r0传递返回值)

再看key2

(gdb) disass key2
Dump of assembler code for function key2:0x00008cf0 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)0x00008cf4 <+4>:	add	r11, sp, #00x00008cf8 <+8>:	push	{r6}		; (str r6, [sp, #-4]!)0x00008cfc <+12>:	add	r6, pc, #10x00008d00 <+16>:	bx	r60x00008d04 <+20>:	mov	r3, pc0x00008d06 <+22>:	adds	r3, #40x00008d08 <+24>:	push	{r3}0x00008d0a <+26>:	pop	{pc}0x00008d0c <+28>:	pop	{r6}		; (ldr r6, [sp], #4)0x00008d10 <+32>:	mov	r0, r30x00008d14 <+36>:	sub	sp, r11, #00x00008d18 <+40>:	pop	{r11}		; (ldr r11, [sp], #4)0x00008d1c <+44>:	bx	lr
End of assembler dump.
   0x00008cfc <+12>:	add	r6, pc, #10x00008d00 <+16>:	bx	r60x00008d04 <+20>:	mov	r3, pc0x00008d06 <+22>:	adds	r3, #40x00008d08 <+24>:	push	{r3}

首先将r6变为0x00008d05,然后看一下bx的解释

BX: 带状态切换的跳转。最低位为1时,切换到Thumb指令执行,为0时,解释为ARM指令执行。

也就是bx跳转到 0x00008d04 <+20>(指令地址会先和0xFFFFFFFE进行按位与,因为最后一位肯定是0,因此最后一位用于做标志位,bx执行时如果地址最后一位是0,表示跳到arm状态,1则跳到thumb态),因为地址最低位是1,所以切换为thumb状态。
然后pc传递给r3,此时r3就是0x00008d08(thumb状态每条指令是2字节长)。接着对r3增加4,变成0x00008d0c。最后把r3给r0作为返回值,也就是0x00008d0c。此后通过bx跳回main(此前bl指令将pc-4存在lr中)时,状态又换为arm。

最后我们来看一下key3

(gdb) disass key3
Dump of assembler code for function key3:0x00008d20 <+0>:	push	{r11}		; (str r11, [sp, #-4]!)0x00008d24 <+4>:	add	r11, sp, #00x00008d28 <+8>:	mov	r3, lr0x00008d2c <+12>:	mov	r0, r30x00008d30 <+16>:	sub	sp, r11, #00x00008d34 <+20>:	pop	{r11}		; (ldr r11, [sp], #4)0x00008d38 <+24>:	bx	lr
End of assembler dump.
   0x00008d28 <+8>:	mov	r3, lr0x00008d2c <+12>:	mov	r0, r3

lr的值给了r3,最后再传给r0返回,重点再lr寄存器保存的是什么,我们看一些lr的解释

连接寄存器r14(LR):每种模式下r14都有自身版组,它有两个特殊功能。(1)保存子程序返回地址。使用BL或BLX时,跳转指令自动把返回地址放入r14中;子程序通过把r14复制到PC来实现返回,通常用下列指令之一:MOV PC, LR BX LR通常子程序这样写,保证了子程序中还可以调用子程序。stmfd sp!, {lr}……ldmfd sp!, {pc}(2)当异常发生时,异常模式的r14用来保存异常返回地址,将r14如栈可以处理嵌套中断。

说简单点lr寄存器存储的是函数的返回地址,也就是lr=0x00008d7c <+64>: bl 0x8d20 < key3>

至此,我们算出了三个返回值,加起来为:0x8ce4+0x8d0c+0x8d80 = 0x1A770 = 108400(输入的是%d这个要注意转换,这里已经转换好了)

直接输入即可获得flag

在这里插入图片描述