CodeEngn Reversing Basic_Level03

문제이다. 일단 실행해 보았다. 

프로그램을 실행하면 위와 같은 에러가 뜬다.

msvbvm50.dll을 구글에 검색하면 금방 다운받을 수 있다. 아무데서나 받지 말고 MS 공식 사이트에서 받자. DLL을 받고 다시 프로그램을 실행해 보았다. 

이런 창이 뜨고, 확인을 누르면 다음 창이 뜬다. 번역기에 돌려보니 독일어이다. “이러한 잔소리를 제거하거나 올바른 암호를 꺼내라” 라고 한다. 이 Nag Meldung창을 제거하거나 암호를 찾으라는 것 같다.

Regcode에 아무거나 치고 Registrieren을 누르면 비밀번호가 틀렸다고 말 한다.  

시작하기 전에, PEiD로 간단한 PE header 정보를 살펴보았다. 패킹 유무를 확인하기 위함이다. 특별해 보이는 것은 없다.

그럼 올리 디버거에 올려보도록 하자. 방금 프로그램에서 어떤 문자열들을 사용하는지 보았다. 어떤 문자열을 사용하는지 알고 있으므로 한 번 referenced text string에서 찾을 수 있는지 보자. 

디스어셈블 창에서 오른쪽키를 누르고 위와 같은 메뉴를 찾으면 된다. 

그러면 이렇게 string을 찾을 수 있다. 굉장히 비밀번호같이 생긴 string도 보인다..만 가정하지 말고 에러 메시지와 성공 메시지에 breakpoint를 걸자. 분명히 어느 부분에서 cmp가 일어나고, 그 결과에 따라 메시지가 바뀔 것이다. 그 바뀌는 지점은 메시지 근처에 있을 확률이 높으니, 메시지로 가보는 것이다. 

에러 메시지에 breakpoint를 걸고 아무 글자나 입력하여 이 곳에서 멈추었다. 코드를 살펴보면 문자열 2G83G35Hs2를 push하고, 바로 뒤에 아까 깔았던 msvbvm50.dll에 속한 vbaStrCmp 함수를 call하는 것을 볼 수 있다.  CodeEngn문제에서 요구하는 flag는 이 녀석이다

이제 이 프로그램 문제를 풀어보자. 그런데 뭔가 이상하다. 문자열을 cmp했다면 그 결과를 사용하여 분기하는게 일반적이지 않나? 근데 여기서는 분기가 일어나지 않는다.  breakpoint 위쪽에 하나 있는 JE가 수상해 보였지만 따라가보면 별 게 없다. 그러고보니 string reference에서 2G83G35Hs2가 두 번 있었다. 

수상해 보이는 string reference에 모두 breakpoint를 걸고 돌려보자.

또 다른 2G83G35Hs2 문자열의 reference이다. 문자열을 push하고 vbaStrCmp가 불린다. F8로 한 줄씩 진행시켜 수상한 JE까지 진행해 보았다.

JE가 어디로 jmp하는지 찾아보았다(빨간 화살표를 따라간다)

점프문을 통해 도달하는 곳은 에러 메시지가 있는 부분 근처이다. 쭉 F8로 실행해 보면 그대로 에러 메시를 내는 것을 확인할 수 있다. 

따라서 첫 번째 비교를 통해 맞냐 틀리냐를 판별하고 있다는 것을 알 수 있다. 첫 번째 비교 루틴을 살펴보자.

메시지 문구에 영향을 끼치는 JE 위의 코드를 살펴보았다. 함수를 나온 뒤 바로 EAX의 값을 EDI로 옮기고 있다. JE 위의 CMP에서는 DI와 SI를 비교하므로 눈여겨 보아야 할 것 같다. 

옮기기 전 EAX의 값은 FFFFFFFF이다. vbaStrCmp에서 string이 같지 않을 때 이 값을 리턴하는 것 같다.

그 다음 EBP-58 주소의 스택의 주소를 ECX에 불러온다. 

그 값이 무엇인지 확인해 보았다. 내가 입력한 값의 주소가 들어있는 주소이다. 더블포인터인가? 그 다음 NEG 라는 instruction을 적용한다. 찾아보니 2의 보수를 적용한 not연산인 것 같다.  그 다음엔 EDI가 자기 자신과 SBB 연산을 한다. 이건 또 뭔가 싶어 찾아보았더니 복잡하다.

좀 더 직관적인 설명을 찾아보니

sbb edx, edx     ; if (cf == 0) then edx == 0, else edx == -1

이런 연산이라 한다.

코드를 따라가다 보면 결국 EDI는 0으로 설정이 된다. 이 루틴 속에서 처음부터 0이었던 ESI와 같은 값이다. 따라서 JE에서 점프를 하게 된다. 

2G83G35Hs2 를 넣었을 때는 어떨까?

vbaStrCmp 후에 EAX는 0으로 설정되고, 그에 따라 EDI도 0이 된다. 

cmp하고 je하기 전 레지스터의 상태이다. ESI와 EDI는 다른 값을 가진다. 따라서 점프하지 않고 성공 메시지를 출력한다.

비밀번호가 2G83G35Hs2것은 이제 확실한 것 같다. 한 번 2G83G35Hs2를 입력해보자. 

이렇게 축하 메시지가 뜬다. 

이제 마지막 문제, Nag를 없애볼 차례이다. Nag 메시지가 있는 곳에 breakpoint를 걸자.

사실 처음 풀 때는 메시지를 띄우는 부분을 NOP이나 쓸데없는 연산으로 바꿔버리면 되지 않을까 생각했다. 실제로 그런 노가다를 했었다. 메시지박스가 불리는 곳, 인자를 주는 곳까지 전부 NOP으로 밀어버렸다. 

근데 그것보다 효율적인 방법이 있었다 교재에.. 코드의 위를 보면 PUSH EBP 등 함수의 프롤로그로 보이는 코드가 있다. 이 Nag를 띄우는 루틴이 함수로 묶여있는 것이다. 또한 Retn이 있는 곳까지 찾아보면 이 함수는 Nag를 띄우는 일만 하고 끝이 난다. 

그래서 그냥 Push EBP 하는 코드를 retn코드로 바꿔버리면 아무것도 안 하고 돌아간다. 이 함수에서 Retn하며 정리하는 스택이 있는지 확인하니 4만큼 정리한다.

따라서 이렇게 바꿔주면 메시지가 뜨지 않는다.

이걸로 프로그램의 요구사항도 전부 충족시켰다. 


tags: writeup, reversing, pe file, windows, x86asm