본문 바로가기

Reverse Engineering

리빌더를 이용한 CrackMe 16 문제 풀이

리빌더(Rebuilder) : 덤프 툴을 이용하여 메모리상에 존재하는 실행 파일 코드들을 추출하더라도 원래의 실행파일에 정의되어 있는 Import 함수의 정보(IAT : Import Address Table)는 복원되지 않는데 이를 복원해준다.

주로 메뉴얼 언패킹할 때에 사용되며, OEP 주소 부분에서 메모리상에 로드되어있는 프로그램을 파일로 덤프시키고, IAT를 복원하기 위해서 사용한다.


이제 이 리빌더를 이용하여 crackme 16 을 풀어보겠다.


실행시켜보니 단순히 PDA 모양의 창만 뜰뿐 특이한 점은 보이지 않는다.
PEID 로 열어봐서 분석을 해보면


UPX 패킹이 되어있다.
upx 언패커로 언패킹을 해보니


일반 upx가 아닌 다른 변종의 upx 패킹인것 같다. 그래도 upx 패킹이므로
수동언패킹을 한다.

1. 올리디버거(OllyDbg)로 OEP를 찾는다.

2. 올리덤프(Ollydump)플러그를 이용하여 메모리 덤프한다.

3. 미처 복원되지 않은 것들을 리빌더로 완벽히 복원한다.


먼저 올리디버거로 파일을 열어보았다.

00477FDA   >  61            POPAD
00477FDB   .^ E9 60E8FDFF   JMP 's+Crack.00456840

다음과 같은 OEP 구문을 알려주는 부분이 보인다.
많은 UPX를 수동으로 언패킹해보면 알겠지만 일반적으로 POPAD 한후 JMP 문이 있고 그밑에 DB 00 DB 00 이 쭉 나온다면 이 JMP 하는 주소가 OEP이다.

이제 OEP 주소로 가서 OllyDump 플러그인을 이용하여 메모리 덤프를 한다.
참조 http://dakuo.tistory.com/entry/올리디버거OllyDBG를-이용한-CrackMe-문제-풀이-2

Rebuild Import 옵션을 체크된 상태로 Dump하면 정상적으로 패킹이 풀려서 저장이 된다.
하지만 대부분 파일은 이렇게 해도 완전히 복구가 되지 않는다. 이럴때 리빌더 ImpREC를 사용하면 된다.
(하지만 이 예제는 Rebuild Import 옵션만으로 완전히 풀리므로 여기서는 연습을 위해 옵션을 체크해제한다)

덤프를 하고 실행을 해보면 다음과 같은 오류가 발생한다.
 

ImpRec 프로그램을 실행해서 잘못된 주소를 복원해보겠다. 수동 언패킹을 하다보면 덤프만 해서는 복원이 완벽하게 다 이뤄지지 않아서 이런 상황이 발생한다. 기존에 패킹되어 있는 파일을 실행시켜놓고


목록에서 해당 프로세스를 선택해준다.
OEP 값에 올리디버거에서 찾은 값인 456840 - 400000 인 56840 을 넣어준다.
(ImageBase = 400000)
AutoSerach 버튼을 누르면 IAT 정보가 자동으로 검색되고, Get Imports 버튼을 누른다.


Imported Functions 창에 목록들을 확인해보고 이제 아까 올리디버거로 메모리 덤프한 파일을 리빌드 하겠다.
FixDump 버튼을 눌러 수정할 파일을 선택만 해주면 완료된다.
(다음과 같이 로그에서 성공메시지가 뜬다)


생성된 파일을 실행하면 메모리 덤프에서 손실된 IAT 정보가 복원되어 정상적으로 실행된다.



이제 문제를 풀어보겠다.
PDA 화면의 여러부분을 막 눌러보니 왼쪽 좌측 전원?을 클릭하니 메시지가 출력된다.



device가 고장나서 아마 수정 패치를 하라고 하는것 같다....

음.. 우선 이 메시지가 어느 부분에서 출력되는지를 알아야 할것 같다.
왜냐하면 이 메시지 출력 시점을 찾아야 왜 이 에러 메시지로 흐름이 오게 됬는지를 알수 있기 때문이다. 
Serach For -> All referenced text strings 기능으로 스트링을 분석해보면


Search for text에서 device로 검색을 해봤는데 해당 문자열이 없다. 그래서 스크롤로 분석을 해보니 label1.caption 에서 수상한 문자열이 보인다.

#66#19#21#32#86#95#85#31#18#29#27#83#38#21#19#22#27#79#24#77#75#18#14#40#98#14#24#
33#24#14#76#14#86#75#89#33#12#34#94#40#10#37#10#15#76#13#15#83#24#33#19#35#17#83#90"

인코딩 된것으로 보인다. 이 문자열이 있는 45628A 주소 부근에서 집중적으로 분석해보겠다.

"The Program does not accept your password,fix it"
"pig"

이에러를 해결하고 나서 패스워드인 "pig"를 넣으면 문제가 끝이 날것 같다. 이제 에러를 수정하기 위해 label1.caption 이 있는 456263에 Break Point를 걸고 StepOver 로 하나씩 진행하면

0045629B  |.  E8 4CF4FFFF   CALL unp1_.004556EC
004562A0  |.  8B55 FC       MOV EDX,DWORD PTR SS:[EBP-4]

CALL 을 하고 난뒤 MOV 에서 인코딩된 문자열이 디코딩되서 Value창에 나타난다.


자 이제 왜 이쪽 흐름으로 왔는지를 분석하기 위해 위를 분석해본다.

00456211  |.  E8 26C9FCFF   CALL unp1_.00422B3C
00456216  |>  B8 02000000   MOV EAX,2
0045621B  |.  83F8 02       CMP EAX,2
0045621E      0F85 8C000000 JNZ unp1_.004562B0

CALL한후 EAX 값에다가 2를 대입한다. CMP 구문에서 EAX와 2를 비교하고 두 값이 같으므로 Z플래그가 1로 세트되어 JNZ구문에서 점프를 하지 않는다.
정리하면 콜을 한후 리턴값이 저장되는 EAX가 어떻든지 간에 그담에 EAX를 2로 바꾸기에 무조건 점프를 하지 않게 된다. 따라서 에러 메시지가 항상 출력된다.

그래서 이 JNZ를 JMP로 변경하고 실행을 해보면 다른 화면이 뜨게 된다.


아까 찾은 암호인 pig를 입력해보면 다른 에러메시지가 출력된다.


Restart 한뒤 아까처럼 다시 수정한 후 Serach For -> All referenced text strings 기능으로 해당 문자열을 찾아보면

00456452  l  MOV EDX.unp1_.00456570  l ASCII "The Program does not accept your password, fix it"

이 주소로 이동한뒤 이 에러 메시지로 오세된 흐름을 조사해보면 위에서

00456401  |.  B8 02000000   MOV EAX,2
00456406  |.  83F8 02       CMP EAX,2
00456409  |.  75 5C         JNZ SHORT unp1_.00456467

이런 구문이 발견되는데 방금전과 같이 EAX에 2를 대입한후 CMP EAX, 2 로 인하여 Z플래그가 1로 세트되어 JNZ에서 무조건 점프를 하지 않게된다 어디로 점프하는지 보니까 에러메시지를 건너뛰게 된다.
따라서 이 JNZ를 JMP로 바꿔서 실행해보면

문제를 풀었고 솔루션을 보내달라는 메시지가 출력된다.