*** Easy CrackMe 리버싱하기 ***



처음으로 스스로 (구글의 도움없이) 리버싱을 해냈습니다.


물론 WinAPI 함수는 구글링을 했습니다.


처음부터 끝까지 레지스터의 스택포인터 따라가며, 헥사값을 보며, 동작의 의미를 알고,


그 느낌을 알면 아 이곳에는 이 값이겠구나 추론을 하고(물론 맞췄습니다.)


아무튼...


제가 지금까지 리버싱 공부하면서 수많은 블로그 글들을 참조하였듯이,


이 파일을 해결하지 못하신 분들도 제 블로그를 보시고 제가 따라온 루트를 밟고 꼭 푸시는데 도움이 되었으면 좋겠습니다!


(Easy_CrackMe.exe 파일의 출처는 Reversing.kr 입니다!)



(주의 : 포스트 하단에 정답이 공개되어 있습니다!!!)


-------------------------------------------------------------------------------------------------------------------------------------------------------------


일단 파일을 실행시켜 보겠습니다.



음...비밀번호를 입력해서 correct / wrong 을 맞추는 프로그램이라는 것을 추측할 수 있겠네요.


물론 그 비밀번호는 Flag 가 되겠구요.


일단 아무거나 쳐보겠습니다.



한 번에 맞추면 좋겠지만... 아쉽게도 틀렸네요.


하지만 여기서 큰 힌트를 얻을 수 있는데, 바로 문자열


"Incorrect Password"


입니다.


바로 올리디버그로 열어봅시다.



EP는 00401188이네요.


일단 처음에 생각할 수 있는 것은, 저 위에서 발견한 "Incorrect Password" 문자열을 출력하는 위치를 알아내는 것입니다.


[우클릭] > [Search for] > [All referenced text strings] 를 클릭하여 해당 문자열의 위치를 알아봐야 합니다.




위치는 0040113C 네요.


더블클릭해서 이동해봅시다.



저기 위쪽에 빨간배경(BreakPoint, BP), 브레이크포인트를 걸어놨습니다. 느낌 상 저기서 뭔가가 시작된다는 느낌이 왔습니다.


이 상태에서 F9을 누르면 저 위치까지만 실행됩니다.


저 BP는 풀지 않는 것이 좋습니다. 리버싱 하게 되면서 핵심 동작의 시작부분에 BP를 걸어두는 습관을 기릅시다.



그러면 실행파일이 뜹니다. 문자열을 입력받을 차례입니다.


아래에 보면 GetDlgItemTextA 함수가 있는데, 이는 WinAPI 함수로써, 구글에 어떤 함수인지 검색해 보았습니다.


하지만 느낌상...문자열을 입력받는 함수라는 것을 알 수 있죠?


Get + Dlg(=Dialog) + ItemText(=문자열) + A(=API)


즉, 대화상자에서 문자열을 Get 하는 API 라고 해석할 수 있습니다.




저기서 컨트롤이란...MFC에서 볼 수 있는 입력창(Edit Control)을 말하는 것 같네요.


아무튼 문자열을 입력받습니다.


어떤 동작인지 알았으니까 더 볼 필요 없겠죠? 일단 문자열을 입력하고 확인을 누릅니다.



이후에 F8로 쭉쭉 넘어갑니다.


GetDlgItemTextA 함수 아래까지 왔습니다. CMP 명령어로 어떤 두 가지를 검사하네요.


이후 ZF(제로 플래그)를 띄울지 안띄울지 판단 후, 이 ZF로 점프여부도 판단하겠네요.


왜냐면... 그 아래에 JNZ(Jump if Not Zero)가 있으니까요 ㅎㅎ


일단 두 가지가 무엇인지 볼게요.


하나는 ESP+5 의 주소의 값이고, 하나는 61입니다.


61은 딱 보니...아스키 값인 것 같네요. 61은 아스키 값으로 a를 의미합니다. (0x61 = 'a')


그럼 ESP+5의 위치에 무엇이 있는지 보겠습니다.


우선 현재의 ESP 값은



0019F6F0 입니다. 여기서 +5를 해주게되면 0019F6F5가 됩니다.



0019F6F0 의 줄에는 80 00 60 10 이 있고, 0019F6F4 줄에는 46 44 53 41 이 있네요. 그리고 그 아래 0019F6F8 줄에는 00 44 53 41 이 있습니다.


윈도우 운영체제는 Little Endian 방식을 사용합니다.


위의 값들은 실제로 이런 방식으로 들어가 있습니다.



