Tuuna Computer Science

[ UPack의 PE 헤더 분석 ] 본문

Reversing

[ UPack의 PE 헤더 분석 ]

GuTTe 2018. 6. 12. 01:29
    
[ UPack의 PE 헤더 분석 ] 


1.헤더 겹쳐쓰기 

:: MZ 헤더와 PE 헤더를 교묘하게 겹쳐쓰는 것. 


정상적인 프로그램의 e_lfanew = MZ 헤더 크기(40) + DOS stub 크기(가변 : VC++의 경우 A0) = E0


BUT :: UPack에서는 10이다. 

-> UPack은 IMAGE_OPTIONAL_HEADER 148로 변경한다.  ( COFF File Header -> SizeOfOptionalHeader

e_lfanew : -> (e_lfanew 값이가리키는 자리에 NT header이 존재해야함) NT header의 옵셋을 표시(파일에 따라 가변적인 값을 가짐)

즉, 위의 말을 정리하자면 

e_lfanew값이 가리키는 (VC++의 경우 E0)자리에 NT_HEADER이 있어야한다. 

하지만 UPack을 쓰면 e_lfanew값이 10으로 변하고 오프셋(10)에 NT_HEADER구조체의 시작주소가 된다. 

또한 UPack은 IMAGE_OPTIONAL_HEADER구조체 크기를 148로 변경한다. 

Size Of Optional Header :: IMAGE_OPTIONAL_HEADER의 크기


IMAGE_OPTIONAL_HEADER 구조체의 크기를 따로 입력하게 한 이유 

->IMAGE_OPTIONAL_HEADER의 종류가 여러 개이므로 구조체의 크기를 따로 입력할 필요가 있는 것! 
(64bit용 IMAGE_OPTIONAL_HEADER구조체 크기는 F0) 

Size Of Optional Header의 또다른 의미는 섹션헤더(IMAGE_SECTION_HEADER)의 시작 옵셋을 결정

상식 : IMAGE_OPTIONAL_HEADER에 이어서 IMAGE_SECTION_HEADER이 오는걸로 착각할 수 있다.
그러나 IMAGE_OPTIONAL_HEADER(OFFSET)에 Size Of Optional Header을 더한 값이 가리키는 위치에 
IMAGE_SECTION_HEADER이 위치한다. 

UPack은 Size Of Optional Header의 값이 148이므로 IMAGE_SECTION_HEADER의 값은 170(OFFSET)에서 시작 

값이 커진 이유는 DECODING(디코딩)을 넣기 위해서다! 


또한, Number Of Rva And Sizes 값을 변경시킨다. (Number Of Data Directories)

이 또한 디코딩목적이다....  

Number Of Rva And Sizes의 값은 바로 뒤에 이어지는 IMAGE_DATA_DIRECTORY 구조체 배열의 원소개수를 나타낸다

 



원래는 10h값이지만 UPack하면 Ah개로 변한다. 

PE스펙에 따르면 Number Of Rva And Sizes의 값을 배열의 원소 개수로 인정하도록! 
(D8 ~ 107)영역은 무시된다. 


UPack은 PE헤더, 첫 번째 섹션과 세 번째 섹션이 서로 겹쳐있다. 

하지만 메모리상의 OFFSET은 서로 다르다.  즉, PE로더는 위 3개를 각 각의 메모리 위치에 매핑시킨다. 

그리고 자세히 보면 2번째 섹션의 크기가 존나 큰데 이는 원본실행파일이 압축되어 있다. 

즉, 이를 첫 번째 섹션에 압축해제하는 것이다. 


RAW - PointerToRawData = RVA - VirtualAddress

-> RAW = RVA - VirtualAddress +PointerToRawData

RAW : File 상에서의 offset 
RVA : Memory 상에서의 RVA
VirtualAddress : Offset을 찾으려는 RVA가 속해있는 section의 RVA
PointerToRawData : Offset을 찾으려는 RVA가 속해있는 section의 offset

즉,  VirtualAddress는 아예 섹션자체의 메모리 시작주소이고 RVA는 그 섹션에 들어있는 특정 DLL의 주소를 의미

PointerToRawData는 섹션 그자체의 오프셋이고 RAW는 섹션에 들어있는 DLL등의 파일 오프셋이다! 


연습삼아 하면 EP의 RAW를 구해보자! EP (RVA)는 1018이다.
여기서 PointerToRaw가 10이고 VirtualAddress가 1000이다.

RAW는 28이겠지  

But.... 아니였넹 


PointerToRaw의 값은 FileAlignment의 배수이다. UPack의 FIleAlignment의 값은 200이다. 

즉, PointerToRaw의 값은 0, 200, 400,...등의 값을 가져야 한다. 

한마디로 PointerToRaw의 값을 강제로 맞춰야 한당! 

음... 0으로 박자 (원래 10이니깐 200하기엔 그렇잖아) 

그럼 다시 구하면 RAW값은 18이겠지 이동 gogo 


[ Import Table(IMAGE_IMPORT_DESCRIPTOR 구조체 배열 ] 



::총 8바이트 중 앞의 4바이트가 Import Table의 주소(RVA), 뒤의 4바이트가 Import Table의 크기

271EE = RVA  -> RAW값을 찾아보자 

1.일단 어느 섹션에 있는지 확인해보자  => 세 번째 섹션이넹  (27000 = VirtualAddress) 
2.RawOffset = PointerToRaw? = 10이 아니라 FileAliginment의 배수여야 하므로 0으로 봐야한다. 
3.RAW = RVA - VirtualAddress + PointerToRaw => 1EE ~ 201

-> 시작은 아는데 끝은 어떻게 알징 

실행할때 200옵뎃 이하는 세 변째 섹션 메모리에 매핑되지 않는다

0 ~ 1FF 영역이 메모리 주소27000~271FF에 매핑되고 

200~201(세 번째 섹션의 남은 메모리 영역)은 27200~28000에 매핑된다. 

디버거로 확인시 010271D0 ~ 010271FF까지만 매칭되고 01027200 이후부터는 NULL로 채워진다. 

================================================

IAT  (IAT와 EAT 구조 다시 공부하기 ㅋㅋㅋㅋㅋ ㅈ됐다) 

IMAGE_IMPORT_DESCRIPTOR 구조체 확인 


(1EE)OriginalFisrtThunck(INT) :: 0   (파일 옵셋 읽는 법좀 배워야 겠다 ㅋㅋㅋㅋㅋ) 

(1FA)Name :: 2   RVA의 값임 

(1FE)FisrtThunck(IAT) :: 11E8 



보통은 OriginalFirstThunk(INT)를 쫒아가면 API 이름 문자열이 나타나지만 

UPack은 OriginalFirstThunck(INT)가 0일때는 FisrtThunck(IAT) 값을 따라가도 상관 없다. 


이제 API의 문자열을 찾아보장  RVA가 첫 번째 섹션에 있네 

RAW = RVA(11E8) - VirtualAddress (1000) + PointerToRaw(0)  => 1E8



위 박스는 INT이자 IAT이다. 

RVA 28과 RVA BE 이 2개의 API를 임포트하고 있다. 

상식 : 헤더영역의 RVA값과 RAW값은 서로 같다. 

이 RVA 위치에 Import 함수의 [ordinal + 이름 문자열]이 있다.  //ordinal의 값도 다시 공부해야겠다 ㅋㅋㅋ 



즉, LoadLibraryA와 GetPorcAddress API를 임포트 한다는 것을 알 수 있다. 
Comments