일단 여기까지만 보면 시간과 관련된 디버거를 통해 시간과 관련된 함수를 찾아 문제를 해결할 수 있겠다 하고 유추해볼 수 있다.
압축 해제 후 exe 파일을 실행해 보면 CodeEngn.com by Lee Kang-Seok이 출력되는 것을 볼 수 있다. 본 문제는 몇 밀리세컨드 후에 종료되는지를 확인해야 하기 때문에 켜진 상태로 대기해보니 10초에서 11초 사이에 꺼진다는 것을 확인할 수 있었다.
PEiD로 확인해보니 위에 보이는 것처럼 UPX로 패킹되어 있는 것을 알 수 있다. upx.exe를 활용하여 언패킹을 진행해보자.
upx.exe 파일을 활용하여 언패킹 진행 -> 언패킹이 잘 되었는지 다시 한번 확인
확인 결과 언패킹이 정상적으로 수행되었음을 알 수 있다. 이제 디버깅을 통해 파일을 분석해보도록 하자.
첫 화면 -> F9를 통해 실행파일로 이동
진입 후, F9를 통해 파일을 실행시켜보았다. 그런데 일반적으로 실행했을 때 나온 출력 값이 아닌 다른 화면이 출력되었다.
간단히 해석하자면 이건 AutoIt 스크립트라는 소리같은데 처음 듣는 단어라 우선 조사해보았다.
AutoIt란?
마우스와 키보드 이벤트를 프로그래밍하여 자동으로 원하는 위치를 클릭하거나 키값을 입력 할 수 있도록 해주는 프로그램이다.
혹시 디버거를 탐지하는 프로세스가 있는지 확인하기 위해 모듈 분석을 진행해보았는데 디버거 탐지방법 중 하나인 IsDebuggerPresent() 를 확인할 수 있었다.
IsDebuggerPresent() 함수 확인
함수의 위치로 이동 후, 확인해 본 결과 에러 메시지에 나왔던 문구가 나온 것을 확인할 수 있다. 그렇다면 맨 위의 노드에서 Jne에서 에러메시지를 출력하는 왼쪽으로 이동하면 안됨으로 Jne를 수정하여야 한다. 우리의 경우 계속 디버깅을 진행해야 하기 때문에 Jne를 je로 수정한 후 패치파일을 생성해주도록 하겠다.
Jne 분기문을 je로 수정
올바르게 동작하는 것을 확인하였으니 패치 파일 생성 (이때도 역시 10초에서 11초 후에 프로그램이 종료되는 것을 알 수 있다.)
파일 패치 이후 다른 이름으로 저장
이제 패치한 파일을 가지고 파일 분석을 시작해 보도록 하겠다.
처음 말했듯이 이 프로그램은 일정 시간이 지나면 종료가 되는데 그 시간을 알아내는 문제이다. 그렇다면 시간에 관련된 함수를 찾으면 될 것이다. 시간이란 단어를 생각했을 때, 가장 먼저 떠올랐던 단어는 clock과 time이였다. 검색 후 찾아본 결과
다음과 같은 함수들이 나오는 것을 확인할 수 있다. 나온 함수들은 timeGetTime(), KillTimer(), SetTimer(), SendMessageTimeout() 등이 있었는데 이 중 timeGetTime() 함수는 윈도우가 시작되고 난 뒤 지난 시간을 밀리세컨드 단위로 리턴해주는 함수라는 것을 알게 되었다.
우선 실행을 시켜봐야 하니 관련 함수들에 모두 중단점을 걸어보았다. 그 후 동작 과정을 살펴보아야 하니 계속 실행해보았다.
대부분 call dword ptr ds:[<timeGetTime>]의 형태를 띄고 있는 것을 볼 수 있는데 이중 한 부분만 call edi로 다른 값을 갖는 것을 확인할 수 있다. 실은 처음부터 저 코드를 들어가 보면 답이 나올 것 같았지만, 놓치는 부분이 있을 수도 있기 때문에 모두 중단점을 걸고 처음부터 실행시켜 보았다. F9로 실행을 해보면 중단점이 설정된 위의 코드에서 실행이 되는 것을 확인할 수 있었다. 하지만 일단 분석을 해야 하기 때문에 중단점에서 F8을 이용하여 한줄 씩 확인해보도록 하겠다. 우선 코드들을 해석해보도록 하자.
우선 edi에 4바이트 timeGetTime을 저장하고 edi를 호출한다.
그 후, edi 호출을 통해서 나온 리턴 값 eax를 esi에 저장하고 있다.
다음 je문으로 비교를 하는데 je를 따라가면 점프하지 않고 그대로 아래로 진행된다.
이제 00444C55에서 ebx에 [esp+14] 값을 저장하고 Sleep 함수를 실행한다.
그리고 00444C5F에서 다시 edi, 즉 timeGetTime 함수를 다시 가져오고 있다.
그 후, eax와 esi를 비교하는데, 이때 timeGetTime의 리턴 값이 eax에 저장된다.
그리고 jae를 통해 eax값이 esi보다 크거나 같으면 444D38로 점프하게 되어있다.
실행 값을 확인해보면 eax에는 4EC95D77이 저장되어 있는 것을 확인할 수 있고, esi에는 4EC3E818가 저장되어 있음을 확인할 수 있다. 이것을 보면 eax가 더 큰 값을 가지고 있으므로 444D38로 점프가 되는 것을 알 수 있다.
이동 후, 위의 그림의 위치로 이동하게 되고, 해당 위치에서 eax에 eax-esi를 저장하한 후, eax와 [ebx+4]를 비교하고 있는 것을 볼 수 있다.
여기서 비교한 결과 값이 eax가 클 경우 444C71로 이동하고, 그렇지 않으면 444C5F의 위치로 이동한다. 444C5F는 위에서 본것과 같이 Sleep 다음에 timeGetTime 함수를 재호출 시키는 위치인 것을 알 수 있는데, 이것을 통해 [ebx+4]에 우리가 찾아야 하는 밀리세컨드의 값이 저장되어 있다는 것을 유추할 수 있다.
그럼 해당 위치에 대해 덤프에서 따라가기를 통해 EBX+4의 위치로 이동해보면
다음과 같이 70 2B가 저장되어 있는 것을 알 수 있고, 리틀 엔디언 방식으로 저장되어 있기 때문에 2B70 값이 저장되어 있다는 것을 알 수 있다.
그럼 이 값을 16진수에서 10진수로 바꾼다면 값을 알 수 있다.
계산 결과 11,120이라는 값이 나오게 된다. 즉, 이 프로그램은 11.12초 후에 종료된다는 것을 알 수 있다.