Be myself :: PE구조

달력

32024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

PE구조

Reversing 2014. 5. 8. 15:50

1 개요

이번엔 PE구조에 대해 살펴보도록 하자. PE구조를 처음 공부할 때는 어디에 쓰이는지 몰라 이걸 왜 공부해야 하는지 잘 모를 것이다. 필자 역시 그러했다. 하지만 공부를 하면 할수록 윈도우 리버싱에서 꼭 알고 가야 할 것이 PE관련 지식들이었다. 간단한 예를 들면 간단한 압축파일의 패치를 할 경우에, 여러가지 방법이 있겠지만, 그 중 쓰지 않는 섹션의 공간에 패치명령어를 입력하는 방법을 쓸 때, PE관련 지식이 없다면 사용 할 수도 없을 것이다. 또한 IAT를 후킹 할 시에도 PE관련 지식이 필수적이다. 또 PE에 관한 지식이 있다면, IDT를 조작하여 DLL인젝션도 가능 하다. 이처럼 여러 가지 기법을 사용함에 있어 PE구조에 관한 지식은 필수라 할 수 있다.

 

2 PE란?

PE란 Portable Executable의 약자로, 윈도우 운영체제에서 사용되는 실행파일 형식이다. 애초에 Portable의 의미대로 여러 운영체제간의 이식성을 좋게 하려는 의도였으나 현실은 윈도우 계열의 운영체제에서만 사용된다. PE를 보고 알 수 있는 점은 참 많다. 프로세스의 각 섹션들이 어떻게 메모리에 적재되는지, 어떤 DLL들이 사용되는지, 필요한 stack과 heap의 크기가 어떤지 등이 모두 적혀져 있다. 참고로 64비트에서의 실행파일은 PE+ 혹은 PE32+라 부른다.

 

3. PE 파일 포맷

위의 그림은 메모장을 Hex에디터로 열어본 것이다. 이 Hex값들을 토대로 PE구조를 살펴 볼 것이다.

3-1 기본 개념

본격적으로 PE구조에 대해 살펴보기 전에, PE구조에 의해 어떻게 파일이 메모리에 적재되는지 살펴 보자.

<출처 reversecore>

그림을 살펴 보면, DOS header에서 Section header까지 PE header라 부르고 그 아랫 부분을 PE body라 부른다. 여기서 주의깊게 봐야 할 점은 header부분은 메모리와 파일의 위치가 같은데, body부분은 위치가 다르다는 것이다. 하지만 그 오프셋(어떤 값의 어떤 위치에서부터의 메모리상에서 위치) 값은 같게된다. VA(Virtual Address)는 프로세스 가상 메모리의 절대주소를 말하며, RVA(Relative Virtual Address)는 오프셋 값을 나타낸다. 따라서 RVA + ImageBase = VA값이 된다. 여기서 또 주목할 점은, 어던 값의 파일에서의 오프셋과 메모리에서의 오프셋은 같으므로 이러한 공식이 성립된다. RAW(파일에서 어떤 메모리의 위치) – PointerToRawData = RVA – VirtualAddress가 성립된다. PointerToRawData란, 데이터에서의 섹션의 시작지점을 나타내고, VirtualAddress란 메모리에서 섹션의 시작주소를 나타낸다. 예를 들어 RVA값이 6000이라면 .text section에 존재하므로 VirtualAddress값은 1000이고, PointerToRawData의 값은 400이 된다. 따라서, RAW(파일에서의 이 값의 위치)는 = 6000 – 1000 + 400 이다. 이 때의 값은 16진수임에 유의해서 계산한다. 이 계산을 자유자재로 할 수 있을 만큼 연습해야 할 것이다.

혹자는 여기까지 읽으면 궁금한 점이 하나 있을 것이다. (필자는 궁금하다.) 과연 PE Format은 누가 어떻게 사용하는 것인가? 이에 대한 답은 윈도우 커널에서 PE loader가 로드 시키는 것이다. 필자의 추측으론, 어떠한 실행파일을 실행 시키면, 시스템 콜을 OS에 전달하고, 이에 대한 처리로써, PE loader를 실행시켜 메모리에 PE구조에 맞게 각 섹션들과 데이터들을 로드시키지 않나 생각한다.

3-2 dos헤더

이제 본격적으로 PE구조에 관한 사항들을 살펴 보자. 먼저 DOS Header이다. 마이크로 소프트는 DOS파일에 대한 호환성을 고려해 PE구조를 디자인하였다. DOS Header의 모습은 어떠한지 살펴 보자.

위의 부분이 DOS Header 부분이다. 이 중 눈 여겨 볼 것은 딱 2가지 부분이다. e_magic 부분과 e_lfanew 부분이다. E_magic은 젤 앞에 4D 5A부분으로 ASCII 값으로 'MZ'이다. 이는 DOS signature 값으로 항상 이 값이 존재 해야 한다. E_lfanew부분은 제일 마지막 E0 00 00 00 부분으로 이 값은 NT Header 구조체를 가리킨다. 그리고 그 다음 ASCII값으로 PE가 나오기 전까지의 부분은 DOS Stub코드 이다. 이 부분은 도스의 호환성을 위해 마련된 자리이므로 없어도 상관없다. 따라서 컴파일러 설정만 조정하면 이 부분을 없앨 수 있다.

3-3 Nt 헤더

NT헤더 구조체가 어떻게 생겼는지 구경해 보자.


<출처:http://blog.naver.com/PostView.nhn?blogId=cor2quard&logNo=150169612062&beginTime=0&jumpingVid=&from=search&redirect=Log&widgetTypeCall=true>

