System

윈도우 PE(Portable Execute)

Ohs_ 2021. 1. 9. 22:58

PE(Portable Execute)

PE는 윈도우에서 사용되는 파일들의 포맷이다. 실행파일, 폰트파일, 드라이브파일, DLL 파일 등이며 PE 포맷을 사용한 이유는 윈도우 계열에서 버전이 달라져도 실행할 수 있게 호환성을 높이기 위해서이다. 그래서 PE 포맷을 뷰어로 확인할 경우 DOS 헤더를 확인할 수 있으며 MS-DOS에서도 해당 파일을 실행할 수 있게 되어있다. 물론 그래픽이나 API요소를 전부 표현하지 못하고 DOS 운영체제에서 구동될 수 있는 최대한으로 표현한다. 즉, 실행은 된다. 다만 모든 기능을 최신 운영체제에서 사용하듯이 쓰기는 어렵다. 반대로 DOS 운영체제에서 사용하던 프로그램을 윈도우10에서도 실행이 가능하다.

PE 포맷의 파일 종류에는 실행, 드라이버, 라이브러리, 오브젝트 총 4개의 계열이 있다. 각각의 대표적인 확장자명으로는 [실행 = EXE, 드라이버 = SYS, 라이브러리 = DLL, 오브젝트 = OBJ]가 있다. 이외에도 여러 확장자를 포함한다.

PE 포맷을 통해 해당 파일의 자세한 정보를 알 수 있다. 어떤 API를 사용하며 어느 메모리에 적재되는지 등을 알 수 있다. 아래 링크는 위키에서 보여주는 PE 포맷의 구조이다. 복잡한 형태로 나와있지만 PE View로 볼 경우 트리형태로 보기쉽게 나온다. PE Viewer는 WJR Software - PEview (PE/COFF file viewer),... (wjradburn.com) 링크에서 설치할 수 있다.

upload.wikimedia.org/wikipedia/commons/7/70/Portable_Executable_32_bit_Structure_in_SVG.svg

 

PE View로 실행파일 분석

위의 그림에서 pFile은 파일 내부에서의 오프셋 값, Raw data는 실제 파일에 들어있는 바이너리 데이터, Value는 바이트 값을 ASCII코드 값으로 바꿔서 보여주는 영역이다.

PE 포맷의 주소 지정방법은 세 종류로 구분된다.

  • pFile : PE 파일 내부에서의 오프셋 값으로 정적 분석일 때 즉, 실행 전 하드디스크에 있을 때의 주소이다.
  • RVA(Relative Virtual Address) : PE 파일이 메모리로 로드 됐을 때 저장되는 상대 위치이다. 메모리에 올라가게 되면 어느 위치에 올라갔느냐에 따라 주소가 달라지게 된다. (상대 주소)
  • VA(Virtual Address) : 가상 메모리 상에서 저장되는 실제 주소이다. (실제 실행했을 때의 주소) 해당 주소는 RVA + Image Base를 하면 RVA에서도 주소값을 구할 수 있다. Image Base는 IMAGE_NT_HEADERS > IMAGE_OPTIONAL_HEADER에서 확인할 수 있다. (절대 주소)

Image Base 값


PE View에서는 해당 주소로 보기좋게 변경해주는 기능이 있다. 상단의 파란색 화살표 아이콘으로 변경이 가능하다.

주소 형태 변경 기능

이처럼 주소의 형태가 여러개인 이유는 프로그램이 메모리에 적재될 때 주소가 지정되어 있다면 해당 메모리 주소에 다른 프로그램이 사용중이면 새로 실행하려는 프로그램은 동작하지 못할 것이다. 때문에 이미 메모리를 사용중이라면 다른 위치에 프로그램을 적재시켜서 사용할 수 있게 한다. 즉, 상대 주소인 RVA는 그대로 유지한 상태로 해당 프로그램을 메모리의 어느 주소부터 RVA 주소대로 로드할 것인지를 Image Base가 결정하는 것이다. 그래서 RVA + Image Base를 하면 실제 메모리에 적재된 절대 주소를 알 수 있다.

 

PE 포맷 Header 정보

IMAGE_DOS_HEADER

윈도우 운영체제의 하위 호환성을 지원하는 역할을 한다. 윈도우10에서의 PE 파일도 MS-DOS에서 실행할 수 있는 것이다. PE 포맷의 파일 실행 시 IMAGE_DOS_HEADER 헤더의 처음에 있는 5A4D를 발견하고 MS-DOS Stub Programe 영역에 있는 메시지를 출력한다. 해당 메시지 출력 후 new offset으로 이동하는데 해당 오프셋 주소는 IMAGE_NT_HEADERS의 주소이다. 즉, 윈도우의 실질적인 헤더는 IMAGE_NT_HEADERS이다.

