"Hello World!" - 내 생애 첫 디버깅 (1)
Level
초급
Content
"Hello World!" 프로그램을 디버깅 해보고 간단한 패치를 해보도록 하겠습니다.
이를 통하여 디버깅에 대한 감을 잡으실 수 있습니다.
Goal
- 기본적인 디버거 사용방법의 이해
- 간단한 Disassembly code 이해
- 간단한 프로그램 패치(patch)
Tool
Visual C++ 2008 Express Edition
OllyDbg 1.10
Hello World!
모든 C 프로그래머가 최초로 만들어 본 프로그램 Hello World! 를 최초의 디버깅 프로그램으로 결정하였습니다. SW 업계에서의 차지하는 Hello World! 의 의미, 처음 C 를 배울때의 두근거림 그리고 소스 코드의 간결함까지... 이 모든 것이 최초의 디버깅 프로그램으로써 더 할 나위 없이 딱 들어맞는군요.
자신에게 익숙한 C/C++ 개발툴을 이용하여 Hello World! 를 만들어 봅니다.
(Release 모드로 빌드하면 코드가 좀 더 간결해져서 디버깅하기 편합니다.)
<Fig. 1>
디버깅 목표
위에서 만든 HelloWorld.exe 를 실행해보면 당.연.히. "Hello World!" 메시지 박스가 출력될 것입니다. 그냥 디버깅만 하면 재미없으니까 이 문자열을 "Hello Reversing!" 으로 바꾸는걸 목표로 하겠습니다.
그러기 위해서는 먼저 HelloWorld.exe 를 디버깅하여 main() 함수내의 MessageBox() 함수 호출 코드를 찾아야 합니다.
그리고 적절히 해당 문자열 위치를 알아내어 변경시키면 되겠지요.
디버깅 시작
첨부된 HelloWorld.exe 파일을 OllyDbg.exe 로 열어보겠습니다.
* VC++ 도 소스가 있을때 어셈블리 수준의 디버깅이 가능합니다만, 일반적으로는 분석할때 소스가 없으므로 OllyDbg 같은 Win32 전문 디버거를 사용하게 됩니다.
<Fig. 2>
디버깅을 시작하기 전에 간단히 <Fig. 2> 에 보이는 OllyDbg 의 메인 화면 구성에 대해 설명드리겠습니다.
- code : 기본적으로 disassembly code 를 표시하고 각종 comment, label 을 보여주며 코드를 분석하여 loop, jump 위치 등의 정보를 표시한다.
- register : CPU register 값을 실시간으로 표시하며 특정 register 들은 수정도 가능함.
- dump : 프로세스내의 원하는 memory 주소 위치를 hex 와 ASCII 값으로 표시하고 수정도 가능함.
- stack : ESP register 가 가리키는 프로세스 stack memory 를 실시간으로 표시하고 수정도 가능함.
디버거가 멈춘 곳은 EntryPoint(EP) 코드로써 HelloWorld.exe 의 실행 시작 위치(4011A1)입니다. 일단 EP 코드에서 눈에 띄는건 CALL 명령과 그 밑의 JMP 명령입니다.
Address OP code Disassembly comment
----------------------------------------------------------------------------
004011A1 EB A6160000 CALL 0040284C ; 0040284C (40284C 주소의 함수를 호출)
004011A6 E9 A4FEFFFF JMP 0040104F ; 0040104F (40104F 주소로 점프)
- Address : 프로세스의 가상메모리(Virtual Address:VA) 내의 주소 위치
- OP code : OPeration code 의 줄임말로써 IA32(또는 x86) CPU 명령어
- Disassembly : OP code 를 보기쉽게 디스어셈 해준 코드
- comment : 디버거에서 추가한 주석 (옵션에 따라 약간씩 다르게 보임)
디스어셈 코드를 처음 보신 분들이라도 위 두줄의 코드는 그 의미가 명확합니다.
"40284C 주소의 함수를 호출(CALL)한 후 40104F 주소로 점프(JMP) 하라"
계속 디버깅을 진행해 보겠습니다.
목표는 우리가 작성한 main() 함수내의 MessageBox() 함수 호출을 확인하는 것입니다.
40284C 함수 따라가기
- Step Into [F7] : 하나의 OP code 실행 (CALL 명령을 만나면, 그 함수 코드 내부로 따라 들어감.)
- Step Over [F8] : 하나의 OP code 실행 (CALL 명령을 만나면, 따라 들어가지 않고 그냥 함수자체를 실행함.)
- Execute till Return [Ctrl+F9] : 함수 코드 내에서 RETN 명령어 까지 실행 (함수 탈출 목적)
F7 단축키로 함수 안으로 따라갈 수 있습니다.
<Fig. 3>
위 코드를 얼핏 보아도 Hello World! 에서는 사용된 적 없는 API 들이 호출되고 있습니다.
이곳은 분명 우리가 찾는 main() 함수가 아니겠군요.
이곳은 VC++ 에서 프로그램 실행을 위해서 추가시킨 (우리 소스코드에는 없지만) VC++ stub code 입니다. (각 컴파일러 종류/버전별로 stub code 는 틀려집니다.)
지금은 신경쓰지 말고 우리의 목표 main() 을 찾아 계속 진행해 보겠습니다.
* 주의사항 : 처음에는 Win32 API 함수들(OllyDbg 의 comment 상에서 빨간색 API 함수 호출 부분)은 따라가지 마세요. 너무 헤멜 수 있습니다. Step over[F8] 로 넘어가세요.
4028E1 주소에 RETN 명령어가 있습니다.
(RETN 은 함수의 끝을 나타내며 함수가 호출된 명령어 바로 다음 명령어로 되돌아 갑니다.)
그곳까지 Step over [F8] 하시거나 또는 Execute till Return [Ctrl+F9]로 한방에 가봅니다.
그리고 RETN 명령어를 실행하면[F7/F8] <Fig. 2> 에서 봤던 4011A6 주소로 오게됩니다.
(C 언어에서 함수를 호출하고 리턴하면 그 다음 명령어로 오는 것을 생각하시면 됩니다.)
40104F 따라가기
4011A6 주소의 JMP 0040104F 명령을 실행해서 40104F 로 갑니다.
<Fig. 4>
<Fig. 5>
<Fig. 6>