본문 바로가기

RVS

UPX Manual Unpacking

 프로그램 개발자는 프로그램의 크기를 줄이거나 혹은 데이터를 보호하기 위하여 프로그램을 실행 압축하는데 이를 패킹 (Packing)한다고 한다. 악성코드에서는 분석이 불가능하게 하기 위한 목적으로 패킹을 수행한다.

그리고 분석가들은 주로 이렇게 패킹된 프로그램을 분석하기 위하여 언패킹(UnPacking)을 수행하여야 한다.

 

 

패킹하는 방식은 여러 가지가 존재하며 대표적으로 UPX 방식의 패킹이 있다.

UPX 방식으로 패킹된 프로그램을 언패킹 (Unpacking)할 수 있는 도구는 많이 공개되어 있다.

그러나, 이 글에서는 언패킹 프로그램이 사용하는 것이 아닌 디버거를 통해 손수 언패킹하는 과정을 기술할 것이다.

 

그 전에 OEP(Original Entry Point)에 대해서 알 필요가 있다.

OEP는 프로그램의 시작 위치이다. 패킹된 파일은 OEP, 즉 시작 부분이 다르다.

그렇기 때문에 언패킹을 통해 정상적인 파일의 시작부분으로 돌아가게 되고, 이 부분을 바로 OEP라고 한다.

 

즉, 언패킹은 패킹된 프로그램으로부터 OEP를 찾아내는 과정이다.

 

UPX로 패킹된 프로그램의 간단한 절차는 다음과 같다.

PUSHAD 명령을 통해 레지스터의 값을 스택에 저장한다. 그리고 언패킹 루틴이 끝나면 POPAD 명령을 통해 스택에 저장된 레지스터를 복구하고, 언패킹된 정상 프로그램이 실행된다.

 

따라서, 레지스터가 복구되는 시점을 활용해서 OEP를 찾는 것이다.

 

아래의 예시는 reversing.kr의 Ransomware 문제를 활용한 것이다.

 

 

1. PUSHAD 명령 찾기

 

  먼저, 언패킹된 프로그램을 디버거를 통해 열어보면 (그림 1)처럼 00ECECE0 주소에서 pushad 명령을 볼 수 있다.

앞서, UPX로 패킹된 프로그램의 경우 pushad 명령을 통해 레지스터의 값을 스택에 저장한다고 하였다.

 

pushad 명령이 실행되기 전 레지스터는 pushad가 실행된 후 ESP 레지스터에 세팅된 주소에 저장된다.

아래 (그림 2)는 pushad 명령이 실행되기 전에 세팅된 레지스터이다.

 

pushad 명령 실행 후 스택에 레지스터의 값이 저장된 것은 좀 더 뒤에서 살펴보자.

 

그림 1
그림 2

 

 

2. POPAD 명령 찾기

 

  마찬가지로 앞서 패킹된 프로그램이 언패킹 루틴이 종료되면 POPAD 명령을 통해 레지스터를 복구한다고 하였다.

그렇다면 우리는 POPAD 명령을 찾아야 한다.

 

 우선, (그림 1)의 상태에서 [F8] 단축키를 통해 PUSHAD 명령을 한 줄 수행해보자. 

PUSHAD 명령이 수행되고 난 후 (그림 3)과 같이 레지스터가 세팅된다. 앞서 언급했듯이 POPAD 명령이 수행된 후에POPAD 명령이 수행되기 전의 레지스터 값이 POPAD 명령이 수행된 후 세팅된 ESP 레지스터 값의 스택 주소에 저장된다.

 

그림 3

 

  (그림 4-1)는 (그림 3)의 ESP 레지스터에 저장된 0019FF64 스택 주소에 저장된 데이터이다. 그리고 아래의 (그림 4-2)는 POPAD 명령이 수행되기 전의 레지스터 값이다. 

PUSHAD 명령은 EAX ECX EDX, EBX, ESP, EBP, ESI, EDI 레지스터의 데이터를 순서대로 저장하는데, (그림 4-1)과 (그림 4-2)를 통해 레지스터 값이 스택에 저장된 것을 확인할 수 있다.

 

 

그림 4-1
그림 4-2

 

여기서부터는 프로그램을 재실행하였기 때문에 위의 그림과 스택 주소가 조금씩 다를수 있다.
UPX 언패킹 과정만 보자!

 

(그림 5)에서처럼 0019FF64 주소에 하드웨어 브레이크 포인트를 걸어주자.

 

