pwnable.kr 의 leg 문제를 풀어보자.
문제를 들어가면 arm 에 배워야 한다고 말한다.
하지만 본인은 leg 가 더 좋다고 한다..
pwnable.kr 의 감성 아직은 따라가기 버겁다...
우선 ARM architecture 에 대해 알아보자.
임베디드 기기에서 많이 사용되는 RISC 프로세서로,
저전력을 사용하도록 설계하여 ARM CPU 는 모바일이나 싱글 보드 컴퓨터에서
뚜렷한 강세를 보인다고 한다.
출처 : https://ko.wikipedia.org/wiki/ARM_%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98
이 문제를 풀기 위해 가장 중요한 점은 ..
우리가 이 때까지 보아왔던 intel 계열이 아니라 ARM 계열이기 때문에
어셈블리어가 다르다는 점이다.
먼저 leg.c 소스코드를 보자.
사용자가 입력한 key 값이 key1() + key2() + key3() 결과값과 같으면
flag 를 읽을 수 있다.
그런데 key1,2,3() 함수 안을 살펴보면 asm() 괄호 안에 어셈블리어가 작성되어 있다.
asm() 은 Inline Assembly 인데,
Inline Assembly 란 어셈블리 명령들을 inline 함수로 작성하는 것이다.
주로 시스템 프로그래밍에서 사용된다.
어셈블리어가 ARM 기반 어셈블리어이기도 하고 다른 CTF 에서도
ARM 아키텍처 문제가 종종 나오니 ARM 아키텍처에 대해 알아야 한다.
간단한 ARM 어셈블리 명령어들은 아래 링크를 참조하자.
leg.asm 파일을 보며 자세히 분석해보자.
key1() 함수부터 살펴보자.
mov r3, pc 를 통해 pc 의 값을 r3 에 저장하고 있다.
pc 는 program counter 로, 다음에 실행할 명령어의 주소를 가리킨다.
여기서는 0x00008ce0 가 다음 명령어의 주소이므로 pc 에 0x00008ce4 가 들어간다.
왜 바로 다음 명령어의 주소인 0x00008ce0 가 아니라 0x00008ce4 가 들어가는지는
CPU 가 명령어를 수행할 때 어떤 과정을 거치는 지 알아야 하는데,
지금은 간단히 pipe line 단계를 통해 병렬적으로 처리되어서 그렇다고만 이해하고
자세한 내용은 밑의 글을 참조하면 도움이 될 것이다.
이후 mov r0, r3 에서 r3 에 들어있던 pc 값이 r0 로 복사된다.
이제 key2() 함수를 살펴보자.
mov r3, pc 이번에도 역시 pc 값을 r3 에 담는다.
r3 에 담기는 값은 0x00008d08 이고,
adds r3, #4 명령을 통해 r3 에 4 를 더해 저장하기 때문에
r3 에는 최종적으로 0x00008d0c 이다.
이후 mov r0, r3 명령어를 통해 r0 에 r3 값이 복사된다.
key3() 함수를 살펴보자.
mov r3, lr 이번에는 lr 레지스터의 값을 r3 에 저장하고 있다.
lr 레지스터는 함수 호출 후에 돌아갈 주소를 저장하는 레지스터이다.
key3() 함수 호출 후엔 main 함수로 돌아가야 하는데,
아래 disas main 의 결과를 확인해보자.
key3() 함수 호출 후에 돌아갈 주소는 main 의 0x00008d80 인 것을 알 수 있다.
즉, key3() 함수에서 r3 에는 0x00008d80 값이 들어가고,
역시나 이 값이 r0 로 들어간다.
이제 key1,2,3 함수의 return 값을 다 얻었다.
정리해보면,
key1() : 0x00008ce4
key2() : 0x00008d0c
key3() : 0x00008d80
가 된다.
그리고 이 세 개의 값을 더한 값과,
우리가 입력한 10진수를 비교한다.
세 개의 값을 더하면 0x0001A770 이고 10진수로 바꾸면 108,400이다.
따라서 우리는 key 값으로 108,400 을 입력하면 된다.