앞서 소개해 드린 각 기법들 중에서 Debug 기법에 대한 설명입니다.

실습으로 메모장의 kernel32!WriteFile() API 를 후킹하여 기존과는 다른 동작을 하도록 만들어 보겠습니다.


<실습 예제 – WriteFile() API hooking>

API Hooking 의 기본 설명은 아래 글을 참고하세요.

API Hooking - 리버싱의 '꽃'
API Hooking - Tech Map



Debug Technique




<Fig. 1>

Debug 방식의 API 후킹을 설명 드리겠습니다. (위의 Tech Map 에서 빨간색 표시 부분을 참고하세요)

이 방식의 장점은 후킹을 위해서 '디버깅'을 사용하므로 좀 더 interactive 한 후킹을 수행할 수 있습니다. 즉, 간단한 GUI 를 제공하여 후킹 대상 프로그램의 실행을 제어하고, 메모리를 자유롭게 사용할 수 있습니다.

하지만 먼저 Debugger 구조에 대한 이해가 필요합니다.



Debugger 설명


# 용어

간단한 용어 정리부터 하겠습니다.

Debugger – debugging tool
Debuggee – application to debug

# 디버거 기능

Debugger 의 기능은 Debuggee 가 올바르게 실행되는지 확인하고 (예상치 못한) 프로그램의 오류를 발견하는 것입니다.

Debugger 는 Debuggee 의 명령어 instruction 을 하나씩 실행 가능하며, 레지스터와 메모리에 대한 모든 접근 권한을 가집니다.

# 디버거 동작 원리

일단 Debugger 프로세스로 등록되면 OS 는 Debuggee 에게 debug event가 발생할 때 Debuggee 의 실행을 멈추고 해당 event 를 Debugger 에게 통보합니다. Debugger 는 해당 event 에 대해 적절한 처리를 한 후 Debuggee 의 실행을 재개합니다.

* 일반적인 예외(Exception)도 Debug event 에 해당합니다.
* 만약 해당 프로세스가 디버깅 중이 아니었다면 debug event 는 자체 예외처리 아니면 OS 의 예외처리 루틴에서 처리됩니다.
* Debugger 는 debug event 중에서 처리할 수 없거나 관심 없는 event 들은 OS 가 처리하도록 만들어 줍니다.

아래 그림은 위 설명을 도식화 한 것입니다.


<Fig. 2>

# Debug event

Debug event 입니다.

EXCEPTION_DEBUG_EVENT     
CREATE_THREAD_DEBUG_EVENT  
CREATE_PROCESS_DEBUG_EVENT 
EXIT_THREAD_DEBUG_EVENT     
EXIT_PROCESS_DEBUG_EVENT   
LOAD_DLL_DEBUG_EVENT       
UNLOAD_DLL_DEBUG_EVENT     
OUTPUT_DEBUG_STRING_EVENT  
RIP_EVENT

* 출처 : http://msdn.microsoft.com/en-us/library/ms679302(VS.85).aspx

<List 1>

위 Debug event 중에서 Debugging 관련된 event 는 아래에 표시된 EXCEPTION_DEBUG_EVENT 입니다.

EXCEPTION_ACCESS_VIOLATION
EXCEPTION_ARRAY_BOUNDS_EXCEEDED
EXCEPTION_BREAKPOINT
EXCEPTION_DATATYPE_MISALIGNMENT
EXCEPTION_FLT_DENORMAL_OPERAND
EXCEPTION_FLT_DIVIDE_BY_ZERO
EXCEPTION_FLT_INEXACT_RESULT
EXCEPTION_FLT_INVALID_OPERATION
EXCEPTION_FLT_OVERFLOW
EXCEPTION_FLT_STACK_CHECK
EXCEPTION_FLT_UNDERFLOW
EXCEPTION_ILLEGAL_INSTRUCTION
EXCEPTION_IN_PAGE_ERROR
EXCEPTION_INT_DIVIDE_BY_ZERO
EXCEPTION_INT_OVERFLOW
EXCEPTION_INVALID_DISPOSITION
EXCEPTION_NONCONTINUABLE_EXCEPTION
EXCEPTION_PRIV_INSTRUCTION
EXCEPTION_SINGLE_STEP
EXCEPTION_STACK_OVERFLOW

* 출처 : http://msdn.microsoft.com/en-us/library/aa363082(VS.85).aspx

<List 2>

각종 예외(Exception) 중에서 Debugger 가 반드시 처리해야 하는 예외는 바로 EXCEPTION_BREAKPOINT 예외입니다.

BreakPoint 는 어셈블리 명령어 "INT3" 이며, IA-32 Op code 는 0xCC 입니다. 코드 디버깅 중에 INT3 명령어를 만나면 Debugger 에게 EXCEPTION_BREAKPOINT 예외 이벤트가 날아갑니다.

Debugger 에서 BreakPoint 를 구현하는 방법은 간단합니다.

BreakPoint 를 설치하기 원하는 코드의 메모리 시작 주소에서 1 byte 를 0xCC 로 바꿔 치는 것입니다. 디버깅을 계속 진행하고 싶을 때는 다시 원래 값으로 복원시키고 실행해줍니다.

Debug 방식의 API Hooking 은 이와 같은 BreakPoint 의 특성을 이용하는 것입니다.



API Hooking - Debug Method


