1. PE
- PE파일은 윈도우 운영체제에서 사용되는 실행 파일 포맷이다. 윈도우 시스템에서 동작되는 대부분의 실행 파일은 PE파일.
- 윈도우 운영체제에서 사용되는 실행 파일(exe), DLL파일, object파일, 폰트 파일, sys파일 그리고 드라이버 파일 등을 위한 파일 형식이다. (컴파일의 결과물인 OBJ 파일을 제외한 모든 것은 실행 가능한 파일.)
종류 | 주요 확장자 |
실행 계열 | EXE, SCR |
드라이버 계열 | SYS, VXD |
라이브러리 계열 | DLL, OCX, CPL, DRV |
오브젝트 파일 계열 | OBJ |
PE구조는 DOS헤더, PE 헤더, 각 Section 헤더, 각 Section 테이블로 구성되어 있다.
- PE포맷은 정해진 포맷을 가짐. 이 정보들을 추출하여 언제 어떤 머신에서 어떤 컴파일러로 제작 되었는지를 확인할 수 있음.
- Section테이블: 데이터 존재. 들어있는 데이터에 대한 메타데이터가 각 Section헤더에 저장.
- Section 헤더: 파일이 실행되기 위해 필요한 모든 정보 존재.
[그림 1] 설명
CatchMind.exe 파일을 PEView로 열면 PE Header부분을 확인할 수 있다. 해당 툴을 통해 PE파일의 구조를 분석할 수 있다. PE 파일의 Header내용을 확인할 수 있음.
[그림 2] 설명
# PE Header = DOS header ~ Section header
# PE Body = 나머지 Section들 모두
파일에서는 offset으로, 메모리에서는 VA(virtual Address, 절대주소)로 위치를 표현하며 파일이 메모리에 로딩되면 모양이 달라진다. 파일의 내용은 보통 코드(.text), 데이터(.data), 리소스(.rsrc) 섹션에 나뉘어 저장됨.
→ 각 용도별로 여러 섹션이 나뉘어 저장
PE Header와 각 Section의 끝에 존재하는 NULL은 padding이다. 이는 최소기본단위를 사용하기 위해서 존재한다.
2. 각 헤더와 섹션의 역할
[그림 1]의 좌측을 보면 CatchMind.exe파일의 PE 헤더와 PE 바디를 모두 확인할 수 있다. PE 헤더는 많은 구조체로 구성되어 있는데, 아래에 각 구조체와 구조체 멤버에 대해 간단히 설명할 것이다.
PE 헤더:: DOS 헤더
1) DOS_HEADER
PE파일이 제작되던 당시 DOS 파일이 널리 사용되고 있었기에, DOS 파일에 대한 하위 호환성을 제공하기 위해 만든 헤더이다. [그림 2]에 따라 PE Header의 제일 앞부분에 DOS Header가 존재한다. 기존의 DOS EXE Header를 확장한 IMAGE_NT_HEADERS 구조체를 확인할 수 있다. IMAGE_NT_HEADERS 구조체의 크기는 40이며, 주요 멤버 두 가지는 아래와 같다.
WORD e_magic | DOS signature ( [그림 1]의 첫 2byte 부분. 4D5A를 의미 ) |
LONG e_lfanew | NT header의 offset을 표시 (해당 위치에 NT header 구조체가 존재해야 함) |
2) DOS Stub
DOS Header 아래에 존재. DOS Stub의 존재는 선택 사항이다. CatchMind.exe 파일을 DOS 환경에서 실행하거나, DOS전용 디버거를 이용해서 실행하면 이 부분의 코드를 실행시킬 수 있다.
3)NT Header
이 구조체의 멤버는 3가지로 구성되어 있다.
DOS Header의 마지막 멤버(e_lfanew)가 가리키는 위치에서부터 시작한다.
DWORD Signature // Signature는 PE..의 값을 가진다. 50450000(PE00) IMAGE_FILE_HEADER FileHeader //구조체 멤버 IMAGE_NT_HEADER32 OptionalHeader //구조체 멤버 |
IMAGE_FILE_HEADER 구조체는 파일의 개략적인 속성을 나타낸다.
해당 구조체에서 Machine, NumberOfSections, SizeOfOptionalHeader, Characteristics 값이 정확하게 셋팅되어 있지 않으면 파일을 정상적으로 실행되지 않는다. 각 멤버에 대해 간단히 서술하면 다음과 같다.
Machine | CPU별 고유한 값 |
NumberOfSections | 코드, 데이터, 리소스 등 섹션의 개수. 반드시 0보다 커야한다. 정의된 섹션 수와 실제 섹션의 수가 다르다면 에러가 발생한다. |
SizeOfOptionalHeader | 이 멤버의 값은 IMAGE_NT_HEADER 구조체의 마지막 멤버인 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 나타낸다. 윈도우의 PE Loader는 IMAGE_FILE_HEADER의 SizeOfOptionalHeader 값을 보고 IMAGE_OPTIONAL_HEADER32 구조체의 크기를 인식한다. 이 멤버에 구조체 크기를 명시하는 이유는, PE32+ 형태의 파일인 경우 IMAGE_OPTIONAL_HEADER32 구조체 대신에IMAGE_OPTIONAL_HEADER64 구조체를 사용하는데 이 두 구조체의 크기가 다르기 때문이다. |
Characteristics | 파일의 속성을 나타내는 값. 실행 가능한 형태(executable or not) 혹은 DLL파일인지 등의 정보가 존재함. |
IMAGE_OPTIONAL_HEADER32구조체는 PE 헤더 구조체 중 가장 크기가 크다. 주요 멤버는 9가지로, 이 값들이 잘못 셋팅되어 있다면 파일이 정상적으로 실행되지 않는다.
IMAGE_OPTIONAL_HEADER32 구조체 주요 멤버 |
|
Magoic | 32bit 환경의 경우 10ㅠ, 64bit는 20B를 가진다. |
AddressOfEntryPoint | EP(Entry Point)의 프로세스 시작 위치 주소값(RVA)을 가짐 |
ImageBase | PE 파일이 로딩되는 시작 주소. 메모리에 파일 load 후 EIP = ImageBase+AddressOfEntryPoint |
SectionAlignment | 메모리에서의 섹션 최소 단위 |
FileAlignment | 파일에서의 섹션 최소단위 |
SizeOfImage | PE파일이 메모리에 로딩되었을 때 가상메모리에서 Pe Image가 차지하는 크기 |
SizeOfHeader | PE헤더의 전체 크기 |
Subsystem | .sys인지 일반 실행 파일(.exe, .dll)인지 구분 |
NumberOfRvaAndSizes | DataDiretory 배열의 개수. PE Loader는 해당 값을 보고 배열 크기 인식 |
DataDirectory | IMAGE_DATA_DIRECTORY 구조체의 배열. |
PE헤더: Section Header
섹션 헤더는 각 섹션의 속성을 정의한 것이다. 섹션 별로 IMAGE_SECTION_HEADER 구조체가 존재한다.
[표 3] IMAGE_SECTION_HEADER 구조체 주요 멤버 |
|
VirttualSize | 메모리에서 섹션이 차지하는 크기 |
VirtualAdress | 메모리에서 섹션의 시작 주소(RVA) |
SizeOfRawData | 파일에서 섹션이 차지하는 크기 |
PointerToRawData | 파일에서 섹션의 시작 위치 |
Characteristics | 섹션의 속성 |
# 이미지(image)
PE 파일이 메모리에 로딩될 때 파일이 그대로 올라가는 것이 아닌, Section Header에 정의된 대로 맞추어서 올라감.
- 파일에서의 PE와 메모리에서의 PE는 서로 다른 모양
- 이를 구별하기 위해 메모리에 로딩된 상태를 이미지(image)라고 함.
Section의 역할
섹션은 PE파일이 가상 주소 공간에 로드된 이후.. Code, DATA, resource 등 프로그램 실행에 필요한 정보가 위치하는 영역을 의미한다.
1) Code(“.text”): 프로그램의 실행 코드
2) DATA(".data")
- data: 전역변수, 정적변수 위치. 읽기 쓰기가 가능한 데이터 섹션.
- rdata: 상수형 변수, 문자열 상수 등이 위치. 읽기만 가능한 데이터 섹션
- bss: 초기화되지 않은 전역변수가 위치함.
3) RESOURCE(".rsrc"): icon, cursor 등의 Windows Application Resource 관련 데이터 위치.
PE파일 분석
PE파일을 분석할 때 내가 찾는 데이터 위치를 알기 위해서는 헤더 정보의 주소를 확인하면 된다.
주소의 종류는 크게 세 가지로 나뉜다.
pFile | PE파일 내부에서의 오프셋 (파일) PE파일이 물리적으로 하드디스크에 저장되었을 떄 의미있는 값. |
RVA | 메모리에 로드됐을 때, 기준값에서 얼마나 떨어져 있는지를 나타내는 상대 위치 (메모리) PE파일이 메모리에 로드됐을 때 의미있는 값. |
VA | 가상 메모리상에 저장되는 실제 주소 (메모리) PE파일이 메모리에 로드됐을 때 의미있는 값. |
IAT(Impoart Address Table)
- PE 파일 안에 어떤 라이브러리의 어떤 함수를 가져다 쓰는가를 기록..
- 로더는 PE파일을 메모리로 로딩 시 IAT에 기록된 API이름을 참조해서 실제 주소를 찾아 API를 가리키는 주소를 적어둔다.
'System > windows' 카테고리의 다른 글
APC (Asynchronous procedure call) (0) | 2023.03.30 |
---|---|
Obfuscating Function Call (0) | 2023.03.29 |
나의 Public IP 찾기 TIP (curl) (1) | 2022.10.05 |
windows 관리자권한 가진 계정 생성 (0) | 2022.07.19 |
cmd에서 권한 변경하는 명령어 runas (0) | 2022.07.19 |