61이 아스키 값이라고 추측해보면, 저 숫자들은 아스키 값이라는 것을 추측할 수 있습니다.


41 = A    /    53 = S    /    44 = D    /    46 = F

41 = A    /    53 = S    /    44 = D    /    00 = (NULL)


ASDFASD 가 나왔네요. 이는 저희가 저 위에서 입력한 값이네요.


즉, ESP+5 와 61을 비교한다는 것은, 두번째 자리가 a인지 아닌지를 검사한다는 것을 알 수 있습니다.


몇자리가 될 지는 모르겠지만, 하나는 알게됬습니다. 문자열의 두 번쨰 자리의 값은 'a'가 되어야 합니다.


a가 아닐 경우에는 00401135 주소로 점프하는데, 이는 Incorrect Password 문자열을 출력(틀렸음을 의미)하게 되는 함수가 있습니다.


=> @a@@@....


이번 문자열은 틀렸으니까 다시 Ctrl + F2 를 눌러서 재실행 시킨 후, 아무거나 입력합니다. 대신 두번째 자리는 a로 합니다.



대충 이렇게 입력했습니다.


다시 그 위치로 가볼까요.



두 번째 자리에 a를 입력했기 때문에 실패조건을 넘어갑니다.


이번엔 ASCII "5y"라는 힌트가 있네요.


왠지 모르겠지만 일단 2를 PUSH합니다.


그리고 LEA(MOV와 비슷한 명령어)를 하는데, ECX 레지스터에 ESP+A 위치의 값을 넣어줍니다.


현재 ESP는...



0019F98C네요.(스택포인터의 값은 실행마다 바뀝니다. 그때 항상 다르기때문에 체크하셔야 합니다.)


이 주소에 +A(10)을 해주면...


C는 12이므로, 22가 됩니다. 22는 16+6이므로,


0019F996이 되겠네요.


0019F996에 무슨값이 있나 봐야겠습니다.



끝에서부터 94 = 41 / 95 = 61(a) / 96 = 46 / 97 = 46


로 볼 수 있습니다. 즉, 0019F996 위치에는 46(='F')가 들어있습니다.


이는 저희가 입력한 문자열(AaFFFFF)에서 세번째 자리를 의미하겠네요.


아무튼 LEA명령어를 실행하면...



이렇게 주소값이 옮겨집니다. MOV는 값을 옮기지만 LEA는 주소값을 옮기는 것을 알 수 있습니다.


이 주소값은 ECX 레지스터로 이동합니다.


친절하게 저희가 입력한 문자열이 다 뜨네요.


다음 명령어는...



PUSH 406078 입니다.


4060XX 이 주소값의 영역은 바이너리 값이 들어있습니다. 바이너리 값은 올리디버그 좌측 하단에 있습니다.


볼까요?



00406078 위치에 35 가 있습니다. 35는 5이고 79는 'y'입니다.


저 헥사값 두자리가 1바이트입니다.(헥사값 1자리 = 4비트 => 0x0~0xF => 0b0000~0b1111)


PUSH 406078 명령어와 PUSH ECX 명령어를 실행했습니다.


PUSH 동작이므로 스택메모리를 봐야겠죠?




현재 스택 포인터(스택의 꼭대기)는 0019F984이고, 스택의 최상위 2개는 주소값이 있습니다.


맨 위의 주소는 유저가 입력한 문자열이 있는 스택메모리의 주소,


그 아래의 주소는 바이너리 값의 주소 입니다.


(참고로 스택 포인터는 스택을 쌓을수록 작아집니다. 스택은 아래에서 쌓는 것이 아니라 꼭대기에서 내려옵니다. 마치 종유석과 같습니다.)


왜 저 문자열을 주석으로 달아놨는지는 잘 모르겠습니다. 왜 1Byte 값만 가져오지 않을까요?


아무튼, 이 두 주소를 PUSH 한 다음, CALL 00401150 을 실행합니다.


이로써, PUSH 한 두 주소값은 함수의 파라미터라고 볼 수 있습니다.


대충 이런 함수겠네요.


func_00401150(addr& binary_addr, addr& user_input_addr)


이 함수는 그냥 넘깁시다. F8을 눌러서 Step Over 를 실행합니다.



함수 내부 동작에서 EAX 를 1로 만들었고, ECX 를 1로 만들었습니다.



다음 명령어를 보니, 또 다시 실패 창을 띄우는 명령어가 다가오네요.


일단 ADD ESP, 0C 를 합니다.



ESP 값이 0019F984 에서 C값이 더해진 0019F990 으로 바꼈습니다.