Debug 기법을 통한 API Hooking 에 대해 좀 더 자세히 설명 드리겠습니다.

기본적인 아이디어는 Debugger-Debuggee 관계를 가진 상태에서 Debuggee 의 API 시작 부분을 0xCC 로 바꿔 제어를 Debugger 로 가져온 상태에서 원하는 작업을 수행한 후 Debuggee 를 다시 실행상태로 바꾸는 것입니다.

작업 흐름은 아래와 같습니다.

- 후킹을 원하는 프로세스에 ‘attach’ 하여 Debuggee 로 만듦
- Hook : API 시작 주소의 첫 바이트를 0xCC 로 변경
- 해당 API 가 호출되면 제어는 Debugger 에게 넘어옴
- 원하는 작업을 수행(파라미터, 리턴 값 조작 등)
- Unhook : Debuggee 의 0xCC 를 원래대로 복원시킴 (<– API 의 정상 실행을 위해)
- 해당 API 실행 (0xCC 가 빠진 정상적인 상태)
- Hook : 다시 0xCC 로 바꿈 (<– 지속적인 후킹을 위해)
- Debuggee 에게 제어를 되돌려줌


위 방식은 가장 간단한 경우를 소개한 것입니다.

이걸 기준으로 다양하게 변형할 수 있습니다. 가령 original API 를 호출 하지 않을 수 도 있고, 사용자가 제공한 custom API 를 호출 할 수 도 있고, 한번만 후킹 할 수도 있고, 여러 번 후킹 할 수도 있습니다.

작업 목적에 따라서 알맞게 변형해서 사용하시면 됩니다.



Notepad.exe 의 WriteFile() API Hooking


지금 까지 공부한 내용을 바탕으로 실제 코드를 보면서 실습을 해보도록 하겠습니다.

작업할 내용은 Notepad.exe 의 WriteFile() API 후킹입니다.
입력된 파라미터를 조작하여 소문자로 입력된 내용을 전부 대문자로 바꿔 보겠습니다.

즉, Notepad 에서 입력된 모든 소문자는 파일로 저장되는 순간에 대문자로 변경되어 저장됩니다.

Notepad.exe 를 실행시킨 후 PID 를 알아냅니다.


<Fig. 3>

첨부된 후킹 프로그램(hookdbg.exe)을 실행합니다.

hookdbg.exe 는 콘솔 기반 프로그램이며 실행 파라미터로 후킹 할 프로세스의 PID 를 넘겨 받습니다.



<Fig. 4>

위 그림과 같이 hookdbg.exe 를 실행하면 PID 1688 에 해당하는 notepad 프로세스의 WriteFile() API 후킹이 시작됩니다.

그리고 notepad 에 아무 글자나 입력해 보세요.


<Fig. 5>

입력을 마치셨으면 저장을 해주세요.


<Fig. 6>

저장을 마치면 notepad 화면에는 아무런 변화가 일어나지 않습니다. (WriteFile() API 만 후킹 했다는 사실을 기억해 주세요.)

notepad 를 종료해 주시고, hookdbg 프로그램을 봐주세요.


<Fig. 7>

위 그림을 보시면 “original string” 에는 제가 입력한 문자열이 나타나고, “converted string” 에는 변경된(소문자 -> 대문자) 문자열이 나타납니다.

이것은 hookdbg.exe 프로그램 내부에서 후킹의 진행과정을 표시하기 위해서 출력하는 문자열입니다.

실제로 대문자로 저장되었는지 파일을 열어서 확인합니다.


<Fig. 8>

정확히 모든 소문자가 대문자로 변경되어서 저장되었습니다.

이 예제는 아주 간단한 기능을 가지고 있지만 Debug Method 에 대한 기본적인 개념을 잘 설명해줄 수 있습니다.

다음 포스트에서 hookdbg.exe 의 실제 코드를 상세히 살펴보도록 하겠습니다.

API Hooking - 메모장 WriteFile() 후킹 (2)


+---+

ReverseCore

 

