Chap 14. 실행 압축
14.1. 데이터 압축
적절한 알고리즘을 사용하면 파일의 크기를 줄일 수 있다. 압축된 파일을 100% 원래대로 되돌릴 수 있으면 “비손실 압축”, 원래대로 복원할 수 없으면 “손실 압축” 이라고 한다.
- 비손실 압축 : Run-Length, Lempel-Ziv, Huffman 등의 알고리즘이 있다. (ZIP, RAR 등의 포맷)
- 손실 압축 : 의도적으로 파일에 손상을 주어서 압축률을 높인다. jpg, mp3, mp4 등의 파일이 있고, 사람이 차이를 구분할 수 없을 정도의 손상을 준다.
14.2. 실행 압축
실행 파일(PE : Portable Executable)을 압축하여 실행되는 순간에 메모리에서 압축을 해제하여 실행하는 기술이다. 파일 내부에 압축 해제 코드를 포함하고 있다. 실행 압축된 파일 또한 PE 파일이며, EP(Entry Point) 코드에 decoding 루틴이 실행되면서 메모리에서 압축을 해제한 후 실행된다.
--> 주요 차이점 : 실행 압축은 PE 파일의 실행이 가능하다는 것이다.
일반 PE 파일을 실행 압축 파일로 만들어주는 유틸리티를 패커라고 하며, 더 Anti-Reversing 기법에 특화된 패커를 프로텍터라고 한다.
14.2.1. 패커
- 목적 : PE파일의 크기를 줄임, 내부 코드와 리소스를 감춤
- 현황 : DOS 시절에는 PC속도가 느리기 때문에 압축을 해제하는 과정이 큰 오버헤드 였지만, 현재는 널리 사용되고 있음
upx.exe 파일이 있는 곳에 notepad.exe 파일을 옮긴 후 커맨드 창에 다음과 같이 실행하면 실행 압축 파일이 만들어진다.
67586 -> 48128로 파일 크기가 줄어든 것을 확인할 수 있다.
두 파일을 PE file format 관점에서 보면 다음과 같다.
<특징>
- PE header의 크기는 동일(0 ~ 400)
- 섹션 이름 변경
- 첫 번째 섹션의 RawDataSize = 0 (파일에서의 크기가 0)
- upx 파일의 EP(Entry Point)는 두번째 섹션에 위치
- .rsrc의 크기는 거의 변하지 않음
<PEview로 notepad_upx.exe 파일을 열어서 확인한 결과>
UPX0의 헤더를 확인하면 Size of Raw Data(파일에서 섹션이 차지하는 크기)가 0인데 반해, Virtual Size(메모리에서 섹션이 차지하는 크기)는 10000으로 세팅되어 있음을 확인할 수 있다. 즉, UPX로 실행 압축된 파일은 실행되는 순간에 파일에 있는 압축된 코드를 메모리에 있는 첫번째 섹션에 풀어 버린다. 파일에서 압축해제 코드와 압축된 원본 코드는 두번째 섹션에 존재한다. —> 15장은 디버거를 이용하여 실제 압축 해제 과정을 디버깅 함
Chap 15. UPX 실행 압축된 notepad 디버깅
15.1. notepad.exe의 EP code
010073AC 주소에서 GetModuleHandleA() API를 호출하여 ImageBase를 구하고 있다. 또한 010073B4, 010073C0 주소에서 각각 MZ, PE 시그니처를 비교하고 있다.
15.2. notepad_upx.exe의 EP code
EP 주소는 01015330이며, PUSHAD 명령을 통해 EAX ~ EDI 레지스터의 값을 스택에 저장한다. 그리고 ESI 레지스터에는 두번째 섹션의 시작 주소인 01011000을 저장하고, EDI 레지스터에는 첫번째 섹션의 시작 주소인 01001000을 저장한다. 아래의 그램은 01015336 주소의 명령을 실행한 직후의 레지스터 값이다.
따라서 두번째 섹션(ESI)로부터 데이터를 읽어서 압축을 해제한 후 첫번째 섹션(ESI)에 저장할 것을 예측할 수 있다.
참고로 원본 EP를 OEP(Original Entry Point)라고 하는데, 우리의 목표는 실행 압축 파일의 EP를 통해 OEP를 찾아내는 것이다.
압축을 해제하는 과정에는 많은 루프가 있는데, 루프를 적절히 탈출해야 빠르게 진행할 수 있다. OllyDbg에서는 아래의 단축키를 사용하면 편리하다.
Animate Over 명령어를 사용하여 진행하다 보면 첫번째 루프를 만나게 된다.
루프의 내용은 EDX(01001000)에서 한 바이트를 읽어서 EDI(01001001)에 쓰는 것이다. 즉, 메모리에서의 첫번째 섹션을 NULL로 채우는 것이다. 루프를 내용을 확인했기 때문에 빠져나오면 된다. 빠져나오는 방법은 루프의 다음 줄인 010153E6에 Break Point[F2]를 설치한 후 실행 명령[F9]을 하면 된다. 다음과 같이 두번째 루프를 만나게 된다.
두번째 루프는 본격적인 docoding을 하는 루프이다. 내용은 다음과 같다.
ESI가 가리키는 두번째 섹션에서 차례대로 값을 읽어서 적절한 연산을 거쳐 EDI가 가리키는 첫번째 섹션에 써주는 것이다.
두번째 섹션을 넘어가기 위해 01015402 주소에 BP를 설치하고 실행한다.
다시 트레이싱을 시작하면 세번째 루프를 만날 수 있다.
세번째 루프의 내용은 원본 코드의 CALL/JMP 명령어의 destination 주소를 복원시켜주는 것이다. 이 루프도 01015436에 BP를 설치하고 실행하여 탈출한다. 이어서 네번째 루프를 만나게 된다.
이 과정은 IAT(Import Address Table)을 세팅하는 것이다. 첫번째 줄에서 EDI가 01014000으로 세팅되는데, 해당 주소를 dump를 확인하면 다음과 같다.
API 이름들이 나열되어 있다. 이는 원본 파일 notepad.exe를 UPX가 압축시킬 때, IAT를 분석하여 API 이름의 목록을 뽑아놓은 것이다. 따라서 네번째 루프의 내용은 API 목록을 확인하고 01015467 주소에서 GetProcAddress()를 호출하여 API 시작 주소를 얻은 후 IAT 영역에 입력하는 과정이다. 일반 적인 실행 압축 파일은 이 과정이 끝나면 OEP로 점프하게 된다.
010154AD 에서는 최초에 PUSHAD 명령과 대응되는 POPAD 명령을 하게 된다. 즉, 스택에 있는 레지스터 값들을 복원시키는 명령이다. 최종적으로 010154BB 주소의 JMP 명령에 의해서 OEP로 점프한다.
15.4. UPX의 OEP를 빨리 찾는 방법
<첫번째 방법>
UPX 패커의 특징은 EP코드가 PUSHAD/POPAD 명령어로 둘러싸여 있고, OEP 코드로 가는 JMP 명령어는 POPAD 명령어 바로 이후에 나타난다는 것이다. 따라서 해당 JMP 명령어를 찾아 BP를 설치하면 바로 OEP로 갈 수 있다.
<두번째 방법>
PUSHAD/POPAD의 특성을 이용하는 방법이다. PUSHAD를 하면 스택에 레지스터 값이 쌓이게 된다.
스택의 가장 윗 부분인 0006FFA4를 Dump 창에서 찾는다.
정확히 0006FFA4를 클릭한 후, 우측 메뉴에서 Breakpoint -> Hardware, on access -> Byte를 설정한다. 이는 하드웨어 BP인데, 하드웨어 BP란 CPU에서 지원하는 BP이며, 5개까지 설치 가능하다. POPAD가 호출되는 순간 0006FFA4 주소를 엑세스하면 제어가 멈추게 된다.
'리버싱핵심원리' 카테고리의 다른 글
Reverse Engineering Chap 18-20 (0) | 2016.01.17 |
---|---|
Reverse Engineering Chap 23 (0) | 2016.01.17 |
Reverse Engineering Chap 16-17 (0) | 2016.01.14 |
Reverse Engineering Chap 13 (0) | 2016.01.12 |
Reverse Engineering Chap7-12 (0) | 2016.01.10 |