Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

White Security

abex' crackme 2 분석 본문

Reversing

abex' crackme 2 분석

POSIX 2019. 2. 13. 18:46


crackme2.exe

abex' crackme 2번째 버전입니다.

포스팅을 매일 적어도 하나씩은 올리기로 했었는데

열심히 분석하다보니 이틀이 지나가버린거 있죠.


크게 어려운 문제는 아니지만

한줄한줄 분석하다보니

시간이 참 빨리갑니다.



프로그램을 실행해, About 버튼을 눌러보면 

제작시기를 확인할 수 있습니다.

지금이 2019년이니.. 무려 19년이 넘었군요.



Check 버튼을 눌러 인증을 시도해보면

serial 값이 잘못되었다고 알려줍니다.

프로그램 내부에서 생성하는 시리얼 값을 찾아

입력해주면 되는 문제같습니다.



참고로 name이 3글자 이하가 되면

4글자 이상을 입력하라며 오류가 나타납니다.



올리디버거를 사용해 바이너리를 열어 보았습니다.

파란 배경으로 표시된 401238이 바로 EntryPoint 입니다.


먼저 PUSH를 통해 401E14를 스택에 넣고

JMP를 통해 바로 윗줄인 ThunRTMain이라는

함수를 호출하게 되는데요.


ThunRTMain

비주얼 베이직 기반 프로그램에서 실행되는 함수로서 

401E14 주소의 RT_MainStruct 구조체를 인자로 받아

프로그램 실행에 필요한 모든 정보를 얻습니다.



프로그램을 쭉 따라가다보면

윈도우를 생성하고는 Running 상태가 됩니다.


이벤트 기반 프로그래밍 언어의 특성이라고 할 수 있는데요.

윈도우 폼에 각각의 이벤트 리스너를 두는 것으로

이벤트가 발생하면 다시금 디버깅이 가능해집니다.



적절히 폼을 채워주고 Check 버튼을 클릭해주면

Check 버튼에 걸어두었던 이벤트 리스터가 동작하면서

함수로 진입하게 됩니다.



Check 버튼을 클릭해주면 등장하는 바로 이 녀석이

Check 버튼의 Click 이벤트 리스터 함수입니다.


앞으로 등장하는 이미지는

여러분이 보시는 화면과 조금 차이가 있을텐데

제가 일일히 코멘트를 붙여 두었기에

참고하여 이해하시면 분명 도움이 되리라 생각합니다.



아래로 조금 내려보면

MOV [EBP-XX], EDI 구문이 연속적으로 존재하는데

각각의 변수들을 초기화 하는 것으로


이를 참고해, 변수들의 주소값을 예측할 수 있습니다.

저는 구별하기 쉽게, 이름을 붙여 두었습니다.


RT_Var_Object:
+00 SizeOf(RT_Text_Object)
+04 Method1
+08 Data (length of string)
+0C etc (other values or methods)

RT_Text_Object:
+00 SizeOf(RT_Text_Object)
+04 Method1
+08 TextPointer
+0C etc (other values or methods)


각 변수들은 16바이트씩 공간을 차지하고 있는데요.

위와 같은 형식으로 되어 있다고 이해하시면 되겠습니다.


따라서 변수의 실제 데이터는 대부분

변수의 시작값이 아니라 +8인 주소에 들어 있습니다.

예를 들어 EBP-9C 메모리가 변수의 시작 부분이라면

실제의 텍스트 포인터나 정수값은

EBP-94 주소에 들어있는 것이지요.



천천히 실행해보면서, 흐름을 파악하는 것도 좋지만

우클릭 > Search for > All referenced text string

를 이용해 명령어에서 사용되는 참조 문자열을 확인하면

더욱 빠르게 찾아갈 수 있습니다.



“Error!” 문자열 주소에 접근하니

문자열 인자를 스택에 넣고

rtcMsgBox 함수를 호출하고 있네요.


name이 3글자 이하일때, 등장하는 부분인데요.

조금만 위로 올려보면

글자수를 점검하는 분기문이 나타납니다.



vbaLenVar(arg1, arg2) : 문자열 인자 arg2의 길이를 구해 arg1에 저장합니다.


vbaLenVar 함수를 통해

텍스트 변수 EBP-74 의 문자열 길이를

변수 EBP-9C 에 넣어주고 있습니다.



vbaVarTstLt(arg1, arg2) : arg1 < arg2 이면 0, 그렇지 않으면 FFFFFF을 리턴합니다.


그리고 변수 EBP-DC 에 4를 대입하고

vbaVarTstLt 함수를 사용해

EBP-9C 가 4보다 작은지 확인하고 있군요.