Trackback Address :: http://www.reversecore.com/trackback/56 관련글 쓰기

  1. 늅늅 2009/10/28 13:58 댓글주소 | 수정 | 삭제 | 댓글

    언제나 친절하게 설명 되어있는 강좌 잘 보고 있습니다 ^^

  2. 냥냥 2009/10/28 23:59 댓글주소 | 수정 | 삭제 | 댓글

    와... 정말 많은걸 보고 배워가요 ^^*
    좋은글 앞으로도.. 많이 많이 very 많이 많이 부탁드릴께요..ㅎㅎ
    댓글중에 회사원이라고 들었는데요... 하는 일이 무엇인가요..?
    전 학생이라... IT업계의 일에는 무엇이 있는지 궁금해서요... 프라이버시라면... 말 안해주셔도되요~
    히힛~..~ 양질의 좋은 자료 부탁드리겠습니다~ ㅎㅎ.. 근데 퍼가도 되는건가요..?!

    • ReverseCore 2009/10/29 06:14 댓글주소 | 수정 | 삭제

      냥냥님, 안녕하세요.

      도움이 되셨다니 기쁘네요~ ^^

      그냥 평범한 IT 관련 회사원이구요, 개발과 리버싱에 많은 관심을 가지고 있지요.

      퍼가지는 마시구요, 링크만 해주세요~
      (미리 물어봐 주셔서 감사합니다.)

  3. 이승철 2009/10/29 11:07 댓글주소 | 수정 | 삭제 | 댓글

    언제나 잘 보고 있습니다. 감사합니다.

  4. 김형근 2009/10/29 18:05 댓글주소 | 수정 | 삭제 | 댓글

    좋은글 감사합니다.
    궁금한것이 더 많아지네여 ^^
    좋은글 앞으로도 부탁드립니다.

  5. 몽상가 2009/10/30 03:19 댓글주소 | 수정 | 삭제 | 댓글

    저도 요즘 공부 차원에서 다시 보고 있습니다. ^^
    목표는 모 프로그램의 크랙입니다. 물론 나쁜 용도는 아니고, 최소한 내가 의도한 정도까지? 지만, 욕심이 크다보니 할 수 있는데 까지 해보는게 목표입니다.

    근데 일단 가장 기본적인 디버거를 붙이면 강제 종료되는데, 아마 이게 다른 쓰레드에서 디버그가 붙으면 강제종료를 하는 스레드가 있나 봅니다. 이럴땐 어떤 방식으로 해야되는가 궁금합니다.
    아예 실행하고 붙이는게 아니라 실행전에 수정을 해야되는건가요?

    강좌가 계속 나와서 이런 방식까지 나왔으면 좋겠습니다.
    제 블로그 링크도 해주시고 감사합니다. RSS 로 꾸준히 보고있어요 :D

    • reversecore 2009/10/30 07:19 댓글주소 | 수정 | 삭제

      몽상가님, 안녕하세요.

      디버깅 당했다고 판단되면 스스로 종료 <- 안티 디버깅 기법을 사용한 듯 하군요.

      계획에는 일반적인 안티디버깅에 대한 강좌를 하고, 특별한 안티기법은 파일을 분석하는 도중에 따로 정리해보려고 합니다.

      말씀하신 내용도 참고할께요~ (attach 할 수 없는 프로세스)

      감사합니다.

  6. 졸작땜시날새는코더 2009/10/30 13:51 댓글주소 | 수정 | 삭제 | 댓글

    글잘보았습니다 참고많이되었구요
    혹시
    특정 이벤트처리가아니라
    그 해당 프로그램이 사용하는 API함수들을 모두 체크할수있는 방법이없을까요
    변조하는게 아닌 그저 무슨 API함수를 사용했다 정도로만요
    대충 이미지는 나오는데 어떻게해야할지 막막합니다 ㅠㅠ

    • ReverseCore 2009/10/31 12:30 댓글주소 | 수정 | 삭제

      안녕하세요.

      프로그램이 사용하는 모든 API 함수를 확인하고 싶으시다는 말씀이시죠?

      IDAPro, OllyDbg 에서 그런 기능을 지원하는데요, 그 원리는 코드의 CALL, JMP 명령어을 전부 파싱하는 것입니다.

      혹시 이런류의 프로그램이나 OllyDbg 플러그인등이 존재할 수도 있겠네요.

      참고로 예전에 제가 했던 비슷한(?) 방법으로는 관심있는 API 를 전부(약 120여개) 후킹해서 각 파라미터/리턴값을 확인하고 호출 순서등을 확인한 적이 있었습니다.

  7. 냥냥 2009/10/31 03:16 댓글주소 | 수정 | 삭제 | 댓글

    NotePad에 관한 질문드리겠습니다 ㅠ
    제가 더미dll ( Attatch 시 빈 메세지 박스만띄우는 ) 을 만든후 Notepad.exe에 dll injection을 통해서 구현해보려고 했습니다.
    방식은 Notepad를 디버거로서 연다음 Notepad.exe의 메모리공간을 할당받은후 LoadLabrary()를 실행해서 더미dll을 Attatch시키게하는 명령어 코드를 복사한뒤 EIP수정후 실행하게한후, 실행후 다시 EIP를 원래의 EIP주소로 변경하도록 만들었습니다,
    근데... 제대로 동작을안하고 Notepad.exe가 그냥 죽어버리는군요...
    코드가 잘못됐는지 확인하려고... 아무런 보안기능이 없는 전에 만든 툴로 대상을 변경후 실행하니 아무런 이상없이 실행이 잘됐습니다 ( 빈 메세지박스를 확인 )
    Notepad.exe에서는 제대로 실행되지 않는 이유가 무엇을까요..?
    운영체제에서 특별한 알고리즘을 가지고 보호하는건가요..? ( 부팅시 원도우 기본파일들인 calc,notepad 같은 프로그램은 수정되었을시 복원하는 기능은 있다고 들었지만... )

    • ReverseCore 2009/10/31 12:35 댓글주소 | 수정 | 삭제

      안녕하세요

      어떤 OS 를 쓰시나요?

      전 주로 XP 를 쓰는데, DLL Injection 은 아무런 문제가 없습니다.

      다른 프로그램에서는 잘 동작하는데 notepad 만 동작하지 않는다고 하셨는데요, 저도 잘 이해가지 않는군요.

      참고로 DLL Injection 을 위해서 반드시 notepad 를 디버깅 할 필요는 없습니다. 그리고 DLL Injection 의 코드를 확인 해보시는 것이 좋을것 같습니다.

      가능하시면 저에게 보내주시기 바랍니다. 이곳에 올리셔도 되구요.

    • 나그네 2009/11/03 05:34 댓글주소 | 수정 | 삭제

      WFP(Windows File Protection) 때문에 그런거 아닐까요? system 폴더에 있는 notepad.exe 를 다른 폴더로 옮겨서하면 괜찮을 것 같은데요.ㅋ

    • reversecore 2009/11/03 22:25 댓글주소 | 수정 | 삭제

      나그네님, 소중한 답변 감사드립니다. ^^

  8. 냥냥 2009/10/31 16:09 댓글주소 | 수정 | 삭제 | 댓글

    메일 주소가 어떻게 되시나요..? ;;
    마땅히 보낼만한...ㅠㅠ

  9. vice 2009/12/16 11:15 댓글주소 | 수정 | 삭제 | 댓글

    잘 보구 갑니다. 간단하구 쉽게 설명 잘 해주시네요 ^^

  10. thav 2009/12/21 13:23 댓글주소 | 수정 | 삭제 | 댓글

    잘봤습니다. 근데. 님께서 첨부해주신 샘플 소스 구동하다가 notepad 저장할때 정지됩니다.
    혹시 이런 현상 보신적 있나요?
    OS 는 윈도우 XP 입니다.

    • ReverseCore 2009/12/22 11:30 댓글주소 | 수정 | 삭제

      thav님, 안녕하세요.
      notepad 저장시에 프로그램이 멈춘다는 말씀이시죠?

      제 환경(XP SP3)에서는 그런 일이 없었지만, 충분히 그럴 가능성은 있습니다.

      * 참고로 제 테스트 PC 는 OS 만 설치된 클린 PC 환경입니다.

      제가 여러가지 상황에서 충분히 테스트하지 않아서 그럴 수 있고요. thav님 PC 에 실행중인 프로세스들에 의해서 그렇게 될 수 도 있습니다. (충돌!)
      (또한 실습 예제 파일들은 코드의 간결성을 위해서 에러처리가 거의 되어있지 않습니다.)

      (가능하시다면) 다른 PC 에서도 테스트 해보시기 바랍니다.

      감사합니다.


