[pwnable.kr] leg(2 pts) :: Write-Up
두비니
·2020. 7. 6. 23:46
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)
우선 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;
}
asm함수를 처음봤을 수도 있는데, 간단히 c코드 안에 어셈블리어로 코딩할 수 있게 해주는 함수입니다.(정확히는 '인라인 어셈블러'라고 합니다.)
더 궁금하시다면 다음 링크로: https://araikuma.tistory.com/600
우선 이 문제를 풀기 위해서는 key1, 2, 3을 더한 값을 알아야하네요. 즉 어셈분석을 하면 되겠네요!
인텔어셈으로해도 고통스러운데 이건 arm어셈이네요.
arm어셈에 대한 글: https://blog.naver.com/onlyou_4ever/40006966899
(gdb) disas 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.
어셈블리어를 보아하니 모두 함수의 리턴값이 r0에 저장되고, 마지막에는 r2에 key의 합이 들어가있는 듯 합니다.
그래서 key함수 각각의 어셈블리어/c언어를 살펴보면,
(gdb) disas 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.
int key1(){
asm("mov r3, pc\n");
}
key1의 경우,
key1 = pc
pc값이 뭔지 파악해야합니다.
여기서 pc는 program counter의 줄임말로, 인텔 어셈에서 eip와 비슷한 역할을 합니다.
다만 둘 사이의 차이점은 eip는 현재 실행중인 명령어의 주소를 담지만, pc는 자신이 실행중인 명령어의 다음 명령어의 주소를 받는다는 차이점이 있습니다.
그럼 key1 = 0x8ce0
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.
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"
);
}
key2의 경우에는 <key2+32>에서 r0의 값에 r3을 옮기므로 r3의 값을 봐주면 될 것 같다. r3에 pc의 값을 옮기고, 4를 더한다. 즉
r3 = 0x8d06 + 0x4 = 0x8d0a, key2 = 0x8d0a 이다.
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.
int key3(){
asm("mov r3, lr\n");
}
이번에는 r0가 아닌 lr값을 봐줘야 한다.
lr은 보통 bx명령어로 다른 주소로 점프했을 때, 그 주소에서 돌아왔을때 그다음으로 실행할 주소라고 한다.
따라서 현재 상황이 main함수에서 key3으로 넘어온 상황이므로 lr은 main문에서 확인할 수 있다.
(gdb) disas main
Dump of assembler code for function main:
...
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
...
End of assembler dump.
main함수에서 0x08d7c에서 key3을 불러왔으니 lr에는 0x08d80이 저장되어있겠네요
key3 = 0x08d80
그럼 3개를 더한 값은 0x8ce0 + 0x8d0a + 0x08d80 = 0x1a76a = (dec)108394
/$ ./leg
Daddy has very strong arm! : 108394
I have strong leg :P
/$
응 아니야~
여기서 정말 한참을 헤맨 것 같다. 아래 글 보고 겨우 이해했다. 정말 설명 너무 잘돼있으니까 여기 보도록
http://recipes.egloos.com/4982170
위 글을 요약하면, cpu는 명령어 처리를 할 때,
fetch > decode > execute 순으로 진행이 된다고 한다.
다음과 같이 진행이 되는데, 이 방법으로 진행이 될때는 하나의 과정이 진행될 때, 나머지 두 과정이 그냥 놀게 된다. 이걸 그냥 보고만 있을 수 없는 알뜰한 인간들은 pipeline이라는 것을 도입합니다.
pipeline이란 최대한 "놀고"있는 과정을 줄이기 위한 것인데, 1~5번까지의 instruction을 진행해야하는 프로그램에서 1번 instruction에 대해 decoding과정에 들어간 후에, 2번 fetch과정을 바로 시작하는 식으로 진행이 됩니다.
근데 역설적으로 너무 많이 돌리면 성능이 떨어진다고 하네요. 아무튼 오늘은 과정 자체만 이해하면 되는 것이니, 넘어가도록 합시다.
아무튼 이 과정이 중요한 이유는, 어떤 명령어가 실행(execute)가 되고 있다면, pc는 이미 다음 주소를 가르키고 있다는 것이다. 따라서 key1과 key2의 pc값을 다음 주소로 바꿔주면, 결국
key1 = 0x8ce4
key2 = 0x8d0c
key3 = 0x08d80
key = 0x8ce4 + 0x8d0c + 0x8d80 = 0x1a770 = (dec)108400
이 된다. (key3은 lr을 물어보는 값이였으니 상관없음)
어셈 싫어...
'War Games > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] shellshock(1 pts) :: Write-Up (0) | 2020.07.09 |
---|---|
[pwnable.kr] mistake(1 pts) :: Write-Up (0) | 2020.07.07 |
[pwnable.kr] input(4 pts) :: Write-Up (0) | 2020.02.25 |
[pwnable.kr] random(1 pt) :: Write-Up (0) | 2020.02.24 |
[pwnable.kr] passcode(10 pts) :: Write-Up (0) | 2020.01.28 |