UPX MUP (Manual Unpacking)
UPX MUP를 해보겠습니다.
0x01. Packing
MUP 대상 program은 reversing 입문자 용 문제인 abex' crackme 1 입니다.
Packing 전의 등록정보를 확인해 보겠습니다.
크기가 8.00KB인 것을 확인할 수 있습니다.
이제 해당 program을 UPX로 packing 한 후 등록정보를 확인해 보겠습니다.
크기가 6.50KB로 줄어들었습니다.
이처럼 UPX는 파일 크기를 줄여주는 packer의 목적에 충실합니다.
0x02. PE File Format
Packing 전후의 PE 구조를 비교해 보겠습니다.
왼쪽이 packing 하지 않은 program이고
오른쪽이 packing 한 program입니다.
Section의 수가 줄어들고 section 명에 UPX라고 적혀있습니다.
Packing된 program의 각 section의 header를 확인해 보겠습니다.
UPX0 section의 header입니다.
UPX0 section의 0ffset은 0x00000400입니다.
살펴보면 특이한 부분이 있습니다.
Virtual Size는 0x00006000인데 반해
Size of Raw Data는 0x00000000입니다.
일단은 넘어갑니다.
UPX1 section의 header입니다.
UPX1 section의 offset은 0x00000400입니다.
UPX0 section의 offset과 동일합니다.
이유는 UPX0의 offset이 0x00000400이지만 크기가 0인 관계로
바로 다음에 오는 UPX1 section이 같은 offset을 갖게 된 것입니다.
UPX0 section은 disk 상에서는 크기가 0으로 아무것도 없지만
Memory에 mapping 될 때는 크기가 0x00006000으로 바뀝니다.
이는 program이 실행될 때 UPX1에 저장된 packing된 code들이
UPX0에 unpacking되어 저장되기 때문입니다.
실제로 UPX0 section과 UPX1 section의 offset인 0x00000400을 가보면
Packing된 code들이 있는것을 확인할 수 있습니다.
0x03. Manual Unpacking
UPX MUP는 매우 간단합니다.
Debugger를 통해 packing된 파일을 열어보겠습니다.
제일 먼저 PUSHAD가 보입니다.
UPX로 packing된 program의 흐름을 간단히 설명하겠습니다.
먼저 PUSHAD를 통해 register 값을 stack에 저장합니다.
그리고 unpacking routine이 끝나면 POPAD를 통해 register를 복구하고
Unpacking된 정상 program이 실행됩니다.
Register가 복구되는 시점을 활용해 한번에 OEP로 갈 수 있습니다.
PUSHAD 이전의 register 상태입니다.
PUSHAD를 실행합니다.
Stack에 PUSHAD를 실행하기 이전의 register 값들이 들어가는 것을 확인할 수 있습니다.
ESP를 따라가 hardware break point를 걸어줍니다.
Register의 크기는 DWORD이므로 DWORD로 걸어줍니다.
실행합니다.
위와 같은 위치에서 멈추는 것을 확인할 수 있습니다.
정지한 위치의 위를 보면 POPAD 명령이 보입니다.
JMP 명령을 따라갑니다.
OEP에 도착했습니다.
Dump를 뜨고 IAT를 복구해주면 unpacking된 프로그램이 완성됩니다.