Reversing/Reverse Engineering

매뉴얼 언패킹(MUP) 사례 - ESP Trick으로 OEP 찾기

📌 매뉴얼 언패킹(MUP: Manul UnPacking)의 핵심은 OEP를 찾는 것!

ESP 레지스터를 활용한 OEP 찾기

  • 스택 프레임: 함수가 사용하는 독립적인 메모리 영역 >> 함수가 실행 중일 때 존재하고 실행이 끝나면 사라짐
  • 스택: 메모리와 달리 기준점(EBP 레지스터; 프레임 포인)을 중심으로 데이터를 참조
  • EBP(프레임 포인터) : 하나의 루틴에서 서브루틴으로 이동할 때 EBP 레지스터의 값을 따로 보관해둬야한다. 서브루틴에서 원래의 루틴으로 복귀할 때(return할 때) 따로 보관해뒀던 값을 EBP 레지스터에 복구시킨다.
  • ESP(스택 포인터) : 현재 사용하는 스택의 맨 위를 가리킨다. 또한, EBP 레지스터의 값을 유지하는데 사용한다.
PUSH EBP
MOV EBP, ESP
// 원래 루틴의 ebp값을 스택에 push = esp가 가리키는 주소에 ebp 값 저장
.....
// 서브루틴
// 서브루틴 중 ebp에는 이전 루틴의 ebp를 가리키는 스택의 주소 저장
.....
MOV ESP, EBP
POP EBP
// esp에 들어간 데이터(이전 루틴의 프레임 포인)를 ebp로 옮김
JMP EAX

 

서브루틴 시작/종료할 때의 레지스터 변화

서브루틴 시작

서브루틴 시작

①~② 서브루틴에 처음 진입했을 때 ESP 레지스터에는 0018ff8c가 저장되어 있고, EBP 레지스터에는 0018ff94가 저장되어 있다. ESP는 현재 실행되고 있는 스택의 최상단 주소라는 것을 확인할 수 있다.

③~④ 스텝오버[F8]를 통해 명령어를 하나 실행해보면 "PUSH EBP"가 실행된다. 이 명령을 통해 ESP 레지스터가 가리키는 위치(스택의 최상단)에 EBP의 값을 저장한다 : 현재의 서브루틴 이전 루틴에서의 ebp 값을 저장한다. --> 여기서 내가 헷갈렸던 부분이기도 한데, 사실 정말 당연한 얘기지만,, 스택이기 때문에

⑤~⑥ 스텝 오버로 명령어를 하나 더 실행해보면 "MOV EBP, ESP"가 실행된다. 이 명령을 통해 EBP에 ESP의 값을 복사한다 : 현재 서브루틴에 대한 새로운 스택 프레임을 생성한다.

서브루틴 종료

서브루틴 종료

① 서브루틴의 모든 작업이 끝나면 EBP에 대한 복구 작업이 시작된다.

② EBP 복구가 시작되기 전에 ESP 레지스터에는 0018fb60이 저장되어 있고, EBP 레지스터에는 '0018ff88'이 저장되어 있다.

③~④ 스텝오버 [F8]를 통해 명령어를 하나 실행하면 "MOV ESP, EBP"가 실행된다. 이 때 EBP 레지스터에 저장된 주소(현재 루틴의 프레임 포인터)가 ESP에 복사된다. 따라서, ESP가 가리키는 위치는 현재 루틴의 프레임 포인터 부분이다.

⑤ 스텝 오버를 통해 명령어를 하나 더 실행하면 "POP EBP"를 실행한다. 이 때, ESP 레지스터에 저장된 데이터가 EBP 레지스터로 복사된다. >> EBP 레지스터에는 서브루틴이 시작되기 전 들어가있던 값이 복구된다.

 

ESP 레지스터를 활용한 OEP 찾기

위에서의 서브루틴을 이제는 언패킹하는 로직이라고 생각해보자.

서브루틴에 들어가기 전

  • ESP 레지스터에 저장된 주소가 가리키는 값 = 현재 루틴의 EBP 레지스터

