본 포스트는 이전 포스트에 이어지는 내용입니다.
UPack 상세 분석 - PE Header 완전 정복 (1)
UPack 상세 분석 - PE Header 완전 정복 (2)
#4. IMAGE_SECTION_HEADER
IMAGE_SECTION_HEADER 구조체에서 프로그램 실행에 사용되지 않는 항목들에 UPack 자신의 데이타를 기록합니다.
이 기법 역시 PE Header 에서 쓰이지 않는 영역에 자신의 코드와 데이타로 덮어쓰는 기법입니다.
(생각보다 PE Header 에서 제대로 사용되지 않는 영역이 상당히 많죠?)
Hex editor 로 IMAGE_SECTION_HEADER 구조체를 보겠습니다. (옵셋 170 ~ 1E7 영역)
<Fig. 1>
위 그림에서 보이는 영역을 IMAGE_SECTION_HEADER 구조체에 맞게 보기좋게 뽑아보면 아래 그림과 같습니다. (예전에 제가 만든 PE Viewer 를 사용했습니다.)
---------------------------------------------------
file memory data description
---------------------------------------------------
00000170 01000170 5053FFD5 Name (PS諾揖?
00000174 01000174 ABEBE7C3
00000178 01000178 00014000 virtual size
0000017C 0100017C 00001000 RVA
00000180 01000180 000001F0 size of raw data
00000184 01000184 00000010 offset to raw data
00000188 01000188 0101BDD0 offset to relocations
0000018C 0100018C 0101FCCB offset to line numbers
00000190 01000190 0132 number of relocations
00000192 01000192 0000 number of line numbers
00000194 01000194 E0000060 characteristics
00000198 01000198 00100001 Name ()
0000019C 0100019C 00FD0101
000001A0 010001A0 00012000 virtual size
000001A4 010001A4 00015000 RVA
000001A8 010001A8 0000AE28 size of raw data
000001AC 010001AC 00000200 offset to raw data
000001B0 010001B0 0100739D offset to relocations
000001B4 010001B4 01013FFF offset to line numbers
000001B8 010001B8 FE28 number of relocations
000001BA 010001BA 0101 number of line numbers
000001BC 010001BC E0000060 characteristics
000001C0 010001C0 5A4B0101 Name (ZK?)
000001C4 010001C4 FC0F0001
000001C8 010001C8 00001000 virtual size
000001CC 010001CC 00027000 RVA
000001D0 010001D0 000001F0 size of raw data
000001D4 010001D4 00000010 offset to raw data
000001D8 010001D8 0101FC98 offset to relocations
000001DC 010001DC 0101FC9B offset to line numbers
000001E0 010001E0 FCAA number of relocations
000001E2 010001E2 0101 number of line numbers
000001E4 010001E4 E0000060 characteristics
<Fig. 2>
위에 노란색으로 표시된 구조체 멤버들은 프로그램 실행에는 의미 없는 멤버들입니다. 일례로 파일 옵셋 1B0 위치에 있는 값 0100739D 는 원본 notepad.exe 의 OEP 값입니다.
이 외에도 섹션헤더에는 몇 가지 비밀이 더 숨어 있습니다. (아래에서 설명됩니다.)
#5. 섹션 겹쳐 쓰기
UPack 의 주요 특징 중 하나가 바로 섹션과 헤더를 마구 겹쳐 쓰는 것입니다.
(PE Header 의 기초를 공부하신 분들은 이러한 기법에 당황하실 수 있습니다.)
간략한 뷰를 제공하는 Stud_PE 를 이용해서 UPack 의 IMAGE_SECTION_HEADER 를 살펴보겠습니다. Stud_PE 의 “Sections” 탭을 선택해 주세요.
<Fig. 3>
위 그림을 잘 보시면 뭔가 이상한 부분들이 보입니다.
먼저 눈에 띄는 것은 첫 번째 섹션과 세 번째 섹션의 파일 시작 옵셋(RawOffset) 값이 10 으로 되어 있습니다. 옵셋 10 은 헤더 영역인데 UPack 에서는 이곳에서부터 섹션이 시작됩니다.
그 다음 눈에 띄는 내용은 첫 번째 섹션과 세 번째 섹션의 파일 시작 옵셋(RawOffset) 과 파일에서의 크기(RawSize) 가 완전히 동일하다는 것입니다.
단, 섹션의 메모리 시작 RVA (VirtualOffset) 항목과 메모리 크기(VirtualSize) 값이 서로 틀립니다. PE 스펙에 따르면 이렇게 해도 문제가 없습니다. (더 정확히는 PE 스펙에 이러지 말아야 한다는 내용이 없는 것이죠.)
위 두 가지 이상한 사실을 종합해 보면 UPack은 Header, 1st 섹션, 3rd 섹션이 겹쳐 있습니다.
숫자만 보고서는 이게 무슨 의미인지 잘 와 닿지 않습니다.
이해를 돕기 위해서 아래 그림을 봐주시기 바랍니다.
<Fig. 4>
위 그림 왼쪽은 파일에서 섹션 정보를, 오른쪽은 메모리에서의 섹션 정보를 보여주고 있습니다.
섹션헤더(IMAGE_SECTION_HEADER)에 정의된 값에 의해서 PE Loader 는 파일 옵셋 0 ~ 1FF 영역을 3군데 다른 메모리 위치(Header, 1st Section, 3rd Section)에 매핑합니다.
같은 파일 이미지를 가지고 각각 다른 위치와 다른 크기의 메모리 이미지를 만들 수 있다는 사실에 주목하시기 바랍니다.
파일의 Header(1st/3rd Section) 영역의 크기는 200 입니다. 사실 매우 작은 크기입니다.
반면에 두 번째 섹션(2nd Section) 영역의 크기는 파일의 대부분을 차지할 정도로 큽니다. 바로 이곳에 원본 파일(notepad.exe)이 압축되어 있습니다.
또 하나 주목해야 하는 부분은 메모리에서의 1st Section 영역입니다. 섹션의 메모리 크기는 14000 입니다. 이는 원본 파일(notepad.exe) 의 Size of Image 와 같은 값입니다.
즉, 2nd Section 에 압축된 파일 이미지를 1st Section 에 (notepad 의 메모리 이미지) 그대로 압축해제 하는 것입니다. 참고로 notepad.exe 원본은 3개의 섹션이 있습니다. 이를 하나의 섹션에 풀어내는 것이지요.
압축이 해제된 1st Section 은 아래 그림과 같습니다.
<Fig. 5>
#6. RVA to RAW
각종 PE 유틸리티들이 UPack 앞에서 맥을 못 추던 원인이 바로 RVA <-> RAW 변환에 어려움을 겪었기 때문입니다. UPack 제작자는 많은 테스트 (혹은 PE Loader 리버싱)를 통해서 Windows PE Loader 의 버그(혹은 예외처리)를 알아낸 후 이를 UPack 에 적용하였습니다.
이 기법을 처음 접한 PE 유틸리티들은 대부분 “잘못된 메모리 참조에 의한 비정상 종료” 를 당해버렸습니다. (후에 많은 유틸리티들은 패치가 이루어졌습니다.)
먼저 일반적인 RVA <-> RAW 변환 방법을 복습해 보겠습니다.
RAW = RVA - VirtualAddress + PointerToRawData
(VirtualAddress, PointerToRawData 는 RVA 가 속하는 섹션 헤더에서 얻은 값입니다.
참고 : PE(Portable Executable) File Format (5) - PE Header
그렇다면 위 공식대로 UPack 의 EntryPoint(EP) 의 file offset (RAW) 을 계산해 볼까요?
UPack 의 EntryPoint(EP) 는 RVA 1018 입니다. (아래 그림 참조)
<Fig. 6>
RVA 1018 은 <Fig. 3>, <Fig. 4> 에 의하면 1st Section 에 존재합니다.
따라서 공식에 그대로 대입하면 아래와 같습니다.
RAW = 1018 – 1000 + 10 = 28
RAW (file offset) 28 영역을 hex editor 로 살펴보겠습니다.
<Fig. 7>
RAW 28 영역은 코드가 아니라 (ordinal:010B)”LoadLibraryA” 문자열 영역입니다. 우리는 지금 UPack 의 트릭에 걸려들었습니다. (OllyDbg 초기 버전은 실제로 UPack 의 EP 를 찾아내지 못하였습니다.)
비밀은 바로 1st section 의 PointerToRawData 값 10 에 있습니다.
일반적으로 섹션 시작의 파일 옵셋을 가리키는 PointerToRawData 값은 FileAlignment 의 배수가 되어야 합니다. UPack 의 FileAlignment 는 200 이므로 PointerToRawData 값은 0, 200, 400, 600 등의 값을 가져야 합니다.
따라서 PE Loader 는 1st Section 의 PointerToRawData (10) 이 FileAlignment (200) 의 배수가 아니므로 강제로 배수에 맞춰서 인식합니다. (이 경우는 0)
이것이 바로 UPack 파일이 정상적으로 실행은 되지만, PE 유틸리티들이 에러를 발생했던 이유입니다.
정상적인 RVA <-> RAW 변환은 아래와 같습니다.
RAW = 1018 – 1000 + 0 = 18
디버거로 해당 영역의 코드를 살펴보겠습니다.
<Fig. 8>
이제 여러분은 UPack 파일에 대해서 정상적인 RVA <-> RAW 계산을 할 수 있게 되었습니다.
+---+
다음 포스트로 이어집니다.
UPack 상세 분석 - PE Header 완전 정복 (4)
ReverseCore
'analysis' 카테고리의 다른 글
| UPack 디버깅 - OEP 찾기 (23) | 2009/09/18 |
|---|---|
| UPack 상세 분석 - PE Header 완전 정복 (4) (2) | 2009/09/17 |
| UPack 상세 분석 - PE Header 완전 정복 (3) (4) | 2009/09/14 |
| UPack 상세 분석 - PE Header 완전 정복 (2) (6) | 2009/09/08 |
| UPack 상세 분석 – PE Header 완전 정복 (1) (20) | 2009/09/04 |
| Inline Patch 실습 (2) (4) | 2009/08/27 |
| Inline Patch 실습 (1) (6) | 2009/08/22 |
| UPX 실행 압축된 notepad 디버깅! (27) | 2009/06/16 |
| 키로거(KeyLogger) 분석 (3) (4) | 2009/05/03 |
| 키로거(KeyLogger) 분석 (2) (50) | 2009/05/03 |
| 키로거(KeyLogger) 분석 (1) (14) | 2009/04/24 |
