minhui study

패킹 & UPX ( 툴없이 언패킹하기 ) 본문

Hacking/Reversing

패킹 & UPX ( 툴없이 언패킹하기 )

minhui 2020. 10. 27. 02:39

데이터 압축

▷ 비손실 압축 : 파일 크기를 줄여서 보관 및 이동에 용이하도록 하려는 목적으로 사용됨. 

▷ 손실 압축 : 파일에 의도적인 손상을 주어서 그 댓가로 압축률을 높이는 목적으로 사용된다. (jpg,mp3,mp4..)

* 압축된 파일을 100% 원래대로 복원할 수 있으면 비손실 압축이고, 원래대로 복원 불가하면 손실 압축이다.

실행 압축

  : 실행압축이란 말 그대로 실행 파일을 대상으로 파일 내부에 압축해제 코드를 포함하고 있어서 실행되는 순간에 미모리에서 압 축을 해제시킨 후 실행시키는 기술이다. 

실행 압축된 파일 역시 PE파일이며 내부에 원본 PE파일과 decoding 루틴이 존재한다. EP(Entry Point) 코드에 decoding루틴이 실행되며서 메모리에서 압축을 해제시킨 후 실행된다.

* 실행 압축과 일반 압축의 차이점은 'PE 파일의 실행이 가능하다'이다.

 

 

UPX를 이용한 패킹 실습(notepad.exe이용)

사용할 패커 : UPX

패킹 대상 : notepad.exe(win7 32bit)

UPX을 다운받아 Command창에 실행시키면 다음과 같이 사용법에 대해 설명이 나타난다.

 

notepad.exe파일을 작업 폴더에 복사한 후 다음과 같이 실행 파라미터를 주고 notepad.exe파일을 실행 압축한다. 

파일 크기가 110775에서 93879으로 줄어든 것을 확인할 수 있다. 일반적인 ZIP압축보다 압축률이 떨어지는데 그 이유는 결과물이 PE파일이기 때문에 PE헤더를 추가해야 하고, 압축해제를 위한 코드도 넣어야 하기 때문이다.

 

 

 

notepad.exenotepad_upx.exe의 파일 비교

*패커 종류와 각 옵션에 따라 실행 압축된 파일의 모양은 다르다.

https://jihoon6078.tistory.com/48

> PE header의 크기는 동일

> 섹션 이름 변경(".text" -> "UPX0", ".data" -> "UPX1")

> 첫 번째 섹션의 RawDataSize = 0(파일에서의 크기 0)

> Entry Point는 두 번째 섹션에 위치(원본 notepad.exe EP는 첫 번째 섹션에 위치)

> 리소스 섹션(.rsrc)의 크기는 거의 변하지 않음

 

 

PEview를 통해 notepad_upx.exe의 첫 번째 섹션 헤더를 보면 VirtualSize값은 10000으로 세팅되어 있는 반면 Size of Raw Data값은 0으로 되어 있는 걸 확인할 수 있다.

* UPX는 왜 첫 번째 섹션을 비어있게 만들었을까?

  : 압축해제 코드아 압축된 원본 코드는 두 번째 섹션에 존재하며 UPX로 실행 압축된 파일은 실행되면 먼저 압축 해제 코드가 실행되어 압축된 원본 코드를 첫 번째 섹션에 해제시켜 압축해제 과정이 끝나면 원본 EP코드를 실행하게 한다.

 

UPX 실행 압축된 notepad 디버깅 & 디버거로 OEP 찾기(언패킹)

1.1 notepad.exe의 EP Code

010073B2 주소에서 GetModuleHandleA() API를 호출해서 notepad.exe프로세스의 ImageBase를 구한다.

그리고 010073B4와 010073C0 주소에서 각각 MZ와 PE 시그니처를 비교한다. 

 

 

1.2 notepad_upx.exe의 EP Code

EP 주소는 0101C950이며, 이곳은 두 번째 섹션의 끝부분이다. 실제로 압축된 notepad 원본 코드는 EP 주소(0101C950) 위쪽으로 존재한다. 코드 시작 부분(0101C950)을 살펴보자

0101C950   60              PUSHAD
0101C951   BE 00100101     MOV ESI, 01011000
0101C952   8DBE 0000FFFF   LEA EDI, DWORD PTR DS:[ESI-10000]