MS-ODS Stub Program 주소값 [50]
IMAGE_NT_HEADERS 주소값 [60]

* IMAGE_NT_HEAEDERS의 Signature를 보면 Data값에 4550이 저장되어 있는데 ASCII로 변환하면 PE라는 뜻이다. 즉, 해당 파일은 PE 포맷의 파일임을 의미한다. PE 파일의 핵심적인 기능은 IMAGE_FILE_HEADER와 IMAGE_OPTIONAL_HEADER에서 담당한다.


IMAGE_NT_HEADER > IMAGE_FILE_HEADER

Machine CPU의 종류(Intel, AMD 등) i386은 인텔 CPU를 의미한다.
Number of Sections 섹션의 개수로 0보다 커야하며, 실제 섹션과 정의된 섹션의 수가 다르면 오류가 발생한다.
Time Date Stamp 파일 생성 시간으로 빌드한 시간이 나온다. 1970년 1월 1일 0시를 기준으로 경과된 시간이 16진수로 표시되어 있다. 해당 시간이 파일 생성된 시간(컴퓨터 시간)으로 초단위로 나와 있다. 해당 값은 시스템 시간을 변경하여 조작할 수 있으므로 참고 정도로만 확인하는게 좋다.
Size of Optional Header Optional header의 크기를 저장한다. 32bit PE 파일의 경우 00E0, 64bit PE 파일의 경우 00F0이 저장된다.

자세한 내용은 MSDN에서 확인할 수 있다. => docs.microsoft.com/en-us/windows/win32/debug/pe-format

 

PE Format - Win32 apps

This specification describes the structure of executable (image) files and object files under the Windows family of operating systems. These files are referred to as Portable Executable (PE) and Common Object File Format (COFF) files, respectively.

docs.microsoft.com


IMAGE_NT_HEADER > IMAGE_OPTIONAL_HEADER

Size of code 코드 영역의 크기기를 나타낸다. 계산법이 살짝 복잡하다. 해당 값에는 16진수의 값이 들어가있는데 해당 16진수 값을 10진수로 변환하고 해당 값에 곱하기 8을 해준다. 계산해준 값을 다시 16진수로 변환하여 코드영역의 시작부분에 더하면 코드 영역의 끝부분을 알 수있다. 즉 해당 값은 코드 영역의 바이트 수를 16진수로 표현한 것이다.
Address of entry point 엔트리 포인트이다. 프로그램 실행에 관한 제어권이 커널 영역에서 코드 영역으로 처음 너어오는 주소이다. 엔트리 포인트에 저장된 값은 RVA 값이다.
Base of code 코드 영역이 시작되는 주소(RVA)를 가리킨다. 이 값에 Size of code를 계산한 값을 더하면 해당 코드 영역의 끝 주소를 알 수 있다.
Base of data 데이터 영역이 시작되는 주소(RVA)를 가리킨다. 디버거로 프로그램을 열었을 때 메모리 영역의 맨 윗부분의 주소이다.
Image base 메모리에서 PE 파일이 저장되는 시작 주소이다. 즉 절대 주소 값을 완성시켜주는 값이다. 엔트리 포인트, 코드영역, 데이터 영역의 시작 주소는 모두 RVA이며, Image base와 합산하여 실제 주소를 산정한다. 윈도우 7 이상부터는 ASLR(Address Space Layout Randomiztion) 기능이 추가되어 있어서 디버거에서 로딩할 때마다 Image base가 랜덤하게 설정된다. 하지만 RV 주소는 그대로이기 때문에 메모리의 어디에 적재되어도 Image base만 알면 해당 프로그램을 읽을 수 있다.
종종 Image base가 중복될 때가 있는데 이때는 Base relocation table라는 값을 참조한다. 아래에서 추가로 설명한다.
Section alignment 메모리에서 섹션 영역에 할당하는 최소 크기이다. 메모리 상에서 섹션 영역의 크기는 본 값의 배수로 설정된다. 섹션은 실제 영역을 블록화 해주는 블록의 단위이다. 섹션 크기만큼 데이터가 들어가며 섹션 크기가 너무 크면 낭비되는 자원이 발생하고 너무 작으면 읽어야하는 섹션 단위가 많아져서 처리속도가 느려질 수 있다. 때문에 적절한 설정값으로 이용해야 한다. Section alignment는 운영체제에서 메모리 관리의 효율성을 확보하기 위해 본 값을 지정한다.
File alignment 하드디스크에서 섹션 영역에 할당하는 최소 크기이다. 용도는 section alignment와 동일하다.
Number of data directories 뒤에 이어지는 IMAGE_DATA_DIRECTORY 구조체 개수를 지정한다. 16진수의 값으로 들어간 Data를 10진수로 변환하면 구조체의 개수를 알 수 있다. 단, 마지막 구조체에는 종료를 의미하는 NULL이 채워지므로 실제 사용되는 구조체는 Number of data directories - 1이다.
IMAGE_DATA_DIRECTORIES 8 바이트로 구성된 구조체 배열이다. 데이터가 저장되는 테이블의 시작 주소와 크기가 저장된다. 파일이 사용하는 라이브러리 목록과 파일 외부에 제공하는 함수 목록 등 동작에 필요한 정보를 알 수 있다.

