본문 바로가기

0x00 /0x01 Reversing

- OllyDbg , IDA , GDB를 이용한 간단한 실행 파일 디버깅

# 이번 포스팅에서는 디버깅시에 자주 쓰이는 OllyDbg, IDA Pro, GDB를 이용하여 Window 및 Linux 환경에서 간단한

   실행 파일 디버깅해보겠습니다. 리버싱 공부를 시작한지 얼마안되서 틀린 내용이나 부족한 부분이 많을 수 있으니,

   그러한 점은 양해부탁드립니다. (틀린 내용이 있을 시 지적해주시면 정말 감사하겠습니다 ^^)



우선 Visual Studio를 이용하여 C 언어로 "Hello World!" 라는 문구를 출력하는 프로그램을 작성했습니다.



[ 그림 1 ] "Hello World!" 문구를 출력하는 콘솔 프로그램 작성





1. 첫 번째로 OllyDbg를 이용하여 디버깅해보도록 하겠습니다.


  

[ 그림 2 ] OllyDbg를 이용하여 "Hello World.exe" 파일 불러오기

- OllyDbg로 앞에서 작성한 "Hello World.exe" 파일을 실행했을 때, 처음 시작하는 부분입니다. (ENTRY POINT)

- 가장 먼저 mainCRTStartup 이라는 부분으로 점프하는 명령어를 볼 수 있는데요. 이 mainCRTstartup 이라는 함수는 저희가 작성한 코드의 main

  함수부분일까요? 해당 명령어를 실행시켜 어떤 함수인지 한번 살펴보도록 하겠습니다.





[ 그림 3 ] mainCRTStartup 함수로 이동

- mainCRTStartup 부분으로 점프한 모습입니다. 대충 명령어들을 살펴보면, security_init_cookie 함수와 tmainCRTStartup 이라는 함수를 호출

  하고, 리턴하는간단한 함수입니다. 호출하는 security_init_cookie 함수와 tmainCRTStartup 함수가 어떤 작업을 하는지 간단하게 확인해보도

  록 합시다.





[ 그림 4 ] security_init_cookie 함수