UPX 실행 압축된 notepad_upx.exe 를 디버깅 함으로써 실행 압축에 대한 개념을 이해합니다. 코드를 하나하나 tracing 하면서 결국 원본 notepad.exe 코드를 찾아내는 것이 목표입니다. 마지막에는 UPX 실행 압축 파일을 디버거에서 간단히 통과하는 팁을 설명하겠습니다.


 

notepad.exe 의 EP code

먼저 원본 notepad.exe 의 EP code 를 보겠습니다.

<Fig. 1>

010073B2 주소에서 GetModuleHandleA() API 를 호출해서 notepad.exe 프로세스의 ImageBase 를 구합니다.
그리고 010073B4 와 010073C0 주소에서 각각 MZ 와 PE 시그니처를 비교하는군요.

원본 notepad.exe 의 EP 코드를 눈에 잘 익혀두시기 바랍니다.

 

notepad_upx.exe 의 디버깅

notepad_upx.exe 를 OllyDbg 로 열어보면 아래와 같은 경고 메시지 박스가 나타납니다.

 

<Fig. 2>

디버거가 해당 파일이 압축되었다고 판단하였습니다.
상관 없으니 Y/N 아무거나 선택해서 넘어가면 아래 그림과 같이 UPX stub 코드가 나타납니다.

 

<Fig. 3>

EP 주소는 01015330 으로서 두 번째 섹션의 끝부분입니다.
실제로 압축된 notepad 원본 코드는 EP 주소 위쪽으로 존재합니다.

코드 시작 부분을 살펴보겠습니다.

01015330    60              PUSHAD                
01015331    BE 00100101     MOV ESI, 01011000
01015336    8DBE 0000FFFF   LEA EDI, DWORD PTR DS:[ESI+FFFF0000]

PUSHAD 명령으로 EAX ~ EDI 레지스터 값을 스택에 저장하고,
ESI 와 EDI 레지스터를 각각 2nd 섹션 시작과 1st 섹션 시작 주소로 세팅합니다.
ESI 와 EDI 는 각각 source 와 destination 버퍼를 가리킨다는 것은 다 알고 계실 것입니다.

이후에 나타나는 코드는 <Fig. 3> 에서 처럼 수많은 조건 분기명령어들로 이루어진 코드가 나타납니다.
이 코드가 바로 압축 해제 코드입니다. (ESI 에서 값을 읽어서 EDI 에 써주는 코드)

우리 목표는 이 코드를 전부 tracing 해서 결국 <Fig. 1> 과 같이 원본 notepad 의 EP 코드를 찾아내는 것입니다.

* 리버싱에서는 원본 EP 를 OEP(Original Entry Point) 라고 표현합니다.

* 실제 리버싱에서는 이러한 실행 압축 코드를 일일이 tracing 하지 않고 자동화 스크립트, 노하우 등을 통하여 OEP 로 바로 갑니다. 하지만 실행압축 리버싱을 처음 공부하는 사람들의 입장에서는 하나하나 코드를 tracing 하는 것이 올바른 방법입니다.

이제부터 저와 같이 Tracing 을 시작해 보겠습니다.
일단 아래 동영상을 보시죠.

<Video. 1>

조금 trace (F7) 하다 보면 짧은 루프가 나타납니다.
해당 루프를 아래 그림에서 더 자세히 살펴보겠습니다.

<Fig. 4>

