반응형

환경 : Windows 7 64bit에서 C:\Windows\SysWOW64\notepad.exe 파일 x32dbg와 HxD로 확인

 


참고 사항

 

Base Relocation Table

 

base relocation table의 주소는 PE 헤더에서 DataDirectory 배열의 여섯 번째 항목에 들어있다.

(배열 index는 5이다.)

IMAGE_NTHEADER.IMAGE_OPTIONAL_HEADER.IMAGE_DATA_DIRECTORY[5]

 

 

IMAGE_BASE_RELOCATION

 

Base Relocation Table은 IMAGE_BASE_RELOCATION 구조체 배열이다.

 

IMAGE_BASE_RELOCATION 구조체의 첫 번째 멤버 VirtualAddress는 기준 주소(Base Address)이며, 실제로는 RVA 값이다.

두 번째 멤버 SizeOfBlock은 각 단위 블록의 크기를 의미한다.

 

마지막으로 구조체 멤버는 아니지만 주석으로 표시된 TypeOffset 배열의 의미는 이 구조체 밑으로 WORD 타입의 배열이 따라온다는 뜻이다.

 

그리고 이 배열 항목의 값이 바로 프로그램에 하드코딩된 주소들의 옵셋이다.

 

//
// 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_MACHINE_SPECIFIC_5    5
#define IMAGE_REL_BASED_RESERVED              6
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_7    7
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_8    8
#define IMAGE_REL_BASED_MACHINE_SPECIFIC_9    9
#define IMAGE_REL_BASED_DIR64                 10

 

 

PE 재배치 동작 원리

 

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

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

여기서 핵심은 하드코딩된 주소 위치를 찾는 것이다.

 

이를 위해 PE 파일 내부에 Relocation Table이라고 하는 하드코딩 주소들의 offset(위치)을 모아놓은 목록이 존재한다.

(Relocation Table은 PE 파일 빌드(컴파일/링크) 과정에서 제공된다.)

 

Relocation Table로 찾아가는 방법은 PE 헤더의 Base Reloaction Table 항목을 따라가는 것이다.

 


PE 재배치란

 

PE 파일이 프로세스 가상 메모리에 로딩될 때 PE 헤더에 있는 ImageBase 주소에 로딩되는데,

프로그램에 적혀있는 ImageBase 값에 해당하는 주소에 로딩할 수 없을 때 다른 비어있는 주소에 배치하여 로딩하는 것이다.

 

프로그램에 하드코딩된 메모리 주소를 현재 로딩된 주소에 맞게 변경해주는 작업이다.

 

즉, PE 재배치는 PE 파일이 ImageBase에 로딩되지 못하고 다른 주소에 로딩될 때 수행되는 일련의 작업들을 의미한다.

 

참고1)

SDK(Software Development Kit) 또는 Visual C++로 PE 파일을 생성하면 기본적으로 exe의 경우 ImageBase = 00400000, DLL의 경우 ImageBase = 10000000으로 설정되며, DDK(Driver Development Kit)로 생성된 SYS 파일은 기본적으로 ImageBase = 10000으로 설정된다.

 

참고2)

ASLR 기능은 DLL/SYS 파일에도 적용되는 개념이다.

MS에서는 각 OS의 주요 시스템 DLL들에 대해 버전별로 각자 고유한 ImageBase를 부여하기에 한 시스템에서 kernel32.dll, user32.dll 등은 자신만의 고유 ImageBase에 로딩되기 때문에 실제로 시스템 DLL들끼리는 Relocation이 발생할 일이 없다.

Windows Vista/7의 시스템 DLL들도 자신만의 고유한 ImageBase를 가지고 있지만, ASLR 기능으로 인해 로딩 주소는 매 부팅 시마다 달라진다.


 

HxD에서 EP 영역 확인

 

 

HxD에서 notepad.exe를 열어 ImageBase 값을 확인하면 1000000이다.

 

 

위의 사진은 notepad.exe의 EP 코드이다.

 

Windows 7의 ASLR 기능에 의해 B40000 주소에 로딩되었다.

 

call 명령들의 opcode를 보면 프로세스 메모리 주소가 리틀엔디언 방식으로 하드코딩되어 있다.

 

GetStartupInfoA - FF15 0011B400 : B41100

InterlockedCompare - FF15 0411B400 : B41104

mov eax, dword ptr ds:[B4C0A4] - A1 A4C0B400 : B4C0A4

 

 

 

B41100, B41104 주소는 “.text” 섹션의 IAT 영역이고, B4C0A4 주소는 “.data” 섹션의 전역 변수이다.

 

x32dbg에서 notepad.exe를 재실행할 때마다 위에서 봤던 주소 값들은 로딩 주소에 맞게 매번 변경된다.

 

이렇게 프로그램에 하드코딩된 메모리 주소를 현재 로딩된 주소에 맞게 변경해주는 작업이 PE 재배치이다.

 

 

Windows 7 64bit에서 32bit notpad.exe의 AddressOfEntryPoint를 보면 31C9이다.

 

 

.text 섹션의 VirtualAddress는 1000이고, 사이즈는 A6FC이다.

 

 

PointerToRawData는 400이다.

 

 

 

즉, HxD에서 EP 영역으로 가려면 31C9(RVA) - 1000(.text VirtualAddress) + 400(PointerToRawData) = 25C9이다.

 

25C9 ~ 262E 사이에 하드코딩된 주소들이 있다. 

 

위의 사진을 참고하여 표로 만들면 아래와 같다.

 

HxD에서의 EP 사진에서는 하드코딩된 주소가 ImageBase(1000000) 기준으로 되어 있다.

 