PUSHAD 명령으로 EAX~EDI 레지스터 값을 스택에 저장하고, ESI 레지스터를 두 번째 섹션 시작 주소(01011000)EDI 레지스터를 첫 번째 섹션 시작 주소(01001000)로 세팅한다.

 

*첫번째 섹션인지 두번째 섹션인지 PEview를 통해 확인 가능하다. Image Base는 1000000이고

첫 번째 섹션의 RVA는 1000이다.

Image Base + 1000 = 1001000이므로 EDI에는 첫번 째 섹션의 시작 주소가 들어가있는 것이고

두 번째 섹션의 시작주소는 Image Base + 11000 = 1011000이므로 ESI에는 두번 째 섹션의 시작 주소가 들어가있는 것이다.

UPX 파일에서 첫 번째 섹션은 메모리에서만 존재합니다. 이곳이 바로 압축해제된 원본 파일의 코드가 저장될 장소이다. 

디버깅할 때 이처럼 ESI와 EDI가 동시에 세팅되면 ESI가 가리키는 버퍼에서 EDI가 가리키는 버퍼로 메모리 복사가 일어날 거라고 예측할 수 있다. 이 경우는 Source(ESI)로부터 데이터를 읽어서 압축을 해제한 후 Destination(EDI)에 저장시킬 것이다. 

우리 목표는 UPX EP 코드를 전부 쫓아가서(트레이싱) 원본 notepad의 EP 코드(OEP)를 찾아내는 것이다.

 

*OEP : Original Entry Point 패킹된 파일의 실제 시작 부분 원본 EP를 뜻한다.

*트레이싱 : 코드를 하나하나 실행하면서 쫓아가는 것을 말한다.

 

ESI이 가리키고 있는 두번째 섹션 부분(01011000)
EDI이 가리키고 있는 첫번째 섹션 부분(01010000)

 

2. UPX 파일 트레이싱

2.1 루프 #1

EP 코드에서 Animate Over[Ctrl + F8] 명령을 내리면 트레이싱이 시작된다. 트레이싱을 멈추고 싶을 때는 [F12] 명령을 내리면 된다. 조금 진행하다 보면 짧은 루프가 나타나는데 해당 루프를 자세히 살펴보자

첫번째 루프 #1

 

0101C9F5 | 8A02   | mov al,byte ptr ds:[edx]     #edx값을 al로 옮긴다.
0101C9F7 | 42     | inc edx                      #edx++
0101C9F8 | 8807   | mov byte ptr ds:[edi],al     #edi값을 al로 옮긴다.                                  
0101C9FA | 47     | inc edi                      #edi++                                     
0101C9FB | 49     | dec ecx                      #ecx--                                       
0101C9FC | 75 F7  | jne notepad_upx.101C9F5      #ZF가 0아니라면 101C9F5로 분기                                      

루프의 내용은 EDX(0100101F)에서 한 바이트를 읽어 EDI(01001020)에 쓰는 것이다. EDX 레지스터가 가리키는 01001000주소는 첫 번째 섹션의 시작 주소이며 메모리에서만 존재하는 섹션이다. 

0101C9FE 주소에 BP(Break Point)를 걸고 실행 명령으로 해당 루프를 탈출한다.

 

 

2.2 루프 #2

두번째 루프 #2

이곳이 본격적인 디코딩 루프 즉, 압축해제 루프이다.

ESI가 가리키는 두 번째의 주소에서 차례대로 값을 읽어서 적절한 연산을 거쳐 압축을 해데하여 EDI가 가리키는 첫 번째 섹션의 주소에 값을 써준다. 핵심 명령어는 다음과 같다.

0101C9F8 | 8807    | mov byte ptr ds:[edi],al        
0101C9FA | 47      | inc edi       

...

0101CA09 | 8907    | mov dword ptr ds:[edi],eax   
0101CA0B | 83C7 04 | add edi,4                        

*al(EAX)에는 압축해제된 데이터가 있고, EDI는 첫 번째 섹션의 주소를 가리키고 있다.

이 두 번째 루프를 탈출하기 위해서는 0101CA1A 주소에 BP를 걸고 실행하면 된다. 여기까지 실행이 잘 되었다면 다음과 같이

덤프창에 첫번째 섹션 영역에 압축해제된 코드가 쓰여진 걸 확인할 수 있다.