루프 회전 수 ECX = 36Bh 이고, 그 내용은 EDX (01001000) 에서 한 바이트를 읽어 EDI (01001001) 에 쓰는 것입니다.

01001000 주소는 첫 번째 섹션 (“UPX0”) 주소이며, 메모리 상에서만 존재하는 섹션입니다.
(내용은 전부 NULL 입니다.)

* 참고 : 실행 압축 (Run-Time Packer) 에 관하여

실행 압축된 파일을 디버깅 할 때 이러한 루프를 만났다면 그 루프를 빠져 나와야 합니다.

010153E6 주소에 BP 를 설치 후 실행 명령(F9) 으로 해당 루프를 탈출합니다.

조금 더 진행하다 보면 다시 아래와 같은 루프를 만나게 됩니다.

<Video. 2>

이것이 본격적인 디코딩(decoding) 루프입니다.
ESI 가 가리키는 두 번째 섹션(“UPX1”) 의 주소에서 차례대로 값을 읽어서 적절한 연산을 거쳐 압축을 해제하여 EDI 가 가리키는 첫 번째 섹션(“UPX0”) 의 주소에 값을 써주게 됩니다.

이 과정에서 사용되는 명령어들은 아래와 같습니다.
AL/EAX 에는 압축이 풀린 데이타가 있고, EDI 는 첫 번째 섹션의 주소를 가리키고 있습니다.

0101534B    8807    MOV BYTE PTR DS:[EDI],AL
...
010153E0    8807    MOV BYTE PTR DS:[EDI],AL
...
010153F1    8907    MOV DWORD PTR DS:[EDI],EAX

이 두 번째 루프를 탈출하기 위해서는 아래 그림과 같이 01015402 주소에 BP 를 설치하면 됩니다.

 

<Fig. 5>

01015402 주소까지 디버깅을 하셨다면 위 그림처럼 첫 번째 섹션(“UPX0”) 영역에 코드가 쓰여진걸 확인 할 수 있습니다. (원래는 0 으로 채워져 있던 영역입니다.)

조금 더 내려가면 아래와 같은 루프를 만나게 됩니다.

 

<Fig. 6>

이것은 원본 코드의 CALL/JMP 명령어(op code : E8/E9)의 destination 주소를 복원시켜주는 코드입니다.
이 루프도 01015436 주소에 BP 를 설치하여 탈출할 수 있습니다.

이제 거의 막바지입니다. 
IAT 세팅만 하면 UPX 압축 해제 코드는 끝납니다.
조금만 더 내려가 보겠습니다.

 

<Fig. 7>

위 그림에서 파란색으로 표시된 부분이 바로 IAT 를 세팅하는 루프입니다.

01015436 주소에서 EDI = 01014000 으로 세팅되며, 이곳은 두 번째 섹션(“UPX1”) 영역입니다.
이곳에는 원본 notepad.exe 에서 사용되는 API 이름 문자열이 저장되어 있습니다.
이 문자열들을 가지고 01015467 주소의 GetProcAddress() 를 호출하여 API 시작 주소를 얻은 후 EBX 레지스터가 가리키는 원본 notepad.exe 의 IAT 영역에 API 주소를 입력합니다.

이 과정을 API 이름 문자열이 끝날 때까지 반복하면 원본 notepad.exe 의 IAT 를 완벽히 복원하게 됩니다.

원본 notepad.exe 의 압축을 모두 해제하였으므로, OEP 로 제어를 돌려줘야겠지요?
아래 그림이 바로 OEP 로 jump 하는 코드입니다.

 

<Fig. 8>

010154AD 주소의 POPAD 명령은 바로 UPX 코드 중 가장 첫 번째 명령인 PUSHAD 에 대응되는 명령으로써 이제 레지스터를 원래대로 복원시키는 명령입니다. (<Fig. 3> 참고)

최종적으로 010154BB 주소의 JMP 명령어로 OEP 로 갑니다.
JMP 하는 주소 0100739D 는 원본 notepad.exe 의 EP 주소입니다. (각자 확인해보세요.)

 

UPX 의 OEP 를 빨리 찾는 방법

위 설명대로 tracing 을 잘 하셨나요?
리버싱 초보자라면 꼭 한번 해보시기 바랍니다.
다른 packer 로 압축된 파일을 디버깅 할 때 큰 도움이 될 것입니다.

매번 위와 같은 방법(루프 탈출)으로 OEP 로 가는 것은 힘들겠지요?

실전 리버싱에서는 간단히 OEP 로 들어갑니다.
그 방법들을 설명 드리겠습니다. (UPX 기준입니다.)

#1. POPAD 명령어 이후의 JMP 명령어에 BP 설치

UPX 는 POPAD 명령어 이후에 나오는 JMP 명령어가 바로 OEP 로 가는 명령어 입니다.
이곳에 BP 를 설치하고 실행하면 바로 OEP 로 갈 수 있습니다.

#2. Hardware BP 설치

이 방법도 UPX 특징인 PUSHAD/POPAD 명령어의 특성을 이용하는 것입니다.

<Fig. 3> 에서 01015330 주소의 PUSHAD 명령을 실행한 후 스택을 보면 아래와 같습니다.

<Fig. 9>

EAX 에서 EDI 까지 스택에 차곡차곡 저장되어 있습니다.

OllyDbg의 Dump 윈도우에 저 스택 주소 0006FFA4 를 표시한 후 마우스 우측 메뉴를 이용하여 Hardware BP 를 설치합니다.

