Try Attack/Reverse Engineering[basic]

PE 파일 포맷

D4tai1 2020. 1. 21.

사람을 볼 때

외면보다는 내면의 아름다움을 보는 것이

중요하다는 말이 있죠?

 

실행파일도 마찬가지랍니다.

 

겉에 보이는 아이콘과 파일명을 보지말고

실행파일의 내면(PE구조)를 보면 나쁜파일을 걸러내기 유리하겠죠?

 

이러한 마인드를 소유하고 계시다면

분석할 기본소양이 갖추어지셨다고 생각합니다.

 


 

구글에 "PE구조"라고 검색 후 이미지를 보면 

... ...

할 말을 잃죠..

"이것을 모르고 분석을 하면 어떨까?" 라는 생각을 하게 되죠?

 

그러나 프로그램을 실행시키면 메모리에 올라가서 프로세스가 됩니다.

프로세스에 로드할 때 어떻게 적재가 되는지도 알 수가 없습니다.

 

1) 어디서 시작하고 각 섹션별 위치나 크기정보를 알 수 없으며

 

결론은 해야한다는 말이 되겠네요..

 

그러나 외우다가 지쳐서 분석은 해보지도 못할 가능성이 크죠..

외우지 말고 돌아가는 과정을 이해하고 찾아서 할 수 있을 정도로만 해보려고 합니다.

 

 

 

그럼 PE구조를 사용하는 파일을 먼저 보겠습니다.

 

1. PE구조를 사용하는 파일

 

 

SCR파일의 경우 화면보호기(스크린세이버)로 사용되나 

인터넷상에서 구한 SCR파일의 경우 오염된 것이 많습니다.

 


 

2. VA, RVA

 

 

PE헤더 내 정보는 대부분 RVA로 되어 있습니다.

그 이유는 프로세스 가상메모리의 특정위치에 로딩될 때 다른 PE파일이 로딩될 수 있습니다.

그 때 Relocation(재배치)과정을 통해 다른 위치에 로딩이 되어야 하기 때문에

상대주소(RVA)를 사용합니다.

 


 

3. PE헤더

사람이라면 심장이 하나고

손가락이 10개와 같은 구조를 가지고 있습니다.

 

그러나 팔의 길이, 손가락의 길이 등은 사람마다 다릅니다.

이와 같이 형태는 비슷하지만 개별적으로 다른 내용을 

PE파일은 헤더에 기록하고 있습니다.

 

같은 사람이지만 남자와 여자가 다르듯

같은 실행파일이지만 32비트와 64비트의 다른점도 

PE헤더를 보면 확인할 수 있습니다.

 


 

1) IMAGE_DOS_HEADER

 

먼저 IMAGE_DOS_HEADER 구조체에서

가장가장 중요한 것 3가지를 말씀드릴게요.

 

 

 

 

 

 

맨 뒤 4바이트인 0x100이 IMAGE_NT_HEADERS의 시작주소가 되겠네요.

 

 

 

 

먼저

MapViewOfFile()에서 리턴한 lpFileBase가 PE의 시작이며

IMAGE_DOS_HEADER구조체 형태로 형변환 후 PE구조가 시작됩니다.

 

시작주소의 2바이트가 "MZ"면?

PE파일포맷이라네요.

 


 

2) DOS Stub

 

IMAGE_DOS_HEADER 아래 존재하며 고정된 크기는 아닙니다.

또한 없어도 파일 실행에 문제가 되지 않지요.

 


 

3) IMAGE_NT_HEADERS

IMAGE_NT_HEADERS는 3가지 멤버로 구성되어 있습니다.

IMAGE_NT_HEADERS에 대해 요약을 해보면?

 

 

 

그럼 파일의 속성을 나타내는 두 번째 멤버인

IMAGE_FILE_HEADER 구조체를 볼까요?

 

 

 

중요한 값이 많이 담겨있는 세 번째 멤버인

IMAGE_OPTIONAL_HEADER 구조체를 볼까요?

 

 

 

 

 

 

위 소스코드와 같이

IMAGE_OPTIONAL_HEADER32의 [2]에서 다룬

code섹션의 크기와 주소영역을 확인할 수 있습니다.

 

 

 

이 그림은 위 소스코드를 실행한 화면으로

code섹션의 크기와 주소영역을 확인할 수 있습니다.

 

 

이후 섹션헤더까지가 PE헤더를 의미하고 

PE바디는 여러 섹션으로 구성되어 있습니다.

 

 

섹션헤더를 보고

파일을 메모리에 로딩 시 매핑

 

RAW : 파일의 offset(0부터니까 그냥 주소라고 보아도 무방합니다.)

PointerToRawData : 파일 내 섹션의 시작주소

 

RVA : 메모리에 로딩 후 offset

VirtualAddress : 메모리에 내 섹션의 상대주소(섹션 헤더 내 필드/실제로는 RVA)

 

RAW - PointerToRawData = RVA - VirtualAddress

RAW = RVA - VirtualAddress + PointerToRawData

 

 

예를 들어 

 

0xE43689가 EP입니다.

 

파일에서 EP의 위치를 찾고 싶다면?

 

파일의 주소 = 메모리 로딩 후 오프셋 - 메모리 내 섹션의 상대주소 + 파일 내 섹션 시작주소

 

메모리 로딩 후 오프셋은 OPTION_HEADER의 ImageBase를 제외한 주소입니다.

0xEB3689 - 0xEB0000 = 0x3689

 

메모리 내 섹션의 상대주소는 .text섹션헤더의 RVA입니다.

0x1000

 

파일 내 섹션 시작주소는 .text섹션헤더의 PointerToRawData입니다.

0x400

 

RAW = 0x3689 - 0x1000 + 0x400

=0x2A89

 

프로세스의 EntryPoint

 

파일의 EntryPoint

메모리에 올라간 후와 파일로 존재할 때의 EP를 확인할 수 있습니다.

 

그러나 조금 자세히보면 전부 같지는 않다는 것을 알 수 있습니다.

 

이 부분은 이 파일이 원래 ImageBase가 0x01000000이라 재배치되었기 때문입니다.

 

참고로

 

파일이 메모리에 로딩될 때 재배치과정을 살펴보면

 

1) 하드코딩된 주소를 찾은 후

 

2) ImageBase만큼 뺍니다.(VA에서 RVA만 남겠죠?)

 

3) 실제 로딩된 ImageBase를 더해줍니다.(RVA에서 VA로 변경)

 

4) 그리고 메모리에 적재가 됩니다!

 

 

 

 

댓글