[전] EDI이 가리키고 있는 첫번째 섹션 부분(01010000)
[후] EDI이 가리키고 있는 첫번째 섹션 부분(01010000)

 

2.3 루프 #3

세번째 루프 #3

이 부분은 원본 코드의 CALL/JMP 명령어의 destination 주소를 복원시켜주는 코드이다.

이 루프도 마찬가지로 0101CA4E에 BP를 걸고 다시 진행하여 탈출한다. 이제 IAT(Impot Address Table)세팅만 하면 UPX 압축 해제는 끝이 난다.

* 일반적으로 실행 압축 파일은 원본 파일의 코드, 데이터, 리소스의 압축해제 과정이 끝나면 IAT를 세팅하고 OEP로 간다.

 

 

2.4 루프 #4

네번째 루프 #4

위의 빨간색 부분이 바로 IAT를 세팅하는 루프이다. 

0101CA4E주소에서 EDI = 101B000으로 세팅되며, 이곳은 두 번째 섹션 영역(UPX1)이다. 이곳에는 원본 notepad.exe에서 사용되는 API 이름 문자열이 저장되어 있다.

API 이름 문자열

이 문자열은 UPX가 notepad.exe를 실행 압축할때, 원본 파일의 IAT를 분석하여 사용하는 API이름 목록을 따로 뽑아 놓은 것이다. 이 API 이름 문자열이 저장된 곳을 가리키는 EDI에서 API 이름 문자열을 하나씩 뽑아 

101CA7F주소의 GetProcAddress()를 호출하여 API시작 주소를 얻어, EBX가  레지스터가 가리키는 원본 notepad.exe의 IAT 영역 API주소를 입력한다.

이 과정을 API 이름 문자열이 끝날때까지 반복하면 , 원본 notepad.exe의 IAT 복원과정이 마무리 된다.

 

☆ 원본 notepad.exe의 압축을 모두 해제하였으므로 이제 남은 것은 OEP로 제어 돌려주는 것이다. 

 

2.5 마지막 OEP로 ,,, #5

0101CAC5주소의 POPAD 명령은 UPX코드 중 가장 첫 번 째 명령인 PUSHAD에 대응되는 명령으로 레지스터를 원래대로 복원시키는 명령이다. 

최종적으로 0101CAD3 주소의 JMP명령어에 의해서 OEP로 간다. 참고로 JMP하는 주소 100739D는 원본 notepad.exe의 EP주소이다. 이로써 OEP 코드 찾는 실습을 마무리하겠다~!!!

 

 

OEP 빨리 찾는 법

3.1POPAD 명령어 이후의 JMP 명령어에 BP 설치  

   UPX 패커의 특징 중 하나가 EP코드가 PUSHAD/POPAD명령어로 둘러 쌓인 것이다. 그리고 OEP코드로 가는 JMP명령어가 ㅖPOPAD명령어 이후에 나타나므로 JMP명령어에 BP를 걸고 실행하면  한번에 OEP코드로 이동한다.

 

4.2 스택에 하드웨어 브레이크 포인트 설치

이 방법 역시 UPX 특징인 PUSHAD/POPAD 명령어 특성을 이용한 것이다.

위는 PUSHAD 명령을 실행한 후 스택이다. Dump창에서 000DFF54 스택 주소로 간 후 마우스 커서를 정확히 그 주소에 위치시킨 후 다음과 같이 하드웨어 BP를 설치한다.


하드웨어 BP란 CPU에서 지원하는 BP로 4개까지 설치 가능하며, 일반적인 BP와 달리 BP설치된 명령어가 실행된 이후에 제어가 멈추게 된다. 이 상태에서 실행하면 압축이 해제되면서 코드가 실행되고 POPAD가 호출되는 순간에 하드웨어 BP가 설치된 000DFF54주소를 액세스하고 제어가 멈추면 바로 밑에 OEP로 가는 JMP 명령어가 있다. 결과는 다음과 같다.

 

'Hacking > Reversing' 카테고리의 다른 글

32bit로 IAT, EAT 로딩 과정  (0) 2020.10.12
Wargame 'Dreamhack' rev-5  (0) 2020.10.01
Wargame 'Dreamhack' rev-4  (0) 2020.10.01
Wargame 'Dreamhack' rev-3  (0) 2020.09.19
WOW64 file system redirection  (0) 2020.09.17
Comments