본문 바로가기

프로젝트 개인공부/악성코드 분석

[악성코드분석] PE (portable Executable) 기초 개념잡기

PE포맷 이란?

윈도우 운영체제에서 사용되는 실행파일, DLL Object코드, FON 폰트파일 등을 위한 파일형식입니다.
OE파일은 윈도우 로더가 실행가능한 코드를 관리하는데 필요한 정보를 캡슐화한 데이터 구조체 입니다.
출처(위키백과): https://ko.wikipedia.org/wiki/PE_%ED%8F%AC%EB%A7%B7

풀어쓰자면
PE(Portable Executable) 말 그대로 옮겨다니면서 실행시킬 수 있는 파일을 뜻합니다.
MS에서 다른 운영체제와 이식성을 좋게 하기위해 만든 파일 포맷입니다

PE파일의 종류

실행, 드라이버, 라이브러리, 오브젝트 총 4개의 계열이 있습니다
- 실행계열:           EXE, SCR
- 드라이버계열:     SYS,VXD
- 라이브러리 계열: DLL, OCX, CPL, DRV
- 오브젝트 계열:    OBJ

왜 해야하는가?

- PE구조는 파일이 실행되기 위한 모든 정보를 볼 수 있기 때문입니다.
- 프로그램이 사용하는 API나 어느 메모리주소에 파일이 로딩되는지 알 수 있기 때문입니다

PE파일의 기본 구조

메모리에 적재(loading 또는 mapping)될때의 모습



[ PE파일(메모장.exe)이 메모리에 로딩되는 모습 ]

PE header: DOS header ~ Section header

PE Body: 그밑 Section들                       

     

[ VA(Virtual Address, 절대주소) ]

메모리에서는 절대주소로 위치를 표현하며 그것을 VA라고 합니다



Null값이 존재하는 이유

자세히 보시면 헤더의 끝부분과 각섹션 끝에 Null값이 붙는것을 확인하실 수 있습니다
Null은 padding이라고 불리우기도 합니다

즉. 최소 기본단위를 사용하기위해 Null값이 존재합니다.
-최소 기본단위?    컴퓨터에서 파일, 메모리, 네트워크 패킷 등을 처리할때 효율을 높이기 위한 개념

파일/메모리에서 섹션의 시작위치는 각각 최소 기본단위의 배수에 해당하는 위치여야하고 빈공간은 Null로 채워버립니다. (이해가 안되시는분은 아래그림 참고)

[ 최소기본단위의 배수로 시작 ]


한번더 정리를 하자면..

각 세션의 시작위치는 최소기본단위의 배수 해당하는 위치여야 하며 그값이 부족할 경우 Null값을 줘서 부족한 공간을 채워줍니다 이렇게 해서 최소기본단위가 적용된다고 말할 수 있겠습니다



VA & RVA

VA는 바로 위에 사진을 통해 설명드렸습니다 (Virtual Address, 메모리의 절대주소)

RVA(Relative Virtual Address): 어느 기준 위치(ImageBase)에서부터의 상대주소
어떤 기준에서부터 상대주소를 뜻합니다
1) 먼저 PE가 메모리에 적재되기 전에 기본 ImageBase는 0입니다
2) 그리고 PE안에서는 상대주소로 주소가 적히게 됩니다
- 메모리에 적재시 절대주소가 들어가게 된다면 Relocation이 어렵기 때문입니다
- 상대주소가 들어가면 ImageBase에서 얼마만큼 떨어진 곳으로 이동하면 되기때문에 Relocation이 쉽게되어 상대주소를 사용합니다
Relocation: 재배치

이해가 안되시면 밑에서 다시한번 설명하니 내려오세요

VA와 RVA의 관계식

RVA + ImageBase = VA
상대주소 + 기준위치 = 절대주소

왜 RVA(상대주소)를 사용해야 하는가?

PE헤더 내의 정보는 RVA형태로 된 것이 많습니다.
PE파일(주로DLL)이 프로세스 가상메모리의 특정위치에 로딩되는 순간 이미 그위치에 다른PE파일(DLL)이 로딩되어 있을 수 있습니다. 이때 재배치(Relocation)를 통해서 비어있는 다른자리에 로딩되어야 하는데 만약 PE헤더 정보들이 VA로 되어있다면 정상적인 엑세스가 이루어지지 않았을것입니다

이해가 안되신다면 그림으로 설명해드리겠습니다

[ 재배치(Relocation) 상황 ]

