Wargame/Lena’s Reversing for Newbies

Lena's Reversing for Newbie 18 Write-Up

0xe82de_ 2019. 8. 9. 15:08
728x90
728x90
728x90

파일 링크: https://tuts4you.com/e107_plugins/download/download.php?list.17

 

Downloads / Lenas Reversing for Newbies - Tuts 4 You

 

tuts4you.com

Tutorials Index: 18. Diversion code, encryption/decryption, selfmodifying code and polymorphism

사용도구: 올리디버거 2.01

 

Lena's Reversing for Newbie 18(이하 원본파일) 분석해보자.

 

[사진 1]

원본파일을 실행하면 Nag 창이 뜬다. 2bytes만 패치해서 Nag 창을 지우라고 한다.

 

 

[사진 2]

[사진 1]의 창에서 확인 버튼을 클릭하면 위 창이 뜬다. 첫 번째 창을 지워보자.

 

 

[사진 3]

올리디버거로 원본파일을 열어보면 위와 같이 [사진 1]에서 확인했던 Nag 창에서 사용한 문자열들을 확인할 수 있다.

 

 

[사진 4]

그래서 메시지 박스 함수의 인자를 스택에 저장하는 0x004012AF 주소에 브레이크 포인트를 설정하고 실행해봤지만 중지되지 않고 Nag 창이 떠버린다. 즉, 0x004012AF ~ 0x004012BD 주소의 메시지 박스 코드는 가짜 코드이다.

 

 

[사진 5]

따라서 처음부터 상세분석을 해야하므로 다시 올리디버거로 원본파일을 열어보자. 그러면 GetModuleHandleA 함수를 통해 반환 받은 EAX 레지스터의 값(핸들)을 메모리 0x00403130 주소에 저장한다. 그리고 EDI 레지스터에 0x00401011 값을 저장한 후 0x0040130F 함수를 호출한다. GetModuleHandleA 함수는 다음 링크를 참고하면 된다.

참고 링크: https://jade9reen.tistory.com/15

 

GetModuleHandleA Function

출처: https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-getmodulehandlea GetModuleHandleA function (libloaderapi.h) Retrieves a module handle for the specified module..

jade9reen.tistory.com

 

[사진 6]
[사진 7]

0x0040130F 함수로 이동하면 위와 같이 EAX 레지스터에 0x00401000 값을 저장한다. 그리고 해당 값을 주소로 하여 0x00401218 주소까지의 1bytes 값씩 0x5A 값과 XOR 연산한다.

 

 

[사진 8]

[사진 6], [사진 7]의 XOR 연산 전의 0x00401000 주소의 코드는 위와 같다.

 

 

[사진 9]

[사진 6], [사진 7]의 XOR 연산 후의 0x00401000 주소의 코드는 위와 같다. 코드들이 복호화된 것을 알 수 있다.

 

 

[사진 10]

다음은 0x00401011 함수를 호출하는데 0x00401011 주소는 [사진 9]에서 확인할 수 있는 복호화된 코드 영역이다.

즉, 0x00401011 함수가 수행되기 전에 복호화를 진행하고 수행되는 것이다.

 

 

[사진 11]

0x00401011 함수는 위와 같이 EDI 레지스터의 값을 증가시키면서 EDI 레지스터의 값이 가리키는 곳에 어떠한 데이터를 복사한다. 여기서 EDI 레지스터의 값은 [사진 5]에서 이미 0x00401011 값으로 초기화됐었다. 그리고 0x00401011 값은 [사진 11]에서 확인할 수 있는 XOR EAX, EAX 명령어의 주소이다.

 

 

[사진 12]

첫 번째 복사가 이뤄지는 0x00401013 주소의 MOV WORD PTR DS:[EDI],6A 명령어가 수행되면 0x00401011 주소의 명령어가 PUSH 0으로 바뀐 것을 확인할 수 있다. 0x0040104F 주소 직전까지 EDI 레지스터의 값이 증가되면서 명령어들이 이 복호화되고 있는 것이다.

