취약점분석/Reversing

[Reversing] PE 헤더란? (PE Header)

poiri3r 2025. 6. 10. 22:50

*해당 포스팅은 Dreamhack의 CaptainHook문제의 PE구조를 분석하며 작성되었습니다. 포스팅 내용을 직접 확인하고 싶으신 분들은 해당 문제와 PE bear, IDA로 함께 확인하시면 될 것 같습니다!

 

PE는 Portable Executable File Format의 약자로 우리가 만든 파일이 실행할 수 있는 다른 곳에 옮겨져도 실행이 가능하도록 만든 포멧이다.

프로그래밍 코드의 빌드 과정은 다음과 같다

1. test.cpp안에 코드를 작성하고 빌드하면

2. 컴파일러는 test.cpp와 관계된 모든 헤더 파일과 소스 파일을 합쳐서 기계어 코드로 만들어낸다(obj 파일)

3. 운영체제에서 파일을 실행하기 위해 링커가 동적 라이브러리, 리소스 데이터, import, export table을 처리할 수 있는 정보를 적어두는데, 이때 윈도우는 약속된 규약에 맞춰 정보를 입력하며, 이것이 PE 포멧으로 생성된다.

따라서 PE헤더 안에는 실행 파일을 실행하기 위한 각종 정보가 기록되어 있다!

개발자가 만든 코드가 실행되기 전에 PE의 정보부터 읽어와서 바이너리를 메모리에 올리기 위한 각종 데이터를 설정하는 작업을 한다.

 

리버싱/포너블 관점에서의 PE

  • PE는 실행 파일의 구조(헤더, 섹션)을 명확하게 정의하고 있기 때문에 코드 섹션과 데이터 섹션을 파악할 수 있게 해준다.
  • PE를 확인하면 패킹/난독화 여부를 확인할 수 있다.
  • EP(Entry Point),IAT,함수 주소를 확인 가능하다.
  • 취약점이 존재하는 코드 영역이나 쉘코드를 삽입하거나 실행 가능한 위치를 찾을 수 있다.

포렌식 관점에서의 PE

  • 악성 코드의 PE헤더의 분석을 통해 생성 시점, 컴파일러 정보 등을 확인하고 이상 여부를 확인 가능하다.
  • 구조체 중 TimeDateStamp의 인자값을 읽으면 파일이 언제 만들어졌는지 파악할 수 있다.
  • PE에 포함된 디지털 사인을 통해 파일의 무결성과 신뢰성을 검증 가능하다

PE파일의 구조체는 다음과 같다.

IMAGE_DOS_HEADER 구조체이다

e_magic : 현재 파일이 PE파일인지 체크하는 용도로 4D 5A(MZ)의 값을 가진다. MZ signiture라고도 한다.

e_lfanew : 다음에 나오는 IMAGE_NT_HEADER의 구조체 위치를 알려준다(실질적인 PE의 오프셋)

=>해당 파일이 정상인지 확인할 때 사용 : 패킹, 변조, 이상 여부를 탐지할 수 있다.

 

CaptainHook.exe의 IMAGE_DOS_HEADER

IMAGE_NT_HEADER 구조체이다.

한개의 DWORD(4바이트)와 두개의 구조체로 나누어진다.

CaptainHook.exe의 NT_HEADER

signiture: 50 40으로 고정되어 있다(MZ signiture와 비슷)

NT_HEADER의 시작 위치는 방금 전 e_lfanew값인 00 00 00 F8 부터 시작함을 알 수 있다.

 

NT_HEADER안의 IMAGE_FILE_HEADER

Machine : 어떤 CPU에서 실행되는 파일인지 알려준다.

NumberOfSections : 섹션의 개수를 알려준다. 패킹이나 프로텍팅 등의 이유로 섹션 수가 증가하면 값도 같이 변하는데, 유의해서 봐야된다!

TimeDateStamp : 파일이 빌드도니 시간을 알려준다.

SizeOfOptionalHeader : NT_HEADER의 OptionalHeader의 크기를 담고 있다. optional 헤더는 PE로딩을 위한 중요한 구조체를 담고 있으므로 중요하다.

 

CaptainHook.exe의 IMAGE_FILE_HEADER

1. 64 86: x86-64bit에서 실행되는 파일이다.

