Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
Tags
- PMA
- shellcraft
- reversing
- TUCTF
- pwn
- BOF
- format
- toddler
- CTF
- picoCTF
- rev
- shellcode
- Toddler's Bottle
- Reverse
- Leak
- pwnable.kr
- string
- CANARY
- Bottle
- anti
- Read
- writeup
- Rookiss
- Bug
- FSB
- pico
- pwnable
- 2018
- practicalmalwareanalysis
- ASM
Archives
- Today
- Total
제리의 블로그
pwnable.kr leg 본문
leg - 2 pt
Daddy told me I should study arm.
But I prefer to study my leg!
Download : http://pwnable.kr/bin/leg.c
Download : http://pwnable.kr/bin/leg.asm
ssh leg@pwnable.kr -p2222 (pw:guest)
/ $ id
uid=1000(busy) gid=1000 groups=1000
/ $ ls
bin dev flag linuxrc root sys
boot etc leg proc sbin usr
/ $ ls -l flag leg
-r-------- 1 1001 0 38 Nov 10 2014 flag
---s--x--- 1 1001 1000 636419 Nov 10 2014 leg
목차
1. 소스
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() 을 합한 값을 알아내야 합니다.
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
어셈블리어를 분석해보면
각 함수들의 r0 가 r2로 합해져 비교(cmp)됩니다.
이제 각 함수를 디스어셈블링하여 분석할텐데
공통적으로 각 함수들은 r3 의 값을 r0로 한다.
즉, r3 가 함수의 결과이다.
int key1(){
asm("mov r3, pc\n");
}
0x00008cdc <+8>: mov r3, pc
0x00008ce0 <+12>: mov r0, r3
key1 함수에서는 pc 값을 r0 로 보내고 있습니다.
PC는 Program Counter 로
intel 계열 레지스터로 치면 Instruction Point 와 같은 역할이라고 볼 수 있습니다.
int key2(){
asm(
...
"mov r3, pc\n"
"add r3, $0x4\n"
...
);
}
0x00008d04 <+20>: mov r3, pc
0x00008d06 <+22>: adds r3, #4
...
0x00008d10 <+32>: mov r0, r3
key2 함수에서는 pc 값에 4 를 add 하고 있습니다.
int key3(){
asm("mov r3, lr\n");
}
0x00008d28 <+8>: mov r3, lr
0x00008d2c <+12>: mov r0, r3
key3 함수에서는 lr 값을 r0 에 저장합니다.
key1() + key2() + key3()
= 0x8cdc + 0x8d04 + 4 + 0x8d7c
= 108384
각 함수의 값을 계산해보면 108384 인데
/ $ ./leg
Daddy has very strong arm! : 108384
I have strong leg :P
실패 ㅠ
직접 gdb 로 분석해보고 싶은데
/ $ gdb ./leg
sh: gdb: not found
pwnable.kr 서버에는 gdb 가 없어서
직접 ARM 환경을 구축해서 pc 값을 알아보도록 하자
2. 로컬에서 디버깅 해보기
로컬에 컴파일해본거라 오프셋 정보 등은 다르지만!
(gdb) disas
Dump of assembler code for function key1:
0x000084c4 <+0>: push {r7}
0x000084c6 <+2>: add r7, sp, #0
=> 0x000084c8 <+4>: mov r3, pc
0x000084ca <+6>: mov r0, r3
0x000084cc <+8>: mov sp, r7
0x000084ce <+10>: pop {r7}
0x000084d0 <+12>: bx lr
End of assembler dump.
(gdb) i r r3 pc
r3 0x1 1
pc 0x84c8 0x84c8 <key1+4>
(gdb) ni
0x000084ca in key1 ()
(gdb) i r r3 pc
r3 0x84cc 33996
pc 0x84ca 0x84ca <key1+6>
pc+4
처음 예상했던 pc 값보다 +4 인 값이 도출이 되었습니다.
(gdb) disas
Dump of assembler code for function key2:
0x000084d4 <+0>: push {r7}
0x000084d6 <+2>: add r7, sp, #0
=> 0x000084d8 <+4>: mov r3, pc
0x000084da <+6>: add.w r3, r3, #4
0x000084de <+10>: mov r0, r3
0x000084e0 <+12>: mov sp, r7
0x000084e2 <+14>: pop {r7}
0x000084e4 <+16>: bx lr
End of assembler dump.
(gdb) i r r3 pc
r3 0x84cc 33996
pc 0x84d8 0x84d8 <key2+4>
(gdb) ni
0x000084da in key2 ()
(gdb) i r r3 pc
r3 0x84dc 34012
pc 0x84da 0x84da <key2+6>
(gdb) ni
0x000084de in key2 ()
(gdb) i r r3
r3 0x84e0 34016
(pc+4)+4
0x00008534 <+60>: bl 0x84e8 <key3>
0x00008538 <+64>: mov r3, r0
(gdb) disas
Dump of assembler code for function key3:
0x000084e8 <+0>: push {r7}
0x000084ea <+2>: add r7, sp, #0
=> 0x000084ec <+4>: mov r3, lr
0x000084ee <+6>: mov r0, r3
0x000084f0 <+8>: mov sp, r7
0x000084f2 <+10>: pop {r7}
0x000084f4 <+12>: bx lr
End of assembler dump.
(gdb) i r r3 lr
r3 0x84e0 34016
lr 0x8539 34105
(gdb) ni
0x000084ee in key3 ()
(gdb) i r r3 lr
r3 0x8539 34105
lr 0x8539 34105
lr 는 Link Register 로
함수가 다 수행된 후
돌아갈 주소를 저장하는 레지스터입니다.
개인적으로 0x8538 이 이상적이라고 생각하는데 +1 이 되어있습니다.
3. Flag
(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
key1 = 0x8ce0 + 4
(gdb) disas key2
...
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
key2 = 0x8d04 + 4 + 4
int key3(){
asm("mov r3, lr\n");
}
0x00008d7c <+64>: bl 0x8d20 <key3>
0x00008d80 <+68>: mov r3, r0
key3 = 0x8d7c + 5
>>> 0x8ce0 + 4 + 0x8d04 + 4 + 4 + 0x8d80 + 1
108401
/ $ ./leg
Daddy has very strong arm! : 108401
I have strong leg :P
/ $ ./leg
Daddy has very strong arm! : 108400
Congratz!
My daddy has a lot of ARMv5te muscle!
그러나 제가 계산한 것과 -1 차이가 났다.
아무래도 lr 레지스터의 값이 0x8d80 이 맞나보다.
(그런데 로컬에서는 왜...)
4. 참고
PC(프로그램 카운터) == r15
각 명령어에 대해 1워드 (4바이트) 씩 증가하거나 Thumb 상태에서 실행되는 명령어의 크기만큼 증가합니다.
명령어 dest, src
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0204ik/CEGIBCCG.html
lr == r14
lr 은 RET.
sp == r13
http://www.jkelec.co.kr/img/lecture/arm_arch/arm_arch_4.html
함수의 반환값은 r0 에 담긴다.
cpu는 명령을 실행할 때 fetch > decode > execute 과정을 거칩니다.
예전에는
1 |
2 |
3 |
4 |
5 |
6 |
fetch |
decode |
execute |
fetch |
decode |
execute |
2개의 명령어를 실행하려면 6 cycle 이 필요했는데
pipe line 으로 인해
1 |
2 |
3 |
4 |
fetch |
decode |
execute |
|
|
fetch |
decode |
execute |
효율적이게 처리할 수 있게 되었습니다.
PC 는 fetch 를 가리키고 있습니다.
ARM 은 32bit 가 1 word 이므로 실행하고 있는 코드의 +8 byte (2 word) 를 가리키고 있습니다.
(seungmin4239's leg writeup - http://blog.naver.com/PostView.nhn?blogId=seungmin4239&logNo=220921113078)
ARM 레지스터 : http://forum.falinux.com/zbxe/index.php?document_srl=571055&mid=lecture_tip
instruction pointer vs program counter : https://stackoverflow.com/questions/15739489/program-counter-and-instruction-register?rq=1
instruction piplining - Wikipedia : https://en.wikipedia.org/wiki/Instruction_pipelining
'Wargame > pwnable.kr' 카테고리의 다른 글
pwnable.kr Rookies echo2 - 50 pt (0) | 2018.12.14 |
---|---|
pwnable.kr Rookies crypto1 - 120pt (CBC mode. byte-at-a-time decryption) (0) | 2018.12.06 |
pwnable.kr asm (0) | 2018.09.04 |
pwnable.kr syscall (0) | 2018.09.04 |
pwnable.kr fix (0) | 2018.09.03 |
Comments