모든 명령어가 복호화되고 0x0040104F 주소의 명령어에 의해 EDI 레지스터의 값에서 0x24 값을 빼고 있는데, EDI 레지스터의 값은 0x00401000 값이 된다. 그리고 0x00401052 주소의 명령어에 의해 0x00401000 주소를 함수처럼 호출한다.

 

 

[사진 13]

위와 같이 모든 명령어들이 복호화되면 메시지 박스 함수를 호출하는 코드를 확인할 수 있다. 그리고 0x00401000 주소가 호출되고 EAX 레지스터에 0x00403000 값을 저장하고 0x00403128 값까지 0xB3 값과 XOR 연산을 수행한다. 이번엔 0x00403000 ~ 0x00403128 주소 사이의 값들을 복호화하는 것이다.

 

 

[사진 14]

복호화되기 전의 0x00403000 ~ 0x00403128 주소의 값들은 위와 같다.

 

 

[사진 15]

복호화되면 [사진 1], [사진 2]에서 확인했던 문자열들이 나온다.

 

 

[사진 16]

복호화 루틴이 끝나면 메시지 박스 함수의 인자 값들이 정상적으로 보이는 것도 확인할 수 있다.

 

 

[사진 17]

이대로 실행시키면 [사진 1]에서 확인한 Nag 창이 뜬다. 즉, 이 곳이 Nag 창을 실행하는 코드인 것이다.

 

 

[사진 18]

Nag 창을 실행하고나면 0x0040106A 주소로 점프한다. 그리고 [사진 11]의 코드처럼 EDI 레지스터의 값을 증가시키면서 EDI 레지스터의 값이 가리키는 곳에 어떠한 데이터를 복사한다. 점프 직후의 EDI 레지스터의 값은 0x00401000 값이지만 0x0040106A 주소의 명령어 ADD EDI,11에 의해 EDI 레지스터의 값은 0x00401011 값이 된다.

[사진 16]에서 Nag 창을 실행했던 메시지 박스 함수를 호출하는 명령어가 바뀌게 되는 것이다.

그리고 0x004010B1 주소의 명령어에 의해 EDI 레지스터의 값이 0x15 값만큼 감소하고 함수처럼 호출되는데 여기서 EDI 레지스터의 값은 0x00401011 값이다.

 

 

[사진 19]

위와 같이 0x00401011 주소가 함수로 호출되어 실행되면 [사진 2]에서 확인했던 메인 창이 뜨는 것을 확인할 수 있다.

즉, Nag 창을 띄우는 메시지 박스 함수가 메인 창을 실행하는 어떠한 함수(메시지 박스 함수가 아님)로 바뀐 것이다.

 

 

[사진 20]

메인 창을 종료하면 0x00401054 주소로 점프한 후 0x00401320 함수를 호출한다.

 

 

[사진 21]

함수 내부로 이동하면 0x00403000 주소를 EAX 레지스터에 저장하고 0x00403128 주소까지 0x8D 값과 XOR 연산을 수행한다. [사진 13]에서 복호화되었던 0x00403000 ~ 0x00403128 영역을 다시 암호화하는 것이다.

 

 

[사진 22]

암호화되면 0x00403000 ~ 0x00403128 영역은 위와 같이 바뀐다.

 

 

[사진 23]

암호화를 마치면 0x004012C2 함수가 호출된다.

 

 

[사진 24]

0x004012C2 함수는 ExitProcess 함수를 호출하여 프로그램을 완전히 종료시킨다.

 

정리하자면 프로그램 동작 과정은 다음과 같다.

 

1. 0x0040130F 함수를 호출하여 0x00401000 ~ 0x00401218 영역의 명령어들이 복호화된다.

2. 0x00401011 함수를 호출하고 함수 내부적으로 한 번 더 복호화되며, 메시지 박스 함수를 호출하는 코드로 바뀐다.

3. 0x00401000 함수가 호출되고 0x00403000 ~ 0x00403128 영역의 데이터들이 복호화된다.

4. 복호화된 데이터로 Nag 창을 띄운다.

5. 0x0040106A 주소로 점프하여 2번에서 복호화했던 메시지 박스 함수의 인자를 바꿔버린다.

6. 0x00401011 함수를 호출하고 메인 창을 띄우고 프로그램이 종료된다.

 

