Packer로 프로그램을 실행 압축하게 되면 disassembler로 분석하기 어려워진다.
그렇다면 실행 압축을 하면 왜 분석하기 어려울까?
프로그램을 실행하면 EntryPoint부터 해당 프로그램의 Code를 실행하고 그 위치는 PE 구조에서 .text영역에서 시작한다. 하지만 실행 압축을 하면 원본 Code가 있는 .text영역을 압축하고 그 압축을 해제하는 코드를 새로운 영역에 넣고 EntryPoint를 그 위치로 옮기기 때문에 정적 disassembler로 원본 Code를 확인할 수 없게 된다. Packer로 압축한 프로그램을 IDA와 같은 정적 disassembler로 분석할 때 보여주는 코드는 원본 Code를 압축해제하기 Unpacking Code다.
UPX가 Packer의 한 종류로 많이 알려지고 용량을 줄이기 위해 단순 실행압축만 하기 때문에 구글에 검색하면 unpacker를 쉽게 찾아 볼 수 있다.
문제는 Protector라는 녀석들이다.
Protector는 실행 파일을 압축하고 난독화하는 Packer의 한 종류로 실행 파일의 설계를 파악하기 어렵게 여러가지 분석방해 기술을 사용한다. 상용 프로그램인 VMProtect와 Themida 등이 있고 이런 Protector로 Protecting된 실행 파일은 분석 도구가 없거나 별도의 도구를 만들어서 분석을 해야 된다. 우리가 많이 사용하는 KakaoTalk, 금융 보안 프로그램, 여러 게임 등이 Themida를 많이 사용하고 있다.
프로그램의 시작지점은 Entrypoint라 불리는데 Packer로 프로그램을 Packing하면 원본 코드가 압축되고 그 압축을 해제하는 코드 지점이 Entrypoint로 지정된다. 따라서 원본 코드를 분석하고자 하면 그 시작점, OEP(Original EntryPoint)를 알아야 된다.
Basic RCE L5 파일을 가지고 진행하였다.
PUSHAD 명령이 수행되면 추후 언패킹된 코드에서 사용하기 위해서 EAX,ECX,EDX,EBX,ESP,EBP, ESI,ESI 값이 스택에 저장된다. 해당지점에 hardware breakpoint를 걸도록 하겠다.
ESP위치(스택위치) 0x19FF78로 이동한 후 레지스터 값이 다 써진 0x19FF58에 hardware breakpoint 접근 -> byte를 설정하였다.
해당 영역으로 가면 packing된 data를 확인할 수 있다.
해당 부분으로 가면 0x401000인 부분으로 가게 되고 이 영역에 unpacking된 code가 써진다.
popad 다음 레지스터를 복원할 때 멈추게 되는데 이후 jmp 05.441270이라는 명령어를 확인할 수 있는데 바로 이 명령어가 OEP로 이동하는 부분이다. 즉 OEP는 0x441270이 됨을 알 수 있다.
Plugin -> Scylla에서 OEP에 441270(찾은 OEP)를 입력하고 IAT AutoSearch를 클릭한다.
이후 Get Imports를 누르면 저장되지 않았던 Imports 함수를 찾아주고 Fix Dump를 사용하여 메모리에서 추출한 실행파일을 선택하면 수동으로 Import Table을 복구하고 정상적으로 실행할 수 있다. (원본 파일은 그대로 남겨두기 위해서 Dump 후 해당 파일에 Fix Dump를 하였다.)
정상적으로 실행됨을 확인 할 수 있다.
UPX 같은 경우 pushad와 popad로 unpacking 패턴을 찾아서 unpacking이 쉽게 가능하지만 다른 protector들의 경우에는 그러한 패턴을 찾기 쉽지 않다. 그럴 경우에는 실행 권한을 가지는 코드 영역을 이용해서 unpacking을 할 수 있는데 Memory Map을 보면 다음과 같이 각 영역 마다 권한이 있는 것을 확인할 수 있다.
원본 코드가 실행되기 위해서는 해당 영역에 실행 권한을 가져야 된다. 실행권한이 있는 영역의 권한을 제거 후 프로그램을 실행하면 Unpacking이 전부 진행된 후에 원본 코드가 실행되기 위한 OEP에서 실행 권한이 없다는 에러가 발생한다. 이후 이 주소를 잘 기억해 놓았다가 Unpacking Stub이 끝나는 지점으로가면 원본 코드 분석이 가능하다. 물론 그 지점을 가기 위해서 중간 breakpoint를 거는 함수를 찾아야 된다. 보통 Unpacking을 하면서 원본 코드를 메모리에 다시 써야되는 작업을 하기위한 VirtualAlloc이나 VirtualProtect 등의 함수를 사용하여 해당 지점까지 간다.(여러 번 반복해야된다.)