본문 바로가기

리버스 엔지니어링

PE재배치 동작 원리

 

 

Windows의 PE 로더가 수행하는 PE 재배치 작업의 기본 동작 원리는 다음과 같다.

1.프로그램에서 하드코딩된 주소 위치를 찾는다
2. 값을 읽을 후 ImageBase만큼 뺀다(VA -> RVA)
3. 실제 로딩 주소를 더한다.(RVA -> VA)


여기서 핵심은 하드코딩된 주소 위치를 찾는 것이다. 이를 위해 PE파일 내부에 Relocation Table이라고 하는 하드코딩 주소들의 옵셋(위치)을 모아 놓은 목록이 존재한다.(Relocaation Table은 PE 파일 빌드(컴파일/링크)과정에서 제동된다) Relocation Table로 찾아가는 방법은 PE 헤더의 Base Relocation Table 항목을 따라간다.

 
1.Base Relocation Table

Base Relocation Table 주소는 PE헤더에서 DataDirectory 배열의 여섯번째 항목에 들어있다.

IMAGE_NT_HEADERS\IMAGE_OPTIONAL_HEADER\IMAGE_DATA_DIRECTORY[5]

PEView에서 notepad.exe의 Base Relocation Table 주소를 확인해보자.

그림에서 Base Relocation 주소는 RVA 2F000이다. 이는 PEView에서 확인 가능하다.


2. IMAGE_BASE_RELOCATION 구조체

위의 그림의 Base Relocation Table에 하드코딩 주소들의 옵셋(위치)들이 나열되어 있다. 이 테이블만 읽어 내면 하드 코딩 주소 옵셋을 정확히  알아 낼 수 있다. Base Relocation Table은  IMAGE_BASE_RELOCATION 구조체 배열이다. IMAGE_BASE_RELOCATION 구조체 정의는 다음과 같다.

/*
 * Based relocation format.
 */

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD        VirtualAddress;
    DWORD        SizeOfBlock;
//  WORD        TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

/*
 * Based relocation types.
 */
#define IMAGE_REL_BASED_ABSOLUTE             0
#define IMAGE_REL_BASED_HIGH                 1
#define IMAGE_REL_BASED_LOW                  2
#define IMAGE_REL_BASED_HIGHLOW              3
#define IMAGE_REL_BASED_HIGHADJ              4
#define IMAGE_REL_BASED_MIPS_JMPADDR         5
#define IMAGE_REL_BASED_MIPS_JMPADDR16       9
#define IMAGE_REL_BASED_IA64_IMM64           9
#define IMAGE_REL_BASED_DIR64               10



IMAGE_BASE_RELOCATION 구조체의 첫번째 맴버 VirtualAddress는 기준 주소(Base Address)이며 실제로 RVA값이다. 두 번째 맴버 SizeOfBlock은 각 단위 블록의 크기를 의미한다. 마지막으로 구조체 맴버는 아니지만 주석으로 표시된 TypreOffset 배열의 의미는 이 구조체 밑으로 WORD 타입의 배열이 따라온다는 뜻이다.그리고 배열의 항목의 값이 바로 프로그램에 하드코딩된 주소이다.


3. Base Relocation Table의 해석 방법

다음 표는 위의 그림의 Base Relcation Table의 일부 내용을 표시한 것이다.


RVA

 Data

 Comment

 0002F000

 00001000

 VirtualAddress

 0002F004

 00000150

 SizeofBlock

 0002F008

 3420

 TypeOffset

 0002F00A

 342D

 TypeOffset

 0002F00C

 3436

 TypeOffset



IMAGE_BASE_RELOCATION 구조체 정의에 따르면 VirtualAddress 맴버(기준주소)의 값은 1000이고, SizeOfBlock 맴버의 값은 150이다. 즉 표에서 표시된 TypeOffset 배열의 기준 주소(시작 주소)는 RVA 1000이며, 블록의 전체 크기는 150이다.(기준 주소별로 이러한 블록이 배열 형태로 존재한다.) 블록의 끝은 0으로 표시한다. TypeOffset 값은 2바이트(16비트)크기를 가지며 Type(4비트)과 Offset(12비트)이 합쳐진 형태이다. 예를 들어 TypeOffset 값이 3420이라면 아래의 표와같이 해석된다.


Type(4비트)

 Offset(12비트)

 3  420


최상위 4비트는 Type로 사용된다.  PE 파일에서 일반적인  값은 3(IMAGE_REL_BASED_HIGHLOW)이고, 64비트용 PE+ 파일에서는 A(IMAGE_REL_BASED_IR64)이다.

참고------------------------------------
 간혹 악성 코드 중에서 정상 파일의 코드를 패치한 후 해당영역을 가리키는 Relocation Table을 수정한느 경우가 있다.(PE 로더의 재배치 과정을 피하기 위해서 Type을 0(IMAGE_REL_BASED_ABSOULUTE)으로 수정해 버린다.)
----------------------------------------

TypeOffset의 하위 12비트가 진짜 Offset을 의미한다. 이 Offset 값은 Virtual Address 기준의 옵셋이다. 따라서 프로그램에서 하드코딩 주소가 있는 옵셋은 다음과 같이 계산된다.

VirtualSize(1000)+Offset(420) =1420(RVA)

실제로 RVA 1420에 PE 재배치