Nag 창을 없애고 메인 창만 띄우기 위해서 코드를 패치해야 하는데, 메시지 박스 함수의 인자를 패치하여 Nag 창을 없애보자. 메시지 박스 함수의 인자는 다음과 같다.

 

hWnd : 생성할 메시지 박스의 핸들. NULL이면 메시지 박스는 생기지 않는다.

lpText : 표시할 메시지

lpCaption : 메시지 박스 제목

uType : 메시지 박스의 내용 및 동작

 

위 4개의 인자에서 hWnd의 특성을 이용할 것이다. [사진 12]에서 메시지 박스 함수를 호출하는 코드가 복호화되면서 가장 먼저 PUSH 0 명령어를 확인할 수 있었다. 이 명령어가 hWnd 인자를 스택에 저장하는 것인데, NULL을 의미하는 0x0A 값으로 패치하면 된다. 즉, PUSH 0A 명령어가 되게끔 패치하면 되는 것이다.

 

 

[사진 25]

위와 같이 0x00401013 주소의 명령어 MOV WORD PTR DS:[EDI],6A를 MOV WORD PTR DS:[EDI],0A6A로 패치하면 0x00401011 주소의 명령어가 PUSH 0A로 바뀌게 된다.

하지만 문제가 있다. 위와 같이 패치해도 정상적으로 프로그램이 수행되지 않는다. 이유는 동작 과정 2번에서 메시지 박스 함수를 호출하는 코드로 복호화하기 전에 1번에서 먼저 같은 영역의 코드들이 복호화된다는 것이다.

즉, [사진 25]의 영역을 패치해봤자 [사진 6], [사진 7]의 복호화 루틴에 의해서 다른 값으로 바뀌는 것이다. 따라서 [사진 6], [사진 7]의 복호화 루틴이 수행되고 [사진 12]에서 코드가 한 번 더 복호화되면 [사진 25]의 코드처럼 되도록 해야 한다.

 

[사진 25]에서 0x00401013 주소의 명령어 MOV WORD PTR DS:[EDI],0A6A에서 0A 값은 정확히 0x00401017 주소에 위치한다. [사진 6], [사진 7]의 복호화 루틴을 통해 0x5A 값과 XOR 연산 후 이 주소의 값은 0x00 값이 된다.

 

 

[사진 26]

[사진 6], [사진 7]의 복호화가 수행되기 전 0x00401017 주소의 값은 0x5A 값이다. [사진 6], [사진 7]의 복호화가 수행되면 이 값은 0x5A 값과 XOR 연산하기 때문에 0x0 값으로 바뀐다. 그리고 이 값은 Nag 창을 띄우는 메시지 박스 함수의 인자가 된다. 따라서 이 값이 [사진 6], [사진 7]의 복호화를 수행하고 0x0A 값이 되도록 하면 된다.

 

 

[사진 27]

위와 같이 0x50 값으로 패치하면 된다. 0x50 ^ 0x5A = 0x0A

 

 

[사진 28]

위와 같이 [사진 6], [사진 7]의 복호화가 수행되고 0x00401013 주소의 명령어가 MOV WORD PTR DS:[EDI],0A6A 명령어로 복호화되었다. 그리고 0x00401013 주소의 명령어에 의해 0x00401011 명령어는 PUSH 0A 명령어로 바뀐다.

 

 

[사진 29]

완전히 복호화가 진행되면 메시지 박스 함수의 hWnd 인자는 0x0A 값인 것을 확인할 수 있다.

 

 

[사진 30]

위와 같이 0x0040101F 주소의 CALL 명령어로 메시지 박스 함수를 호출해도 hWnd 인자가 NULL이기 때문에 메시지 박스는 생기지 않는다.

 

 

[사진 31]

동작 과정 5번에 의해서 0x00401011 주소 이후의 코드들이 다시 바뀌게 되고 메인 창이 띄워진다. 여기서 0x00401011 주소의 명령어가 여전히 PUSH 0A 이지만 메인 창을 띄우는 함수는 메시지 박스 함수가 아니기때문에 상관 없다.

728x90
728x90