위의 사진에서 보다시피 NT Header는 ASCII값으로 PE라고 적힌 Signature부분과 FileHeader, OptionalHeader부분으로 구성되어 있다. 이제 각각의 경우를 살펴보자. 부분을 살 펴 보자.

3-3-1 Signature

Signature부분은 ASCII코드 값으로 PE를 나타내며, NT Header의 시작을 표현한다.

3-3-2 file header

파일의 개략적인 속성을 나타내는 구조체이다.

<출처 MSDN>

여기서 우리가 주목해야 할 점은 Machine부분과 NumberOfSections, SizeofOptionalHeader, Characteristics 부분이다.

<Machine>
CPU별로 고유한 값을 가진다. Intel x86호환칩은 14C값을 가진다.

<NumberOfSections>
글자 그대로 섹션들의 개수를 나타낸다.

<SizeofOptionalHeader>
글자 그대로 OptionalHeaer의 크기를 나타낸다. OptionalHeader가 무엇인지는 뒤에서 살펴 볼 것이다.

<Characteristics>
파일의 속성을 나타내는 값이다. 실행파일인지 dll파일인지 등의 정보들이 or연산으로 저장되어 있다.

그림에 표시된 부분이 PE signature 와 PE FileHeader 부분이다.

3-3-3 file optional header

어떤 구조체인지 모습을 살펴 보자

제일 밑에 보면 IMAGE_DATA_DIRECTORY가 있는데 이것 또한 어떻게 생겼는지 훑어 보자.

Optional Header에서 중요한 멤버들을 찍어 보겠다.

Magic, AddressOfEntryPoint, ImageBase, SectionAlignment, FileAlignment, SizeOfImage, SizeOfHeaders, Subsystem, NumberOfRvaAndSizes, DataDirectory 부분이다. 하나하나 살펴 보도록 하겠다.

<Magic>
32비트환경인 경우 10B, 64비트인 경우 20B의 값을 가진다.

<AddressOfEntryPoint>
EP(Entry Point)의 RVA값을 가지고 있다.

<ImageBase>
메모리 상에서 PE파일이 로딩되는 시작 값을 가진다. 즉, PE로더는 메모리에 PE파일을 실행 시킬 때, AddressOfEntryPoint + ImageBase의 값을 EIP값으로 지정한다.

<SectionAlignment, FileAlignment>
파일에서 섹션의 최소단위가 FileAlignment이고 메모리에서 섹션의 최소단위가 SectionAlignmnet이다.

<SizeOfImage>
가상메모리 상에서 PE Image가 차지하는 크기이다.

<SizeOfHeader>
PE헤더의 전체 크기를 나타낸다. (파일상에서) 따라서 파일 시작위치에서 SizeOfHeader만큼 떨어진 위치에서 섹션이 시작된다.

<SubSystem>
시스템 드라이버파일인지, 일반 실행 파일인지 구분한다.

<NumberOfRvaAndSizes>
DataDirectory 배열의 개수를 나타낸다.

<DataDirectory>
데이터 디렉토리 배열에 어떤 값들이 정의되어 있는지 살펴보자.

<출처 : http://keybreak.tistory.com/98>

위에 밑줄 그어져 있는 것들을 유심히 잘 보시기 바란다. 이러한 내용들 까지 다 설명 하려면 날이 다 샐 것 같으니 다음 글에서 설명하도록 하겠다. 간단히 설명하자면, 외부에서 사용할 수 있도록 한 함수들이 정의된 부분이 EXPORT부분이고 외부에서 가져와 쓰는 함수들이 정의된 부분이 IMPORT이고 TLS같은 경우 쓰레드의 생성 시 EP시작 전에 시작되는 부분인데, 보통 이 부분에 바이러스 프로그램을 넣거나, 혹은 디버깅이 불가능하게 만들도록 Anti Debugging기법에서도 많이 쓰인다.

지금까지의 값들을 Hex로 확인하면 다음과 같다.

3-4 section header

이제 거의 다왔다. 조금만 더 참고 인내하자.

섹션은, 안정적인 구조를 위해 구현 되었다. 읽고 쓰기 등의 권한을 주어서 관리하는 것이다. 그래서 이러한 값들을 저장할 공간이 필요하게 되었고 섹션헤더 라는 공간을 만들게 되었다.

<출처: MSDN>

여기서 우리가 주목해야 할 멤버는 VirtualSize, VirtualAddress, SizeOfRawData, PointerToRawData, Characteristics정도 이다.

항목

의미

VirtualSize

메모리 상에서 섹션의 크기

VirtualAddress

메모리 상에서 섹션의 시작주소(RVA)값

SizeOfRawData

파일 상에서 섹션의 크기

PointerToRawData

파일 상에서 섹션의 위치

Characteristics

읽기, 쓰기, 실행 등의 설정 값

여기서 VirtualSize값과 SizeOfRawData값이 따로 존재하는지 궁금해 하시는 분이 있을 수 있습니다. 그 이유는 메모리 상에서의 사이즈 값과, 실제 파일에서의 크기가 다를 수 있기 때문입니다. 이는 아까 NT Header -> Optional Header -> FileAlignment, SectionAlignment 에서도 다루었습니다. 이 부분은 RVA -> RAW변환에서 상당히 중요한 개념입니다. 잘 기억하시기 바랍니다.

'Reversing' 카테고리의 다른 글

인라인 패치  (0) 2014.05.11
Upack 분석중 정리  (0) 2014.05.10
UPX압축 디버깅  (0) 2014.05.09
IAT와 EAT  (0) 2014.05.08
기본 레지스터  (0) 2014.05.04
Posted by flack3r
|