반응형


<사진 출처 : UPack 제작자 dwing's homepage>


그 동안 총 5 회에 걸쳐서 UPack 의 PE Header 분석과 Debugging 에 대해서 연재를 하였습니다.

UPack 상세 분석 – PE Header 완전 정복 (1)
UPack 상세 분석 – PE Header 완전 정복 (2)
UPack 상세 분석 – PE Header 완전 정복 (3)
UPack 상세 분석 – PE Header 완전 정복 (4)
UPack 디버깅 - OEP 찾기


다른 Packer 도 많은데 굳이 이 UPack 에 이렇게 많은 공(?)을 들인 까닭은 제 개인적인 추억(경험) 때문입니다.

예전에 PE 공부를 마치고 PE File Format 에 자신 있던 그때에, 전혀 새로운 PE 세계가 있다는 걸 알려준 소중한 Packer 입니다. 또한 PE 스펙은 그냥 스펙일 뿐이고, 실제 구현은 PE Loader 개발자에 의해 좌우되기 때문에 OS 별로 실제 테스트를 해봐야 한다는 깨우침(?)을 주었습니다.

제 블로그를 방문하시는 여러분들께서도 저와 같은 경험과 느낌을 가져보시라는 뜻으로 UPack 을 상세히 소개하였습니다.

물론 UPack 에서 소개된 내용이 PE Header Patch 의 전부는 아닙니다.

하지만 제가 분명히 장담할 수 있는 것은 "UPack 을 정복한 사람에겐 앞으로 어떤 PE Header 변형이 나타나더라도 두렵지 않다." 는 것입니다. PE Header 에서 실제 사용되는 값들과 사용되지 않는 값들을 잘 숙지 하고 있다면 어떤 변형도 무리 없이 분석이 가능합니다. (제 경험입니다. ^^)

감사합니다.


ReverseCore


반응형
반응형


PE File Format


지금까지 오랜 시간에 걸쳐 PE(Portable Executable) File Format 에 대해 살펴보았습니다.

PE 스펙을 보면 각 구조체 멤버 하나하나 자세히 기술하고 있지만
리버싱에서 주목해야 하는 멤버들만 추려서 설명드렸습니다.

특히 IAT, EAT 에 관한 내용은 실행압축(Run-Time Packer), Anti-Debugging, DLL Injection, API Hooking
매우 다양한 중/고급 리버싱 주제들의 기반 지식이 됩니다.

hex editor 와 연필, 종이만 가지고 IAT/EAT 의 주소를 하나하나 계산해서
파일/메모리 에서 실제 주소를 찾는 훈련을 많이 해보시기 바랍니다.

쉽지 않은 내용이지만 그만큼 리버싱에서 중요한 위치를 차지하고 있기 때문에
고급 리버싱을 원하는 분들께서는 반드시 습득하셔야 합니다.



PEView.exe


간단하고 사용하기 편리한 PE Viewer 프로그램(PEView.exe)을 소개해 드립니다.
(개인이 만든 무료 공개 SW 입니다.)

http://www.magma.ca/~wjr/PEview.zip

아래는 PEView.exe 의 실행 화면입니다.


PE Header 를 각 구조체 별로 보기 쉽게 표현해주고, RVA <-> File Offset 변환을 간단히 수행해줍니다.
(제가 설명드렸던 내용과 용어 사용에 있어서 약간 틀릴 수 있습니다. 둘 다 익혀두시는게 의사소통에 좋습니다.)

위와 같은 PE Viewer 를 직접 제작해 보시는 것을 추천드립니다.
저 또한 처음 PE Header 를 공부할 때 (검증을 위해) 콘솔 기반의 PE Viewer 를 만들어서 지금까지 잘 사용하고 있습니다.
직접 제작하다 보면 자신이 잘 몰랐거나 잘 못 이해하던 부분을 정확히 파악하고 제대로 공부할 수 있습니다.



PE Patch


PE 스펙은 말 그대로 권장 스펙이기 때문에 각 구조체 내부에 보면 사용되지 않는 많은 멤버들이 많이 있습니다.

또한 말 그대로 스펙만 맞추면 PE 파일이 되기 때문에 일반적인 상식을 벗어나는 PE 파일을 만들어 낼 수 있습니다.

PE Patch 란 바로 그런 PE 파일을 말합니다.
PE 스펙에 어긋나지는 않지만 굉장히 창의적인(?) PE Header 를 가진 파일들입니다.
(정확히 표현하면 PE Header 를 이리저리 꼬아 놨다고 할 수 있습니다.)

