PE포맷 이란?
PE파일의 종류
왜 해야하는가?
PE파일의 기본 구조
[ PE파일(메모장.exe)이 메모리에 로딩되는 모습 ]
PE header: DOS header ~ Section header
PE Body: 그밑 Section들
[ VA(Virtual Address, 절대주소) ]
메모리에서는 절대주소로 위치를 표현하며 그것을 VA라고 합니다
Null값이 존재하는 이유
[ 최소기본단위의 배수로 시작 ]
한번더 정리를 하자면..
각 세션의 시작위치는 최소기본단위의 배수에 해당하는 위치여야 하며 그값이 부족할 경우 Null값을 줘서 부족한 공간을 채워줍니다 이렇게 해서 최소기본단위가 적용된다고 말할 수 있겠습니다
VA & RVA
VA와 RVA의 관계식
왜 RVA(상대주소)를 사용해야 하는가?
[ 재배치(Relocation) 상황 ]
예를 들기위해 최대한 쉽게 네이밍을 했으며 네이밍과 주소는 실제와 전혀 상관없습니다 개념만 집고가세요
game.dll가 00000000이라는 위치에 로딩되는순간 notepad.dll이 먼저 로딩이 되어버렸습니다
하나의 위치에 2개의 dll파일이 올라갈 수 없기 때문에 이때 재배치(Relocation)을 해줘야 하는 상황이 생기게 됩니다
이때 PE헤더 정보들이 VA(절대주소)로 되어있다면 정상적인 엑세스가 이루어 지지 않기 때문에
RVA(상대주소)로 해서 재배치가 발생해도 기준에 대한 상대주소는 변하지 않기 때문에 문제없이 원하는 정보에 엑세스 할 수 있게 됩니다.
절대주소? 상대주소?
[ 물리주소와 가상주소 ]
빨간색: 사용중
파란,초록: 사용안함
현재 0x1~0x3, 0x7이 사용중입니다 중간중간 비어있는 공간이 있죠.
하지만 프로그램은 연속적으로 메모리를 사용해야 하기 때문에 빈공간인 0x4~0x6, 0x8~0x9(물리)를 사용하면 안됩니다
그래서 나온 개념이 가상주소입니다 이렇게 사용하게 되면PE Header
DOS header ~ Section header 인 것을 확인하실 수 있습니다
DOS Header
Microsoft는 PE파일 포맷을 만들때 DOS파일에 대한 하위 호환성을 고려해서 만들었습니다
그 결과로 PE헤더 제일 앞부분에는 기존 DOS EXE Header를 확장시킨 IMAGE_DOS_HEADER구조체가 존재합니다
즉 IMAGE_DOS_HEADER는 DOS EXE Header를 확장시킨것
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | typedef struct _IMAGE_DOS_HEADER { WORD e_magic; // DOS signature : 4D5A ("MZ") WORD e_cblp; WORD e_cp; WORD e_crlc; WORD e_cparhdr; WORD e_minalloc; WORD e_maxalloc; WORD e_ss; WORD e_sp; WORD e_csum; WORD e_ip; WORD e_cs; WORD e_lfarlc; WORD e_ovno; WORD e_res[4]; WORD e_oemid; WORD e_oeminfo; WORD e_res2[10]; LONG e_lfanew; // offset to NT header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; | cs |
[ IMAGE_DOS_HEADER 구조체 ]
IMAGE_DOS_HEADER 구조체의 크기는 40입니다
이 구조체에서 가장 중요한 멤버는 e_magic, e_lfanew 입니다
e_magic: DOS signature (4D5A 이며 ASCII값으로 "MZ")
e_lfanew: NT header의 옵셋을 표시(파일에 따라 가변적인 값)
모든 PE파일은 시작부분(e_magic)의 DOS sinature(MZ)가 존재하고 e_lfanew값이 가리키는 위치에 NT header 구조체가 존재해야 합니다 그 구조체의 이름이 IMAGE_NT_HEADERS입니다
정리
모든 PE파일 시작에는부분은 e_magic이 존재합니다
해당 사진은 윈도우 계산기 파일을 불러온 사진입니다
e_magic에 의해서 맨처음 MZ의 값인 4D 5A로 시작하는 것을 확인하실 수 있습니다
DOS Stub
쉽게 말하자면 DOS환경에서 돌아갈 명령어가 16bit 형태로 있으며
분석해보면 This program cannot be run in DOS mode 라는 것으로 보아 이 프로그램은 DOS mode에서 실행될 수 없다고 볼 수 있습니다
NT Header
1 2 3 4 5 | 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; | cs |
[ NT Header 구조체 ]
앞서 말햇듯 Signature, IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER32가 정의된 것을 확인하실 수 있습니다
DOS signature
[ DOS signature ]
NT header 구조체의 시작을 알리며 ASCII값이 PE로 시작합니다
IMAGE_FILE_HEADER
1 2 3 4 5 6 7 8 9 | 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; | cs |
[ IMAGE_FILE_HEADER ]
가장 중요한 빨간색 4개의 값만 설명드리겠습니다
헥사 에디터로 따지면 이부분이 되겠으며 말 그대로 파일의 속성에 대한 부분입니다
여기서 SizeOfOptionalHeader의 값이 E0입니다 10진수로 바꾸면 224가 되는데 이것이 헤더의 총 길이가 되겠습니다
IMAGE_OPTIONAL_HEADER 32
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 31 32 33 34 35 36 37 38 39 40 | typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; | cs |
역시 중요한 부분만 빨간색으로 칠했으며 해당 부분만 설명드리겠습니다
1. Magic
이 구조체가 32bit용이면 10Bh
64bit 용이면 20Bh 의 값을 가집니다
2. AddressOfEntryPoint
EP(Entry Point)의 RVA 주소값이 들어있습니다
쉽게말해 프로그램에서 최초로 실행되는 코드의 시작주소. 매우중요한 값입니다
3. Image Base
PE파일이 맵핑되는 시작주소를 가리킵니다
4. SectionAlignment
메모리에서의 섹션의 최소단위를 나타냅니다
5. FileAlignment
파일에서의 섹션의 최소단위를 나타냅니다
6. SizeOfImage
메모리에서의 PE구조 크기를 나타냅니다
7. SizeOfHeader
PE header의 크기를 나타냅니다
8. Sybsystem
-1: 드라이버파일(SYS,VXD)이라는 뜻입니다
-2: GUI파일이라는 뜻입니다
-3: CUI파일이라는 뜻입니다
9. NumberOfRvAndSizes
바로 밑에 있는 IMAGE_DATA_DIRECTORY 구조체의 배열크기를 정합니다
HxD로 찾는것은 아직 이해가 잘 안되서 이해가 되는대로 다시 정리해서 업데이트 하겠습니다..
오늘 아침부터 하루종일 PE구조 찾아보고 정리했는데 이해하고 만드느라 시간이 되게 오래걸렸네요ㅠㅠ
'프로젝트 개인공부 > 악성코드 분석' 카테고리의 다른 글
[악성코드 분석] API 기초와 API를 이용한 악성코드 분류 (0) | 2016.07.04 |
---|