마지막 12주차 과제의 마지막 문제는 pwnable.kr의 leg를 선정했다. 이유는 최근 도제 교육을 받게된 회사에서 arm 아키텍처 리버싱을 주로 배우기 때문에, arm 관련 문제인 leg를 포스팅하고 싶었다.
일단 문제 파일(c코드 하나, 어셈블리 코드 하나)이 주어진다. 이 문제 파일을 분석 후 아래 주소로 접속해 flag를 따내는 것이다.
#include <stdio.h>
#include <fcntl.h>
int key1(){
asm("mov r3, pc\n"); // pc 레지스터의 값을 반환
}
int key2(){
asm(
"push {r6}\n"
"add r6, pc, $1\n"
"bx r6\n"
".code 16\n"
"mov r3, pc\n" // r3에 pc 레지스터의 값을 넣음
"add r3, $0x4\n" // r3에 0x4를 더한 값을 반환
"push {r3}\n"
"pop {pc}\n"
".code 32\n"
"pop {r6}\n"
);
}
int key3(){
asm("mov r3, lr\n"); // lr 레지스터의 값을 반환
}
int main(){
int key=0;
printf("Daddy has very strong arm! : ");
scanf("%d", &key);
if( (key1()+key2()+key3()) == key ){
// key1, key2, key3 함수의 반환값을 더한 값이 입력 값과 같다면 flag 획득
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언어 코드에 주석을 넣어본 모습이다. 여기서 key? 함수들은 모두 arm으로 이루어져 있다.(lr, pc, r3 등은 arm에서 쓰는 레지스터고 bx, bl 등 역시 arm에서 쓰는 함수다.) 보면 r3 레지스터로 반환 값을 전달한다는 것을 눈치챌 수 있다.
key1은 pc 레지스터의 값을 반환
pc는 x86의 eip 레지스터처럼 다음 실행할 명령어의 주소 값을 담는다. 여기서 arm은 pipe line 형식이기 때문에 eip처럼 바로 다음 주소를 가리키는게 아닌 다음 다음 주소를 가리킨다.(arm은 명령어를 실행할 때 fetch(가져, 불러오는?)-decode(해석)-execute(실행) 단계를 거치는데 pc는 fetch 단계를 거칠 명령어의 주소를 가리키기 때문이다. 다음 주소는 decode에 해당)
따라서 key1은 다다음 명령어의 주소를 반환한다.
key2는 pc 레지스터에 +0x4한 값을 반환
마찬가지로 다다음 명령어의 주소 + 0x4 값을 반환한다.
key3은 lr 레지스터의 값을 반환
lr은 ret와 같다. 함수가 끝날 후 돌아갈 리턴 주소를 담고 있는 레지스터다.
따라서 key3을 호출할 주소의 다음 주소를 리턴하게 될 것이다.
(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 0x8crd4 <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 코드다. 이 코드에 주소 값이 나와있기 때문에 이 코드를 보고 반환 값을 구할 수 있을 것 같다.
0x00008cdc <+8>: mov r3, pc
0x00008ce0 <+12>: mov r0, r3
0x00008ce4 <+16>: sub sp, r11, #0
위가 key1의 핵심 어셈 코드다. pc는 다다음 주소를 가리키므로 key1의 반환 값은 0x8ce4가 된다.
0x00008d04 <+20>: mov r3, pc
0x00008d06 <+22>: adds r3, #4
0x00008d08 <+24>: push {r3}
위는 key2의 핵심 어셈 코드다. 역시 pc는 다다음 주소를 가리키므로 0x8d08을 r3에 저장하고, 이 r3에 3을 더하면 0x8d0c가 된다. 따라서 반환 값은 0x8d0c다.
호출 전:
0x00008d7c <+64>: bl 0x8d20 <key3>
0x00008d80 <+68>: mov r3, r0
key3:
0x00008d28 <+8>: mov r3, lr
위는 key3의 핵심 어셈 코드다. lr은 함수 종료 후 다음에 실행할 명령어의 주소를 저장하므로 0x8d80이 반환될 것이다.
이 3개의 반환 값을 더해보면 0x1A770이 나온다. 이 값을 10진수화 한 108400을 입력해주면 flag을 얻을 수 있을 것 같다.
'Wargame > Pwnable.kr' 카테고리의 다른 글
pwnable.kr [random] (0) | 2021.04.08 |
---|---|
pwnable.kr [flag] (0) | 2021.04.08 |
pwnable.kr [bof] (0) | 2021.04.04 |
pwnable.kr [collision] (0) | 2021.04.02 |
pwnable.kr [fd] (0) | 2021.04.01 |