pwnable.kr (leg)

c0wb3ll ㅣ 2020. 12. 5. 11:59

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

ㅗㅜㅑ;;; 정신 나간다 이게 다 뭐람

 

우선 플래그를 얻기 위한 조건을 보자.

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;
}
  1. key를 입력받는다.
  2. key1() + key2() + key3() == key 이면 if문 통과

따라서 입력한 값이 key1() + key2() + key3()이면 될 것 같다.

그럼 key1()부터 뜯어보자.

int key1(){
    asm("mov r3, pc\n");
}

asm("mov r3, pc\n"); 를 보면 r3 레지스터에 pc를 넣는 것이 전부이다.

음... 일단 난 pc가 무엇인지 모르니 pc 부터 공부를 해야겠다.

arm assembly

Register

  • r0 ~ r12 : 범용 레지스터, 인자값 및 임시 계산값 등이 들어감
    • rax, rbx, rcx, ... 등과 같다고 보면 될 것 같다.

 

  • r13(sp) : Stack Pointer
    • rsp와 비슷하다고 보면 될 것 같다.

 

  • r14(lr) : 함수 호출이 끝난 후 실행시킬 명령어 주소를 넣어둠
    • x86 및 x64에서는 call하면 ret를 스택에 쌓아 돌아갈 주소를 지정해주지만 arm에서는 lr에 넣어두어 호출이 끝나면 다음 명령어를 실행한다.

 

  • r15(pc) : CPU가 현재 실행하고 있는 instruction의 주소를 가리킴
    • rip와 비슷하다고 보면 될 것 같다.

 

key1()

int key1(){
    asm("mov r3, pc\n");
}
(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.

자 본론으로 들어와 key1()을 다시 보자.

일반적으로 함수의 반환값은 r0 레지스터에 들어가는 것 같다.(rax의 역할을 하는 것 같음)

r0에 들어가는 값은 mov r0, r3에 의해 r3의 값이 되며 r3는 mov r3, pc에 의해 pc 값이 된다.

pc는 위에 설명한 것처럼 CPU가 현재 실행하고 있는 instruction의 주소를 가리킨다고 해서 0x8cdc인 줄 알았으나 arm은 명령을 실행할 때

  1. fetch
  2. decode
  3. execute

와 같은 단계로 실행을 하는데 이 단계를 실행할 때 한가지 단계를 실행하면 나머지 두 단계가 논다... 라는데서 아이디어를 착안하여 pipe line을 이용하여 decode를 하는동안 다음 번 instruction fetch를 해 오는 방식을 적용하면 어떨까 부터 시작해서 위 3단계를 모두 한꺼번에 실행시켜버리자는 말도 안되는 생각을 해 그것을 실현해냈다고 한다.

원래의 흐름이 이렇다 하면

arm의 흐름은 이런식이다. ( 왜 arm이 빠르다는지 알것같기도 하다...)

결론은 pc는 fetch값을 가리키고 있고 실행할 시점에 pc의 주소는 +8을 해주어야 한다는 것이다.

따라서 pc는 0x8ce4의 값을 가진다.

결론은 key1()의 반환값은 0x8ce4라는 것이다.

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.

이번에도 r0레지스터에는 r3의 값이 들어간다.

r3레지스터는 pc의 값을 최초로 가지고 4를 더한 값이다.

따라서 key2()의 반환 값은 0x8d08 + 0x4 = 0x8d0c 이다.

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.

이번에도 r0레지스터에는 r3레지스터 값이 들어간다.

mov r3, lr 명령어로 r3레지스터 값이 정해지는데 lr은 함수 호출이 끝난 후 실행할 명령어 주소를 가지고 있기 때문에 0x8d80의 값을 가진다.

key3()의 반환값은 0x8d80이다.

key1() + key2() + key3() = ?

여태까지 얻은 정보를 종합해보자.

  1. key1() : 0x8ce4
  2. key2() : 0x8d0c
  3. key3() : 0x8d80

따라서 key1() + key2() + key3() = 0x1a770 = 0d108400가 된다.

Reference

https://sunrinjuntae.tistory.com/114

http://recipes.egloos.com/4982170