Try Attack/Reverse Engineering[basic]

PE재배치

D4tai1 2020. 3. 23.

화장실에 갔는데 말이죠!

 

앞사람이 계속 나오지 않는다면 어떻게 할까요?

 

다른 방법을 찾아봐야겠져?

 

 

실행파일도 마찬가지에요.

 

내가 메모리에 적재되어야 하는 장소에 

 

다른 실행파일이 적재되어있다면

 

그 파일이 종료될 때까지 목빠지게 기다리다가

 

망부석이 될 가능성이 크지요.

 

 

망부석이 되면 안되기 때문에 

 

다른 메모리장소(기준주소)를 얻어서 재배치 후 

 

메모리에 적재가 됩니다.

 

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

 

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

 

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

 

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

 

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

 

아 그런데 하드코딩된 주소는 누가 어떻게 찾냐구요?

 

IMAGE_NT_HEADER - IMAGE_OPTIONAL_HEADER - IMAGE_DATADIRECTORY[5]

= BASE RELOCATION Table = IMAGE_BASE_RELOCATION(구조체)

 

 

IMAGE_BASE_RELOCATION의 원형

typedef struct _IMAGE_BASE_RELOCATION {
	DWORD VirtualAddress; 	//실제로 RVA입니다.
    DWORD SizeOfBlock;
    
    //WORD TypeOffset[1];	//하드코딩된 주소의 Offset!
    //WORD TypeOffset[1];
    //WORD TypeOffset[1];
    //WORD TypeOffset[1];
    // n개...(예측불가)
} IMAGE_BASE_RELOCATION;

 

첫 번째 멤버인 VirtualAddress는 기준주소를 의미합니다.

 

즉, 0x401000이 코드영역이라면 0x1000가 VirtualAddress가 됩니다.

 

ImageBase로부터 0x1000만큼 떨어져있다는 말이므로

 

 RVA라는말이겠죠?


두 번째 멤버인 SizeOfBlock은 섹션의 크기를 의미합니다.

 

해당 섹션에서 하드코딩되어 재배치해야할 주소가 저장된 공간의 크기

 

의미합니다.

 

간단한 예제를 가지고 왔습니다.

[그림1] IMAGE_BASE_RELOCATION

노란 박스가 첫 번째 멤버인 VirtualAddress입니다.

 

파란 박스는 두 번째 멤버인 SizeOfBlock입니다.

 

0x1000영역의 재배치할 주소가 

 

0x150크기만큼 들어있다는 것을 알 수 있겠죠?

 

 

0x150이후에는 어떤 것이 있는지 확인해보니

 

[그림2] IMAGE_BASE_RELOCATION

0x2000영역의 재배치할 주소가

 

0x158크기만큼 들어있다는 것을 알 수 있겠죠?

 

이런식으로 알 수 있답니다.

 

[그림2]의 세 번째 멤버(TypeOffset)를 보면

 

2바이트(16비트)로 구성되어 있는 것을 알 수 있습니다.

 

여기서 상위 4비트는 TYPE을 의미합니다.

 

TYPE은 WinNT.h에 정의되어 있으며

 

직접 찾으시기 귀찮으실테니 제가 직접 가지고 왔습니다.

 

짝짝짝!!

 

[그림3] WinNT.h의 Based relocation types

이와 같이 상수로 정의되어 있습니다.

 

32비트파일은 보통 3인 IMAGE_REL_BASED_HIGHLOW로 정의되어 있고,

 

64비트파일은 보통 0xA(10)인 IMAGE_REL_BASED_DIR64로 정의되어 있습니다.

 

 

가끔 정상파일의 코드를 패치한 후 

 

재배치과정을 피하기 위해서 Type을 0(IMAGE_REL_BASED_ABSOLUTE)로

 

변경하는 악성코드도 있답니다.

 

[그림4] IMAGE_BASE_RELOCATION

세 번째 멤버인 TypeOffset이 0x300D네요.

 

이 중 상위 4바이트인 3은 type을 의미하며

 

[그림3]으로 미루어볼 때 IMAGE_REL_BASED_HIGHLOW를 의미합니다.

 

그리고 Offset은 0xD가 되겠네요.

 

그렇다면 실제로 저 0xD는 메모리에 적재 시 

 

VirtualAddress[기준주소](0x2000) + Offset(0xD) = 0x200D

 

새롭게 설정된 ImageBase+0x200D주소에

 

하드코딩된 주소가 들어있겠군요? 

 

[그림5] 하드코딩된 주소

ImageBase(0xE40000) + RVA(0x200D) = VA(0xE4200D)

 

0x00E4C100이라는 주소로 되어있는 것을 확인할 수 있죠?

 

전 장에서 다룬 

 

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이라 재배치되었기 때문입니다.

 

댓글