따라서 입력한 name의 값이

4보다 작으면 FFFFFF를 리턴하게 됩니다.


TEST 명령어는 AND 연산을 수행하므로

만약 AX 가 0이라면 ZF(ZeroFlag)가 1로 세팅되어

JE 명령어를 수행하게 됩니다.


하지만 4보다 작으면 EAXFFFFFF가 됩니다.

따라서 ZF 는 0이 되므로 JE 명령어를 수행하지 않고

그대로 통과하여 에러 메시지를 출력하게 됩니다.



조금 스크롤을 올려보면

402F78 에서 텍스트박스 폼을 먼저 가져온 후에

name 값을 불러오고 있습니다.


vbaVarMove() : EDX의 변수를 ECX로 복사합니다.


vbaVarMove 로 임시변수에서 자원을 복사한 다음


vbaVarFree() : ECX 주소의 자원을 해제합니다.


필요를 다한 자원을 vbaFreeObj 로 해제해 주네요.



조금 내려오면 아까 보았던 부분이죠?

vbaLenVar 를 통해 EBP-74 의 문자열 길이를

EBP-9C 에 대입하고

EBP-DC 에 4 값을 넣어 vbaVarTstLt 로

대소를 비교하고 있습니다.


Lt로 끝나는 vbaVarTstLt 함수는 

비주얼 베이직에서의

관계연산자 < (less than)을 의미하는데요.


마찬가지로 

vbaVarTstLe <= (less than or equal)

vbaVarTstGt > (greater than)

vbaVarTstGe >= (greater than or equal) 

vbaVarTstEq == (equal)

vbaVarTstNe != (not equal)

등의 함수를 사용해 비교할 수도 있습니다.



글자수가 4글자 이상이라면 403026 에서

4030F9 로 점프하게 됩니다.


그런데 여길 보면 어쩐지 익숙한 모양새이죠?

점프하기 전과 동일하게

name의 글자수 조건을 확인하여 분기하고 있습니다.


만약 글자수가 3글자 이하라면

403130 에서 다시한번 점프하게 됩니다.



점프하는 곳을 확인해 보니

클릭 이벤트 리스너 함수의 끝 부분이네요.


종료하기 전

모든 자원을 해제하고 있습니다.

따라서 글자가 3글자 이하이면

오류메시지를 출력하고

곧바로 함수를 종료하게 되겠군요.



만약 3글자를 넘는다면 어떨까요?

403130 의 점프문을 넘기고

변수 여섯개를 스택에 넣어 6개 인자로

vbaVarForInit 함수를 호출하고 있습니다.


For i = 1 To 10 Step 1
	...
Next


비주얼 베이직에는

위처럼 For ... Next 라는 반복 구문이 존재하는데요.

vbaVarForInit 와 대응됩니다.


vbaVarForInit(arg1, arg2, arg3, first_loop_index, last_loop_index, loop_step)


몇번 돌려가면서 디버깅 해본 결과

4~6 번째 인자는 각각 루프 인덱스에 대한

최초값, 최후값, 증가값으로 되어 있었습니다.



루프 인덱스의 증감에 대한 관리는

반복문 내에서 수행하는 것이 아니라

vbaVarForNext 함수에서 수행하여 주고 있습니다.



다만 루프 인덱스는 4바이트로 관리되며

ForVarForInit 수행 직후에

상위 바이트에 0071과 같은 값이 들어가는데

무슨 역할을 하는 것인지는 알아낼 수 없었습니다.


vbaVarForNext(loop_index, first_loop_index, last_loop_index) : loop_index >= last_loop_index이면 EAX = 0


만약 반복문이 끝났다고 여겨지면

EAX에 0 값을 넣어 리턴하는 것이지요.



그럼 403197 의 TEST EAX, EAX 에서

ZF(Zero Flag) 가 세워지고 직후 점프하여

반복문에서 빠져나가게 됩니다.



이제 반복문 내부를 한번 살펴볼까요?


vbaI4Var(arg1, arg2): arg2의 하위 4바이트 정수값을 EAX에 저장합니다.


vbaI4Var 함수를 통해 EAX에 EBP-24 변수의

하위 4바이트 값을 저장하고 있습니다.




따라서 EAX에는 루프 인덱스의 값이 들어가게 되는 것이죠.

추가로 1값을 가진 EBP-9C 변수도 인자로 받지만

무슨 역할을 하는 것인지는 알 수 없었습니다.



rtcMidCharVar(arg1, arg2, arg3) : arg2의 arg3번째 문자열을 arg1에 저장합니다.


가져온 루프 인덱스의 값과

입력한 name 문자열을 인자로 하여

name의 N번째 문자열을