PE Patch 만 해도 따로 고급 주제로 다뤄야 할 만큼 (리버싱에 있어서) 넓고도 깊은 분야입니다.

한가지만 소개해 드리겠습니다.
지금까지 배웠던 PE Header 에 대한 상식이 사뿐히 깨지는 경험을 하실 수 있습니다.
(그러나 PE 스펙에 벗어난건 없답니다.)

아래 사이트는 tiny pe 라고 가장 작은 크기의 PE 파일을 만드는 내용입니다.

http://blogs.securiteam.com/index.php/archives/675

411 byte 크기의 (정상적인) PE 파일을 만들어 냈습니다.
IMAGE_NT_HEADERS 구조체 크기만 해도 248 byte 라는걸 생각하면 이것은 매우 작은 크기의 PE 파일입니다.

다른 사람들이 계속 도전해서 304 byte 크기의 파일까지 나타나게 됩니다.

그리고 마지막으로 어떤 다른 사람이 위 사이트를 본 후 자극을 받아서
아래와 같이 극단적이고 매우 황당한 PE 파일을 만들어 내었습니다.

http://www.phreedom.org/solar/code/tinype/

이곳에 가면 Windows XP 에서 정상 실행되는 97 byte 짜리 PE 파일을 다운 받을 수 있습니다.
(2009년 4월 현재까지 최고 기록입니다.)
또한 PE Header 와 tiny pe 제작과정에 대한 내용을 자세히 설명하고 있어서 읽어보시면 크게 도움이 되실 겁니다.
(약간의 assembly 언어에 대한 지식이 요구됩니다.)

모두 다운 받아서 하나씩 분석해 보시기 바랍니다. 분명 크게 도움이 됩니다.



Epilogue


이러한 PE patch 파일들은 저뿐만 아니라 일반적인 리버서들의 고정관념을 깨트리는 내용이며
그래서 리버싱 공부가 더 즐겁습니다.

PE Header 에 대해서 다시 한번 강조 하고 싶은 내용은 아래와 같습니다.

- PE 스펙은 그저 스펙일 뿐이다. (만들어 놓고 사용되지 않는 내용이 많다.)
- 내가 지금 알고 있는 PE Header 에 대한 지식도 잘 못된 부분이 있을 수 있다.
   (tiny pe 외에도 PE header 를 조작하는 여러 창의적인 기법들이 계속 쏟아져 나온다.)
- 항상 모르는 부분을 체크해서 보강하자.

앞으로 제 블로그에서 다양한 형태의 PE 파일들을 분석할 예정입니다.
재밌고 특이한 PE Header 조작 기법에 대해서는 그때 그때 소개해 드리도록 하겠습니다.




반응형
반응형


NT header



NT header 구조체 IMAGE_NT_HEADERS 입니다.

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                          // PE Signature : 50450000 ("PE"00)
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

* 출처 : Microsoft 의 Visual C++ 에서 제공하는 winnt.h


위 구조체는 32 bit 용이며, 64 bit 용은 세번째 멤버가 IMAGE_OPTIONAL_HEADER64 입니다.

IMAGE_NT_HEADER 구조체는 3개의 멤버로 되어 있는데요,
제일 첫 멤버는 Signature 로서 50450000h ("PE"00) 값을 가집니다. (변경불가!)
그리고 FileHeader OptionalHeader 구조체 멤버가 있습니다.

notepad.exe 의 IMAGE_NT_HEADERS 의 내용을 hex editor 로 살펴보겠습니다.


IMAGE_NT_HEADERS 구조체의 크기는 F8h 입니다. 상당히 큰 구조체 입니다.
FileHeader 와 OptionalHeader 구조체를 하나하나 살펴보겠습니다.



IMAGE_NT_HEADERS - IMAGE_FILE_HEADER


파일의 개략적인 속성을 나타내는 IMAGE_FILE_HEADER 구조체 입니다.

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;

    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;

} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

* 출처 : Microsoft 의 Visual C++ 에서 제공하는 winnt.h

IMAGE_FILE_HEADER 구조체에서 아래 4 가지 멤버들이 중요합니다.
(이 값들이 정확히 세팅되어 있지 않으면 파일은 정상적으로 실행되지 않습니다.)


#1. Machine
Machine
넘버는 CPU 별로 고유한 값이며 32 bit Intel 호환 칩은 14Ch 의 값을 가집니다.
아래는 winnt.h 파일에 정의된 Machine 넘버의 값들입니다. (일반적인 14Ch 의 값을 기억하면 됩니다.)

