언패킹(Unpacking)이란?
언패킹이란 패킹의 반대되는 개념으로, 패킹했던 파일의 패킹을 푸는 것을 의미합니다.
패킹된 프로그램이 실행되면 먼저 패킹이 된 원본 코드를 언패킹시키는 해제하는 코드를 실행한 다음 언패킹된 원본 코드를 실행시키게 되는데 그 코드를 덤프를 뜨는 것입니다.
언패킹 과정
프로그램을 실행시키면 가장 먼저 언패킹 코드가 실행되어 패킹된 코드를 복호화합니다.
복호화가 모두 끝나면 원래 코드의 주소로 점프하게 됩니다.
이 때 원래 코드의 시작 주소를 OEP(Original Entry Point)라고 부릅니다.
중요한 점은 언패킹된 코드는 메모리에만 존재합니다.
따라서 언패킹이 된 상태의 코드를 덤프 떠서 저장하는 것까지가 언패킹이라고 할 수 있겠습니다.
언패킹 방법
1. 언패킹 도구 사용
UPX나 ASPACK같이 널리 알려진 패킹 방식은 자동으로 언패킹을 해주는 도구가 있습니다.
그런 도구를 이용해서 쉽게 언패킹을 진행할 수 있습니다.
2. MUP(Manual UnPacking)
대부분의 패킹된 프로그램은 주로 시작부분에 PUSHAD가 있습니다.
PUSHAD는 레지스터를 스택에 PUSH하기 위해서 사용됩니다.
이 명령어가 패킹된 프로그램의 시작점에 있는 이유는 언패킹 과정에서 레지스터를 사용해야 하는데
언패킹 루틴 이후에 오는 원래 코드를 실행할 때 레지스터의 값이 바뀜으로 인해 프로그램이 정상적으로 실행되지 않는 것을 막기 위해서 레지스터의 값을 백업하는 것입니다.
그리고 언패킹 루틴이 종료되면 스택에 백업했던 레지스터들을 POPAD로 다시 복원하게 됩니다.
이러한 패킹된 프로그램이 실행되는 구조를 이용해서 패킹을 해제할 수 있습니다.
가장 먼저 PUSHAD를 실행시키면 ESP의 값이 바뀝니다.
그 바뀐 값을 메모리의 주소로 해서 이동합니다.
그리고 ESP의 값을 주소로 하는 값에 HW BP(HardWare BreakPoint)를 걸어줍니다.
그리고 F9로 실행시키면 POPAD가 있는 곳에서 멈추고 그 주변에서 OEP로 점프하는 코드를 찾을 수 있습니다.
그래서 많은 레지스터 중 하필이면 ESP에 HW BP를 걸어주는 이유가 무엇일까요?
ESP는 스택의 TOP부분의 주소를 가지고 있는 레지스터입니다.
PUSHAD로 스택에 넣은 값을 꺼낼 때 ESP의 주소에 있는 값에 접근하게 되고 그때 HW BP가 작동하면서 스택에 저장된 값이 레지스터로 돌아오는지 확인할 수 있게 됩니다.
덤프
덤프(Dump)는 주기억장치의 데이터를 보조기억장치에 저장하는 것을 뜻합니다.
덤프는 패킹된 프로그램의 OEP를 찾았을 때 그 데이터를 저장해서 언패킹 된 파일을 만들 때 사용됩니다.
먼저 패킹된 프로그램의 OEP를 찾아줍니다.
덤프를 뜨기 위해서는 플러그인이 필요합니다.
저는 OllyDumpEx라는 플러그인을 사용했습니다.
https://low-priority.appspot.com/ollydumpex/
여기서 파일을 다운로드 한 다음 압축을 풀어서 자신이 사용하고 있는 디버거에 맞는 DLL파일을 적당한 경로에 넣어주시면 됩니다.
이제 플러그인을 이용해서 덤프를 떠보겠습니다.
플러그인을 실행시키면 이런 창이 뜹니다.
플러그인을 실행시키면 이런 창이 뜹니다.
"Get EIP as OEP" 버튼을 눌러주고 "Dump" 버튼을 눌러줍니다.
경로는 적당히 지정해줍니다.
그리고 덤프된 파일을 실행시키면 놀랍지 않게도 오류가 발생합니다.
프로그램은 실행될 때 DLL이라는 라이브러리를 이용하는데 DLL에서 어떤 함수를 사용하는지에 대한 정보가 PE헤더에 저장되어있는데 덤프를 뜨는 과정에서 정보가 손상되어서 오류가 발생하는 것입니다.
이것은 importREC나 LordPE 같은 도구를 이용하여 해결할 수 있습니다.
저는 LordPE를 이용해보겠습니다.
https://www.softpedia.com/get/Programming/File-Editors/LordPE.shtml
프로그램을 실행시킵니다.
"Rebuild PE" 버튼을 누르고 파일을 선택합니다.
그러면 이런 창이 뜨고 프로그램이 정상적으로 실행됩니다.