'2018/06'에 해당되는 글 1건

  1. 2018.06.03 Visual Basic 6.0 바이너리 분석



1. 개요

2. 예제 소스 코드

3. N-Code ( Console Program )

4. P-Code

5. N-Code ( GUI Program )

6. 기타 링크





1. 개요

  VB 리버싱 관련 내용을 찾아보면 크랙미 분석이 대부분이다. 또한 이것도 거의 전부 Event 관련된 내용이라서 사용될만한 API에 BP를 걸고 진행한 후 문자열 비교 같은 내용만 다룬다. 그래서 여기에서는 전체적인 구조를 공부하고 정리하려고 한다.


  먼저 VB의 경우 컴파일 방식에 따라 P-Code, N-Code로 나뉜다. P-Code 방식은 Pseudo Code로서 바이트코드로 컴파일되어 인터프리터에 의해서 해석되면서 실행되는 방식이다. Java나 C#과 비슷한 형태이다. N-Code는 Native Code로서 일반적인 실행 파일처럼 어셈블리를 통해 해석할 수 있는 방식이다. 물론 VB의 경우 MSVBVM60.dll 같은 VB 엔진에 의해서 실행되기 때문에 바로 스텁을 지나 Main()으로 가는 일반적인 실행 파일과는 차이점이 존재한다.





2. 예제 소스 코드

  N-Code 방식을 공부하려고 했는데 역시 리버싱은 직접 만들어가면서 분석해 보는 것이 가장 좋은 것 같다. 그래서 다음 링크의 소스 코드를 이용했다. 직접 보면 알겠지만 이것은 간단한 형태의 키로거이다. 물론 실제 악성코드는 아니고 메시지 박스를 보여주며 간단한 기능만 포함된 샘플 예제 형태이다. "하지만 악의적인 용도로 사용할 경우 불법이므로 테스트 용도로만 사용해야 한다." 이 샘플은 적당한 크기의 코드이기도 하고 API를 직접 호출하는 부분도 있어서 컴파일해 가면서 분석하기로 했다.

