CodeEngn Reversing Basic_Level19


실행 시 뜨는 창이다. 내버려두면 창이 꺼진다.

PEiD에 넣어보았다. UPX 패킹이 되어있다. 패킹을 해제하고 올리디버거로 돌려보았다.

이런 경고창이 뜨고, 제대로 실행되지 않는다. Autoit이 무엇인지 찾아보았다.
오토잇(Autoit)은 마이크로소프트 윈도를 위한 프리웨어 자동화 언어이다. 초기 버전은 주로 마이크로소프트 윈도의 프로그램을 위한 자동화된 스크립트를 만드는 데에 사용되었다. 그러나 지금은 전반적인 디자인과 기능 모두에서 향상된 기능을 포함하는 프로그래밍 언어로 발달하였다. 위키백과
Autoit으로 작성한 프로그램은 앞 부분은 일반적인 EXE형태이고, 뒤쪽에는 스크립트 내용이 압축되어 저장된다고 한다.
오류를 검색하다 보니 Autoit decompiler가 있다는 것을 알게 되었다. 한 번 사용해 보았다.

Autoit decompiler를 돌렸더니 Autoit 코드가 나왔다. MsgBox 함수의 인자가 무엇인지 찾아보았다.

맨 뒤에 있는 숫자가 timeout이다. 11.12 초이므로 11120 ms 이다. 즉, 플래그는 11120 이다.
이번엔 프로그램 자체를 분석해 보았다.

에러메시지에 bp를 걸고 실행했다. 조금 위를 살펴보니 위에서 분기하여 인자 push코드로 온다는 것을 알 수 있었다.

그래서 분기하는 코드를 찾아 올라갔다. 위에서 IsDebuggerPresent 함수로 디버깅중인지 체크한 후, 디버깅중이라면 점프하여 에러를 낸다.
분기 코드에서 레지스터를 조작하여 조작해 보았다. 그 후 실행하면 더이상 Autoit 에러 메시지는 뜨지 않고, 처음에 프로그램을 그냥 실행했을 때 뜨던 창(CodeEngn.com by Lee Kang-Seok 문구)이 뜬다.
Referenced text에는 CodeEngn.com by Lee Kang-Seok 문자열이 보이지 않는다.
창이 뜨는 순간마다 bp를 걸어보다 끝이 없어서, intermodular calls로 쓸만한 함수를 찾아보았다.

시간과 관련이 있는 함수 호출문에 모두 bp를 걸었다.
timeGetTime 함수는 시스템 시간을 구하는 함수이다. 알고리즘이 완료되는 데 얼마나 걸리나 시간차를 구할 때 이용하곤 한다.

코드를 실행하니 timeGetTime bp에 걸렸다. 이 함수의 결과로 EAX에 시스템 시간이 저장된다.
바로 아래에서 48E8D3 위치에 저장된 값과 0을 비교하고, 0이라면 아래쪽으로 점프한다.

점프문을 따라가면 위 코드가 나온다. 스택을 정리하고 돌아간다. 48E8D3에 0이 들어있으면 위의 코드를 실행하지 않고 돌아간다.

48E8D3에 저장된 값은 1이다. 따라서 아래로 점프하지 않는다.

주석에 분석을 썼다. timeGetTime 함수에서 리턴한 시스템 시간값은 ESI에 백업해두고, sleep한 뒤 다시 시간을 구한다. 그 두 값을 비교하여, 현재 시간이 더 크거나 같으면 점프한다.
점프해서 도달하는 코드이다.

방금 측정한 시간과 처음에 측정한 시간의 차를 구한다. 그것을 EBX+4에 저장된 값과 비교한다. 차가 더 크거나 같으면 점프한다.
EBX + 4에 저장된 값이 무엇인지 찾아보았다.

회색으로 표시된 값이 EBX + 4에 저장된 값이다. 즉, 위에서 구한 시간차를 2B70과 비교한다. 이것은 십진수로 11120이다.
시간의 차가 11120 미만이라면 아래 코드를 실행한다.

커서가 놓인 부분 부터가 점프하지 않았을 경우(시간 차가 11120보다 작은 경우)에 실행하는 코드이다.
EBP에는 sleep 함수의 주소가 들어있다. 인자로 0x0A를 push하고 sleep을 실행한다. 즉, 10 밀리초를 sleep한다.
그리고 아까도 나왔던, 48E8D3에 저장된 값을 비교하여 0이 아닐 경우 점프한다. 0인 경우 아래 있는 스택 정리 코드로 넘어가고 함수가 종료된다.
48E8D3에는 여전히 1이 들어있어 점프했다.

점프해 올라간 코드는 timeGetTime함수를 호출하는 코드이다. 다시 시간을 구하고 그 차를 비교한다.

이제 시간 차가 11120보다 크다. 위 코드로 점프한다. 이를 쭉 진행하면 종료하는 로직으로 간다.
대체 48E8D3의 값이 의미하는 게 뭘까 찾아보았다. search for constant 기능을 이용하여 상수 48E8D3를 이용하는 코드에 전부 bp를 걸었다.

위 bp에서 48E8D3에 1을 넣고, 아래 bp에서는 0을 넣는다. 0을 넣은 후 바로 WaitForSingleObject를 호출한다.
이 함수는 간략하게 설명하자면 스레드가 종료되기를 기다리는 함수이다.
위 이미지에서 주소가 검은색으로 표시된 부분 (48E8D3값을 1로 설정하는 코드와 0으로 설정하는 코드 사이에 있는) call 코드에서 호출하는 함수에 들어가보면 다음과 같은 코드가 나온다.

call되는 함수를 살펴보면 대충 스레드를 만드는 함수라는 것을 알 수 있다.
결론은, 시간을 재는 함수는 자식 스레드로 생성된다. 자식 스레드는 48E8D3에 1을 가지고 있다. 부모 스레드는 wait하기 전 48E8D3 값을 0으로 바꾼다.
즉, 48E8D3는 스레드를 구분하는 값이다.