참고 사이트 :
- [누구나가 다 이해할 수 있는 프로그래밍 첫걸음] 리버스 엔지니어링 스터디 1편. PE(Portable Executable) 구조
- [A Kind of Magic] 링크(Linkers) 와 로더(Loaders)
PE 포맷
PE 포맷 (Portable Executable)은 윈도우 운영 체제에서 사용되는 실행 파일, DLL, object 코드, FON 폰트 파일 등을 위한 파일 형식이다. "Portable"의 단어 뜻 그대로 의식성이 있으며 플랫폼에 독립적이다. PE 포맷은 윈도우 로더가 실행 가능한 코드를 관리하는 데 필요한 정보를 캡슐화한 데이터 구조체이다. 이것은 링킹을 위한 동적 라이브러리 참조, API 익스포트와 임포트 테이블, 자원 관리 데이터 그리고 TLS 데이터를 포함한다.
윈도우 NT 운영체제에서 PE는 현재 IA-32, IA-64, x86-64 (AMD64/Intel64), 그리고 ARM instruction set architectures (ISAs)를 지원한다. PE와 비슷한 형식으로 ELF (리눅스와 대부분의 다른 유닉스 버전들)와 Mach-O (Mac OS X)가 있다.
PE 파일의 종류
종류 |
주요 확장자 |
종류 |
주요 확장자 |
실행 계열 |
EXE, SCR |
드라이버 계열 |
SYS, VXD |
라이브러리 계열 |
DLL, OCX, CPL, DRV |
오브젝트 파일 계열 |
OBJ |
엄밀히 말하면 OBJ(오브젝트) 파일을 제외한 모든 것은 실행 가능한 파일이다.
아래는 노트패드 (notepad.exe 10.0버전) 파일을 헥스 에디터 HxD를 이용하여 열어본 것이다.
notepad.exe 파일의 시작 부분이며, PE 파일의 헤더 (PE header) 부분이다. 실제로 'PE'라는 글자가 적혀있음을 확인할 수 있다. 여기에 파일이 실행되기 위한 모든 정보가 적혀있다. 즉, PE 파일 포맷을 공부한다는 것은 PE 헤더 구조체를 공부한다는 것과 같은 말이다.
기본 구조
섹션 헤더에 각 Section에 대한 파일/메모리에서의 크기, 위치, 속성 등이 정의되어 있다. 파일은 메모리에 로딩되면 모양이 달라진다(Section의 크기, 위치 등).
VA와 RVA 전산학에서 가상 주소(Virtual Address)는 가상적인 개체를 인식하는 주소로서, 논리 주소(Logical Address)라고도 하며, 실주소(물리 주소)의 반대 개념이다. 가상메모리에서의 가상 주소는 프로세스의 관점에서 사용하는 주소이며, 운영체제가 제공하는 가상 주소 공간에 대한 주소이다. 즉, VA (Virtual Address)는 프로세스 가상 메모리의 절대 주소를 말한다. RVA (Relative Virtual Address)는 어느 기준 위치 (ImageBase)에서부터의 상대주소를 말한다. RVA + ImageBase = VA
|
Layout
PE 파일은 동적 링커에게 파일을 메모리로 어떻게 매핑할지 설명하는 많은 헤더들과 섹션들로 이루어져 있다. 실행 가능한 이미지는 (각각 다른 메모리 보호를 요구하는) 여러 다른 영역으로 이루어져 있다. 그래서 각 섹션의 시작은 반드시 페이지 경계에 맞춰 정렬되어야 한다. 그러나 공간 낭비를 피하기 위해서, 다른 섹션들은 디스크에서 페이지 정렬되어 있지 않다. 동적 링커의 일 중 하나는 각 섹션을 개별적으로 메모리에 매핑하고, 헤더에 있는 지시에 따라 그곳에 정확한 권한을 할당하는 것이다.
아래는 위키백과의 PE 포맷 형태이다.
Linkers 링킹(Linking)이란 코드와 데이타들을 한데 묶어 메모리에 로드될 수 있는 하나의 실행파일 형태로 만드는 작업을 만든다. 링킹은 compile time, load time (로더에 의해) 혹은 run time (어플리케이션에 의해) 에도 이루어질 수 있다. 과거에는 수작업으로 이루어졌지만 현재는 링커(Linker)가 동적으로 링크되는 공유 라이브러리 (Shared Library)와 같은 복잡한 작업을 수행해 주고 있다. |
Import Address Table
Import Address Table (IAT) 섹션은 응용 프로그램이 다른 모듈에서 함수를 호출할 때 검색 테이블로 사용된다. 이것은 서수에 의한 들여오기나 이름에 의한 들여오기가 될 수 있다. 컴파일 된 프로그램은 의존하는 라이브러리의 메모리 위치를 모르기 때문에 API 호출 시 간접적인 점프가 요구된다. 동적 링커가 모듈을 로드하고 연결시키고, 실제 주소를 IAT에 넣는다. 그 후, 상응하는 라이브러리 함수의 메모리 위치를 가리키게 된다.
비록 이 추가로 인해 모듈 내부의 호출에도 추가적인 점프가 요구되지만, 이것은 중요한 이점을 가진다. 로더에 의해 변경되며, copy-on-write가 요구되는 메모리 페이지들의 수가 최소화돼서 메모리와 디스크의 입출력 시간을 절약할 수 있다. 만약 컴파일러가 호출이 모듈 내부에서 일어나는 것이라고 미리 안다면, 더 최적화된 코드를 생산할 수도 있다.
재배치
PE 파일들은 위치 독립적 코드를 포함하지 않는다. 대신에 선호되는 우선되는 베이스 주소로 컴파일되고, 모든 주소들은 그 전에 고정된다. 만약 PE 파일이 우선되는 주소에 로드될 수 없다면 (다른게 이미 위치하고 있는 경우), 운영 체제는 베이스 주소를 바꾼다. 이것은 모든 절대 주소를 재계산하고 새로운 값을 사용하도록 코드를 바꾸는 것을 포함한다.
로더는 이것을 우선되는 주소와 실제 로드될 주소를 비교하고 델타 값을 계산함으로써 수행한다. 그 후 우선되는 주소에 새로운 주소의 위치에 대한 값이 더해진다. 베이스 재배치들은 메모리 위치에 목록화되고 저장된다. 결과 코드는 이제 프로세스에 사적이 되고 공유될 수 없게 됨으로써 DLL의 수많은 메모리 절약 이점이 사라진다. 또한 모듈을 로딩하는 시간도 매우 느려지게 된다. 이 이유로 이러한 재배치는 가능한 한 피해지며, 마이크로소프트에서 나온 DLL들은 겹쳐질 수 없게 만들어져 있다.
'Study > 리버싱' 카테고리의 다른 글
PE Structure (3) DLL (0) | 2016.06.29 |
---|---|
PE Structure (2) PE 헤더 (1) | 2016.06.29 |
Stack (0) | 2016.06.12 |
Register (1) | 2016.06.08 |
Endianness (0) | 2016.06.08 |
댓글