다음 명령어를 봅시다. TEST EAX, EAX 입니다.

TEST 명령어는 CMP 명령어와 다르게 AND 연산을 합니다.(CMP 명령어는 SUB연산입니다!)

즉 EAX, EAX 를 AND 연산을 하는데... 결과가 0이면 ZF가 1로 set 될 것입니다.

JNZ는 Jump if Not Zero, 즉 연산의 결과 값이 0이 아니면, 다시 말해 ZF가 0이면 점프를 합니다.

안타깝게도 EAX 가 1이므로 EAX끼리 AND를 시키면 1이 되고, 이는 ZF가 0으로 유지됩니다.

그러면 JNZ 명령어로 실패 창으로 가게 됩니다.

실행해볼게요.


안타깝게도 실패했습니다.

하지만 리버싱의 느낌으로, 왠지 5y가 들어가야 할 것 같습니다.

406078 부분에 5y 앞뒤로 NULL 값으로 도배가 되어 있기 때문에, 3번째 자리와 4번째 자리에 5y를 넣어야 할 것 같습니다.

그러면 00401135 함수는 이 두 문자열을 검사하는 함수일 것 같네요.

함수 결과가 EAX = 1로 셋 되었는데, 만약 입력값과 5y가 같으면 EAX를 0으로 클리어하고 ZF를 1로 셋 할 것 같습니다.

Ctrl + F2 를 눌러 재시작을 하고, Xa5yXXX...를 입력해봅시다.


이번엔 Aa5yFFFF를 입력했습니다.


네 성공적으로 4010CD(실패 창 이동) 을 넘었습니다.

이로써 두번째 자리는 a / 세번째 자리는 5 / 네번째 자리는 y 가 와야 함을 알 수 있습니다.

이번엔 PUSH EBX, PUSH ESI 동작이 있네요. 두개 다 실행하겠습니다.


두개 전부 스택에 올라갔습니다. (EBX = 0x1, ESI = 0xF0772)

다음 명령어를 볼까요?


4060XX 영역은 바이너리 영역이므로, 이 40606C 에서 NULL 까지 문자열을 봐야겠습니다.

일단 ESI 에는 40606C라는 값이 들어갈 것입니다.


(바뀐 ESI, 친절하게 문자열을 알려줍니다.)


0040606C(=0x52) 부터 NULL 이전까지의 문자열 ("R3versing") 을 확인할 수 있습니다.

다음 명령어는...


LEA 명령어입니다. 주소값을 옮기는 명령어입니다.

ESP+10의 위치네요. 현재 ESP의 값은


0019F6E8 입니다. 여기에 +10을 하면

0019F6F8 입니다. 명령어를 실행해 볼게요.


EAX 레지스터에 0019F6F8 주소가 옮겨졌습니다. 주석으로 ASCII "FFFF"라고 되어있네요.

왠지...FFFF랑 R3versing 이랑 같아야 할 것 같습니다.

확인해 볼게요.

다음 명령어는


하나의 Routine 으로 묶여있습니다.

=====================================================================
처음에 EAX 의 주소값(EAX 레지스터에 담겨져 있는 주소값) 위치의 값을 DL(EDX 레지스터의 하위 8비트) 에 옮깁니다.

그리고 ESI 의 주소값 위치의 값을 BL(EBX 레지스터의 하위 8비트) 에 옮깁니다.

그리고 DL의 값은 다시 CL로 옮기고, BL과 DL을 비교합니다.

CMP 명령어 이기 때문에, DL과 BL이 같으면 결과 값이 0이 될 것이고, 그러면 ZF는 1이 될 것입니다.

만약 두 값이 같지 않으면(ZF가 0이면) 00401102 로 점프합니다. (00401102 위치에는 EAX 를 -1로 만들어서 실패하게 만드는 아주 무서운 과정이 들어있습니다.)

위의 과정을 통과했으면, CL을 TEST 연산 합니다.

이는 CL이 0인지 아닌지 검사합니다. 이 말은, DL 이 NULL인지를 검사하는 것입니다. 다시 말해서 값의 여부를 판단, 즉 문자열의 끝인지를 판단 후 저 Routine 을 빠져나갑니다.

이로써 저 Routine 은 반복문이고, TEST CL, CL 은 break 조건임을 알 수 있습니다.

그 다음, EAX+1(1바이트를 의미합니다.) 위치의 값을 DL에 넣고, ESI+1 위치의 값을 BL에 넣고, 또 다시 비교를 합니다.

다르면? 00401102(실패 창) 로 점프합니다.