파일(ImageBase : 1000000)  프로세스 메모리(로딩 주소 : B40000)
1001104 B41104
100C0A4 B4C0A4
1001100 B41100

 


Base Relocation Table

 

 

PEView에서 notepad.exe의 Base Relocation Table 주소를 확인해보면 위와 같다.

 

Base Relocation Table 주소는 RVA 2F000이다.

 

 

Base Relocation Table(2F000)에 하드코딩 주소들의 offset(위치)들이 나열되어 있다.

 

RVA Data Comment
0002F000 00001000 VirtualAddress
0002F004 00000118 SizeOfBlock
0002F008 3424 TypeOffset
0002F00A 3431 TypeOffset
0002F00C 343A TypeOffset

 

위의 표는 Base Relocation Table의 일부 내용이다.

 

IMAGE_BASE_RELOCATION 구조체 정의에 따르면 VirtualAddress 멤버(기준 주소)의 값은 1000이고, SizeOfBlock 멤버의 값은 118이다.

 

즉, 위 표에 표시된 TypeOffset 배열의 기준 주소(시작 주소)는 RVA 1000이며, 블록의 전체 크기는 118이다.

(기준 주소별로 이러한 블록이 배열 형태로 존재한다.)

 

블록의 끝은 0으로 표시한다.

 

TypeOffset 값은 2byte(16bit) 크기를 가지고, Type(4bit)와 Offset(12bit)이 합쳐진 형태이다.

 

ex) TypeOffset 값이 3424이라면 아래와 같이 해석된다.

 

최상위 4bit는 Type로 사용된다.

 

PE 파일에서 일반적인 값은 3(IMAGE_REL_BASED_HIGHLOW)이고, 64bit용 PE+파일에서는 A(IMAGE_REL_BASED_DIR64)이다.

 

TypeOffset의 하위 12bit가 진짜 Offset을 의미한다.

 

이 Offset 값은 VirtualAddress 기준의 옵셋이기에 프로그램에서 하드코딩 주소가 있는 옵셋은 다음과 같이 계산한다.

 

VirtualSize(1000) + Offset(424) = 1424(RVA)

 

 

실제로 RVA 1424에 PE 재배치 작업을 수행해야 하는 하드코딩된 주소가 있는지 확인해보면 위와 같다.

 

위의 사진을 보면 notepad.exe는 B40000 주소에 로딩되었기 때문에 RVA 1424는 VA B41424이고, 이 주소에는 B410C8이라는 IAT 주소(VA)가 저장되어 있다.

 

이 값은 PE 재배치 과정을 통해 값이 변경된 것이다.

 

같은 원리로 B41431, B4143A 주소의 내용도 프로그램에 하드코딩된 주소 값이며, 이 offset은 이전에 있던 Base Relocation Table 일부 내용 표에서 구할 수 있다.

 

참고)

TypeOffset 항목에서 Offset을 가리키는 하위 12bit는 최대 1000의 값을 가질 수 있다.

그 이상 되는 주소를 표시하기 위해서는 그에 맞는 블록을 하나 추가하면 된다.

이러한 블록이 배열 형태로 나열되기 때문에 Relocation Table이라고 한다.


Base Relocation Table을 찾고 IMAGE_BASE_RELOCATION 구조체를 이용해 하드코딩 주소의 offset 알아내기

1. 프로그램에서 하드코딩된 주소 위치를 찾는다.

2. 값을 읽은 후 ImageBase만큼 뺀다(VA → RVA)

3. 실제 로딩 주소를 더한다(RVA → VA)

 

notepad.exe가 실행되면서 ImageBase(1000000)에 로딩되지 않고, AF0000 주소에 로딩되었다고 가정한다.

 

1. 프로그램에서 하드코딩된 주소 위치를 찾는다.

프로그램에서 사용되는 하드코딩 주소의 offset(위치)는 Base Relocation Table을 통해서 찾는다.

(이 실습에서는 이전에 구한 RVA 1424를 사용한다.)

 

PEView를 통해 실제 RVA 1424 주소의 내용을 보면 아래와 같다.

 

 

RVA 1424 주소에는 프로그램의 하드코딩된 주소 10010C8 값이 들어있다.

(이 값과 아래 사진의 값 B410C8을 비교해본다.)

 

 

2 값을 읽은 후 ImageBase만큼 뺀다(VA → RVA)

10010C8 - 1000000 = 10C8

 

3 실제 로딩 주소를 더한다(RVA → VA)

10C8 + B40000 = B410C8

 


 

PE 로더는 프로그램 내에 하드코딩된 주소(10010C8)를 위와 같은 과정을 거쳐 실제 로딩된 메모리 주소에 맞게 보정을 한 값(B410C8)을 같은 위치에 덮어쓴다.

 

이 과정을 하나의 IMAGE_BASE_RELOCATION 구조체의 모든 TypeOffset에 대해 반복하면 RVA 1000 ~ 2000 주소 영역에 해당되는 모든 하드코딩 주소에 대해 PE 재배치 작업이 수행된 것이다.

 

TypeOffset 값이 0이 되면 하나의 IMAGE_BASE_RELOCATION 구조체가 끝난다.

 

Relocation Table에 나와 있는 모든 IMAGE_BASE_RELOCATION 구조체에 대해서 같은 작업을 반복하면 프로세스 메모리 전체 영역에 대한 모든 하드코딩 주소이 PE 재배치 작업이 완료되는 것이다.

 

Relocation Table은 NULL 구조체로 끝난다.

(IMAGE_BASE_RELOCATION 구조체 멤버의 값이 모두 NULL)

반응형

+ Recent posts