예를 들기위해 최대한 쉽게 네이밍을 했으며 네이밍과 주소는 실제와 전혀 상관없습니다 개념만 집고가세요

game.dll가 00000000이라는 위치에 로딩되는순간 notepad.dll이 먼저 로딩이 되어버렸습니다

하나의 위치에 2개의 dll파일이 올라갈 수 없기 때문에 이때 재배치(Relocation)을 해줘야 하는 상황이 생기게 됩니다

이때 PE헤더 정보들이 VA(절대주소)로 되어있다면 정상적인 엑세스가 이루어 지지 않기 때문에

RVA(상대주소)로 해서 재배치가 발생해도 기준에 대한 상대주소는 변하지 않기 때문에 문제없이 원하는 정보에 엑세스 할 수 있게 됩니다.


절대주소? 상대주소?

많은분들이 헷갈려 하시는 절대주소와 상대주소를 개념잡고 가겠습니다
절대주소: 메모리의 위치를 식별하는 메모리 고유주소
  즉. 기억장치교유의 주소로서 기억장소를 직접 숫자로 지정합니다

상대주소: 고유주소가 아니며 특정영역에 상대적인 주소를 지정합니다

[ 물리주소와 가상주소 ]

빨간색: 사용중

파란,초록: 사용안함

현재 0x1~0x3, 0x7이 사용중입니다 중간중간 비어있는 공간이 있죠.

하지만 프로그램은 연속적으로 메모리를 사용해야 하기 때문에 빈공간인 0x4~0x6, 0x8~0x9(물리)를 사용하면 안됩니다

그래서 나온 개념이 가상주소입니다 이렇게 사용하게 되면

★프로그램은 가상메모리인 0x1~0x3, 0x4~0x5(가상)까지 접근하게 되고 운영체제는 알아서 물리적인 메모리인  0x4~0x6, 0x8~0x9(물리)를 찾아가서 값을 읽어오게 됩니다

한줄요약하자면.. 물리적인 빈공간을 가상주소에 연결해서 가상주소에 접근하면 운영체제가 알아서 가상주소와 연결된 물리주소를 찾아가서 값을 읽어옴

추가로
가상주소는 0x0 ~ 0xFFFFFFFF 까지이며 프로그램은 4byte 메모리를 사용해야 합니다
위 그림은 이해를 돕기위해 쉽게 했으므로 메모리 용량과 주소는 제 임의대로 한것입니다





PE Header

PE헤더는 구조체로 이루어져 있으며 구조체들에 대해 알아보겠습니다
위 사진[ PE파일(메모장.exe)이 메모리에 로딩되는 모습 ]을 보시면 PE헤더의 영역은

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 Header밑에 존재하는 DOS Stub입니다. DOS Stub은 옵션에 의해 존재여부를 결정하며 크기도 일정하지 않습니다. (DOS Stub이 없어도 파일 실행에는 문제가 없습니다)

쉽게 말하자면 DOS환경에서 돌아갈 명령어가 16bit 형태로 있으며

분석해보면 This program cannot be run in DOS mode 라는 것으로 보아 이 프로그램은 DOS mode에서 실행될 수 없다고 볼 수 있습니다



NT Header

NT헤더 구조체는 크게 3개로 구분합니다
Signature, IMAGE_FILE_HEADER, IMAGE_OPTIONAL_HEADER32 입니다
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

여기서부터 NT header구조체의 시작을 판별하실 수 있습니다

[ 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개의 값만 설명드리겠습니다

1. Machine
CPU별로 고유한 값을 가지면서 IA-32 호환 CPU 14Ch의 값을 IA-64호환 CPU는 200h의 값을 가집니다
- CPU별로 고유한값을 가집니다
- IA-32호환CPU = 14Ch
- IA-64호환CPU = 200h
2. NumberOfSections
이 값은 다음에 나오는 Section들의 갯수이며 최소 1개 이상이여야 합니다
3. SizeOfOptionalHeader
이 값은 IMAGE_NT_HEADERS구조체의 마지막 구조체 IMAGE_OPTIONAL_HEADER 32의 구조체 크기를 나타냅니다
- IMAGE_NT_HEADERS안에 IMAGE_OPTIONAL_HEADER 32크기를 나타냄
4. Characteristics
이 값은 파일속성에 대한 부분

헥사 에디터로 따지면 이부분이 되겠으며 말 그대로 파일의 속성에 대한 부분입니다


여기서 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구조 찾아보고 정리했는데 이해하고 만드느라 시간이 되게 오래걸렸네요ㅠㅠ