그림 5

 

  하드웨어 브레이크를 걸어둔 후 [F9] 단축키를 통해 실행해보면 (그림 6)과 같이 EIP가 00ECEE76 주소로 도달한다.

00ECEE76 바로 위에서 우리는 POPAD 명령어를 찾았다.

 

그림 6

 

여기서, 하드웨어 브레이크를 왜 걸어두는지 이해를 못할 수 있다.

먼저, 하드웨어 브레이크란 디버그 레지스터(8개)를 이용하여 CPU 레벨에서 브레이크포인트를 설정하는 것이다.

주로 설정할 브레이크포인트의 개수가 적을 때나 디버깅 할 소프트웨어의 코드가 변경되면 안될 때 유용하게 사용한다.

하드웨어 브레이크의 동작 방법은 다음과 같다.

 

1. CPU가 명령을 실행하기 전에 해당 주소의 하드웨어 브레이크포인트 설정 여부를 확인한다.

 

2.  수행할 명령이 하드웨어 브레이크포인트가 설정된 주소에 접근하는 지 확인한다.

 

3. 해당 주소가 DR0 ~ DR3 레지스터에 저장되어 있고 읽기 및 쓰기나 실행 조건이 설정되어 있다면 CPU는 명령에 대한 실행을 중지시키고 INT 1 이벤트 발생한다.

 

4. 해당 주소가 디버그 레지스터에 저장되어 있지 않다면, CPU는 해당 명령을 실행하고 다음 명령으로 이동해 하드웨어 브레이크포인트 설정 내용 확인한다.

 

  아래의 참조 그림을 보면 디버그 레지스터 (DR0)에 하드웨어 브레이크포인트를 설정한 주소가 저장되어 있는 것을 볼 수 있다.

 

참조 그림

 

  즉, POPAD 명령이 수행되면 0019FF64 스택 주소에 저장된 레지스터의 데이터를 범용 레지스터에 채우기 때문에 0019FF64 주소로 액세스하게 된다.

 

이렇게 우리는, POPAD의 위치를 찾을 수 있다.

 

 

3. OEP 찾기

 

  POPAD 명령을 찾았다며 이제 OEP 위치를 찾아야한다. POPAD 아래 쪽을 살펴보면 JMP 명령문이 존재한다.

(그림 7)에서 보면 바로 00ECEE83 주소이다. jmp run.44AC9B 명령을 통해 44AC9B 주소로 점프하도록 한다.

[F2] Breakpoint 단축키를 통해 00ECEE83 주소에 Breakpoint를 걸어두고 [F9] 단축키로 실행해보자.

그림 7

 

그러면, (그림 8)과 같이 00ECEE83 주소까지 실행되었다.

[F8] 단축키를 통해 해당 명령을 수행해보자.

 

그림 8

 

 

(그림 8)의 00ECEE83 주소를 실행하면, (그림 9)의 주소로 도달함으로써 OEP를 찾을 수 있다.

OEP는 (그림 9)의 0044ACA7 PUSH EBP이다.

그림 9

 

 

4. 언패킹된 프로그램 덤프

 

  우리는 OEP를 찾았으니 언패킹된 프로그램을 덤프하여, 디버깅 시 귀찮은 언패킹 과정을 하지 않도록하자.

x32dbg 디버거는 메모리를 덤프할 수 있는 Scylla 플러그인을 제공한다.

[Ctrl] + [I] 단축키를 통해 Scylla 플러그인을 실행하면 (그림 10)와 같은 창이 열린다.

 

그림 10

 

  (그림 10)에서 [IAT Autosearch] > [Get Imports] -> [Dump] 버튼을 차례로 클릭함으로써 (그림 11)과 같이 새로운 프로그램을 저장한다.

 

그림 11

 

  그리고 [Fix Dump] 버튼을 클릭하여 (그림 11)에서 새로 저장한 프로그램을 열어 덤프한 파일을 새로 빌드한다.

그러면 (그림 12)의 Log 창에서 run_dump _SCY.exe로 파일을 저장했다는 것을 알 수 있다.

 

그림 12

 

이제 우리는 언패킹된 run_dump_SCY.exe 프로그램을 통해 디버깅을 수행하면 된다.

'RVS' 카테고리의 다른 글

Reversing.kr 11번 문제(Easy ELF)  (0) 2020.03.19
Reversing.kr 7번 문제 (Position)  (0) 2020.03.19
Reversing.kr 9번 문제(Ransomware)  (0) 2020.03.16
Reversing.kr 2번 문제(Easy Keygen)  (0) 2020.03.11
Reversing.kr 4번 문제(Music Player)  (1) 2020.03.04