그리고 EAX와 ESI 에 2씩 더해줍니다. 이유는 문자(character)를 검사했는데, 2개를 검사했기 때문입니다.

2바이트를 더해주어야 그다음에 또 다시 2바이트를 검사할 수 있습니다.

그리고 TEST CL, CL 을 통해 문자열의 끝을 검사합니다.

CL 이 0이 아니면, 다시 말해 끝이 아니면 루틴의 처음(004010DA)으로 돌아갑니다.
=====================================================================

위의 과정의 루틴입니다. 당연히 실패 창으로 넘어갑니다.

왜냐하면 저희가 입력한 문자열 FFFFF는 R3versing 과 다르기 때문입니다.

이로써, a5y 다음에는 R3versing 을 입력해야 함을 알 수 있습니다.

그래도 실행은 해볼게요.


실패 창으로 가기 직전입니다.


레지스터 창입니다.

DL에는 46(='F')이 들어있고, BL에는 52(='R')가 들어있고, CL에는 DL과 같이 46이 들어있습니다.

46은 52와 다르기 때문에, JNZ 조건으로 실패 창으로 갑니다.

그러면 Ctrl + F2 를 누르고 재시작을 한 후, 이번에는


Aa5yR3versing 을 입력해보겠습니다.


해당 루틴을 5번 돈 후, 무사히 실패없이 통과했습니다.

다음 명령어는 XOR EAX, EAX 입니다.

리버싱에서 XOR EAX, EAX 는 EAX = 0 으로 만들겠다는 의미입니다.

같은 값을 Exclusive OR 하게 되면 모든 비트가 같기 때문에 전부 0이 되기 때문입니다.


예상대로 EAX가 0이 됬습니다.


다음 명령어는 JMP 00401107입니다.



EAX 를 -1로 만들어버리는 무서운 과정이 생략됬군요.


참고로 SBB는 캐리플래그도 연산에 포함시킵니다.


SUB 가 dest = dest - src 라면


SBB 는 dest = dest - (src - carry flag) 입니다.


아무튼 생략되었습니다. 이유는 아래에서 말하겠습니다.


바로 POP ESI, POP EBX를 해줍니다.



ESI에는 002D0094 라는 값이 들어갔고, EBX에는 00000001 값이 들어갔습니다.


TEST EAX, EAX 를 해줍니다.


그 아래에는



JNZ (Jump if Not Zero) 명령어가 있습니다.


이 뜻은, ZF가 0이면, 즉 EAX 가 0이 아니면 00401135(실패 창)으로 갑니다.



ZF가 1이네요.  실패하지 않습니다.


만약 SBB 과정을 거치게 되면, 이전에 TEST CL, CL과정에서 ZF가 1로 셋 되지 않고 ZF가 0으로 유지되기 때문에 실패합니다.


그 다음 명령어는



0x45(='E') 와 ESP+4위치의 값과 비교하는 것입니다.


현재 ESP는



0019F990입니다.


여기에 4를 더하면 0019F994입니다.



0x41(='A')입니다.


다르네요. A는 저희가 첫번째 자리에 입력한 문자입니다.


즉, 마지막으로 첫번째 자리에 0x45, 문자 'E'를 입력해야 함을 알아냈습니다.



실행시키면 역시 실패 창으로 갑니다.


다시 재시작 한 후, 이번에는 답으로 추정되는 Ea5yR3versing 을 입력해보겠습니다.



위의 과정을 전부 거친 후 문자열을 검사하고. 성공적으로 정답 창으로 왔습니다.


프로그램은 MessageBoxA에 정답이라는 것을 알려준 후 EndDialog를 통해 대화상자를 종료할 것으로 추측할 수 있습니다.



맞췄습니다.


정답은...


Ea5yR3versing


이었습니다.


영어로 EaSyREversing 이네요 ㅎㅎㅎ 리버싱 쉽대요


-------------------------------------------------------------------------------------------------------------------------------------------------------------


파일명 부터가 Easy_CrackMe.exe 여서 그런지 리버싱하는데 나름 쉬웠습니다.


처음으로 자력으로 해결한 프로그램이어서 더더욱 자세하게 설명한 것 같네요.


해결 못하신 분들은 제가 한 것처럼 따라서 해보시면 될 것 같습니다.


감사합니다.


>>> Reversing.kr


>>> Easy_CrackMe



'보안 & 모의해킹 > 리버싱' 카테고리의 다른 글

[Reversing.kr] - 3. Easy UnpackMe  (0) 2018.07.12
Posted by NDC :