서브루틴 종료 후 (현재 루틴으로 복귀)

  • ESP 레지스터가 가리키는 값을 읽어 원본 EBP에 값을 복구시킨다. 여기서 ESP 레지스터가 가리키는 값을 읽는 동작에 bp를 설정하면 언패킹 로직이 종료되는 시점을 파악할 수 있다. >> 패커가 언패킹 로직 종료 후 OEP 로 점프한다면 프로그램 실행이 멈춘 지점에서 OEP로 가는 부분에 대해 찾아 볼 수 있을 것이다.

하드웨어 브레이크 포인트

소프트웨어 브레이크 포인트 (software breakpoint)

일반적으로 많이 사용하는 브레이크 포인트로, 개수에 제한 없이 필요한 만큼 설정하여 사용할 수 있다.

소프트웨어 브레이크포인트는 브레이크포인트를 설정한 명령어의 맨 앞 1byte가 CC(INT 3)로 대체된다.

프로세서가 명령어를 순서대로 실행하다가 CC를 만나게 되면

  • 인터럽트를 발생시키고 운영체제는 실행에 한 제어권을 인터럽트 핸들러에게 넘긴다.
  • 인터럽트 핸들러는 해당 인터럽트에 대한 인터럽트 서브루틴을 호출한다.
  • 인터럽트 서브루틴이 완료되면 원래 루틴으로 복귀해 프로세스가 계속 명령어를 수행한다.

⇒ 소프트웨어 브레이크 포인트는 실행동작(Execution)에 대해서만 실행할 수 있다.

하드웨어 브레이크포인트 (Hardware breakpoint)

프로세서에서 디버거를 위해 제공하는 레지스터에 브레이크 포인트를 설정하고자 하는 주소를 입력한다.

* 32bit Architecture: 8개의 디버거 레지스터(DR0~DR7) 제공

  • DR0 ~ DR3 : 브레이크 포인트를 위해 사용되는 디버거 레지스터 → 총 4개의 하드웨어 브레이크 포인트를 설정할 수 있다.

⇒ 명령어(실행동작) 뿐만 아니라 메모리에도 브레이크포인트를 설정할 수 있다. 실행되지 않는 메모리에 저장된 데이터에 브레이크 포인트를 설정할 수 있다.

⇒ 접근(Access R/W (Read)) / 쓰기(Write) / 실행(Execute) 에 대한 브레이크 포인트를 구체적으로 설정할 수 있다.

⇒ 바이트(Byte) / 워드(Word; 2bytes) / 더블워드(Dword; 4bytes)의 데이터 크기로 브레이크 포인트를 지정할 수 있다.

IDA에서 hardware breakpoint를 설정하는 방법


OEP를 찾고 언패킹하기

Lena의 강의 20강 UnPackMe_EZIP1.0 exe에서 ESP를 이용 OEP를 찾아 언패킹을 해보자!

◾ 실행 환경: IDA + Local Windows Debugger, Detect It Easy, ollydbg-ollydumpex

1. 파일 확인

실행 화면

UnPackMe 파일은 이름에서도 나타나는 것처럼 EZIP으로 패킹된 파일이다.

Detect It Easy로 확인한 파일 정보

➕ 파일을 다운로드 받을 때 크롬에서 자체적으로 보안검사를 통해 다운로드자체가 안되도록 되어있다. 잠시 보안설정을 꺼두고 다운로드를 완료한 후 다시 켜줬다.

➕ 노트북 자체적으로 또한 보안 설정을 통해 바이러스가 발견되었다며 다운로드 파일을 삭제한다.. 이 실습을 하는 동안만 잠시 꺼두도록 한다..

2. 파일 분석

  1. 실행하기 (entry point 확인)

IDA에서 [Ctrl]+[E]를 통해 쉽게 entry point를 확인할 수 있다. 현재 패킹된 파일인 UnPackMe의 entry point는 0x004650BE이다.

  2. 스텝 오버 2번 [F8] (PUSH EBP 명령 실행 )