<Fig. 10>

Hardware BP 란 CPU 에서 지원하는 BP 이며 4개까지 설치 가능합니다.
일반적인 BP 와 다른 Hardware BP 의 특징은 BP 설치된 명령어가 실행 된 이후에 제어가 멈추게 됩니다.

이 상태에서 실행하게 되면 압축이 해제되고 POPAD 가 호출되는 순간에 Hardware BP 가 설치된 0006FFA4 주소를 access 하게 되고, 그때 제어가 멈춥니다. 바로 밑에 OEP 로 가는 JMP 명령어가 있지요.

+—+

이상으로 UPX 실행압축 파일의 디버깅 설명을 마치도록 하겠습니다.

초보자 분들께서는 위 설명대로 직접 디버깅 하여 루프를 하나하나 탈출해서 OEP 로 가보시길 권해드립니다.

여러분들의 디버깅 실력이 부쩍 향상됨을 느낄 수 있으실 겁니다.

 

Trackback Address :: http://www.reversecore.com/trackback/35 관련글 쓰기

  1. Tracked from www.reversecore.com 2009/06/20 02:06 삭제

    Subject: 실행 압축 (Run-Time Packer) 에 관하여

    실행 압축(Run-Time Packer)은 소프트웨어 역공학 (Reverse Code Engineering)의 단골 주제입니다. 이를 잘 이해하기 위해서는 PE 파일 포멧과 운영체제의 기본 적인 사항들(프로세스, 메모리, DLL, etc)에 대해 잘 알고 있어야 하며, 또한 압축/해제(Compress/Decompress) 알고리즘에 대한 기본 적인 내용에 대해서 알아야 합니다. 이미 많은 부분은 제 블로그에서 다뤘던 내용들입니다. 실행 압축에 대한..
  1. 1phee 2009/11/18 18:08 댓글주소 | 수정 | 삭제 | 댓글

    아.. 언팩은 하나도 몰랐는데.. 요새는 시간이 없어서 크랙미를 못풀지만 예전에는 언팩의 언자도 몰라서 크랙미 풀때마다 패킹안된거만 찾아다녔던 기억이 나네요..ㅎㅎ 어쨌든 이런식으로 UPX 언팩에 대해서 자세하게 설명하는 글은 처음 보네요! 언팩에 대해서 전혀 개념도 없다가 이글보고 대략 감 잡았습니다. UPX가 쉬운 패킹이라 그런지 이해가 잘 되네요. 잘 보았습니다. ^^

    • reversecore 2009/11/20 00:16 댓글주소 | 수정 | 삭제

      1phee님, 안녕하세요.

      Unpack 의 기본원리는 거의 비슷합니다.
      압축풀고 IAT 만들고 OEP 로 가는거죠.

      여기에 각종 옵션이 끼어들면서 복잡해지는 것입니다.

      다른 패커에도 도전해 보세요~

  2. 시간의흔적 2010/03/31 10:52 댓글주소 | 수정 | 삭제 | 댓글

    너무너무 글 잘 보고 갑니다.. 어렵지만 열심히 볼께요 ^^

    • reversecore 2010/04/01 10:11 댓글주소 | 수정 | 삭제

      시간의흔적님, 안녕하세요.

      네~ 공부하시다가 의문 사항 있으시면 질문 남겨주세요~ ^^

      감사합니다.

  3. 안정현 2010/06/23 04:19 댓글주소 | 수정 | 삭제 | 댓글

    지금 나이지리아 전 하는데 그냥 안보고 블로그보는중 ㅡ.,ㅡ;; ㅋㅋㅋ 지금현재는 제삶의 낙인듯 ㅋㅋㅋㅋㅋ 미친듯이해서 달려왔는데
    hook.exe 같이 소스코드 짜놓아서 주석달아 설명해주신것들있잖아여~
    c를 공부하긴했는데 소스파일 들이 주석들봐도 해석이안돼더라구요..
    해석을 할수있을려면 시스템프로그래밍을 공부따로해야하나요 ㅋ

    api를 몰라서그런가..
    암튼 잘읽고있습니당~
    중독성이강하네용 ㅋㅋ 한페이지만더읽고자야지 하다가 밤샐기세 ㄷㄷㄷㄷ ㅋㅋㅋ

    • reversecore 2010/06/23 21:21 댓글주소 | 수정 | 삭제

      월드컵보다 제 블로그를 선택해 주시다뇨... @@~
      리버싱 공부에 대한 열정과 각오가 남다르신 것 같습니다.

      C 언어 예제 소스가 잘 이해안가셨나요?

      사실 Win32 API 프로그래밍 경험이 없으시면 좀 어려울 수 있습니다. 그럴때 질문올려주세요~ ^^

      감사합니다.

  4. newbie 2010/07/15 19:08 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 :)
    게시글 잘 보고 있습니다.
    궁금증이 생겨서 질문드리고 싶은데
    언패킹부분중에 E8, E9 찾아서 주소값 수정해주는 부분이 있는데
    이건 왜 그렇게 해주는거죠? 그냥 통째로 원본 소스를 풀어서 UPX0부분에 넣었다면 고쳐줄 내용이 하나도 없지 않나요..또 EAX로 주소를 받아서 쉬프트연산등을 해주는거 같은데 그건 왜그런걸까요? 패킹할때 주소를 거꾸로 저장해놓은건지...


    그리고 원본 파일의 IAT부분은 압축할때 코드랑 같이 된거 아닌가요? 보니까 IAT부분을 기존의 위치가 아닌 UPX1 위치에다 만들어 주는거 같던데.. 그냥 기존 IAT부분에 채워주지않고 왜 새로 만들어주는건지 궁금하네요

    마지막으로 stolen byte가 왜 생기는지 알고 계시면 알려주세요 ㅜㅡ

    너무 두서없이 질문만했네요
    다시한번 좋은 블로그 만들어주셔서 감사합니다

    • reversecore 2010/07/15 23:38 댓글주소 | 수정 | 삭제

      안녕하세요.

      애석하게도 저 또한 UPX 에서 왜 E8, E9 를 보정해 주는지 이유를 잘 모르겠습니다. 모든 패커에서 나타나는 일반적인 특징이라고 볼 수 도 없고 말이죠. 복잡한 연산은 UPX 프로그래머가 그렇게 만든 것이구요. 4 byte 를 디코딩 해서 원래 값으로 복원시키는 명령입니다. 어떻게 연산하던 자유니까요.

      IAT 는 원본 IAT 영역에 정상적으로 풀어주고 있습니다. 새롭게 GetProcAddress() 를 호출해서 만드는 이유는 압축된 PC 의 kernel32.dll 주소와 실행되는 PC 의 kernel32.dll 의 주소가 다를 수 있기 때문입니다.

      stolen byte 는 아마 protector 에서 사용되는 그 stolen byte 말씀이시죠?

      쉽게 덤프 뜨지 못하도록 그런겁니다.
      그 상태로 덤프 떠버리면 stolen byte 때문에 정상 실행이 안됩니다. packer 로 정상 실행 된 경우만 stolen byte 에 해당되는 (혹은 유사한) 코드를 실행하여 stack, register 를 맞춰준 후 그 이후 코드를 실행하기 때문에 잘 실행되는 것이죠.

      괜히 설명이 복잡한데요... protector 류를 한번 분석해 보시면 금방 파악하실듯... ^^

      * 굉장히 깊이 있게 분석을 하셨나 봅니다. ^^ 자주 들러 주시고요... E8/E9 의 이유를 알게 되시면 저에게도 공유해 주세요~

      감사합니다.

  5. newbie 2010/07/18 20:04 댓글주소 | 수정 | 삭제 | 댓글

    친절한 답변 감사드립니다 :)
    시간이 많이 걸리겠지만 포스팅하신거 다 보고 싶네요
    또 궁금한거 있으면 여쭤볼께요
    좋은시간보내세요~

  6. newbie 2010/07/19 19:19 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요 또 이렇게 질문드리게되네요
    앞으로 질문으로 도배를 할것 같은 느낌이...
    다름이아니고 언패킹과정에서 덤프를뜨고 IAT를 다시설정해주지않으면 초기화에러가나는데
    도대체 이게왜 생기는걸까요 ?
    사실 OEP만 찾으면 패커에 의해 IAT는 복구가 된 상태고 실행하는데 문제가 없잖아요
    (PE헤더에는 제대로 바껴있지 않지만..)
    로더가 어떤 원리로 PE헤더에 있는 IAT가 패커가 사용한 다른 IAT인지 알고 에러를 내는걸까요?
    코드하나하나보면서 API가 나올때마다 IAT랑 대조해볼것 같지는 않은데..
    어차피 그냥 적힌대로 잘못된 IAT지만 그걸 채우고 실행만 하면 정상 실행이 될것 같은데..
    많은 자료를 찾아봤지만 단지 IAT가 복구되지 않아서 에러를 낸다는 설명들만 있는데 왜 그런지에
    대한 설명은 없네요 혹시 알고 계시면 알려주세요
    감사합니다!

    • reversecore 2010/07/19 23:12 댓글주소 | 수정 | 삭제

      예를 들어 UPX 파일을 실행시킨 후 덤프하면 IAT 에는 정확한 API 주소가 들어있지요. 하지만 INT 가 깨져있습니다.

      PE 로더는 INT 에서 API 이름 문자열을 얻어서 LoadLibrary()/GetProcAddress() 조합을 사용하여 실제 API 주소를 얻어서 IAT 에 적어주는데요. 이 과정에서 실행 에러가 발생하는 것입니다.

      간혹 어떤 패커들은 IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk (INT) 를 0 으로 세팅하기도 합니다.

      그래도 잘 실행되는 이유는 IMAGE_IMPORT_DESCRIPTOR.FirstThunk (IAT) 를 따라가면 이름 문자열이 나오고 이를 바탕으로 함수 주소를 구할 수 있기 때문이지요.

      근데 UPX 덤프 파일은 OriginalFirstThunk, FirstThunk 어느것을 따라가도 이름 문자열이 나오지 않습니다. FirstThunk 에 가보면 실제 함수 주소들만 있지요. 그건 PE 로더가 원하는게 아니거든요.

      그래서 실행 에러가 발생하는 것입니다.

      잘 이해안가시면 다시 질문 올려주세요~

      감사합니다.

  7. newbie 2010/07/20 18:14 댓글주소 | 수정 | 삭제 | 댓글

    답변 정말 감사합니다
    궁금증이 해결되었습니다 :)
    디버거로만 패킹된 파일과 덤프된 파일의 PE헤더를 확인하다보니까 두개가 같아서 착각을 했네요..
    패킹된 파일은 이미 로더가 처리를 해서 그렇게 보였던 것인데...

    리버싱관련 공부하면 궁금증이 생겨도 친절이 알려주는 분이 없어서 답답했는데 정말 근사한 오아시스를 발견했네요 :) 좋은 시간 보내시길 바랍니다~

  8. vio 2011/03/25 16:32 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요
    올려주신 글 덕분에 정말 하루하루 리버싱실력이 느는것 같습니다. 감사드립니다
    질문이 한가지 있는데요
    리버싱을 하다보면 모르는 어셈명령어들때문에 뭔지 모를때가 많습니다
    이번 upx 파일 트레이싱을 하는데도 처음보는 명령어들이 너무많습니다.
    어셈블리 명령어 잘 정리된 사이트를 아시나요?
    인텔 매뉴얼?을 언듯 어디서 들은거같은데 못찾겠더군요
    모르는 명령어를 찾아볼 수 있는 사이트를 알려주시면 감사하겠습니다

  9. 2011/05/04 08:40 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2011/05/03 00:45 댓글주소 | 수정 | 삭제

      안녕하세요.

      그런 파일도 리버싱의 원리가 다르지 않습니다.
      그냥 EP 부터 하시면 되구요.

      대부분의 프로텍터는 스레드가 많이 실행됩니다.
      물론 디코딩 스레드가 대부분 이지요.

      감사합니다.

  10. 2011/05/04 08:41 댓글주소 | 수정 | 삭제 | 댓글

    비밀댓글입니다

    • reversecore 2011/05/03 00:42 댓글주소 | 수정 | 삭제

      안녕하세요.

      음, 질문 하시는 내용이 본문 내용과 동떨어진 내용들이군요?

      질문이 잘 이해되지 않습니다. ^^~

      감사합니다.

  11. 아장아장 2011/05/12 23:34 댓글주소 | 수정 | 삭제 | 댓글

    이미 알고있었다고 생각했었는데 다시 보니 새로게 느껴져요.
    째인 설명 인상에 남았습니다.

    한가지 의문은 c++에서 클라스함수호출시 어떤 호출규약을 쓰고 이때 탄창을 어떻게 리용하는지 상세히 알고싶은데요...
    시간나는대로 이 내용을 주재로 하는 강좌 내와주시든가 댓글 보내주시면 고맙겠습니다.

    열심히 강좌 찾겠습니다.

    • reversecore 2011/05/13 20:10 댓글주소 | 수정 | 삭제

      안녕하세요.

      C++ 에서는 C 와 마찬가지로 cdecl 방식을 사용합니다.

      탄창이요? 스택(stack)을 말씀하시는 거죠?
      cdecl 에서는 Caller 가 stack 을 정리합니다.

      향후 C++ 클래스 멤버 함수 호출 관련한 내용을 블로그에 올릴 예정입니다. ^^

      감사합니다.

  12. 2011/06/29 18:54 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요! 여쭤볼게 있습니다 ㅠ.ㅠ
    선생님 말씀대로 루프들 다 빠져나와서 OEP를 찾았는데요
    찾고나서 dump를 뜨면 실행압축 해제한 실행파일이 생성되는건가요?
    LordPE라는 tool을 써야 된다고 하는 사람들도 있던데...
    해제된 실행파일은 어떻게 얻을수 있을까요??
    그리고... 디버깅을 안하고 notepad_upx.exe 를 olldbg로 돌렸을때
    밑으로 쭉~ 내려서 000000많은 부분에서 바로 위에있는 JMP 쪽이
    바로OEP라고 생각해도 되는건가요~?

  13. TeamKhan 2011/09/16 19:59 댓글주소 | 수정 | 삭제 | 댓글

    어떻게 ESI EDI 가 가르키는 주소가 몇번째 섹션에 해당하는 주소인지 어떻게 파악하는지 모르겟습니다.
    그리고 어떤 어셈블리어 코드가 IAT 를 복구해주는 부분의 코드라는걸
    알수있는지 또 이 부분이 어떻게 디코등 루프부분의 코드라는걸 알수있으셧는지
    참으로 궁금합니다.

    • reversecore 2011/09/19 20:10 댓글주소 | 수정 | 삭제

      안녕하세요.

      메모리 복사 명령에서 ESI 는 Source를, EDI 는 Destination 을 각각 의미합니다. 따라서 ESI/EDI 가 가리키는 주소를 본 후 PEView 로 파일을 확인하면 어느 섹션을 가리키는지 알 수 있지요. OllyDbg 의 "Memory Map" 을 확인해도 어느 섹션인지 알 수 있습니다.)

      IAT 복구 하는 것은 GetProcAddress() 호출하면서 API 함수 주소를 반복적으로 얻는 것을 보고 알 수 있었습니다.

      그리고 저는 몇 번의 Unpack 경험이 있기 때문에 처음 하시는 분들보다는 좀 더 수월하게 (예측해 가면서) 진행할 수 있었던 것 같습니다.

      답변이 되었는지요?

      감사합니다.

  14. HAZYCODE 2011/11/25 10:30 댓글주소 | 수정 | 삭제 | 댓글

    010073B2 에서 MZ 를 비교하신다고 했는데 010073B4 를 잘못 적으신게 아닌가 합니다.