Reverse Engineering Code with IDA Pro By Dan Kaminsky, Justin Ferguson, Jason Larsen, Luis Miras, Walter Pearce 정리 : vangelis(securityproof@gmail.com) 이글은 Reverse Engineering Code with IDA Pro(2008년출판 ) 라는책을개인적으로공부하면서정리한것입니다. 목적이책소개가아니라공부이므로글의내용은형식에국한되지않고경우에따라책의내용일부를편역하거나또는철저하게요약해서올리고, 경우에따라필자가내용을추가할것입니다. 내용에오류및오타가있다면지적부탁드립니다.
2 장 Assembly and Reversing Engineering Basics 개관 1 이장에서는리버싱엔지니어링을위해필요한어셈블리어, Intel 아키텍처프로세서, 명령어등기본적인요소들을소개하고있다. 이책은 32-bit Intel architecture(ia-32) 어셈블리어에초점을맞추고있으며, 운영체계는 Windows와 Linux를다루고있다. 독자들은 IA-32 어셈블리어와 C/C++ 에대한이해도가필요하다. 그렇다고해서이장에서관련항목을깊게다루지는않고있다. 어셈블리어와 IA-32 프로세서 2 어셈블러 (assembler) 들은다른문장구조 (syntax) 를가지고있다. 가장대표적인 Intel과 AT&T 문장구조인데, 이책에서는 Intel 문장구조를사용하고있다. 이것은 IDA에의해사용되는것이 Intel 문장구조이기때문인것도있고, 각종문서나 white paper에서도더많이사용되기때문이다. Intel과 AT&T 문장구조의차이점에대해다음예를보자. 다음은간단한 Hello, World 프로그램이다. 첫번째것은 Red Hat 리눅스에서 gcc로컴파일하고, gdb를통해디스어셈블링한것이다. Gdb를통해나오는결과는 AT&T문장구조이다. (gdb) disas main Dump of assembler code for function main: 0x8048328 <main>: push %ebp 0x8048329 <main+1>: mov %esp,%ebp 0x804832b <main+3>: sub $0x8,%esp 0x804832e <main+6>: and $0xfffffff0,%esp 0x8048331 <main+9>: mov $0x0,%eax 0x8048336 <main+14>: sub %eax,%esp 0x8048338 <main+16>: sub $0xc,%esp 0x804833b <main+19>: push $0x8048398 0x8048340 <main+24>: call 0x8048268 <printf> 0x8048345 <main+29>: add $0x10,%esp 0x8048348 <main+32>: mov $0x0,%eax 1 2장에서는시스템해킹을공부한사람이라면많이접했던분야이다. 그래서아주간단하게정리를할생각이다. 2 이섹션에서 Assembly refers to the use of instruction mnemonics 라는부분이나온다. 여기서 mnemonics 는같은기능을가진 instruction opcode 클래스의예약된이름이다. mov eax, 0 라는 instruction의포맷을살펴보면, mov가 mnemocic이고, eax와 0은 operand가되는아규먼트들이다. 몇개의오퍼랜드를가질것인가는 opcode에따라결정되며, 오퍼랜드의수는 0 ~3까지이다.
0x804834d <main+37>: 0x804834e <main+38>: 0x804834f <main+39>: End of assembler dump. (gdb) ret nop 다음은 Dev-C++ 에서컴파일한것을 IDA Pro 로본것이다. ; ; +-----------------------------------------------------------------------+ ; This file is generated by The Interactive Disassembler (IDA) ; Copyright (c) 2007 by DataRescue sa/nv, <ida@datarescue.com> ; Licensed to: Mach EDV Dienstleistungen, Jan Mach, 1 user, adv, 11/2007 ; +-----------------------------------------------------------------------+ ; ; Input MD5 : 88FEF2313AAAB6E089DCD55EF42C7804 ; File Name : C:\Documents and Settings\free2\ 바탕화면 \test.exe ; Format : Portable executable for 80386 (PE) ; Imagebase : 400000 ; Section 1. (virtual address 00001000) ; Virtual size : 000008D4 ( 2260.) ; Section size in file : 00000A00 ( 2560.) ; Offset to raw data for section: 00000400 ; Flags 60000060: Text Data Executable Readable ; Alignment : default ; OS type : MS Windows ; Application type: Executable 32bit.686p.mmx.model flat.intel_syntax noprefix - 중략 - push ebp mov ebp, esp sub esp, 8
and esp, 0FFFFFFF0h mov eax, 0 add eax, 0Fh add eax, 0Fh shr eax, 4 shl eax, 4 mov [ebp+var_4], eax mov eax, [ebp+var_4] call chkstk call main mov [esp+8+var_8], offset ahello ; "Hello" call printf mov eax, 0 retn _main endp - 중략 - 결과를보면파란색으로표시한.intel_syntax noprefix 부분을보면 Intel 문장구조를사용하고있음을알수있다. printf() 함수가호출되는부분부터두문장구조를비교해보자. [AT&T 문장구조 ] 0x8048340 <main+24>: call 0x8048268 <printf> 0x8048345 <main+29>: add $0x10,%esp 0x8048348 <main+32>: mov $0x0,%eax // 책에서는 xor %eax, %eax 0x804834d <main+37>: 0x804834e <main+38>: ret [Intel 문장구조 ] mov [esp+8+var_8], offset ahello ; "Hello" call printf mov eax, 0 // 책에서는 xor eax, eax retn 책에서는 xor %eax, %eax 를사용하고있다. 이연산결과는잘알겠지만 eax 레지스터의값
이 0이된다. xor를사용하고, 두오퍼랜드가같으면그오퍼랜드의값은 0이된다. 그리고 mov $0x0,%eax 의결과를보면 eax 레지스터에 0의값을복사한다. 결과는당연 eax 레지스터의값은 0이된다. 책에나오는것과필자의테스트에서나온결과는같은것이다. 우리가주목할것은위의두결과를보면 source operand와 destination operand의위치가다르다는것이다. 정리하면다음과같다. * AT&T 문장구조 명령어 source operand, destination operand mov $0x0, %eax * Intel 문장구조 명령어 destination operand, source operand mov eax, 0 다음부분은 C 언어에서는 return 0; 을나타낸다. mov eax, 0 // mov $0x0,%eax retn // ret 프로세서는 return 0; 이라는것을이해할수없다. 그런의미에서어셈블리어는인간이읽을수있는마지막코드층이라고할수있다. 컴파일링후에코드를어셈블링하면 opcode 를출력하는데, opcode는어셈블리어명령을이진수형태로나타낸것또는개별명령을실행하기위해필요한 on-off의연속 (sequence) 이다. 보통 opcode는이진수보다는 16진수로나타낸다. 0x8048348 <main+32>: mov $0x0,%eax 0x804834d <main+37>: 0x804834e <main+38>: 0x804834f <main+39>: ret nop End of assembler dump. (gdb) x/8b 0x8048348 0x8048348 <main+32>: 0xb8 0x00 0x00 0x00 0x00 0xc9 0xc3 0x90 (gdb) 이것을 objdump 를이용해좀더알기쉽게확인할수있다. 8048348: b8 00 00 00 00 mov $0x0,%eax 804834d: c9 804834e: c3 ret 804834f: 90 nop
하지만결국컴퓨터의프로세서에전달되는것은 1과 0의연속이다. Opcode에대한좀더자세한자료는 Intel 64 and IA-32 Architectures Software Developer s Manual Volume 2A 3 의 section 2.1.2를참고하길바란다. 4 이책은 http://www.intel.com/products/processor/manuals/ 에서 pdf 파일로구할수있으며, 책으로받으려면 http://www.intel.com/design/literature.htm를참고하면된다. 참고로이책들은무료이다. 신청하면늦어도 2주안으로미국에서한국으로책이배달된다. 필자의경우매뉴얼페이지에있는 6 종류의책을신청하여 2개의박스로받았던적이있다. 다음은섹션 2.1.2 의원문전체를첨부한것이다. ********************************************************************************************************** 2.1.2 Opcodes A primary opcode can be 1, 2, or 3 bytes in length. An additional 3-bit opcode field is sometimes encoded in the ModR/M byte. Smaller fields can be defined within the primary opcode. Such fields define the direction of operation, size of displacements, register encoding, condition codes, or sign extension. Encoding fields used by an opcode vary depending on the class of operation. Two-byte opcode formats for general-purpose and SIMD instructions consist of: An escape opcode byte 0FH as the primary opcode and a second opcode byte, or A mandatory prefix (66H, F2H, or F3H), an escape opcode byte, and a second opcode byte (same as previous bullet) For example, CVTDQ2PD consists of the following sequence: F3 0F E6. The first byte is a mandatory prefix for SSE/SSE2/SSE3 instructions (it is not considered as a repeat prefix). Three-byte opcode formats for general-purpose and SIMD instructions consist of: An escape opcode byte 0FH as the primary opcode, plus two additional opcode bytes, or A mandatory prefix (66H), an escape opcode byte, plus two additional opcode bytes (same as previous bullet) For example, PHADDW for XMM registers consists of the following sequence: 66 0F 38 01. The first byte is the mandatory prefix. Valid opcode expressions are defined in Appendix A and Appendix B. ********************************************************************************************************** 3 http://download.intel.com/design/processor/manuals/253666.pdf 4 어떤책을공부하다보면각종책이나참고자료들이제시된다. 공부과정에서그참고자료들도같이구해서본다면공부하고있는책한권을제대로이해할수있을뿐만아니라더많은것을이해할수있게될것이다.
opcode에대한더자세한내용은위의첨부한원문끝줄에나오듯 Appendix A와 B에나와있다. 그리고 Reversing Engineering Code with IDA Pro라는이책을잘이해하기위해서는 Intel 64 and IA-32 Architectures Software Developer s Manual Volume 2A, 2B 두권이늘필요할것이다. 이두권은 instruction set reference에대한것이다. Tools & Traps Shellcode는보통 C언어에서문자열배열로저장되는일련의 opcode이다. shellcode 라는용어를사용하게된것은그일련의 opcode가쉘 (/bin/sh 또는 cmd.exe) 을실행하는데필요한명령어 (instruction) 들이기때문이다. C 언어에서쉘코드를사용하려면다음과같이한다는것을이글의독자들은다알고있을것이다. unsigned char shellcode[] = \xc9\x31\xc0\xc9 ; 쉘코드는해킹에서필수적인요소이므로관련지식을반드시알아둘필요가있다. 쉘코드를만드는방법에대해서는국내외에많은자료들이있으므로그자료들을참고하길바란다. To be continued