서브웨이

pwnable.kr leg 풀이 본문

Pwnable/pwnable.kr

pwnable.kr leg 풀이

샌드위치메이커 2020. 8. 13. 14:42

자 일단 뭘 다운받으랍니다. 일단 다운받아봅시다.

#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;
}

이게 C파일이고

(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) 

이게 어셈 파일입니다.

 

문제를 보니 지금까지 봐왔던 인텔의 어셈블리코드가 아니라 ARM의 어셈블리코드를 해석하는 문제인 것 같습니다.

만약 어셈블리어에 익숙하지 않다면 공부를 하고 푸는것을 추천드립니다.

일단 한번 읽어봅시다.

C코드는 대충 읽어보니 key1,2,3를 다 더한 값을 입력하면 플래그가 출력되는 구조입니다.

   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

이제 어셈코드의 이 부분을 보면 key1,2,3에서 리턴된 값이 담긴 r0값들을 전부 더해서 r11에 저장된 주소에서 16을 뺸  위치에 있는 값과 비교하는 구조입니다. bl명령어는 복귀할 위치를 저장하고 분기되어 특정위치로 이동하는 명령입니다.

ldr 명령어에 대한 설명은 이 글을 보면 됩니다.

egloos.zum.com/rousalome/v/9973266

 

arm instruction(명령어) - ldr

그럼 ldr 명령어의 정의에 대해서 같이 배워볼까요? LDR 명령어는 메모리에서 워드를 레지스터로 읽어 드리는 동작입니다. 자 그럼 아래 명령어를 예를 들어 같이 볼까요? 참고로 R1은 0xD2FB0000라고

egloos.zum.com

 

자 그럼 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

간단히 +8, +12 부분만 해석하면 됩니다. PC를 r3에 저장하고 그 값을 다시 r0에 저장하는 명령어입니다.

PC는 다음 실행될 명령어의 주소이기 때문에 key1의 리턴값은 0x8ce0이 됩니다..... 라고 생각했지만 arm에서는 PC의 값이 인텔의 방식과는 조금 다릅니다.

먼저 ARM에서 명령을 실행할 때에는 fetch, decode, execute, write의 4단계로 실행이 됩니다.

간단히 설명해서, 첫번째 그림과 같이 순차적으로 작업을 수행한다면 일을 끝마칠 수는 있지만 결국 하나의 작업을 하는동안 다른 기계들은 놀고있는게 되니 두번째 그림과 같이 노는 기계 없이 빠르게 일을 처리하기 위해 fetch, decode, execute, write의 과정이 파이프라이닝이 되어 진행됩니다.

이런 구조에서 PC는 execute 단계를 기준으로 fetch 단계의 주소를 나타내기 때문에 다음 명령어가 아니라 다다음 명령어의 주소가 됩니다. 따라서 key1의 리턴값은 0x8ce4이 됩니다.

 

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

어렵지 않게 밑에서부터 보면 r0에 r3의 값이 들어가고, r3의 값에 4가 더해졌고, 결국 r3는 PC(0x8d08)의 값이라는 것을 알 수 있습니다. 그럼 리턴값인 r0의 값은 0x8d0c가 됩니다.

 

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

간단히 lr의 값을 불러와서 r3에 넣고 그 값을 그대로 r0에 넣어 리턴하네요. 그럼 lr이 무슨 값인지 알아봅시다.

hyunmini.tistory.com/80

 

ARM Assembly 정리 - ARM 기본 개념

ARM Assembly ARM(Advanced Risc Machine) x86 으로 대표되는 CISC 라인과는 반대인 최근에는 모바일이 대세가 되면서 ARM(RISC) 프로세서도 많이 사용되고 있다. 모바일 점검을 하다 보면 간혹 ARM 리버싱을..

hyunmini.tistory.com

그냥 함수가 끝나면 리턴되는 주소를 말합니다. 그럼 key3가 호출된 명령어 다음 주소인 0x8d80이 되겠네요.

 

그럼 이렇게 리턴된 값들을 다 더하면~

예 그렇습니다. 그럼 저 10진수 값을 집어넣어봅시다.

짜잔

'Pwnable > pwnable.kr' 카테고리의 다른 글

pwnable.kr shellshock 풀이  (0) 2020.08.17
pwnable.kr mistake 풀이  (0) 2020.08.14
pwnable.kr input 풀이  (0) 2020.08.11
pwnable.kr random 풀이  (0) 2020.08.11
pwnable.kr passcode 풀이  (0) 2020.07.21
Comments