새로운 변수에 저장하고 있습니다.


만약 루프 인덱스가 1이고

name 문자열이 abcde 라면

a 를 리턴하게 되는 것이죠.


그 값을 다른 변수에 옮겨담아 준 후

임시변수의 자원은 즉시 해제해주고 있네요.


vbaStrVarVal(arg1, arg2) : arg1 문자의 포인터를 리턴합니다.


vbaStrVarVal 함수는

vbsMidCharVar 함수를 통해 가져온 문자열의 주소

name의 N번째 문자의 포인터를

EAX에 넣어줍니다.


rtcAnsiValueBstr(arg1) : arg1 문자의 아스키코드 값을 리턴합니다.


그리고 rtcAnsiValueBstr 은 EAX 주소의

HEX값을 다시 EAX에 넣어주죠.


따라서 name의 루프인덱스 N번째 문자가 a 였다면

 0x61값을 EAX에 저장해 줍니다.


EBP-54 에 그 값을 저장하고

임시자원은 즉시 해제해 주는 모습입니다.



vbaVarAdd(arg1, arg2, arg3) : arg2와 arg3의 값을 더하여 arg1에 저장합니다.


EBP-DC64를 넣어 준 후

아스키코드 값을 저장한 EBP-DC와 더하여

EBP-9C에 저장하고 있습니다.


결론적으로 (N번째 문자의 아스키 값) + 64

EBP-9C에 저장하고 있네요.


rtcHexVarFromVar(arg1, arg2) : arg2의 HEX값을 arg1에 문자열로 저장합니다.


그리고 EBP-54 로 다시 옮겨담아서

rtcHexVarFromVar 함수를 통해 EBP-9C

HEX 문자열을 저장하고 있습니다.


vbaVarCat(arg1, arg2, arg3) : 문자열 arg3, arg2를 붙여, arg1에 저장합니다.


문자열을 다시 EBP-54에 옮겨주고

vbaVarCat 함수를 사용하여

기존 문자열과 붙여주고 있군요.



루프를 돌며 생성된 HEX 문자열들을

하나씩 합쳐주는 것이지요.




이렇게 루프를 네바퀴 돌아주면

암호문은 완성이 되어 EBP-9C에 저장된 상태가 됩니다.


이제 완성된 암호와 사용자가 입력한 암호가

동일한지 확인하기 위해

시리얼 텍스트 박스에서 문자열을 가져와줍니다.


만약 텍스트 박스에서 문자열을 가져오는 중에

오류가 발생하면 0이 아닌 값을 리턴하는데


그러면 4032D5에서 점프하지 못해

4032E3vbaHresultCheckObj 함수를

호출하게 됩니다.


이전 name 텍스트 박스에서 문자열을

가져왔을 때에도 동일하게 존재했던 부분이죠.



vbaVarTstEq(arg1, arg2) : arg1와 arg2가 같으면 FFFFFF, 그렇지 않으면 0을 리턴합니다.


403329에서 vbaVarTstEq를 통해

생성된 시리얼과 입력한 시리얼이 일치하는지 점검합니다.


만약 일치하지 않으면 403332에서 점프하여

오류 메시지를 출력하고 종료하게 됩니다.




비교문을 보면

연속된 EBP-44 와 EBP-34 변수의 값을

인자로 받고 있습니다.


따라서 입력한 name의 첫 4글자를 사용하여

시리얼을 생성하는 성질에 의해

같은 name에는 항상 같은 시리얼 값이 나오므로

디버깅을 통해 생성되는 값을 확인하면

쉽게 문제를 해결할 수도 있겠습니다.


물론 반복문을 상세히 분석하여

암호문을 생성하는 알고리즘을 파악했다면

그렇지 않고 직접 계산해내는 것도 좋습니다.



시리얼이 일치하지 않을시 점프하는 부분입니다.

메시지에 들어갈 문자열 변수들을 인자로 하여

rtcMsgBox를 통해 메시지를 표시합니다.



만약에 시리얼이 일치한다면

점프하지 않고 바로 아래로 내려가 

성공 메시지를 출력하고


vbaVarTstNe를 통해 다시한번 시리얼을 검사하여

만약 일치하면 4034F1 주소로 점프하는데

자원을 정리하고 함수를 바로 종료하게 됩니다.


crackme2.zip


주석을 작성해 파일로 첨부하였으니

분석에 참고하시면 좋겠습니다.



이상으로 abex' crackme 2

분석 포스팅이였습니다.

'Reversing' 카테고리의 다른 글

Lena's Reversing 10강 분석  (0) 2019.02.14
abex' crackme 1 분석  (0) 2019.02.11
Comments