IMAGE_OPTIONAL_HEADER의 주요 정보


IMAGE_NT_HEADER > IMAGE_OPTIONAL_HEADER > IMAGE_DATA_DIRECTORIES

Export table 파일에서 외부로 공개하는 함수 정보를 저장하는 곳이다. 본 항목에 데이터가 있으며 PE 파일이 외부에서 사용할 수 있는 함수를 제공한다는 의미이다.
Import table PE 파일이 다른 라이브러리로부터 가져와 사용하는 함수 정보를 저장한다. Import table에 저장된 RVA가 가리키는 주소로 가면 관련 내용이 구조체 형태로 저장되어 있다. Import table은 악성코드 분석에서도 중요하게 사용된다. 파일이 특정 외부 라이브러리들의 조합으로 사용할 경우 악성행위를 위한 파일이라고 판단하는 등의 패턴 분석의 용도로도 사용할 수 있다.
Resource table PE 파일에서 사용하는 리소스가 저장된 영역을 가리킨다. 문자열, 대화상자 레이아웃, 아이콘, 이미지, 메뉴 등 사용자 인터페이스와 정적인 부분을 정의하는데 사용된다.
Base relocation table 만약 PE 파일의 Image base 값을 사용하지 못하는 경우 PE 파일 내부의 절대 주소를 변경하는데 사용되는 구조체이다. 운영체제는 다양한 프로그램을 동시에 메모리에서 사용하는데 종종 Image base가 이미 사용중인 경우가 있다. 이때 Base relocation table 값을 이용한다.
TLS table 엔트리 포인트 이전에 실행되는 함수를 지정할 때 사용한다.
Import address table 라이브러리에서 사용하는 함수명, 함수 시작 주소 저장한다.

IMAGE_DATA_DIRECTORIES의 Import table 정보 확인


SECTION_HEADER

Virtual Size 메모리에서 섹션이 차지하는 크기를 나타낸다.
Virtual Address 메모리에서 섹션의 시작 주소를 나타낸다.
Size of Raw Data 파일에서 섹션이 차지하는 크기이다.
Pointer to Raw Data 파일에서 섹션의 시작 위치이다.
Characteristics 섹션의 속성을 나타낸다.

* 섹션헤더의 내용은 거의 다 비슷하다.


SECTION_DATA

CODE 프로그램 실행 코드를 담고 있다.
DATA 전역 변수와 Static 변수의 할당을 위해 존재하는 영역이다. 일기와 쓰기가 가능하다.
rdata 문자열과 상수 등을 담고 있는 영역으로 읽기 전용이다.
bss 초기화되지 않은 전역 변수를 담고 있는 영역이다. 보통 data 섹션에 병합되기 때문에 메모리 상에서 따로 존재하지는 않는다.
rsrc 아이콘, 커서 등 리소스 관련 데이터를 담고 있는 영역이다.
idata Import 할 DLL과 관련 API에 관한 정보를 담고 있는 영역이다.
edata Export 할 API에 대한 정보를 담고 있는 영역이다.

* PE 파일에서 참조한 외부 라이브러리는 구글링 할 경우 어떤 기능을 하는지 어떤 인자를 넘겨받는지 알 수 있다.

 

PEview를 통해 파일의 PE 포맷을 볼 수도 있지만 악성코드 분석을 위한 데이터 수집과 여러 목적으로 특정 부분을 추출해야할 필요가 있는데 이때, Python pefile 모듈을 이용하면 PE 포멧 파일에 대한 정보를 추출할 수 있다. Import table 등을 추출하여 벡터화 하여 AI 모델의 학습 데이터로 사용할 수도 있으니 참고.

pefile 모듈을 이용하여 데이터 추출

 

반응형