2. 06 00: 섹션의 개수가 6개이다

3. 0C 3A B4 5E: UNIX 타임스탬프 값

4. F0 00: optional header의 크기는 240이다

 

NT_HEADER안의  IMAGE_OPTIONAL_HEADER

 

CaptainHook.exe의 IMAGE_OPTIONAL_HEADER

 

Magic(0B 02) : Signiture와 비슷함, 32비트의 경우 0x10B, 64비트의 경우 0x20B를 가진다.

SizeOfCode(00 DA 01 00) : 코드 양의 전체 크기, 바이러스나 악성코드도 이 필드를 읽고, 보안 솔루션도 사용한다.

★ImageBase(00 00 00 00 00 40 01) : 파일이 실행될 때 가상메모리에 올라가는 번지(일반적으로 64비트는 140000000, 32비트는 400000을 사용한다)

★AddressOfEntryPoint(9C D8 01 00) : 실제 파일이 메모리에 시작되는 지점(EP, Entry Point)

BaseOfCode : 실제 코드가 실행되는 번지. 보통 0x1000으로 지정되어 있다

CaptainHook의 .text가상 메모리 주소

Imagebase인 140000000 + BaseOfCode인 1000이 더해진 주소에서 시작한다.

SectionAlignment(00 10 00 00) : 메모리에 올라갔을때의 간격이다.

FileAlignment(00 02 00 00) : 파일상의 간격이다.

SizeOfImage(00 F0 03 00) : 이 파일이 메모리에 로딩됐을때의 전체 크기이다.

SizeOfHeaders(00 04 00 00) : PE 헤더의 크기이다.

Subsystem : 이 프로그램이 GUI인지 콘솔인지 알려주는 역할이다. (0x1 : sys와 같은 드라이버 모듈, 0x2 : 윈도우를 가지고 있는 모듈, 0x3 콘솔 프로그램)

NT_HEADER안의  IMAGE_OPTIONAL_HEADER안의 DataDirectory

DataDirectory는 중요한 구조체들의 위치(상대주소)와 크기를 담고 있는 표 형식의 목록이다.

(왼쪽)DataDirectory의 구조체와 (오른쪽)CaptainHook의 DataDirectory

 

 

(왼쪽)Data Directory의 IAT주소 1F000과 (오른쪽)CaptainHook의 IAT주소

실제 IAT 주소 (0x14001F000) - 베이스 주소(0x140000000)을 하면 DataDirectory의 값이 나온다(상대주소 계산법)

 

DataDirectory가 왜 중요할까?

  • 어떤 DLL을 로드하고 API를 사용하는지 알 수 있다.
  • 파일이 자체 제작인지, 툴로 만든건지 확인 가능하다
  • 파일의 수정여부를 확인할 수 있다.
  • Export table, Resource등을 이용해 파일의 유형, 기능 분석이 가능하다.

ex)Import table을 날리고 악성 API를 삽입 후 호출하는 악성 코드가 있을 때 Data Directory로 흔적을 추적 가능하다

 

IMAGE_SECTION_HEADER

각 섹션에 대한 정보를 저장한다.

NUMBER_OF_SECTION의 값에 따라 배열의 개수가 다르다.

섹션 해더를 보면 코드/데이터의 구분이 가능하고, 코드 섹션에서 API 호출 위치를 추적할 수 있다.

실행권한이 있는 섹션에서만 쉘코드가 실행 가능하므로 리버싱/포렌식을 할 때 중요하다.

일반적인 파일의 섹션과 UPX 패킹된 파일의 섹션

IMAGE_IMPORT_DESCRIPTOR

exe나 dll의 PE를 볼 때 import된 DLL의 개수만큼 구조체가 가동된다.

OriginalFirstThunk : 함수들의 이름 목록을 저장한다.

FirstThunk : IAT의 시작주소를 담고 있고, IAT는 해당 DLL에서 가져온 함수들의 실제 주소를 저장한다.

 

이상으로 PE 구조에 대한 포스팅을 마치겠습니다.

제가 교내에서 하는 스터디에서 발표했던 PPT를 기반으로 작성하였습니다. 참고하실분들은 참고 부탁드립니다.

PE 헤더(PE HEADER).pptx
1.76MB