[ http://freesourcecode.net/vbprojects/28938/SpyEx-(Keylogger)-in-Visual-Basic#.WxKaL0iFM2x ]


  해당 소스 코드 또한 GUI 형태이기 때문에 직접 Console 형태로 변환하였다. 일반적인 프로젝트를 생성하면 (표준 EXE) Form이 한 개 생성된다. 이것을 삭제하고 대신 Module을 하나 추가한다. 그리고 해당 링크의 Module1.bas의 소스 코드를 복사해서 붙여넣기 한다. 이후 첫 줄의 Attribute 라인을 삭제하고 다음을 추가한다. 원래 메시지 박스에서 버튼을 클릭했을 때 구현되는 방식이지만 Console 프로그램 형태로 만들기 위해 Form을 삭제하고 직접 호출하는 부분을 추가한 것이다. 수정 후 (일반적인 Visual C++과는 다르게) "파일" 탭에서 "Project1.exe 만들기" 버튼을 클릭해서 exe를 생성한다.


Sub Main()

    Call startup

    Call KeyboardHook

    Call Unhook

End Sub





3. N-Code ( Console Program )

  디버깅 이전에 정적 분석부터 진행해 보자. Import Table을 보면 MSVBVM60.DLL의 여러 함수들이 정의되어 있는 것을 볼 수 있다. 문제는 소스 코드에서 사용된 API 함수들은 보이지 않는다는 점이다. 대신 String을 확인해 보면 해당 API 함수 이름을 확인할 수 있다. 그 이유는 다음에서 살펴볼 API 함수 호출 방식에 대해서 정리하면서 확인해볼 수 있다.


  Ollydbg로 분석을 시작해 보자. 전형적인 VB의 EP를 볼 수 있다.



  특정 구조체를 push한 후 ThunRTMain()을 호출한다. 이 함수는 스텁 코드와 비슷한 역할을 하는데 여러 정보가 담긴 구조체를 기반으로 초기화를 진행한 후 실제 실행이 진행된다. 문제는 그냥 F8을 눌러버리면 끝까지 진행되기 때문에 실제 EP를 찾아야 한다. EP는 해당 구조체 0x00401354에서 오프셋 +2c에 위치한다. 즉 0x00401380에 위치한 0x00401c80이다. 그 외에도 VB5! 등의 시그니처로 시작하는 것을 확인할 수 있다.


  EP에 BP를 걸고 진행해도 되지만 어차피 직접 진행하다 보면 다음과 같이 EP로 분기하는 루틴을 만날 수 있다.



  어쨌든 EP로 가면 다음과 같은 프로시저를 볼 수 있다. 이것이 Main 함수이며 소스 코드를 보면 알겠지만 Main 함수는 3개의 함수를 호출한다. 각각의 함수로 진입해서 확인해보면 알겠지만 개발자가 정의한 프로시저의 경우 이러한 구조를 띄는 것 같다. 물론 Main() 함수는 간단하므로 프로시저도 훨씬 간단하다.



  그 외의 추가 사항을 보자면 프로시저 호출 이후에는 항상 MSVBVM60.__vbaFreeVar()을 호출하는 것으로 보인다. 그리고 인자는 (객체 지향 언어이기 때문에 클래스로 추정된다) 기본적으로 1개이며 인자가 1개 늘어날 때 마다 1개씩 더 추가되는 것으로 보인다. 더 자세한 분석을 위해 각각의 함수로 들어가 보자.


  사실 startup() 함수가 조금 더 복잡한 형태이고, KeyboardHook() 함수의 경우도 자체는 간단하지만 인자 설정과 관련된 내용이 있으므로 가장 간단한 Unhook() 함수부터 살펴보겠다.



  앞의 Main() 함수의 프로시저와 비슷한 형태이다. 확인해 볼 부분은 0x004027F7의 호출문이다. 소스 코드를 보면 알겠지만 이 함수에서는 VB의 함수가 아니라 API를 사용하였다. VB에서 API가 사용될 경우 어셈블리에서 어떻게 보여지는지를 확인하기 위해 이 부분을 살펴보자. 여기에서 의미 있는 함수 호출은 이 부분밖에 없으며 당연히 이 함수 호출이 API UnhookWindowsHookEx()와 관련된 부분을 것이다. 참고로 자세하게 다루지 않았지만 함수 0x00401750 호출 이전에 이 API 함수에 대한 인자가 설정될 것이고 그것을 확인할 수 있다. 여기서는 API 함수 호출 부분을 중점적으로 보겠다.



  0x00401750 함수가 위치한 부분을 위아래로 확인해 보면 이것과 유사한 함수들을 많이 확인할 수 있다. 이 부분만 확실히 보면 이제 직접 API 함수 호출 부분을 찾아나갈 수 있고 추후에 자동화할 수도 있을 것이다.


  MSVBVM60.DllFunctionCall() 함수를 호출하기 전에 특정 주소를 push한다. 이 주소를 확인해 보면 구조체인 것으로 추정되며 두 개의 주소를 멤버로 갖는다. 각각의 주소는 문자열을 가리키는데 첫 번째는 DLL 이름을 그리고 두 번째는 API 함수 이름을 가리킨다. 즉 API 호출 부분이 존재할 경우 이러한 형태의 프로시저가 삽입되며 이것은 호출할 API 함수의 문자열을 가리키는 구조체를 인자로 넣고 DllFunctionCall()을 호출한다. 이 함수는 해당 API 함수의 주소를 EAX 레지스터에 반환하며 마지막으로 "JMP EAX"를 호출하여 API 호출이 완료된다. 참고로 인자의 경우 앞에서 언급하였듯이 0x00401750 호출 이전에 이미 설정되어 있었다.


  지금까지는 API 호출 부분만 살펴보았지만 startup() 프로시저의 소스 코드와 어셈블리를 비교해 보면서 공부할 수 있다. API 호출 외에도 VB 문법에 의거한 변수 선언 및 정의나 조건문 같은 부분을 확인할 수 있다. 변수에 문자열을 선언할 때 사용되는 여러 함수들이 보인다. __vbaStrToAnsi(), __vbaStrToUnicode() 부터 __vbaFreeVar(), __vbaFreeStrList() 등이 그것이다. 이러한 부분은 특별한 구조가 있는 것은 아니고 직접 확인해 가면서 공부해야 할 것 같다.





4. P-Code

  앞의 소스 코드를 P-Code로 컴파일한 후 OllyDbg로 확인해 보았다. 크기의 경우 많이 줄어들었는데 항상 그럴것 같지는 않다. 개인적으로는 간단한 샘플이라서 N-Code에서 디폴트로 포함되는 부분 때문에 그렇지 크기가 커진다면 N-Code에서 추가되는 부분보다는 P-Code에서 추가되는 바이트코드 부분이 훨씬 클 것으로 생각한다.


  EP를 호출하는 부분 까지는 같지만 Main()이 다음과 같은 형태를 가지며 이후 분석 진행도 못하고 예외가 발생하므로 그냥 VB Decompiler를 이용하기로 했다.



  VB Decompiler Pro 버전은 P-Code를 읽어서 디컴파일을 해주는 기능이 있다. (무료 버전은 아예 인식 조차 지원하지 않는다) 테스트 결과 간단한 샘플이기는 하지만 상당히 괜찮은 결과가 나왔다. 혹시나 해서 N-Code 디컴파일러도 테스트한 결과 (VB Decompiler 무료 버전은 디스어셈블까지만 지원한다) 이것도 P-Code 만큼은 아니지만 그래도 볼만한 결과가 나온 것을 확인하였다.



  물론 과거 약간의 난독화가 적용된 N-Code를 테스트해 봤을 때는 반도 인식하지 못했었기 때문에 간단한 형태인 경우에만 괜찮은 결과를 내는 것 같다. 그리고 P-Code의 경우 여러 바이너리를 분석해 본 경험이 없기 때문에 판단하기가 힘들다.


  결론적으로 N-Code의 경우 간단한 것은 VB Decompiler Pro의 디컴파일 기능을 사용하면 되고, 난독화 및 복잡한 샘플은 앞에서 정리한 것을 기반으로 직접 디버깅을 수행해야 할 것이다. P-Code는 디버깅이 불가능하기 때문에 VB Decompiler Pro를 사용할 수 밖에 없으며 난독화는 따로 방법을 알아내야 할 것 같다.





5. N-Code ( GUI Program )

  마지막으로 크랙미 문제에서 많이 보이는 GUI 프로그램을 분석해 봤다. 샘플은 Reversing.kr에서 다운로드 가능한 Music Player이다.


  GUI 형태라서 그런지 구조체 오프셋 +2c에서 EP의 주소를 확인할 수 없었다. 프로시저의 형태라던가 API 호출 부분은 모두 같지만 어디가 초기화 루틴이며 무엇에대해 어떤 핸들러 프로시저가 호출되는지 등의 연관성을 찾지는 못했다. 아마 구조체 쪽을 더 자세히 살펴봐야 할 것으로 보인다.


  하지만 이러한 분석 대신 VB Decompiler Pro만 있어도 된다. 이것은 GUI 형태를 분석할 때 더 도움이 된다.



  스크린샷과 같이 Form을 클릭하면 GUI가 나오며 여기서 "Open" 버튼을 클릭했을 때 호출되는 핸들러 함수를 찾아보았다. 이것은 "CMD_OPEN_CLICK"이었으며 오른쪽 화면에서 이것이 주소 0x004036A0에 위치한 핸들러 함수라는 것을 확인할 수 있었다.


  이렇게 전체 함수들을 주소 및 이름 별로 정리해 주고 GUI에 맞는 핸들러까지 확인할 수 있으므로 GUI 형태를 분석하는데 많은 도움이 된다. 개인적으로 전체 프로시저들에 BP를 걸고 실행했을 때 가장 먼저 호출되던 프로시저가 "Form_Activate"였다는 것을 파악할 수 있었고, 이벤트를 받는 동안 즉 대기 기간 동안 루프문을 돌면서 호출되던 프로시저가 "TMR_Timer"였다는 것도 파악할 수 있었다.





6. RunPE

  VB6는 실제 악성코드를 제작하기 위한 목적보다는 Cryptor 형태로서 자주 사용된다. 즉 실제 악성코드는 보통 exe나 dll 형태로 제작하지만 외형을 VB6로 만드는 경우가 그것이다. VB6 코드는 대부분 쓰레기 코드로 이루어져 있으며 이후 디코딩 루틴을 거쳐 실제 PE를 복구하고 정상 프로세스에 인젝션하거나 자체 메모리를 Unmap한 후 새로 쓰는 방식이다. 이러한 방식을 RunPE라고 부르며 인터넷에도 많은 자료가 나와있다. 가장 대표적인 방식은 쓰레기 코드 이후 디코딩 루틴을 CallWindowProcW() 함수의 인자로 등록하는 방식이다.





7. 기타 링크
  막상 블로그에 올리고 난 후 이것 저것 찾아보다가 좋은 링크들을 많이 발견하였다. 오래된 컴파일러다 보니 이미 많은 분들에 의해 분석되고 잘 정리된 곳도 많아 보인다. EP에서 push 했던 구조체인 EXEPROJECTINFO를 포함해서 중요한 자료들이 자세히 정리되어 있다.




Posted by SanseoLab
이전버튼 1 이전버튼

블로그 이미지
Malware Analyst
SanseoLab

태그목록

공지사항

Yesterday
Today
Total

달력

 « |  » 2018.6
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

최근에 올라온 글

최근에 달린 댓글

글 보관함