War Game/Pwn(pwnable.kr)

[Pwnable.kr] leg 문제풀이

zyari 2019. 12. 2. 17:32
반응형

ssh에서 실행하니 전과는 다른 형태의 쉘이 나와서 칼리 리눅스에서 파일 2개를 다운받아서 진행했다.

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

보니까 key1()과 key2(), key3()의 값들을 다 더한 것과 같은 값을 사용자가 입력하면 플래그를 보여주는 것 같다.

 

그러면 leg.asm을 통해 key1, key2, key3의 어셈블리 코드를 본다.

 

main()

(gdb) disass main
Dump of assembler code for function main:
	. . . 
   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
  	. . .
End of assembler dump.

key1함수 호출 후 리턴된 값인 r0를 r4에 옮기고, key2함수 호출 후 리턴된 값인 r0를 r3에 옮기고, r3과 r4를 더해서 r4에 저장한다.

key3함수 호출 후 리턴된 값인 r0를 r3에 옮기고, r3과 r4를 더해서 r2에 저장하는 방식으로 메인함수가 이루어져있다.

 

key1()

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

pc는 프로그램 카운터로, 다음에 실행할 명령어의 주소를 담는 레지스터이다.

key1()함수는 pc값을 r3에 옮겨주고, r3값을 r0로 옮기면서 끝난다.


그런데 여기서 주의해야할 점은 pc가 다음 실행해야할 명령어를 가리킨다고 해서 4바이트 후의 명령어를 가리키지 않는다.

cpu는 하나의 명령어를 처리할때 fetch>decode>execute의 과정을 거친다.

그래서 2개의 명령어를 처리하려면 6번의 과정을 거친다.

그런데 파이프라인 기법을 사용하기 때문에 아래의 표처럼 명령어가 수행된다.

1 2 3 4
fetch decode execute  
    decode execute

그래서 pc는 다음다음 명령어의 주소를 가지고 있게 된다.


따라서 pc값은 0x0008ce4가 된다.

 

key2()

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

r3에 pc값을 넣고, 4를 더해서 r0에 대입한다.

따라서 key2는 0x00008d08+0x00000004=0x00008d0c이다

 

key3()

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

lr이라는 값을 r3에 넣고, r3값을 r0에 넣는다. 

lr은 함수호출 이전에 실행하던 주소를 담아 호출 후 돌아갈 주소를 가지고 있다.

메인함수에서 보면

   0x00008d7c <+64>:	bl	0x8d20 <key3>
   0x00008d80 <+68>:	mov	r3, r0
   0x00008d84 <+72>:	add	r2, r4, r3

lr은 0x00008d80이다.

 

그래서 이 세 값을 모두 더하면

0x00008ce4 + 0x00008d0c + 0x00008d80 = 0x0001a770 이다.

그런데 정수로 입력받기 때문에 이 값을 10진수로 바꾸어야 한다.

바꾼 108400을 입력하면

플래그가 나온다!

반응형

'War Game > Pwn(pwnable.kr)' 카테고리의 다른 글

[Pwnable.kr] shellshock 문제풀이  (0) 2019.12.05
[Pwnable.kr] mistake 문제풀이  (0) 2019.12.05
[Pwnable.kr] Input 문제풀이  (0) 2019.12.01
[Pwnable.kr] Passcode 문제풀이  (0) 2019.12.01