[ 출처 : http://msdn.microsoft.com/ko-kr/library/ms235362.aspx ]


- 전역 보안 쿠키를 초기화하는 함수라고 합니다. 우선 이러한 함수가 있다는 점으로 보아, 우리가 찾던 C 코드의 main 부분이 아니라는 것을 확실히 알 

  수 있습니다.





[ 그림 5 ] Entry

[ 출처 : http://msdn.microsoft.com/ko-kr/library/f9t8842e.aspx ]


- 디버깅 툴을 이용하여 어떠한 파일을 열었을 때, 시작하는 첫 부분을 Entry Point 라고 합니다. 위의 그림에서 설명하고 있는 Entry 함수에 내용을 

  살펴보니, mainCRTStartup 함수에 관해서 나와있네요. 제가 작성한 프로그램은 CONSOLE 프로그램이므로, mainCRTStartup 함수가 해당 파일의 

  시작점임을 확인할 수 있습니다.


- 참고로 mainCRTStartup 함수는 다음과 같은 일들을 수행합니다.

   

   1. 전체 명령행(commandLine)을 가리키는 포인터를 획득

   2. 환경 변수를 가리키는 포인터를 획득

   3. C/C++ 런타임 라이브러리의 전역 변수를 초기화

   4. C/C++ 런타임 라이브러리의 메모리 할당 함수(malloc, calloc...)와 저수준 입출력 루틴이 사용하는 힙을 초기화

   5. 모든 전역 오브젝트와 static c++ 클래스 오브젝트의 생성자를 호출

   6. 사용자가 정의한 진입점 함수(WinMain 또는 main)를 호출



- 위의 내용들을 정리해보면, 어떠한 프로그램을 디버깅할 때 시작 Entry는 우리가 만든 프로그램의 main 함수가 아닌, 실행전 필요한 작업들을 위한 

  함수들이 먼저 호출된다는 것입니다. 그러한 함수들을 호출한 후, 그 함수들로 하여금 우리가 작성한 main 함수를 호출하게 하는 것입니다.


- 사실 저런 함수들은 실제로 디버깅할 시에는 자세하게 볼 필요가 없을 듯 합니다. 저런 함수가 어떤 기능인지 간단하게 알고 있다면, 다음에 디버깅

  땐 저러한 함수들을 그냥 쉽게 쉽게 넘어가면 될 것 같습니다.



- 이제 우리가 작성한 프로그램의 main 함수를 찾아 보겠습니다.





[ 그림 6 ] main 함수

- mainCRTStartup 함수내에서 실행시키다보면, 쉽게 main 함수를 호출하는 부분을 찾을 수 있을 겁니다. 위 화면은 main 함수를 호출한 모습입니다.

  main 함수 자료형이 int 형이였고, 인자는 void로 작성했는데 첫 째줄 주석부분을 보면 쉽게 확인할 수 있습니다.


- 명령어들을 살펴보면, "Hello World!" 라는 데이터를 입력하기 위해 0C0만큼 할당하는 부분을 볼 수 있고, "Hello World!" 문자열을 인자로 printf

  함수를 호출하는 것을 볼 수 있습니다. 그 이후에 할당했던 스택을 정리하고 리턴, RTC_CheckEsp 함수는 정확하게는 모르겠는데, 대충 함수 

  호출 전후의 ESP를 체크하는 함수로 예상되네요.






2. 두 번째로 IDA를 이용하여 디버깅해보도록 하겠습니다.



[ 그림 7 ] IDA를 이용한 디버깅

- IDA를 이용하여 "Hello World.exe" 파일을 열었을 때 모습입니다. 보시는것 처럼 제가 작성한 코드의 main 함수를 바로 찾아주었네요.

  코드 내용은 차이가 거의 없습니다.


- IDA는 저도 이번에 처음 써보는데요. 정말 좋은 디버깅 툴인것 같습니다. 우선 왼쪽 상단에 보시면 Function name 이라고 해당 프로그램에서

  사용되는 함수들의 이름을 나열해주구요. 저기서 함수명을 더블클릭하면 오른쪽 편에 코드를 바로 찾아서 띄워줍니다. 그 아래에 있는 Graph

  overview 부분은 프로그램의 흐름도를 그림으로 보여주는 건데, 해당 메인 함수의 코드가 너무 간단해서 아무것도 뜨지 않네요.



[ 참고 ] Graph overview

- 이게 바로, Graph overview 기능입니다. 왼쪽에 check_managed_app 함수 부분을 본거에요. OllyDbg로 디버깅할 땐 디버깅하다보면, 어디 

  부분을 디버깅하고 있는지, 헷갈릴 때가 있는데, IDA는 이런 좋은 기능들이 많아서 편하게 디버깅할 수 있을 것 같습니다. 이 외에도 엄청

  나게 많은 기능들이 있는 걸로 아는데, 사용법좀 익힌후 블로깅하도록 하겠습니다.






3. 세 번째로 GDB를 이용하여 디버깅해보도록 하겠습니다.


- 참고 : GDB는 C, C++, Modula-2 로 구현된 프로그램을 디버그할 수 있는 도구입니다.


[ 그림 8 ] 우분투(리눅스)에서 C 코딩

- 우분투에서도, vim 편집기를 이용하여 "Hello World!"라는 문구를 출력하는 프로그램을 작성합니다.





[ 그림 9 ] 작성한 코드 컴파일 후 실행

- 리눅스 환경에서는 gcc 라는 프로그램을 이용하여 코딩한 파일을 컴파일 합니다.

- 작성한 c 파일을 컴파일하고, 실행한 모습입니다. "Hello World!"라는 문구를 출력하는 것을 볼 수 있습니다.

- 이 프로그램을 이제 GDB를 이용하여 디버깅 해보겠습니다.


- 참고 : 저는 gcc로 컴파일할 때 -g 옵션을 주지 않았는데, -g 옵션 주어 컴파일하면 gdb가 필요로 하는 부가 정보들이 추가되어 디버깅할 때

           더 도움이 된다고 합니다.





[ 그림 10 ] GDB를 이용한 디버깅 - 1

- 위 사진을 보시면 우선 첫 째줄에서 'hello' 파일을 gdb로 불러옵니다. 


- (gdb) set disassembly-flavor intel     :  어셈블리 코드를 intel 기반 어셈블리어로 변경하는 작업입니다.

- (gdb) disassemble main                  :  intel 기반으로 변경한 후, main 함수를 보기위한 명령어입니다.


- 보시는 코드가 앞에서 작성한 C 코드의 Main 함수부분에 해당하는 코드입니다. Visual에서 작업했던것보다 더 짧네요.

- 보시면 <+4> 부분에서 edi 레지스터에 0x4005f4 라는 주소를 복사하고 있는데요. 0x4005f4 부분이 아마도 "Hello World!" 

  문자열이 들어가있는 주소로 예상되네요. 그런 후 출력하는 함수를 호출하는 것으로 예상되고, pop으로 할당한 공간을 비우고

  리턴합니다. 


- 그럼 break point를 걸어서 디버깅해보도록 하겠습니다.





[ 그림 11 ] GDB를 이용한 디버깅 - 2

- 우선 break point를 3곳으로 설정하겠습니다. 

   1. 첫번째는 main 함수의 첫번째 줄입니다.

   2. 두번째는 *main + 9 입니다. 이렇게 break point를 걸고, 실행하면 break point가 지정된 코드 직전 코드까지 실행되게 됩니다.

      따라서, *main + 4 까지 실행되는거죠. *main + 4 의 코드가 0x4005f4 라는 주소를 edi 레지스터에 복사하는 것인데 저 레지스터에

      들어가는 주소에 "Hello World!"라는 문구가 있다고 예상했습니다. 그 주소에 있는 문자열을 확인하기 위해 *main + 9 에 break point를

      설정한 것입니다.

   3. 마지막으로 *main+14 입니다. 여기까지 실행하면 중간에 함수를 호출하는 부분을 실행하게 되는데, 예상한 것이 맞다면 "Hello World!"를

      출력할 것입니다. 출력한다면 호출하는 함수는 printf 함수임을 알 수 있습니다.


- break point를 설정한 후 run 명령어를 이용하여 디버깅을 시작합니다. break point에서 멈추는 것이 보이시죠?

  그 다음 break point 직전 코드까지 실행하기 위해서는 continue 명령어를 사용합니다.

  자, 우리가 추측했던 코드를 실행한 후까지 왔습니다. $edi 레지스터에 있는 String을 확인했더니, "Hello World!" 가 정확하네요.

  그런후 한번더 continue 하니 해당 문자열이 출력됩니다. 




# 코드가 간단한 파일이라 정말 쉽게 디버깅할 수 있었는데요. 프로그램이 커질수록 디버깅하기가 정말 힘든것 같습니다. 그리고, 명령어들도 

  많이 알아야 하구요. 명령어나 디버깅 툴에 대한 사용법은 구글링으로 쉽게 찾으실 수 있을 겁니다. 다음에는 조금 더 복잡한 프로그램을 가지고

  디버깅해보도록 하겠습니다. 그럼 이번 포스팅은 여기서 마치겠습니다.












# 참고 자료 및 사이트

http://msdn.microsoft.com/ko-KR/ (MSDN 레퍼런스)

http://micingamja.egloos.com/4774673 ('REVIVALIST 미친감자 김주생'님의 블로그)

http://sweeper.egloos.com/2979306 ('수까락의 프로그래밍 이야기'님의 블로그)