gunnew의 잡설

pwnable.kr 8. leg (ARM Assembly) 본문

System Security

pwnable.kr 8. leg (ARM Assembly)

higunnew 2020. 2. 12. 18:54
반응형

 어떻게 매번 모르는 개념만 나올 수 있을까. ARM assembly를 알아야만 풀 수 있는 문제이다. 뭐 이미 Intel assembly를 알고 있어서 어느 정도는 이해할 수는 있었으나 내가 알던 개념과 조금은 달랐다. 가령 PC register에는 다음 instruction이 아니라 다다음 instruction이 담겨있다는 것과 같은 것 말이다...

 

 코드부터 보자.

#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[101]; 
        int r = read(fd, buf, 100); 
        write(0, buf, r);
    }
    else{
        printf("I have strong leg :P\n");
    }
    return 0;
}

 우리가 입력하는 정수 key값과 (key1() + key2() + key3())의 값이 같아야 한다. 그러면 각 함수가 반환하는 값을 살펴보아야 한다. 이때 leg.asm의 main함수를 살펴보자.

 

 아무래도 key1 호출이 끝난 뒤 r0를 r4로 옮기고,

mov r4, r0

 

key2 호출이 끝난뒤 r0를 r3로 옮긴후 add 둘을 더하고

mov r3, r0

add r4, r4, r3

 

key3 호출이 끝난 뒤 r0을 r3으로 옮기고 r2에 r3과 r4를 더한 값을 저장한 후에

mov r3, r0

add r2, r4, r3

 

r11 - 16의 주소에 있는 값을 r3으로 옮긴 후 r2와 r3을 비교하는 것으로 보아

r2에는 key1() + key2() + key3()이 들어있고, r3에는 우리가 입력한 key값이 들어가 있을 것이다.


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.

mov r3, pc

mov r0 r3

을 통해 pc의 값이 r0에 들어갔다는 것을 알 수 있다.

 

그런데 pc에는 무슨 값이 들어있을까?

https://stackoverflow.com/questions/24091566/why-does-the-arm-pc-register-point-to-the-instruction-after-the-next-one-to-be-e

 

Why does the ARM PC register point to the instruction after the next one to be executed?

According to the ARM IC. In ARM state, the value of the PC is the address of the current instruction plus 8 bytes. In Thumb state: For B, BL, CBNZ, and CBZ instructions, the value o...

stackoverflow.com

요약하면 PC는 pipe lining을 통해 다다음 주소를 담고 있다. ( decode 다음에는 원래 execute인데 pipe line을 통해 fetch로 이어서 instruction 수행 가능)

 

1 2 3 4 5 6
fetch decode execute fetch decode execute
  fetch decode execute fetch decode
    fetch decode execute fetch
      fetch decode execute

따라서 위에서 

   0x00008cdc <+8>: mov r3, pc 
   0x00008ce0 <+12>:    mov r0, r3 
   0x00008ce4 <+16>:    sub sp, r11, #0 

 

여기서 r0에는 0x00008ce4가 들어간다.


key2()

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"
	);
}
(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.

 

main에서 key2 호출이 끝난뒤 r0를 r3로 옮긴후 add 둘을 더한다는 것을 명심하자!

mov r3, r0
add r4, r4, r3

따라서 r0에 어떤 값이 들어가는지 봐야한다.

+ 20에서 pc의 값이 r3로 들어가고, 

+ 22에서 r3의 값이 4가 증가되고

+ 32에서 r3의 값이 r0으로 들어간다.

 

+ 20에서 r3로 들어가는 pc의 값은 0x00008d08이기 때문에

최종적으로 r0에는 0x00008d0c가 된다.


key3()

int key3(){
	asm("mov r3, lr\n");
}

 

(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.
key3 호출이 끝난 뒤 r0을 r3으로 옮기고 r2에 r3과 r4를 더한 값을 저장한다는 것을 상기하자.
mov r3, r0
add r2, r4, r3

r0에는 lr의 값이 들어간다.

 

LR, the Link Register
Register R14 is used to store the return address from a subroutine. At other times, LR can be used for other purposes.
When a BL or BLX instruction performs a subroutine call, LR is set to the subroutine return address. To perform a subroutine return, copy LR back to the program counter. This is typically done in one of two ways, after entering the subroutine with a BL or BLX instruction:
• Return with a BX LR instruction.
• On subroutine entry, store LR to the stack with an instruction of the form: PUSH {,LR} and use a matching instruction to return: POP {,PC}

LR은 main에서 돌아갈 주소이므로 0x00008d80 (<+68>)이 되며 r0에는 이 값이 담긴다.

 

자 이제

  • 여기서 r0에는 0x00008ce4가 들어간다.
  • 최종적으로 r0에는 0x00008d0c가 된다.
  • LR은 main에서 돌아갈 주소이므로 0x00008d80 (<+68>)이 되며 r0에는 이 값이 담긴다.

이 정보를 이용하여 모두 더하고 key값을 찾자

 

108400을 key값으로 입력하면 통과될 것이다.

 

 

반응형

'System Security' 카테고리의 다른 글

pwnable.kr 10. shellshock  (0) 2020.02.14
pwnable.kr 9. mistake (Operator priority)  (0) 2020.02.12
pwnable.kr 7. input (Various I/O in Linux)  (0) 2020.02.06
pwnable.kr 6. random  (0) 2020.02.05
pwnable.kr 5. passcode  (0) 2020.02.04
Comments