본문 바로가기

리버스엔지니어링/프로그램 분석

upx 언팩

드디어 upx 언팩 방법을 쓰는군요.

동아리 활동+ 시험기간이 다가오느라 블로그를 굉장히 오랬동안 못써서 미안해야 되지만 아직 시험기간이므로 5월달 되야지 다음글 쓸것같네요.

 

너무 어렵고 귀찮다 싶으면 크고 강렬하게 색칠한 제목부분만 보고 맨 밑의 요약으로 내려가세요.                                       ↓

도입

저번 코드엔진 L06 파일의 언팩을 안한 상태로 디버거를 실행시키면 004298F0에서 pushad라는 명령어로 upx언팩부분이 시작됩니다.

 

그냥 코드를 쭉 읽어보면

mov  esi,386D13B0.00424000

lea    edi,dword ptr ds : [esi+FFFDD000]

 …

jmp   short 386D13B0.00429912

이렇게 되있는데요 386D13B0은 파일 이름이니 빼면

1) esi레지스터에 00424000이라는 수(주소)를 넣는다.

2) edi레지스터가 가리키는 주소에 [esi+FFFDD000]주소가 가리키는 값을 넣는다

3) 004029912주소로 jmp한다

라는 뜻인데요

upx패킹이 되있는 파일에서 00424000이라는 주소는 upx패커가 만들어낸 UPX1이란 주소의 시작점입니다.

-PEview로 본 UPX1의 시작점

 

또한 esi+FFFDD000의 주소에 들어있는 값은

이렇게 00401000이란 주소인데요

00401000란 주소는 UPX0 섹션의 주소로서 지금은 아무런 값이 없습니다.

그럼 흐름을 보면 우리는 UPX1과 UPX0을 이제 다뤄서 원래 섹션으로 바꾸겠다는 뜻으로 해석하면 될것입니다.

 

 

 

 소루프1

이번에는 jmp구문이 넘어간 다음의 루프를 보시겠는데요

제가 예전에 팩(pack)자체가 용량을 줄이기 위해 쓴다고 했는데요 그러면 용량을 줄이는 알고리즘을 쓸것이므로 저희는 그 알고리즘이 작동하는 루프를 중심으로 보면 되기 때문에 전체 코드를 한줄한줄 해석하는것이 아니라 여기는 어떤 루프이다 정도로 보겠습니다.

 

코드를 써보면

mov al,byte ptr ds:[esi]

inc esi

mov byte ptr ds:[edi],al

inc edi

add ebx,ebx

jnz short 386D13B0.00429919

mov ebx,dword ptr ds:[esi]

sub esi,-4

adc ebx,ebx

jb short 386D13B0.00429908

이렇게 코드가 진행이 되는데요 위의 그림과 같이 순서가 중간부터 시작되는데요

ebx에 esi주소가 가리키고 있는 값이 들어가고 esi에서 -4를 빼기(sub)를 하므로 4씩 주소를 더해간다고 생각하시면 됩니다.(왜 add esi,4를 안썻을까는 호기심이 듬)

그리고 위로 올라가서 al레지스터에 esi주소가 가리키는 값을 넣고

al의 값을 edi로 옮깁니다. 그리고 esi와 edi레지스터의 값을 각각 1씩 증가시킵니다.

 

그리고 어머? 위로 다시 올라가길레 루프인줄 알았더니 그대로 빠져나가네요

흐름에 몸을 맡기고 루프를 찾을때 까지 계속 밑으로 진행해봅시다.

 

 소루프2

코드를 써보면

mov al,byte ptr ds:[ebx]

inc edx

mov byte ptr ds:[edi],al

inc edi

dec ecx

jnz short 386D13B0.0042999D

이번에는 edx의 주소가 가리키는 값을 al에 넣고 al의 값을 edi에 옮긴뒤 edx와 edi레지스터의 값을 각각 1씩 올립니다.

그리고 이번에는 ecx의 값을 1씩 떨어트립니다!!!!

ecx레지스터는 count를 위한 레지스터로 한 루프를 돌때마다 ecx의 값을 떨어트리면서 루프를 돈 횟수를 저장하는데 주로 쓰입니다! 그렇다는 소리는 드디어 이번루프는 제대로된 루프란 소리네요!!!

하지만 이것도 잠시 맨 오른쪽 상단의 레지스터 창을 보시면 ecx의 값이 4라고 되있습니다.

이소리는 루프는 4번을 도는것입니다. 이렇게 루프를 끝내고 빠져나오시면

갑자기 맨 위로 다시 올라가게 됩니다.

바로 드디어 제가 루프들의 이름을 소루프라고 쓰게 된 이유가 나오는데요

 루프1

사실 이 루프의 전체적인 모습은 이렇게 되있으며

루프1은 소루프1,2와 3번 루프도 포함하고 있으면서 가운데 파란색으로 여러 연산들을 합니다.

이 루프의 전체적인 역할은 바로 압축풀기입니다.

 

아까부터 말했던 UPX1과 UPX0의 관계는 사실 이부분에서 드러나게 되는데요

소루프1에서 esi(UPX1)부분에서 압축이 풀린 데이터를 al로 넘기고 다시 al이 edi(UPX0)으로 데이터를 넘기는 것입니다.

그러면 소루프1에서 압축을 다 풀었는데 소루프2,3은 뭐냐? 하는것은 바로 PE구조를 배우면 아실텐데요 보통 평균적인 PE파일들은 데이터를 text,data,resource 3부분으로 나눠서 저장을 하기 때문에 각각 text,data,resource부분의 압축을 푼다고 보시면 됩니다.

중간에 긴 파란색 부분들은 압축 푸는 연산들을 하는곳이라 보시면 되고요.

 

 루프2

 

(루프는 004299C3부터 00429A03까지입니다)

루프1을 끝내고 나오시면 이제 루프2로 들어서게 됩니다.

루프2의 역할은 바로 IAT라는 부분을 원상복귀(압축풀기)를 하는 것입니다.

IAT라는 것은 Import Address Table로 어떤것을 Import하는데 그 Address들을 모아놓은 부분이라고 생각하시면 됩니다.

 

실제 어떤것들을 Import하는지 알아보기 위해 메모리에서 이 주소를 찾아보면 맨처음부터 GetModuleHandleA, FlushFileBuffers, SetStdHandle 등등의 여러가지 함수들을 참조합니다.

(왜 참조하는지는 PE구조등을 공부할때 32bit의 특성+멀티테스킹부분들의 여러가지 환경적요인때문에 이렇게 쓰인다는것을 알게되실겁니다.)

 

이러한 루프들을 다 거치고 나오면 드디어 맨 마지막 jmp구문들 만나면서 원래 파일의 OEP로 넘어가게 됩니다.

 

※정리

시작: 압축을 풀기위한 섹션 주소를 입력합니다.

ESI : UPX1을 가리키게 만듬

EDI : UPX0을 가리키게 만듬

루프1: 섹션에 들어가는 데이터들을 압축풉니다.

소루프1 : ESI->EDI로 압축 푼 데이터를 넘깁니다.

소루프2 : EDX->EDI로 압축 푼 데이터를 넘깁니다.

소루프3 : EDX->EDI로 압축 푼 데이터를 넘깁니다.

루프2: IAT를 원상복귀 시킵니다.

 

이걸로 UPX 언팩을 마치겠습니다.