Transcript SECTION
실행파일(PE)의 구조
정
의
PE(Portable Executable)
정의 : PE구조로 된 PE파일들은 플랫폼에 관계없이 Win32 운영
체제가 돌아가는 시스템이면 어디서든 실행 가능하다는
의미
EXE와 DLL등의 파일구조를 PE파일 포맷이라고 명명함
COFF라는 포맷을 계승한 파일 포맷
PE파일의 전체구조
IMAGE_DOS_HEADER
IMAGE_DOS_HEADER
64byte
DOS STUB CODE
DOS STUB CODE의 Size는 가변
PE Signature
IMAGE_FILE_HEADER
IMAGE_NT_HEADER
224byte or 96byte
IMAGE_OPTIONAL_HEADER
IMAGE_SECTION_HEADER
Section당 40byte
SECTION 1
Ex) .text
SECTION …
Ex) .rdata
IMAGE_DOS_HEADER
DOS HEADER는 항상 64byte의 크기를 가지며 윈도우
내에서 IMAGE_DOS_HEADER라는 구조체로 정의되어
있다(WinNT.h)
DOS HEADER 구조체의 처음 e_magic 값은 항상 MZ로
고정되며, 마지막 e_lfanew 값은 PE 헤더의 시작위치를 가
르키는 offset값이다
DOS STUB CODE는 없어도 되는 부분으로서 프로그램을
도스 모드에서 실행시켰을 때 실행되는 코드이며, 일반
적으로 “This program must be run under Microsoft
Windows”라는 메시지를 출력하고 종료하는 코드가 삽
입되어 있으며 사이즈는 가변이다.
IMAGE_DOS_HEADER
IMAGE_DOS_HEADER
RVA : PE파일 내의 파일 offset과는 무관하며, PE파일이
가상주소 공간에 로드되었을 때의 그 시작주소에
대한 상대주소를 나타냄.
메모리상에서의 PE파일의 시작주소에 대한 offset
PE파일에서의 절대주소 : Imagebase
PE파일에서의 상대주소 : 모든 RVA값
RVA를 통해서 파일상의 offset구하기
RVA값이 속한 섹션을 찾는다.(섹션테이블)
그 섹션의 RVA값과 PointerToRawData값의 차를 구한다.(converter)
찾고자 하는 RVA값에서 converter값을 뺀다.
IMAGE_NT_HEADERS
Signature(4byte)
: PE(50 45 00 00)의 값을 갖는다.
IMAGE_FILE_HEADER(20byte)
Machine : CPU의 ID를 나타낸다. 0x14c(IA32) 또는 0x200(IA64)
NumberOfSections : 섹션의 개수를 의미한다.
TimeDateStamp : 링커가 해당 파일을 만들어낸 시간을 의미한다.
SizeOfOptionalHeader : 파일헤더 뒤에오는 OPTIONAL_HEADER
의 사이즈를 나타낸다.(데이터 디렉토리가 존재할 경우 224byte, 존
재하지 않을경우 96byte)
Characteristics : PE파일의 속성을 의미한다. 일반적으로 실행파
일의 경우 0x10f값을 가진다.
IMAGE_NT_HEADERS
IMAGE_OPTIONAL_HEADER
Magic : IMAGE_OPTIONAL_HEADER를 나타내는 시그너처로서
32bit PE의 경우 0x010b, 64bit의 경우 0x020b값을 가진다.
AddressOfEntryPoint : 로더가 실행을 개시할(최초로 실행될 코드)
주소. 이 주소는 RVA로서 보통 .text 섹션의 시작점이다.
디버거로 실행(calc.exe)
Peview로 calc.exe open
IMAGE_NT_HEADERS
ImageBase : 해당 PE가 가상주소 공간에 로드될때의 그 시작주소.
기본적으로 EXE파일은 0x00400000,
DLL파일은 0x10000000
ImageBase
DOS HEADER
PE Signature
FILE_HEADER
DOS HEADER
PE Signature
FILE_HEADER
OPTIONAL_HEADER
OPTIONAL_HEADER
SECTION HEADER
SECTION HEADER
Section
Section
….
파일상태
….
메모리에 로드된 상태
IMAGE_NT_HEADERS
SectionAlignment : 메모리상에서 섹션의 각 섹션이 차지해야 하는
최소의 단위. 각 섹션은 SectionAlignment X n 의 주소에
서 시작해야 한다. 기본값으로 4K(0x1000)
FileAlignment : PE파일 내에서의 섹션들의 정렬단위.
SectionAlignment와 같은 개념. 보통 0x200 또는 0x100
FileAlignment
를 맞춰주기
위한 padding
OPTIONAL_HEADER
SECTION HEADER
Section
….
파일상태
파일상태
OPTIONAL_HEADER
SECTION HEADER
SectionAlignment
를 맞춰주기 위
한 padding
Section
….
메모리에 로드된 상태
메모리에 로드된 상태
IMAGE_NT_HEADERS
SizeOfImage : 메모리에 로드된 PE Image 전체의 크기를 나타내며,
이 값은 ImageBase 필드가 가리키는 주소값으로 부터
해당 PE의 마지막 섹션까지의 크기이다. SectionAlignment
의 배수가 되어야 한다.
SizeOfHeaders : DOS HEADER에서부터 패딩을 포함한 section
table까지의 크기의 총합. FileAlignment의 배수가 되어야 한다.
NumberOfRvaAndSizes : IMAGE_DATA_DIRECTORY 구조체 배열
의 원소의 개수를 나타낸다. DATA_DIRECTORY를 가지고 있
다면 16(0x10) 값을 가진다.
IMAGE_DATA_DIRECTORY : Virtualaddress와 Size로 구성된 구조
체로서 마지막 구조체의 값은 0x00으로 채워져 있다.
IMAGE_NT_HEADERS
DOS HEADER
DOS HEADER
PE Signature
PE Signature
FILE_HEADER
OPTIONAL_HEADER
FILE_HEADER
SizeOfHeaders
OPTIONAL_HEADER
SECTION HEADER
SECTION HEADER
Section
Section
….
….
파일상태
메모리에 로드된 상태
SizeOfImage
IMAGE_NT_HEADERS
IMAGE_DATA_DIRECTORY
VirtualAddress
IMAGE_DIRECTORY_ENTRY_EXPORT [00]
EXPORT
TABLE
Size
IMAGE_DIRECTORY_ENTRY_IMPORT [01]
…
임포트 테이블의
메모리상에서
…
의 시작점과 크기에 대한 정보를
…
가지고 있다.
IMAGE_DIRECTORY_ENTRY_TLS
[09]
IMAGE_DIRECTORY_ENTRY_IAT
[12]
…
IMAGE_DIRECTORY_ENTRY_NULL
[15]
IMPORT
TABLE
………
…
VirtualAddress
Size
Import_Section
임포트 섹션이 존재하지 않을수도 있으며, 다른 섹션에 포함되어 있을 수
있다.
임포트 섹션이 존재하지 않을 경우 IMAGE_DATA_DIRECTORY내의
IMAGE_DIRECTORY_ENTRY_IMPORT를 통하여 임포트 테이블의 위치를
알아낼수 있다.
임포트 테이블은 IMAGE_IMPORT_DESCRIPTOR라는 구조체의 배열로 구성
되며, 이 구조체는 임포트한 DLL의 정보를 담고 있다.
Typedef struct _IMAGE_IMPORT_DESCRIPTOR{
union{
DWORD Characteristics;
DWORD OriginalFirstThunk;
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
Import_Section
IMAGE_IMPORT_DESCRIPTOR
• OriginalFirstThunk
– INT(Import Name Table)(또는 ILT(Import Lookup Table))을
가르키는 RVA값으로서 INT는 IMAGE_THUNK_DATA라는 구조체
의 배열로 구성된다.
– 이때 IMAGE_THUNK_DATA배열의 각 원소는
IMAGE_IMPORT_BY_NAME이라는 구조체를 가리키는 RVA값을
가진다.
• TimeDateStamp
– 시간과 날짜를 나타내는 시간 스탬프이다.
– 바인딩 전에는 항상 0이다.
• ForwarderChain
– 일반적으로 바인딩되지 않은 경우 0이다.
Import_Section
IMAGE_IMPORT_DESCRIPTOR
• Name
– 임포트된 DLL의 이름을 담과 있는 NULL로 끝나는 아스키 문자
열에 대한 RVA값을 가진다.
• FirstThunk
– OriginalFirstThunk와 마찬가지로 IMAGE_THUNK_DATA의 배열
을 가리키며, IMAGE_THUNK_DATA의 배열의 각 원소 역시
IMAGE_IMPORT_BY_NAME을 가리킨다.
– PE가 가상주소 공간에 매핑되면 IMAGE_THUNK_DATA는 임포
트한 DLL내의 사용할 실제 함수의 주소를 담게 된다.
– 실제 함수의 주소를 담고 있는 IMAGE_THUNK_DATA의 이 배열
을 IAT(Import Address Table)이라고 하며, 실제 함수의 주소가
IAT에 설정된 것을 바인딩 되었다고 한다.
Import_Section
IMAGE_THUNK_DATA
• AddressOfData
– INT와 IAT가 바인딩 전에 모두 IMAGE_IMPORT_BY_NAME 구조
체를 가리키고 있을 때, IMAGE_THUNK_DATA는 AddressOfData
의 의미로 사용되었다고 볼수 있다.
• Ordinal
– 모듈정의 파일을 통하여 서수 파일을 지정하게 되면 링커는
DLL링크시 함수명을 통하지 않고, 서수를 통해서 사용된 함수를
링크하게 된다.
• Function
– 바인딩 후에 IAT테이블이 실제 함수의 주소로 변경되었을 경우
Function의 의미로 사용된 것이며, INT는 Function의 의미로 사
용될 일이 없다.
– IAT는 바인딩전에는 AddressOfData, 또는 Ordinal의 의미로 사
용되고, 바인딩 후에는 Function의 의미로 사용된다.
Import_Section
IMAGE_THUNK_DATA
OriginalFirstThunk
TimeDateStamp
ForwarderChain
Name
…..
…..
…..
<INT>
IMAGE_IMPORT_BY_NAME
…..
…..
…..
FirstThunk
IMAGE_THUNK_DATA
…..
…..
전
바인딩 후
…..
<IAT>
임포트된 DLL내의
실제 함수코드
Import_Section
임포트 테이블의 내용
OriginalFirstThunk
FirstThunk
INT
IAT
바인딩 후의 IAT 변경모습
Import_Section
임포트 과정
PE파일을 메모리에 로드한 후 데이터 디렉토리의 두번째 엔트리
인 IMAGE_DIRECTORY_ENTRY_IMPORT로 부터 임포트 테이블의
주소를 구한다.
임포트 테이블의 Name필드를 통해 임포트 할 DLL의 이름을 알
아낸 후, 공간 확보후 DLL을 가상주소공간에 맵핑 시킨다.
INT를 통해서 임포트 할 함수의 이름 또는 서수(Ordinal)값을 알
아낸다.
위의 정보를 이용하여 해당 DLL의 익스포트 테이블을 통해 필요
한 함수의 실제 주소를 알아내서 IAT를 수정한다.
IMAGE_SECTION_HEADER
IMAGE_SECTION_HEADER는 40byte로 구성되며 다음과 같이
정의된다.
Typedef struct _IMAGE_SECTION_HEADER{
BYTE
Name[8];
union{
DWORD
PhysicalAddress;
DWORD
VirtualSize;
} Misc;
DWORD
VirtualAddress;
DWORD
SizeOfRawData;
DWORD
PointerToRawData;
DWORD
PointerToRelocations;
DWORD
PointerToLinenumbers;
WORD
NumberOfRelocations;
WORD
NumberOfLinenumbers;
DWORD
Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
IMAGE_SECTION_HEADER
DWORD Name
섹션의 아스키 이름을 나타낸다. 8바이트까지 지정가능하며, NULL문자
는 제외된다. 로딩과정과 상관이 없다.
DWORD VirtualAddress
PE에서 해당 섹션을 매핑시켜야 할 가상 주소 공간 상의 RVA값이다.
메모리에서 본 해당 섹션의 시작주소를 의미한다.
DWORD SizeOfRawData
파일 상태에서의 섹션의 사이즈 값(패딩된 상태)
FileAlignment 값의 배수이다.
DWORD PointerToRawData
파일 상태에서의 섹션이 시작하는 offset값이다.
DWORD Characteristics
해당 섹션의 속성을 나타내는 플래그의 집합이다.
IMAGE_SECTION_HEADER
…
Name : .text
VirtualAddress
SizeOfRawData
PointerToRawData
Charcteristics
…
PointerToRawData가 지정한
곳에서 부
…
터 SizeOfRawData 만큼의 데이터를 읽
Name : .text
어들여 VirtualAddress가
가르키는 곳
VirtualAddress
에 맵핑한 후에SizeOfRawData
Characteristics에 설정
PointerToRawData
된 속성 정보적용
Charcteristics
…
.text
.text
…
…
파일상태
메모리에 로드된 상태
SECTION
섹션은 같은 성질의 데이터들을 로더가 구분할 수 있도록
저정해둔 영역을 말한다.
코드
.text
프로그램을 실행하기 위한 코드를 담고 있는 섹션이
다.
데이터
.data
초기화된 전역변수나 정적변수들을 담고 있는 읽기,
쓰기가 가능한 섹션이다.
.rdata
읽기 전용 데이터 섹션으로 소스상에서 상수로 정의
한 것들이 배치된다.
임포트 API
정보
.idata
임포트할 DLL과 그 API들에 대한 정보를 담고 있는
섹션이다. .rdata에 병합되는 추세이다.
재배치 정보
.reloc
실행파일에 대한 기본 재배치 정보를 담고 있는 섹
션이다.
TLS
.tls
스레드 지역 저장소를 위한 섹션이다.
참고 자료
Zesrever의 지식펌프
• 조립하면서 배우는 PE
• http://zesrever.xstone.org/
Null2root
• PE포멧 분석(PDF)
• http://hdp.null2root.org/
Windows 시스템 실행파일의 구조와 원리 – 이호동 저