[Digital Forensic] PE 구조 분석
포렌식에서 메모리 분석을 통해 파일을 카빙한다거나 하드디스크 이미지에서 파일을 카빙하여 해당 파일이 무엇인지 분석하려고 할 때 꼭 필요한 지식이기에 PE 구조를 상세하게 공부해 볼 것이다.
일반적으로 포렌식에서 파일을 분석할 경우는 악성코드로 의심되는 파일을 분석하는 경우로 정적 분석과 동적 분석을 하게 된다.
리버스 엔지니어링 같은 경우에는 정적 분석에 속하며, 정적 분석을 할 경우 어셈블리어와 메모리 구조 등에 대한 지식도 있어야 하지만, PE 구조에 대한 지식도 있어야 하기에 이렇게 상세히 PE 구조를 알아보려고 한다.
PE(Portable Executable) 파일 포맷은 윈도우에 해당하는 파일 포맷으로 .exe, .dll, .cpl, .sys, .scr, .drv, .vxd, .ocx 확장자를 가진 파일들에게서 볼 수 있다.
PE 구조를 보다 보면 IMAGE로 시작하는 구조체들을 많이 보게 되며, 이는 윈도우에서 실행 파일을 IMAGE라는 명칭으로 대신하기 때문에 구조체 이름도 그렇게 지은 것이다.
PE 구조는 다음 그림과 같다.
(1) DOS 영역
-
윈도우 실행 파일을 DOS 모드에서 실행하려고 할 때, 사용자에게 에러 메시지를 보여주기 위해 존재
-
실제로 이 영역은 윈도우 실행 파일이 윈도우에서 실행할 때에는 아무런 영향을 주지 않는 부분이어서 볼만한 내용이 많지 않음
-
DOS MZ Header와 DOS Stub Program 영역으로 이루어짐
1-1. DOS MZ Header
-
WinNT.h 헤더 파일에 정의되어 있음
-
눈 여겨 볼 구조체 멤버는 e_magic과 e_lfanew 구조체 멤버
-
다른 구조체 멤버들은 사용하지 않거나 PE 파일 포맷 내에서는 사실상 의미가 없음
e_magic
-
이 구조체 멤버는 DOS MZ Header의 맨 앞(PE 파일 맨 앞)을 나타내며 16진수는 '4D 5A' 값을 가짐
-
아스키코드로 변환하면 MZ라는 값을 가짐
-
MZ는 도스를 최초로 설계한 사람 중 한 명인 Mark Zbikowksi의 이니셜
-
모든 윈도우의 실행 파일은 MZ로 시작하기 때문에 이 값을 검색해 실행 파일을 찾을 수도 있음
e_lfanew
-
이 구조체 멤버는 PE 파일의 머리 격인 IMAGE_NT_HEADER를 가리키는 오프셋 값을 가짐
DOS MZ Header의 구조체 구조는 다음과 같다.
다음 그림은 notepad.exe의 DOS MZ Header 영역을 보여준다.
-
4D 5A : e_magic
-
F0 00 00 00 : e_lfanew (E0 00 00 00)
이 두 값이 올바르면 프로그램은 실행하는데 문제가 없다.
1-2) Dos Stub Program
-
사용자가 DOS 모드에서 윈도우용 실행 파일을 실행하였을 때 문구를 출력해주기 위한 코드가 있는 부분
출력하는 문구는 다음과 같다.
'This Program cannot be run is DOS mode'
이 부분은 오프셋 0x40 ~ 0x7F까지이다.
(2) PE Header 영역
-
PE 파일의 시작 부분
-
PE File Signature, PE File Header, PE File Optional Header 영역이 있음
-
IMAGE_NT_HEADERS 구조체로 이루어짐
위쪽에 정의된 구조체는 32bit 구조체이며 아래는 64bit 구조체이다.
어느 정도 직관적인 구조체 멤버 이름을 통해서 왜 PE 헤더 영역이 3가지로 나누여졌는지 알 수 있다.
2-1. PE File Signature
-
DOS의 e_magic 구조체 멤버처럼 PE 영역의 시작을 알리는 값을 가지고 있는 영역
-
모든 파일에서 그 값은 항상 동일
-
그 값은 'PE/0/0(0x50)/0x45/0x00/0x00)'
-
메모리나 파일 시스템 분석에서 삭제된 실행 파일을 찾는 데에 중요한 단서가 됨
PE File Signature 값은 다음 그림과 같다.
여기서 중요한 것은 DOS MZ Header 설명 부분에서 설명한 것과 같이 DOS MZ Header 영역의 e_lfanew 구조체 멤버는 IMAGE_NT_HEADER의 주소를 가지고 있다고 설명하였으며, 여기서 그 사실 확인이 가능하다.
2-2. PE File Header
-
실행 파일 자체의 메타데이터를 가지고 있음
-
길이는 20바이트
-
구조체는 IMAGE_FILE_HEADER 구조체 사용
구조체 정보는 다음 그림과 같다.
1) Machine
-
실행 파일이 사용하는 CPU를 나타내는 번호를 가지고 있음
Machine 종류는 다음 표와 같다.
[표 1] Machine 목록
CPU 종류 |
값 |
설명 |
IMAGE_FILE_MACHINE_UNKNOWN |
0x00 |
어떤 머신에도 적용이 가능한 종류 |
IMAGE_FILE_MACHINE_AM33 |
0x01D3 |
마쯔시다 AM33 |
IMAGE_FILE_MACHINE_AMD_64 |
0x8664 |
64bit CPU(Intel 포함) |
IMAGE_FILE_MACHINE_ARM |
0x01C0 |
ARM(Little Endian) |
IMAGE_FILE_MACHINE_EBC |
0x0EBC |
EFI Byte Code |
IMAGE_FILE_MACHINE_I386 |
0x014C |
Intel 386 또는 그 이후 Processor |
IMAGE_FILE_MACHINE_IA64 |
0x0200 |
Intel Itanium Processor |
IMAGE_FILE_MACHINE_M32R |
0x9041 |
미쯔비시 M32(Little Endian) |
IMAGE_FILE_MACHINE_MIPS16 |
0x0266 |
MIPS16 |
IMAGE_FILE_MACHINE_MIPSFPU |
0x0366 |
MIPS(FPU 지원) |
IMAGE_FILE_MACHINE_MIPSFPU16 |
0x0466 |
MIPS16(FPU 지원) |
IMAGE_FILE_MACHINE_POWERPC |
0x01F0 |
Power PC(Little Endian) |
IMAGE_FILE_MACHINE_POWERCFP |
0x01F1 |
Power PC(부동소수점 지원) |
IMAGE_FILE_MACHINE_R4000 |
0x0166 |
MIPS(Little Endian) |
IMAGE_FILE_MACHINE_SH3 |
0x01A2 |
Hitachi SH3 |
IMAGE_FILE_MACHINE_SH3PSP |
0x01A3 |
Hitachi SH3 DSP |
IMAGE_FILE_MACHINE_SH4 |
0x01A6 |
Hitachi SH4 |
IMAGE_FILE_MACHINE_SH5 |
0x01C2 |
Thumb |
IMAGE_FILE_MACHINE_WCEMIPSV2 |
0x0169 |
MIPS(Little Endian WCE V2) |
2) NumberOfSections(NoS)
-
실행 파일이 가지고 있는 섹션의 개수를 의미
-
만약 섹션을 삭제, 추가하면 이 구조체 멤버의 값 또한 변경 필요
3) TimeDataStamp
-
실행 파일이 컴파일된 시간으로 이 시간은 컴파일 한 컴퓨터의 시스템 시간을 의미
-
32비트 유닉스 포맷으로 GMT를 기준으로 함
-
만약 '0x4A5BC60F' 값이 저장되어 있다면, 이 값을 10진수로 변환하였을 경우 '1247528463'라는 숫자를 의미
우리가 알아볼 수 있는 날짜로 변환하게 되면 다음과 같다.
4) PointerToSymbolTable(PTST)
-
COFF 심볼 파일의 오프셋을 값으로 가짐
-
컴파일러에 의해 생성된 OBJ 파일이나 디버그 모드로 만들어져 COFF 디버그를 가진 PE 파일에서만 사용됨
-
COFF 심볼 테이블은 이제 더 이상 사용하지 않아 이 값은 반드시 0으로 설정되어야 함
5) NumberOfSymbols(NoS)
-
심볼 테이블에 있는 엔트리 개수를 나타냄
-
COFF 심볼 테이블은 더 이상 사용되지 않으므로 반드시 0으로 설정되어야 함
6) SizeOfOptionalHeader(SOOH)
-
Optional Header의 크기를 값으로 가짐
-
실행 파일에서만 필요하며, 일반적으로 32bit 시스템은 E0의 값을 가짐
7) Characteristics(Char)
-
파일의 속성을 결정하는 값을 가짐
Characteristics의 값들은 다음 표와 같다.
[표 2] Characteristics
속성 이름 |
값 |
설명 |
IMAGE_FILE_RELOCS_STRIPPED |
0x0001 |
실행 파일 전용, 이 파일은 재배치되지 않으므로 반드시 지정된 베이스 주소에 로그 필요, 지정된 베이스 주소가 사용 불가능하면 에러 발생 |
IMAGE_FILE_EXECUTABLE_IMAGE |
0x0002 |
실행 파일 전용, 실행 파일은 검증되었으며 실행 가능, 이 값이 설정되어 있지 않으면 그것은 링크 에러 의미 |
IMAGE_FILE_LINK_NUMB_STRIPED |
0x0004 |
COFF 라인 번호는 더 이상 사용하지 않으므로 이 값은 반드시 0이어야 함 |
IMAGE_FILE_LOCAL_SYMS_STRIPED |
0x0008 |
로컬 심볼에 대한 COFF 심볼 테이블은 더 이상 사용하지 않으므로 이 값은 반드시 0이어야 함 |
IMAGE_FILE_AGGRESIVE_ES_TRIM |
0x0010 |
해당 값은 윈도우 2000과 그 이후 버전에서는 더 이상 사용되지 않으므로 반드시 0이어야 함 |
IMAGE_FILE_LARGE_ADDRESS_AWARE |
0x0020 |
애플리케이션은 2GB보다 큰 주소를 다룰 수 있음 |
????? |
0x0040 |
예약 영역 |
IMAGE_FILE_BYTES_REVERSED_LO |
0x0080 |
Little Endian, 해당 필드는 더 이상 사용하지 않으므로 반드시 0이어야 함 |
IMAGE_FILE_32BIT_MACHINE |
0x0100 |
머신이 32bit Processor를 사용 |
IMAGE_FILE_DEBUG_STRIPED |
0x0200 |
해당 실행 파일에는 디버깅 정보가 없음 |
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP |
0x0400 |
해당 실행 파일이 이동형 저장 장치에 있다면 실행 파일 전체를 페이지 파일로 복사함 |
IMAGE_FILE_NET_RUN_FROM_SWAP |
0x0800 |
해당 실행 파일이 네트워크로 연결된 저장 장치에 있다면 실행 파일 전체를 페이지 파일로 복사 |
IMAGE_FILE_SYSTEM |
0x1000 |
이 실행 파일은 시스템 파일 |
IMAGE_FILE_DLL |
0x2000 |
해당 실행 파일은 DLL 파일, 사실상 실행 파일로 취급 |
IMAGE_FILE_SYSTEM_ONLY |
0x4000 |
해당 파일은 오직 Single Processor가 있는 머신에서만 실행되어야 함 |
IMAGE_FILE_BYTES_REVERSED_HI |
0x8000 |
Big Endian, 해당 필드는 더 이상 사용되지 않으므로 0값을 가져야 함 |
PE Header 영역의 세 번째 영역인 PE File Optional Header 영역을 알아보기 전에 알아두어야 할 개념은 바로 RVA이다.
PE File Optional Header 영역에서 자주 언급되기 때문에 미리 알아두고 이해하는 것이 훨씬 효과적이다.
2-3. RVA (Relative Virtual Address)
-
실행 파일이 프로세스를 만들고 실행 파일의 코드와 데이터가 가상 메모리에 맵핑이 되면 메모리에 맵핑되는 지점의 시작 주소가 베이스 주소가 됨
-
RVA는 가상 메모리 상의 베이스 주소를 기준으로 로드된 실행 파일의 상대적 위치를 의미
가상 메모리에서의 가상 주소는 아래와 같은 형식으로 구할 수 있다.
가상 주소 = 베이스 주소(Base Address) + RVA
RVA는 가상 메모리에 맵핑된 이후의 오프셋 값이므로 PE 파일상의 오프셋과 같다고 생각하면 안된다.
물론 PE 파일은 가상 메모리에 그대로 맵핑이 되어 순서가 맵핑 되기 전과 같지만, 가상 메모리에 맵핑이 되면 섹션 사이에 패딩이 생겨 오프셋 값이 달라진다.
2-4. PE File Optional Header
-
실행 파일의 실행 정보가 담긴 부분
-
필수적인 부분이므로, PE File Header보다 더 중요
-
Option이라는 이름이 붙은 이유는 오브젝트가 이 영역을 선택적으로 가질 수 있기 때문
-
정작 오브젝트에서는 별다른 기능을 발휘하지 않음
PE File Optional Header 부분은 IMAGE_OPTIONAL_HEADER 구조체로 되어 있다.
크기가 유동적이고 앞서 보았던 IMAGE_NT_HEADER의 SizeOfOptionalHeader 구조체 멤버에 의해 크기가 결정되고, PE File Header 바로 뒤에 위치 한다.
IMAGE_OPTIONAL_HEADER 구조체는 Standard Fields, NT additional Fields, IMAGE_DATA_DIRECTORY로 구분이 된다.
Standard Fields
-
파일을 로드하고 실행하는 것에 대한 정보가 들어있음
1) Magic
-
실행 파일의 상태를 나타내는 부호 없는 정수 값을 가짐
-
이 값이 '0x010B' 라면 일반적인 실행 파일을 뜻하며, '0x0107' 이라면 ROM 이미지, '0x020B'라면 64bit Executable 타입이라는 것을 의미
2) MajorLinkerVersion(V1)
-
링커의 상위 버전 넘버를 저장하는 멤버
3) MinorLinkerVersion(V2)
-
링커의 하위 버전 넘버를 저장하는 멤버
4) SizeOfCode
-
코드 섹션(.text)의 크기 정보를 가지고 있음
-
코드 섹션이 여러 개라면 그것들의 합에 대한 값을 가짐
5) SizeOfInitialized Data
-
초기화된 데이터 섹션의 크기를 값으로 가짐
-
SizeOfCode 멤버와 마찬가지로 자신이 담당하고 있는 섹션의 수가 여러 개라면 그들의 합에 대한 값을 가짐
6) SizeOfUninitialized Data
-
초기화되지 않은 데이터 섹션의 크기를 값으로 가짐
-
섹션이 여러 개일 경우 SizeOfInitialized Data 구조체 멤버와 동일한 성격을 가짐
7) AddressOfEntry Point
-
메모리에 맵핑된 상태에서의 실행 코드 주소를 가짐
-
이 주소는 절대적 주소가 아닌 상대적 주소(RVA)
-
보통 .text 섹션 내의 특정 주소를 가리킴
-
실행 파일에서의 이 주소는 실행 코드의 시작 주소
-
디바이스 드라이버에서는 초기화 함수의 주소가 됨
-
반면 DLL 파일은 이 값이 선택적이며, 이 값이 없다면 반드시 0으로 되어야 함
8) BaseOfCode
-
실행 파일이 메모리에 맵핑된 상태에서의 코드 섹션(.text) 주소를 가짐
-
이 값은 상대적 주소인 RVA 값
9) BaseOfData
-
실행 파일이 메모리에 맵핑된 상태에서의 데이터 섹션(.data) 주소를 가짐
-
이 값은 상대적 주소인 RVA 값
NT Additional Fields
-
해당 영역은 Standard Fields 바로 다음부터 시작되며, 많은 구조체 멤버로 이루어짐
-
이 영역에서 제공되는 정보는 윈도우 링커와 로더가 사용하는 정보
다음 그림은 해당 영역의 오프셋 구조이다.
1) ImageBase
-
가상 메모리에 로드된 실행 파일의 주소를 가짐
-
이 값은 반드시 64KB이어야 함
-
ImageBase 구조체 멤버 값과 Standard Fields의 BaseOfCode 값을 더하면 .text 섹션의 가상 메모리 주소
2) SectionAlignment
-
가상 주소에 맵핑될 때 섹션이 할당 받을 가상 주소의 기준 값을 가짐
-
섹션은 메모리에 맵핑될 때 섹션별로 맵핑되기 때문
-
섹션의 시작 주소는 항상 메모리 페이지의 배수여야 하므로 이 값은 FileAlignment와 같거나 커야 함
-
기본 값은 페이지의 크기와 같음
3) FileAlignment
-
PE 파일 내에서 섹션들이 시작하는 주소의 기준 값
-
따라서 PE 파일 내에서의 섹션이 시작하는 주소는 항상 이 값의 배수가 됨
-
이 값은 2에서 512 사이에 있는 2의 제곱이거나 64KB가 되지만 기본 값은 512
-
SectionAlignment의 값이 메모리 페이지 크기보다 작다면 FileAlignment의 값과 동일해야 함
4) MajorOperatingSystemVersion(mOS1)
-
실행 파일을 실행하는데 필요한 OS의 최소 상위 버전 값을 가짐
5) MinorOperationSystemVersion(mOS2)
-
실행 파일을 실행하는데 필요한 OS의 최소 하위 버전 값을 가짐
6) MajorImageVersion(mIV1)
-
실행 파일의 상위 버전의 값을 가짐
7) MinorImageVersion(mIV2)
-
실행 파일의 하위 버전의 값을 가짐
8) MajorSubsystem(mSV1)
-
실행 파일을 실행하는데 필요한 서브 시스템의 상위 버전의 값을 가짐
9) MinorSubsystem(mSV2)
-
실행 파일을 실행하는데 필요한 서브 시스템의 하위 버전의 값을 가짐
10) Win32Version
-
이 멤버는 VC++6.0 SDK까지는 예약 영역이었음
-
7.0부터 지금의 이름으로 바뀌었으며, 하지만 여전히 사용하지 않아 보통 0으로 되어 있음
11) SizeOfImage
-
메모리에 로드 되었을 때 실행 파일의 크기 값을 가짐
-
이 값은 헤더를 포함했을 때의 크기이며, 반드시 SectionAlignment의 배수여야 함
-
이 값을 기준으로 해서 가상 메모리에서 실행 파일을 맵핑할 공간을 예약
12) SizeOfHeader
-
DOS Stub, PE 헤더, 섹션 헤더의 합 값을 가짐
-
이 값은 무조건 FileAlignment의 배수여야 함
13) Checksum
-
실행 파일의 체크섬 값을 가짐
-
체크섬 알고리즘은 IMAGEHELP, DLL 파일에 있으며 이 값은 모든 드라이버와 부팅 시 로드 된 DLL 파일, 중요한 윈도우 프로세스가 로드한 DLL 파일을 검증할 때 사용
14) Subsystem
-
실행 파일을 실행하는데 필요한 서브 시스템의 값을 가짐
다음 표는 Subsystem 값 목록표이다.
[표 3] Subsystem 값 목록표
상수 |
값 |
목록표 |
IMAGE_SUBSYSTEM_UNKNOWN |
0 |
알 수 없는 SubSystem |
IMAGE_SUBSYSTEM_NATIVE |
1 |
디바이스 드라이버와 Native Windows Process |
IMAGE_SUBSYSTEM_WINDOWS_GUI |
2 |
Windows GUI SubSystem |
IMAGE_SUBSYSTEM_WINDOWS_GUI |
3 |
Windows CUI SubSystem |
IMAGE_SUBSYSTEM_POSIX_CUI |
7 |
Posix CUI Subsystem |
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI |
9 |
Windows CE |
IMAGE_SUBSYSTEM_EFI_APPLICATION |
10 |
EFI (Extensible Firmware Interface) Application |
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER |
11 |
Boot Service가 있는 EFI 드라이버 |
IMAGE_SUBSYSTEM_RUNTIME_DRIVER |
12 |
Runtime Service가 있는 EFI 드라이버 |
IMAGE_SUBSYSTEM_EFI_ROM |
13 |
EFI ROM Image |
IMAGE_SUBSYSTEM_XBOX |
14 |
XBOX |
15) DLLchr(DllCharacteristics)
-
PE 파일이 DLL 파일인 경우 파일의 속성정보를 의미
16) SizeOfStackReserve
-
예약할 스택의 크기 값을 가짐
17) SizeOfStackCommit
-
커밋할 스택의 크기 값을 가짐
18) SizeOfHeapReserve
-
예약할 힙의 크기 값을 가짐
19) SizeOfHeapCommit
-
커밋할 힙의 크기 값을 가짐
20) LoaderFlags
-
예약된 영역으로 0으로 채워져 있음
21) NumberOfRvaAndSize
-
Optional Header의 나머지 부분에 있는 데이터 디렉토리 엔트리의 숫자 값을 가짐
IMAGE_DATA_DIRECTORY
-
WinNT.h를 보면 IMAGE_OPTIONAL_HEADER 안에 있지만, 사실은 NT Additional Fields 하부 구조체
구조체 선언은 다음 그림과 같다.
WinNT.h에 명시되어 있는 것을 보면 IMAGE_DATA_DIRECTORY의 엔트리 개수는 16개로 정의되어 있다.
엔트리는 [그림 16]과 같이 정의되고, 마지막은 0으로 예약되어 있기 때문에 16개가 되는 것이다.
각 부분은 주소와 크기로만 나누어진다.
(3) Section
3-1) Section Header
-
섹션 헤더 영역에는 각 세션의 헤더들이 있으며, 섹션들은 이 헤더에 메타 데이터와 메모리에 로드될 때 필요한 정보를 저장
섹션 헤더의 구조체 형태는 다음 그림과 같다.
위 [그림 18]에는 보이지 않지만 IMAGE_SIZEOF_SHORT_NAME은 8바이트로 정의되어 있으며, 이름 길이가 8바이트를 넘어가게 되면 8바이트 이후는 잘리며, 8바이트 보다 짧을 경우 남는 공간은 NULL로 채워진다.
다음 그림은 .text 섹션의 헤더 영역 오프셋 구조이다.
1) Name
-
섹션의 이름 부분
-
8바이트 내에서 사용자가 임의로 수정 가능
2) VirtualSize
-
메모리에 로드되는 섹션의 전체 크기
-
이 필드는 실행 파일에서만 존재하고 오브젝트 파일에서는 0으로 설정되어 있음
-
SizeOfRawData보다 크다면 섹션의 나머지 부분은 0으로 채워짐
3) VirtalAddress
-
메모리에 로드 되었을 때의 RVA 값을 가짐
-
오브젝트 파일의 경우 0으로 설정 됨
4) SizeOfRawData
-
실행 파일의 경우 디스크 상에서 초기화된 데이터의 크기를 말함
-
Optional Header의 FileAlignment 값의 배수이어야 함
-
VirtualSize보다 작다면 섹션 나머지 부분은 0으로 채워짐
-
오브젝트 파일의 경우 0으로 설정 됨
5) PointerToRawData(PotToRawData)
-
PE 파일 내에서의 섹션 위치의 오프셋을 값으로 가짐
-
실행 파일의 경우 이 값은 무조건 FileAlignment의 배수여야 함
6) PointerToRelocations(PotToRelocations)
-
섹션 재배치에 대한 시작 지점 오프셋을 값으로 가짐
-
이 값은 보통 오브젝트 파일에 사용
-
실행 파일의 경우 0으로 설정 됨
7) PointerToLineNumbers(PotToLineNumbers)
-
해당 섹션에 대한 Line-Number 엔트리의 시작 지점 주소 값을 가짐
-
Line-Number 엔트리는 COFF 포맷으로 지금은 사용하지 않기 때문에 0으로 설정 되어 있음
8) NumberOfRelocations(Nolo)
-
해당 섹션의 재배치 엔트리 숫자를 값으로 가짐
-
실행 파일의 경우 항상 0으로 설정되어 있음
9) NumberOfLineNumber(Nolo)
-
해당 섹션에 대한 Line-Number 엔트리의 숫자를 가짐
10) Characteristics
-
해당 섹션의 속성 정보를 표시하는 값을 가짐
Characteristics에 대한 속성 목록은 다음 표와 같다.
[표 4] 속성 목록표
종류 |
값 |
설명 |
IMAGE_SCN_CNT_CODE |
0x00000020 |
실행 코드를 가지고 있음 |
IMAGE_SCN_CNT_INITIALIZED_DATA |
0x00000040 |
초기화된 데이터를 가지고 있음 |
IMAGE_SCN_CNT_UNINITIALIZED_DATA |
0x00000080 |
초기화되지 않은 데이터를 가지고 있음 |
IMAGE_SCN_MEM_NOT_CACHED |
0x04000000 |
캐시될 수 없음 |
IMAGE_SCN_MEM_NOT_PAGED |
0x08000000 |
페이징되지 않음 |
IMAGE_SCN_MEM_SHARED |
0x10000000 |
메모리에서 공유될 수 없음 |
IMAGE_SCN_MEM_EXECUTE |
0x20000000 |
코드로서 실행될 수 없음 |
IMAGE_SCN_MEM_READ |
0x40000000 |
읽기가 가능 |
IMAGE_SCN_MEM_WRITE |
0x80000000 |
쓰기가 가능 |
더 많은 속성들이 있지만 중요한 것만 목록화 한 내용이다.
더 많은 속성들은 WinNT.h 파일을 참고하면 확인 가능하다.
만약 섹션 이름이 임의로 바뀌어 어떠한 섹션인지 파악이 되지 않는다면 섹션 속성 정보를 보는 것도 하나의 방법이다.
섹션 속성 정보들은 각 섹션마다 기본적으로 정의되어 있어 섹션 속성 정보만 봐도 어떠한 섹션인지 파악 가능하다.
앞에서 알아본 PE 구조들은 실행 파일들의 정보나 메모리에 로드될 때의 정보 등이었다.
이제 알아볼 섹션은 실행 파일의 동작에 관한 정보들이다.
섹션에는 여러 가지 섹션이 있는데, 대부분 .text, .data, .rdata, .rsrc, .edata, .idata, .reloc 섹션이 많이 사용된다.
분석하고자 하는 파일의 섹션 정보는 PE File Header를 보면 NumberOfSections에 색션 개수가 나타나 있으며, 각 섹션의 위치 정보 등은 Section Header 영역을 보면 확인할 수 있다.
3-2. .text 섹션
-
실행 파일의 소스코드가 들어있는 섹션
-
초기화되거나 초기화되지 않은 변수, import와 export 테이블 정보를 제외한 나머지 소스코드들이 저장됨
해당 섹션의 기본 속성 정보는 다음과 같다.
-
IMAGE_SCN_CNT_CODE
-
IMAGE_SCN_MEM_EXCUTE
-
IMAGE_SCN_MEM_READ
섹션의 위치와 크기는 섹션 헤더(PointerToRawData, SizeOfRawData)를 참고하면 알 수 있으며 .text 섹션 헤더에서의 PE 파일 내에서의 .text 섹션 위치와 크기를 확인할 수 있다.
위 [그림 22]의 빨간색 박스를 보면 PE 파일 내에서의 주소를 확인할 수 있다.
.text 섹션의 마지막 오프셋은 주소와 크기를 더하게 되면 결과 값이 나오게 된다.
.text 섹션의 마지막 오프셋으로 이동한 다음, 섹션의 모습을 보면 어느 정도 데이터가 있다가 나머지는 0으로 채워져 있는 것을 확인할 수 있다.
이러한 이유는 SizeOfRawData의 경우 FileAlignment(IMAGE_OPTIONAL_HEADER)의 배수가 되어야 하므로 실제 데이터의 크기를 가지고 있는 VirtualSize 값을 FileAlignment의 값의 배수로 올림하여 크기가 늘어나 채울 데이터가 없어 0으로 채운 것이다.
실제 메모리에 .text 섹션이 올라갈 때는 0을 제외하고 올라가게 된다.
이러한 방법으로 모든 섹션의 영역을 찾을 수 있다.
이 방법을 계속 섹션마다 사용하여 영역을 찾는 것을 보여줄 수는 없기 때문에 이후에 나오는 섹션들은 특징과 속성 정보 등만 소개한다.
3-3. .data 섹션
-
읽기/쓰기가 가능한 일반적인 데이터를 위한 섹션
-
정적 변수와 전역 변수가 저장
해당 섹션의 기본 속성 정보는 다음과 같다.
-
IMAGE_SCN_CNT_INITIALIZED_DATA
-
IMAGE_SCN_MEM_READ
-
IMAGE_SCN_MEM_WRITE
3-4. .rdata 섹션
-
읽기 전용 섹션으로 상수와 배열로 직접 정의된 문자열과 런타임 라이브러리에서 사용하는 에러 메시지 등이 저장
해당 섹션의 기본 속성 정보는 다음과 같다.
-
IMAGE_SCN_CNT_INITIALIZED_DATA
-
IMAGE_SCN_MEM_READ
3-5. .idata, .edata 섹션
-
.idata 섹션은 import table이 들어 있으며, .edata 섹션은 export table이 의미
-
이 섹션들은 실행하는데 꼭 필요한 섹션은 아님
.idata 섹션의 기본 속성 정보는 다음과 같다.
-
IMAGE_SCN_CNT_INITIALIZED_DATA
-
IMAGE_SCN_MEM_READ
-
IMAGE_SCN_MEM_WRITE
.edata 섹션의 기본 속성 정보는 다음과 같다.
-
IMAGE_SCN_CNT_INITIALIZED_DATA
-
IMAGE_MEM_READ
3-6. .reloc 섹션
-
PE 파일이 메모리 상에 로드될 때 재배치에 관해 참조하는 정보가 들어있는 섹션
-
실행 파일의 경우 이 섹션은 필요하지 않을 수 있지만, DLL 파일의 경우 메모리에 로드될 때 재배치를 염두하고 있기 때문에 이 섹션이 없으면 실행되지 않을 수도 있음
.reloc 섹션의 기본 속성 정보는 다음과 같다.
-
IMAGE_SCN_MEM_READ
-
IMAGE_SCN_CNT_INITIALIZED_DATA
-
IMAGE_SCN_TYPE_NOLOAD
이렇게 해서 PE 포맷을 모두 알아보았다.
조금만 더 깊숙이 들어가면 그 구조는 매우 복잡하며, 알아야 할 것들이 너무 많기 때문에 포렌식 업무를 수행함에 있어 알아두어야 할 것들만 소개하였다.
앞서 말했듯이 PE 포맷은 파일 카빙, 파일 시그니처 탐지에 유용하게 사용될 수 있어 이렇게 소개하였다.
포렌식 관점에서 PE 구조를 보면 파일 생성 시간 값을 저장하는 PE File Header 영역에 TimeDataStamp 구조체 멤버가 중요하고 또 파일의 종류를 결정하는 PE File Header 영역에 Characteristics 구조체 멤버가 중요하다.
# Reference
'Digital Forensics > Forensic Theory' 카테고리의 다른 글
[Digital Forensic] CoC(Chain of Custody) (0) | 2020.05.10 |
---|---|
[Digital Forensic] 안티 포렌식 (0) | 2020.05.09 |
[Digital Forensic] Registry 분석 (0) | 2020.05.08 |
[Digital Forensic] EXT 파일 시스템 (3) (0) | 2020.04.02 |
[Digital Forensic] EXT 파일 시스템 (2) (0) | 2020.04.02 |