배점은 100점이며, 가위 바위 보에 연속적으로 이겨서 플래그를 획득할 수 있다는 문제이다.
파일크기가 800kb 가 안되는 apk 파일을 문제로 제공한다.
apk 파일 열어 일단 Manifest 를 봤다.
메인엑티비티와 CallMe, WhyCall 이라는 2개의 리시버가 보인다.
그리고 각 리시버들의 priority 값이 할당되어 있는데 CallMe는 1이고 WhyCall은 2이다.
둘다 core.hdcon.android2017.oops 로 호출이 되는데 priority 값 때문에 단순히 호출만으로는 절대 CallMe를 동작시킬 수 없다.
근데 문제 풀이를 위해서 딱히 중요한 내용은 아니다.
Libraries 를 보면 so 파일이 들어 있는데 앱에서 일부 사용되는 native 함수가 구현되어 들어 있다.
이 so 파일은 문제를 풀기 위해 반드시 분석해야되는 파일이다.
(팁: genymotion 은 x86기반 에뮬레이터다.)
Resources->values->strings.xml 을 보면 아래와 같은 내용을 확인할 수 있다.
소스외에 것들은 대충 본것 같으니 이제 소스코드를 보자.
아래는 MainActivity 에 onCreate 코드이다.
MainActivity.this.e 변수가 들고 있는 값이 putExtra 를 통해서 mm 이라는 이름으로 전달된다.
그럼 mm 의 상호참조를 한번 확인해보자.
CallMe의 onRecevie 에서 mm 을 참조하는가 보다.
CallMe의 onReceive 코드를 보니 getString 으로 mm 이 가지고 있는 값을 읽어 오는게 보이고...
그 바로 아래 try 내부에 구현된 코드를 보면 느낌이 딱 온다.
혹시나 저 코드를 보고 아무런 느낌이 오지 않는다면 절레절레… (그렇다고 너무 실망은 하지 마시고 더 열심히 하시면 됩니다.)
배열에 넣고 길이값 만큼 지지고 볶는코드이고, 오랜만에 보는 삼항연산자도 있다.
v4[v1] = v1 < 0x10 ? A : B
전자의 조건식이 참이면 A를 실행하고 거짓이면 B를 실행하는 무시무시한 코드다.
어쨋든 A, B 모두 ck 함수를 호출하는데 이게 또 native 함수다. 즉 위에서 봤던 so 파일에 기능이 구현되어 있기 때문에 apk 만 봐서는 절대 무슨 형태로 문자열을 작살내는지 확인할 길이 없다.
좋은회사라면 당신에게 hexray 를 사줬을 것이다. (hexray를 사주지 않았다고 해서 꼭 나쁜회사는 아니다. 핸드레이를 열심히 하시면 됩니다.)
그럼 아래와 같이 편하게 ck 함수의 슈도코드를 볼 수 있다.
onReceive 에 구현된 코드와 ck 함수를 본인이 원하는 언어로 옮겨 담으면 90%가 끝난다. (저는 python 을 좋아해서 python 으로 옮겨 담았습니다.)
다시 CallMe 의 onReceive 를 보자.
각 변수들이 어떤 값을 가지고 있는지 무슨 용도일지 감이 대부분 잡힌다.
그런데 v7은 v3를 참조한다는거 말고는 어떤 값을 가지고 있는지 현재로서는 알 수 없다.
v3 또한 getString으로 mm의 값을 가져오는데 mm의 값이 뭔지 알 수 없다.
그럼 putExtra로 mm을 던지는 MainActivity로 다시 가보자.
mm의 값은 MainActivity.this.e 를 참조하고, MainActivity.this.e는 rps_calc 을 통해 만들어지는것을 최종적으로 알 수 있다.
rps_calc은 native 함수다. 다시 so 파일을 보자.
hexray로 rsp_calc 함수부분을 보면 k 값이 17916 이상일때 동작하는 코드가 보인다.
이쯤 되면 느낌이 오지 않는가!
rps_calc 함수 자체는 가위 바위 보 게임을 할때 이기거나, 비기거나, 졌거나 등의 메시지를 다루는데, v10을 보면 뭔가 이상한 문자열이 보인다.
‘S=TX’, ‘^=_=‘, … 그리고 v14의 0(null)까지 하나의 문자열이므로 한줄로 줄을 세워보면…
XT=S=_=^=”= =!=' 이 된다.
그리고 이 문자열을 xor 하는 do while 을 보면 v9 값으로 하나씩 xor 시켜주는걸 볼 수 있다. v9은 ae의 값을 가지며, ae는 0x10 이다.
코드를 간단하게 짜서 돌리면 …
17916 점수를 넘기게 되면 HD-C-O-N-2-0-1-7 이라는 문자열이 만들어진다.
안타깝지만 이 문자열은 flag 값이 아니고, mm 이라는 이름으로 전달되어 flag 를 디코딩(?)하는데 사용되는 값이다.
퍼즐은 다 모았으니 다시 코드를 간단하게 짜서 돌리면 …
이렇게 최종적으로 flag 값을 얻을 수 있다.
모든 문제를 다 풀지는 못했지만 이 문제는 ‘게싱’이 덜 필요한 그나마 정상적인 문제였다고 생각한다.