entry point에서 두 번 스텝오버로 넘어가자 0x004682DC의 명령어 "push ebp"까지 실행됐다.

이 때의 레지스터 값들을 살펴보면

레지스터와 스택

"mov ebp, esp" 직전의 상태에서 ("push ebp"까지 실행한 상태에)

  • ebp 레지스터에 저장된 값 = 이전 루틴의 프레임 포인
  • esp 레지스터에 저장된 값이 가리키는 값 = 스택 최상단에 저장된 값 = ebp에 저장된

⇒ 언패킹이 끝나고 이전 루틴의 프레임 포인터를 복구하기 위해 esp를 이용해 저장

→ ESP 레지스터에 저장된 주소가 가리키는 메모리값에 bp를 설정해 OEP로 이동하는 지점을 찾아낼 수 있을 것이다!

이번엔 메모리를 살펴보면

memory + stack

esp에 저장된 주소가 메모리에서는 "80 FF 19 00"의 값을 가지는 것을 확인할 수 있는데, 이 값은 ESP가 가리키는 스택 값과 동일함을 확인할 수 있다. → 바이트의 순서가 다른 것은 little endian / big endian의 차이때문이다.

  3. 하드웨어 브레이크 포인트 설정

변경되는 데이터의 첫 부분을 선택하고 Hardware breakpoint를 설정한다. 이 때, Read와 Write의 Access에 대한 하드웨어 브레이크포인트로 설정하고, 4byte씩 데이터를 읽고 있기 때문에 size는 0x4로 설정한다.

이렇게 설정한 breakpoint들을 확인할 수 있다.

 

**IDA 사용법(breakpoint 설정하기)**

빨간 응원봉처럼 생긴... 저 아이콘을 누르면 위에 나오는 브레이크 포인터 설정창이 뜬다. 이 아이콘의 바로 왼쪽 아이콘을 누르면 바로 이전 캡처본인 Breakpoints window를 띄울 수 있고, 오른쪽 아이콘을 누르면 이미 설정했던 브레이크 포인트를 해제할 수 있다.


 

  4. 프로그램 실행 [F9]

실행을 하면 Hardware Breakpoint에 걸렸다는 창이 뜨고 위에서 보이는 것처럼 0x00468687의 "pop ebp"까지 명령을 실행한다 ⇒ 현재 루틴의 ebp 레지스터 값을 백업받는다.

  • "mov esp, ebp", "pop ebp": 이전 루틴을 복구해준다. esp에 ebp의 값을 복사해넣고 ebp를 pop 하면 esp가 가리키는 주소에 저장된 값을 ebp에 저장한다.
  • "jmp eax": 하드웨어 브레이크포인트가 걸린 지점에서 다른 서브루틴으로 점프한다. 이 때 EAX 레지스터에 원래 코드의 주소가 저장되어 있을 것이라고 추측할 수 있다.

  5. 스텝 오버[F8] - OEP 찾기

"push ebp" "mov ebp, esp"와 같이 전형적인 스택 프레임이 나타나는 것을 확인할 수 있다. 따라서, OEP는 0x004271B0이라는 것을 추측할 수 있다.

3. 메모리 덤프

ollydbg에 설치한 plugin - ollydumpex 사용

OEP = ImageBase + Entry Point이므로, OEP = 004271B1라면, Entry Point를 271B0으로 한다.

dump 파일이 생성됐다. 이 파일을 IDA에 올려보면

이렇게 바로 main entry를 확인할 수 있다.

또는 x32dbg에서 [F9]을 눌러 실행하면

004271B0에서 멈추고 이 위치를 Entry Point라고 표시한다.


참고: <리버싱 입문>

SMALL

'Reversing > Reverse Engineering' 카테고리의 다른 글

패킹과 언패킹  (0) 2021.05.18
어셈블리 코드 변환  (0) 2021.04.06
HelloWorld.exe  (2) 2021.04.01
Instruction (명령어)  (0) 2021.03.30
리버싱을 위한 기초 지식(구조)  (0) 2021.03.28