[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로 구분이 된다. 이제부터 3가지에 대해서 상세히 알아 볼 것이다.
[Standard Fields]
파일을 로드하고 실행하는것에 대한 정보가 들어있다.
아래는 IMAGE_OPTIONAL_HEADER안에 있는 Standard Fields 영역에 구조이다.
아래 notepad.exe 파일의 Standard Fields 영역을 보면서 각 구조체 멤버에 대해 설명하겠다.
- Magic : 실행 파일의 상태를 나타내는 부호없는 정수 값을 가지고 있으며, 이 값이 "0x010B" 라면 일반적인 실행 파일을
뜻하는 것이고, "0x0107" 이라면 롬이미지, "0x020B" 라면 64bit Executable 타입이라는 것을 의미한다.
오프셋은 0x00 ~ 0x01이며, 크기는 2바이트이다.
위 이미지에서는 "0x010B" 값을 가지고 있으므로 일반 실행파일을 뜻하는 것을 알 수 있다.
- MajorLinkerVersion : 링커의 상위 버전넘버를 저장하는 멤버이다. 오프셋은 0x02이며, 크기는 1바이트 이다.
위 이미지에서는 "0x09" 라는 값을 가지고 있다.
- MinorLinkerVersion : 링커의 하위 버전넘버를 저장하는 멤버이다. 오프셋은 0x03이며 크기는 1바이트 이다.
위 이미지에서는 "0x00" 이라는 값을 가지고 있다.
- SizeOfCode : 코드 섹션(.text)의 크기 정보를 가지고 있으며, 코드 섹션이 여러개라면 그것들의 합에 대한 값을
가지고 있다. 오프셋은 0x04 ~ 0x07이며, 크기는 4바이트 이다.
위 이미지에서는 "0x0000A800" 의 값을 가지고 있다. 이 값은 10진수로 변환하면 "43008" 이 되므로
43008바이트의 크기를 가지고 있는 것이 된다.
- SizeOfInitializedData : 초기화된 데이터 섹션의 크기를 값으로 가지고 있으며, SizeOfCode 멤버와 마찬가지로 자신이
담당하고 있는 섹션의 수가 여러개라면 그들의 합에 대한 값을 가진다. 오프셋은 0x08 ~ 0x11
이며, 크기는 4바이트 이다.위 이미지에서는 "0x00022400" 값을 가지고 있으며 10진수로
변환시 "140288" 이 되므로 140288바이트의 크기를 가지고 있는 것이 된다.
- SizeOfUninitializedData : 초기화되지 않은 데이터 섹션의 크기를 값으로 가지고 있으며, 섹션이 여러개 일시 위의
구조체 멤버와 동일한 성격을 지닌다. 오프셋은 0x12 ~ 0x15이며, 크기는 4바이트 이다.
위 이미지에서는 "0x00000000" 이라는 값을 가지고 있으며 이는 "0"을 뜻한다. 즉 초기화
되지 않은 데이터가 없다는 뜻이 된다.
- AddressOfEntryPoint : 메모리에 맵핑된 상태에서의 실행 코드 주소를 가지고 있다. 이 주소는 절대적인 주소가 아닌
상대적인 주소(RVA)이며, 보통 .text 섹션 내의 특정 주소를 가리키게 된다. 실행 파일에서의
이 주소는 실행 코드의 시작 주소이며, 디바이스 드라이버에서는 초기화 함수의 주소가 된다.
반면 DLL 파일은 이 값이 선택적이며, 이 값이 없다면 반드시 0으로 되어야 한다. 오프셋은
0x16 ~ 0x19 이며 크기는 4바이트 이다. 위 이미지에서는 "0x003689"라는 값을 가지고
있다.
- BaseOfCode : 실행 파일이 메모리에 맵핑된 상태에서의 코드섹션(.text) 주소를 가지고 있다. 이 값은 상대적 주소인
RVA 값이다. 오프셋은 0x20 ~ 0x23이며, 크기는 4바이트 이다. 위 이미지에서는 "0x00001000"
이라는 값을 가지고 있다.
- BaseOfData : 실행 파일이 메모리에 맵핑된 상태에서의 데이터 섹션(.data) 주소를 가지고 있다. 이 값은 상대적 주소인
RVA 값이다. 오프셋은 0x24 ~ 0x27이며, 크기는 4바이트 이다. 위 이미지에서는 "0x0000C000"
이라는 값을 가지고 있다.
위에서 RVA 라는 단어가 많이 나온다. 이쯤에서 RAV란 무엇인지 알고 가는 것이 좋을 것 같다.
[RVA(Relative Virtual Address)]
실행파일이 프로세스를 만들고 실행파일의 코드와 데이터가 가상메모리에 맵핑이되면 메모리에 맵핑되는 지점의 시작 주소가 베이스 주소(Base Address)가 된다. RVA는 이 가상 메모리상의 베이스 주소를 기준으로 로드된 실행 파일의 상대적인 위치를 의미한다. 가상메모리에서의 가상주소는 아래와 같은 형식으로 구할 수 있다.
물론 PE 파일은 가상메모리에 그대로 맵핑이 되어 순서가 맵핑 되기전과 같지만, 가상메모리에 맵핑이 되면 섹션 사이에
패딩이 생겨 오프셋값이 달라진다.
이번에는 NT Additional Fields 영역을 알아볼 차례이다.
[NT Additional Fields]
이 영역은 Standard Fields 바로 다음부터 시작되며, 많은 구조체 멤버로 이루어져 있다.
이 영역에서 제공되는 정보는 윈도우 링커와 로더가 사용하는 정보다.
아래는 해당 영역의 구조이다.
아래 이미지는 32bit 실행 파일이므로 참고하기 바란다.(같은 구조체이기 때문에 오프셋은 계속 이어감)
- ImageBase : 가상메모리에 로드된 실행 파일의 주소를 가지고 있다. 이 값은 반드시 64KB의 배수여야 한다. 오프셋은
0x28 ~ 0x 31이며, 크기는 4바이트 이다. 위 이미지에서는 "0x01000000" 이라는 값을 가지고 있다.
* 참고 : ImageBase 구조체 멤버값과 Standard Fields의 BaseOfCode값을 더하면 .text 가상메모리 주소다
- SectionAlignment : 가상 주소에 맵핑 될 때 섹션이 할당 받을 가상 주소의 기준값을 가지고 있다. 섹션은 메모리에
맵핑 될 때 섹션별로 맵핑되기 때문이다. 섹션의 시작 주소는 항상 메모리 페이지의 배수여야 하므로
이 값은 FileAlignment와 같거나 커야 하며, 기본 값은 페이지의 크기와 같다. 오프셋은
0x32 ~ 0x35이며, 크기는 4바이트 이다. 위 이미지에서는 "0x00001000" 이라는 값을 가지고 있
다. 이 값이 BaseOfCode와 같은 것을 볼 수 있는데 이는 .text 섹션부터 메모리에 맵핑 된다는 것을
의미한다.
- FileAlignment : PE 파일 내에서 섹션들이 시작하는 주소의 기준 값이다. 따라서 PE 파일내에서의 섹션이 시작하는
주소는 항상 이 값의 배수가 된다. 이 값은 2에서 512사이에 있는 2의 멱승이거나 64KB가 되지만
기본값은 512이다. SectionAlignment의 값이 메모리 페이지 크기보다 작다면 FileAlignment의
값과 동일하여야 한다. 오프셋은 0x36 ~ 39이며, 크기는 4바이트 이다. 위 이미지에서는
"0x00000200" 값을 가지고 있는데 이 값을 10진수로 변환하면 512가 된다. 기본 값을 가지고 있다는
뜻이 된다.
- MajorOperatingSystemVerison : 실행 파일을 실행하는데 필요한 OS의 최소 상위버전의 값을 가지고 있다.
오프셋은 0x40 ~ 0x41이며, 크기는 2바이트 이다. 위 이미지에서는 "0x0006"
이라는 값을 가지고 있다.
- MinorOperatingSystemVersion : 실행 파일을 실행하는데 필요한 OS의 최소 하위버전의 값을 가지고 있다.
오프셋은 0x42 ~ 0x43이며, 크기는 2바이트 이다. 위 이미지에서는 "0x0001"
이라는 값을 가지고 있다.
- MajorImageVersion : 실행파일의 상위버전의 값을 가지고 있다. 오프셋은 0x44 ~ 0x45이며, 크기는 2바이트 이다.
위 이미지에서는 "0x0006" 이라는 값을 가지고 있다.
- MinorImageVersion : 실행파일의 하위버전의 값을 가지고 있다. 오프셋은 0x46 ~ 0x47이며, 크기는 2바이트 이다.
위 이미지에서는 "0x0001" 이라는 값을 가지고 있다.
- MajorSubsystem : 실행 파일을 실행하는데 필요한 서브시스템의 상위버전의 값을 가지고 있다. 오프셋은
0x48 ~ 0x49이며, 크기는 2바이트 이다. 위 이미지에서는 "0x0006" 이라는 값을 가지고 있다.
- MinorSubsystem : 실행 파일을 실행하는데 필요한 서브시스템의 상위버전의 값을 가지고 있다. 오프셋은
0x50 ~ 0x51이며, 크기는 2바이트 이다. 위 이미지에서는 "0x0001" 이라는 값을 가지고 있다.
- Win32VersionValue : 이 멤버는 VC++6.0 SDK까지는 예약 영역이었으며 7.0부터 지금의 이름으로 바뀌었다. 하지만
여전히 사용하지 않아 보통 0으로 되어있다. 오프셋은 0x52 ~ 0x55이며, 크기는 4바이트 이다.
위 이미지에서는 0의 값을 가지고 있다.
- SizeOfImage : 메모리에 로드되었을 때 실행 파일의 크기 값을 가지고 있다. 이 값은 헤더를 포함했을 때의 크기이며,
반드시 SectionAlignment의 배수여야 한다. 이 값을 기준으로 해서 가상 메모리에서 실행 파일을
맵핑할 공간을 예약한다.
오프셋은 0x56 ~ 0x59이며, 크기는 4바이트 이다. 위 이미지에서는 "0x00030000" 이라는 값을
가지고 있다.
- SizeOfHeader : DOS Stub, PE 헤더, 섹션 헤더의 합 값을 가지고 있다. 이 값은 무조건 FileAlignment의 배수여야
한다. 오프셋은 0x60 ~ 0x63이며, 크기는 4바이트 이다. 위 이미지에서는 "0x00000400" 값을
가지고 있다.
- Checksum : 실행 파일의 체크섬 값을 가지고 있다. 체크섬 알고리즘은 IMAGEHELP.DLL 파일에 있으며 이 값은 모든
드라이버와 부팅시 로드된 DLL 파일, 중요한 윈도우 프로세스가 로드한 DLL 파일을 검증 할 때 사용된다.
오프셋은 0x64 ~ 0x67이며, 크기는 4바이트 이다. 위 이미지에서는 "0x00039741" 이라는 값을 가지고
있다.
- Subsystem : 실행 파일을 실행하는데 필요한 서브 시스템의 값을 가진다. 오프셋은 0x68 ~ 0x69이며, 크기는
2바이트 이다. 위 이미지에서는 "0x0002" 라는 값을 가지고 있다. 목록표에 따르면 Windows GUI
서브시스템을 뜻한다. 아래는 Subsystem 값 목록표이다.
- DllCharacteristics : PE 파일이 DLL 파일인 경우 파일의 속성 정보를 의미한다. 오프셋은 0x70 ~ 0x71이며, 크기는
2바이트 이다. 위 이미지에서는 "0x8140" 이라는 값을 가지고 있다.
- SizeOfStackReserve : 예약할 스택의 크기 값을 가지고 있다. 오프셋은 0x72 ~ 0x75이며, 크기는 4바이트 이다.
위 이미지에서는 "0x00040000" 이라는 값을 가지고 있다.
- SizeOfStackCommit : 커밋할 스택의 크기 값을 가지고 있다. 오프셋은 0x76 ~ 0x79이며, 크기는 4바이트 이다.
위 이미지에서는 "0x00011000"이라는 값을 가지고 있다.
- SizeOfHeapReserve : 예약할 힙의 크기 값을 가지고 있다. 오프셋은 0x80 ~ 0x83이며, 크기는 4바이트 이다.
위 이미지에서는 "0x00100000" 이라는 값을 가지고 있다.
- SizeOfHeapCommit : 커밋할 힙의 크기 값을 가지고 있다. 오프셋은 0x84 ~ 0x87이며, 크기는 4바이트 이다.
위 이미지에서는 "0x00001000" 이라는 값을 가지고 있다.
- LoaderFlags : 예약된 영역으로 0으로 채워져 있다. 오프셋은 0x88 ~ 0x91이며, 크기는 4바이트 이다.
위 이미지에서는 0 값을 가지고 있다.
- NumberOfRvaAndSize : Optional Header의 나머지 부분에 있는 데이터 디렉터리 엔트리의 숫자 값을 가지고 있다.
오프셋은 0x92 ~ 0x95이며, 크기는 4바이트 이다. 위 이미지에서는 "0x00000010" 값을
가지고 있다.
글이 많이 길어진 관계로 나머지 한 부분인 IMAGE_DATA_DIRECTORY 구조체는 다음 글에서 다루도록 하겠다.