#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;
}
pwnable.kr/bin/leg.c에 있는 코드
(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)
pwnable.kr/bin/leg.asm에 있는 코드
ARM 어셈블리가 섞여 있고, 학교에서 배운적이 있어 그나마 검색 조금으로 더듬더듬 풀 수 있었다.
주로 임베디드 시스템에서 강세를 보이는 아키텍쳐로 배웠다.
어셈 코드 자체는 어렵지 않으니, 기반 지식으로 필요한 부분은 PC와 LR 정도.
(b계열은 branch로 call 정도로 생각하면 됨)
ARM 아키텍쳐에서는 레지스터별로 특정 용도를 주고 레지스터에다가
바로 푸쉬 팝을 하면서 효율성을 올리는 것으로 알고 있는데, PC와 LR이 그런 용도이다.
(사실 R15, R14이지만 이름을 붙여서 직관성을 높임)
PC는 IP와 비슷하지만, 다른 점은 명령어 실행시, fetch, decode, execution 등으로 단계를 나누어서 하고
PC가 가리키는 곳은 다음 fetch할 주소이기 때문에, IP와 약간의 차이가 난다.
명령어 실행 사이클의 경우 4단계, 6단계? 등에다가 파이프 라인을 이용한 여러 방식이 있었던 거로 기억하는데,
자세한 내용은 생략하고,
간단히 현재 명령어 주소가 0xa0이고, 다음 명령어 주소가 0xab, 그 다음 명령어 주소가 0xad라면
현재 명령어에서의 PC는 0xab가 아니라 0xad이다.
(0xa0 : execution 단계, 0xab : decode 단계, 0xad : fetch 단계)
물론 중간에 branch instruction이 있다면
미리 fetch한 인스트럭션이 쓸모 없어져 버리기 때문에,
다른 루틴(branch 예상 방식 등)을 도는 경우도 있는 것으로 알고 있다.
LR의 경우 그냥 리턴주소를 담고 있다. 스택을 사용하지 않고, 레지스터에 넣어놓는 게 특이점.
ARM에 대해서 분석할 경우도 많은가? 자주 나온다면 나중에 더 자세히 보도록 하자.
'Pwnable.kr' 카테고리의 다른 글
Pwnable.kr [shellshock] (0) | 2019.05.01 |
---|---|
Pwnable.kr [mistake] (0) | 2019.05.01 |
Pwnable.kr [input] (0) | 2019.04.30 |
Pwnable.kr [random] (0) | 2019.04.29 |
Pwanble.kr [passcode] (0) | 2019.04.29 |