#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
#define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB             0x01c2
#define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64
#define IMAGE_FILE_MACHINE_AXP64             IMAGE_FILE_MACHINE_ALPHA64



#2. NumberOfSections
PE 파일은 코드, 데이타, 리소스 등이 각각의 섹션에 나뉘어서 저장된다고 설명드렸습니다.
NumberOfSections 는 바로 그 섹션의 갯수를 나타냅니다.

이 값은 반드시 0 보다 커야 합니다.

정의된 섹션 갯수보다 실제 섹션이 적다면 실행 에러가 발생하며,
정의된 섹션 갯수보다 실제 섹션이 많다면 정의된 갯수만큼만 인식됩니다.


#3. SizeOfOptionalHeader
IMAGE_NT_HEADERS 구조체의 마지막 멤버는 IMAGE_OPTIONAL_HEADER32 구조체입니다.
SizeOfOptionalHeader 멤버는 바로 이 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타냅니다.

IMAGE_OPTIONAL_HEADER32 는 C 언어의 구조체이기 때문에 이미 그 크기가 결정되어 있습니다.
그런데 Windows 의 PE Loader 는 IMAGE_FILE_HEADER 의 SizeOfOptionalHeader 값을 보고
IMAGE_OPTIONAL_HEADER32 구조체의 크기를 인식합니다.


IMAGE_DOS_HEADER 의 e_lfanew 멤버와 IMAGE_FILE_HEADER 의 SizeOfOptionalHeader 멤버 때문에
일반적인(상식적인) PE 파일 형식을 벗어나는 일명 '꽈배기' PE 파일(PE Patch) 이 만들 수 있습니다.
(나중에 PE Patch 에 대해서 상세히 설명하도록 하겠습니다.)


#4. Characteristics
파일의 속성을 나타내는 값으로써, 실행이 가능한 형태인지(executable or not)
혹은 DLL 파일인지 등의 정보들이 bit OR 형식으로 조합됩니다.

아래는 winnt.h 파일에 정의된 Characteristics 값들입니다. (0002h 와 2000h 의 값을 기억해 두세요.)

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable 
                                                     // (i.e. no unresolved externel references).

#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from
                                                     // file in .DBG file

#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, 
                                                     // copy and run from the swap file.

#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net,
                                                     // copy and run from the swap file.

#define IMAGE_FILE_SYSTEM                    0x1000  // System File.
#define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.


참고로 PE 파일중에 Characteristics 값에 0002h 가 없는 경우(not executable)가 있을까요?
네, 있습니다. 예를 들어 *.obj 와 같은 object 파일이 그런 경우이고, resource DLL 같은 파일이 그런 경우 입니다.

이 정도면 IMAGE_FILE_HEADER 의 구조를 이해하는데 부족함이 없을 것입니다.

마지막으로 IMAGE_FILE_HAEDER 의 TimeDateStamp 멤버에 대해서 설명드리겠습니다.
이 값은 파일의 실행에 영향을 미치지 않는 값으로써 해당 파일의 빌드 시간을 나타낸 값입니다.
단, 개발 도구에 따라서 이 값을 세팅해주는 도구(VB, VC++)가 있고, 그렇지 않은 도구(Delphi)가 있습니다.
(또한 개발 도구의 옵션에 따라서 달라질 수 있습니다.)


이제 실제로 notepad.exe 의 IMAGE_FILE_HEADER 를 확인해 보겠습니다.


위 그림은 hex editor 로 봤을때의 그림이고, 이를 알아보기 쉽게 구조체 멤버로 표현하면 아래와 같습니다.

[ IMAGE_FILE_HEADER ] - notepad.exe

 offset   value   description

-------------------------------------------------------------------------------
000000E4     014C machine
000000E6     0003 number of sections
000000E8 48025287 time date stamp (Mon Apr 14 03:35:51 2008)
000000EC 00000000 offset to symbol table
000000F0 00000000 number of symbols
000000F4     00E0 size of optional header
000000F6     010F characteristics
                      IMAGE_FILE_RELOCS_STRIPPED
                      IMAGE_FILE_EXECUTABLE_IMAGE
                      IMAGE_FILE_LINE_NUMS_STRIPPED
                      IMAGE_FILE_LOCAL_SYMS_STRIPPED
                      IMAGE_FILE_32BIT_MACHINE


다음에는 NT header 의 세번째 멤버인 IMAGE_OPTIONAL_HEADER32 에 대해서 알아보겠습니다.

(continue)


 

반응형

+ Recent posts