-榮-

디버깅 실습4 - Debug Blocker 동작원리 본문

리버싱 핵심원리

디버깅 실습4 - Debug Blocker 동작원리

xii.xxv 2024. 2. 8. 02:58

1. Debug Blocker

Dubug Blocker 기법은 프로세스가 자기 자신(또는 다른 실행 파일)을 디버깅 모드로 실행하는 기법이다. 이 기법은 다양한 안티 디버깅 기법 중 하나로, 몇몇 PE 프로텍터에서 사용되고 있다.

Debug Bolcker 기법이 적용된 프로세스는 디버거로 동작할 때와 디버기로 동작할 때 각각 다른 코드를 실행하도록 프로그래밍되어 있다.

만약 중요한 코드가 디버기 프로세스에서 실행된다면 그쪽을 디버깅해야 한다. 하지만 디버거-디버기 관계 자체가 안티 디버깅 역할을 하기 때문에 디버기 프로세스의 디버깅이 까다로워진다.

 


 

 

2. 안티 디버깅 특성

Debug Blocker 기법이 안티 디버깅으로 분류되는 이유는 아래와 같다.

● 부모-자식 관계

디버거-디버기 프로세스 관계는 기본적으로 부모-자식 프로세스 관계이다. 이전 실습의 ‘Self-Creation 기법에서 같은 프로그램이 부모 프로세스로 실행될 때와 자식 프로세스로 실행될 때 다른 동작을 하는 경우 자식 프로세스의 디버깅이 까다로워지는 것을 확인한 적 있다.

 디버기 프로세스는 다른 디버거에서 디버깅을 할 수 없다.

Windows 운영체제에서는 여러 디버거로 동시에 같은 프로세스를 디버깅할 수 없다. , 자식 프로세스를 디버거를 사용하여 Attach 하는 것이 불가능하다. 디버기 프로세스를 디버깅하고 싶으면 우선 기존 디버거-디버기 관계를 끊어야 한다..

 디버거 프로세스를 종료하면 디버기 프로세스도 동시에 종료

디버거-디버기 관계를 끊기 위해서 강제로 디버거 프로세스를 종료시키면 동시에 디버기 프로세스도 종료된다.

디버기를 직접 디버깅하자니 연관된 디버거 프로세스 때문에 불가능하고, 그 디버거 프로세스를 종료하자니 디버기 프로세스도 같이 종료되는 어려운 상황에 놓이게 된다.

 디버거에서 디버기의 코드를 조작

Debug Blocker 기법에서 디버거의 역할은 디버기 프로세스의 실행 분기를 조작하고, 코드를 생성/변경시킨다. 또한 지속적으로 디버기 프로세스의 코드 실행에 영향을 끼친다. 다시 말해 디버거 프로세스 없이 디버기 프로세스만으로는 정상적인 실행이 불가능한 경우가 많다.

 디버기의 예외(Exception)를 디버거에서 처리

디버거-디버기 관계에서 디버기 프로세스에 발생한 모든 예외는 디버거에서 처리하도록 되어 있다. 가령 디버기 프로세스가 의도적으로 예외(, Memory Access Violation)를 발생시킬 때, 이 예외가 해결되지 않으면 더 이상 코드를 실행할 수 없다. 이 예외는 디버거 프로세스에서 해결해야 된다..

디버기 프로세스에 예외가 발생하면 실행이 멈추고, 디버거 프로세스가 제어권을 가지게 된다.. 이때 디버거에서는 디버기의 실행 분기를 변경해 버린다. 추가적으로 디버기 프로세스 내의 암호화된 코드를 복호화하거나, 레지스터 혹은 스택에 특정한 값을 입력하는 등의 다양한 작업을 수행하는 경우도 있다.

따라서 리버서는 디버거 프로세스에서 어떻게 예외를 처리하는지 디버거 프로세스를 디버깅하면서 일일이 확인해야 된다. 그래야 정확한 디버기 프로세스의 실행 코드를 얻을 수 있다.

이러한 사실들이 Debug Blocker 기법을 상당히 까다로운 안티 디버깅 기법으로 만든다.

 


 

3. 디버깅 실습

실습 파일(DebugMe4.exe)의 실행 화면을 보면 기능을 대략 짐작할 수 있습니다. 부모 프로세스(Debugger)“Parent Process”라는 문자열과 자식 프로세스(Debuggee)“Child Process” 메시지 박스를 확인 가능하다.

DebugMe4.exe 실행 화면

 1차 시도

처음에는 프로그램의 저체 구조를 파악하기 위한 목적으로 간단히 디버깅해 본다.

디버깅 시작 위치 선정

DebugMe4.exe 파일은 Console 시반의 프로그램이므로 디버깅 시작 위치(main() 함수)를 알 수 있다. 프로그램의 핵심적인 코드를 찾아낼 때 사용할 수 있는 또 다른 방법은 프로그램 내에서 사용되는 문자열을 이용하는 방법과 프로그램에서 호출되는 Win32 API를 예상해서 BP를 설치하는 방법 등이 있다.

  main()

OllyDbgDebugMe4.exe 파일을 열고 main()까지 이동한다.

DebugMe4.exe의 main() 함수

 main() 함수 코드는 매우 간단하게 구성되어 있다. ‘ReverseCore:DebugMe4’라는 이름의 뮤텍스(Mutex) 객체를 생성한다.

00401009 CALL DWORD PTR DS:[408004] ; CreateMutexW()

CreateMutexW() API가 정상적으로 리턴되었다면 Last Error를 구해서 B7(ERROR_ALREADY_EXISTS)과 비교한다.

0040102A
00401030
CALL DWORD PTR DS:[40801C]
CMP EAX, 0B7
; GetLastError()

이 코드는 전형적인 Mutex 객체를 이용한 프로세스 중복 실행 체크 코드이다.

즉 DebugMe4.exe 파일이 부모 모드로 처음 실행되면 ‘ReverseCode :DebugMe4’ 이름의 Mutex가 정상적으로 생성되며 Last Error0이 될 것이다. 그러나 자식 모드로 실행되면 이미 부모 프로세스에서 생성한 같은 이름의 Mutex 객체가 존재하므로 Last ErrorB7이 될 것이다. 이 원리를 이요하여 DebugMe4.exe 프로세스가 부모 모드인지 자식 모드인지 판별할 수 있다.

00401035
00401037
0040103C
0040103E
0040103F
00401041
00401042
JE SHORT 0040103F
CALL 0040160
XOR EAX, EAX
RETN
LEA EAX, EAX
DB 15
DB 7F
; 0040103F
; 00401060


; Illegal use of register

현재는 부모 모드로 실행 중이므로 결국 401060 함수를 호출한다.

401060 함수는 상당히 큰 규모의 함수이다. 간단히 훑어보며 자식 프로세스를 어떤 식으로 생성하는지 확인해 보자.

401060 함수 내에서 자기 자신(DebugMe4.exe)을 디버그 모드(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)로 실행하는 코드를 확인할 수 있다. DebugMe4.exe는 자기 자신을 자식 프로세스로 실행하는데, Debug 모드로 실행시켜서 부모 프로세스는 디버거가 되고, 자식 프로세스는 디버기로 동작하도록 만든다.

CreateProcessW() 호출

※ 1차 시도에서 알아낸 사실

  • ‘ReverseCore:DebugMe4’라는 이름의 Mutex 객체를 이용한 부모/자식 프로세스 구분
  • 부모/자식 프로세스에서 갈라지는 실행 코드(부모 : 401037, 자식 : 40103F)
  • 부모/자식 프로세스의 관계는 디버거/디버기 관계

 

● 2차 시도

Debug Blocker 기법에서는 보통 자식 프로세스(Debuggee)에서 핵심 코드가 실행된다. 앞에서 확인한 Mutex를 이용한 조건 분기를 조작하여 자식 프로세스 모드에서 실행될 코드를 디버깅해 보자. OllyDbgDebugMe4.exe 디버깅을 재실행하고, 401035 주소에 BP를 걸고 실행한다.

우측 Register 창의 ZF(Zero Flag)를 마우스 더블클릭하여 1로 변경한다. 그러면 401035 주소의 JE 명령에 의하여 40103F 주소로 점프한다.

401035 주소의 조건 분기 코드

 40103F 주소의 코드를 보면 ILLEGAL INSTRUCTION예외가 발생한다.

0040103F LEA EAX, EAX  

IA32 MANUAL에서 LEA(Load Effective Address) 명령어의 정의를 보면 LEA 명령어의 첫 번째 Operand는 레지스터(r16/r32/r64)이고, 두 번째 OperandMemory이다.

LEA(Load Effective Address) 명령어 정의

40103F 주소의 명령어(LEA EAX, EAX)를 보면, 두 번째 OperandMemory가 아닌 레지스터(EAX)가 왔기 때문에 Exception이 발생하게 된다. 이러한 잘못된 명령어는 IA32 Opcode Map에 의해 억지로 조합할 수는 있지만 실제로 사용될 수 없다.

자식 프로세스(디버기)에서 4013F 주소의 잘못된 명령어(LEA EAX, EAX)가 실행되면, 예외가 발생하여 부모 프로세스(디버거)에게 제어권이 넘어간다. 제어권을 받은 부모 프로세스에서는 해당 예외를 처리함과 동시에 또 다른 작업을 수행한다.

401041 주소의 명령어를 보면 정상적인 형태의 명령어가 아닌 것을 알 수 있다.(OllyDbg가 해당 코드를 Disassemble하지 못한다.) 아마 제어권을 넘겨받은 부모(디버거) 프로세스에서 해당 코드를 복호화 혹은 다른 주소로 실행 분기를 변경하든지 작업을 할 것으로 예상할 수 있다. 따라서 부모 프로세스(디버거)의 실행 코드를 상세히 분석하는 방법밖에는 없다.

2차 시도에서 알아낸 사실

  • 자식(디버기) 프로세스의 실행 코드에 의도적인 예외 발생 코드 존재
  • 이후 코드는 정상적인 코드로 보이지 않음(암호화된 코드, 데이터 혹은 의미 없는 코드)
  • 부모(디버거)의 실행 코드를 상세히 분석해야 함

 

3차 시도

부모(디버거) 프로세스의 401060 함수를 상세히 디버깅한다. OllyDbg를 재실행하여 40115C까지 재실행한다.

...

   
0040115C
00401160
00401161
00401165
00401166
00401167
00401168

0040116A
0040116B
0040116C
0040116D
00401174
00401175
00401176
LEA EAX, DWORD PTR SS:[ESP+10]
PUSH EAX
LEA ECX, DWORD PTR SS:[ESP+24]
PUSH ECX
PUSH ESI
PUSH ESI
PUSH 3

PUSH ESI
PUSH ESI
PUSH ESI
LEA EDX, DWORD PTR SS:[ESP+3D8]
PUSH EDX
PUSH ESI
CALL DWORD PTR DS:[40800C]

; -pProcessInfo

; -pStartupInfo
; -CurrentDir
; -pEnvironment
; -CreationFlags = DEBUG_PROCESS |DEBUG_ONLY_
   THIS_PROCESS

; -InheritHandle
; -pThreadSecurity
; -pProcessSecutiry

; -CommandLine
; -ModuleFileName
; CreateProcessW()

...

   
004011A9
004011AE
PUSH 409A58
CALL 00401345
; ASCII “Parent Process”
; printf()

...
   
004011C3
004011C5
004011C9
004011CA
PUSH 1
LEA ECX, DWORD PTR SS:[ESP+6C]
PUSH ECX
CALL DWORD PTR DS:[408024]
; -Timeout

; -pDebugEvent
; WaitForDebugEvent()

...
   

401176 주소에서 CreateProcessW() API를 호출하여 자기 자신(DebugMe4.exe)Debug Mode로 실행시킨다. 그리고 4011CA 주소의 WaitForDebugEvent()를 이용해서 디버기 프로세스로부터 Debug 이벤트가 발생하기를 기다린다.

BOOL WaitForDebugEvent(
[out] LPDEBUG_EVENT lpDebugEvent,
[in] DWORD dwMilliseconds
);

출처 : MSDN

WaitForDebugEvent() API를 호출하면 dwMilliseconds에서 정한 시간 동안 디버기 프로세스의 Debug 이벤트를 기다린다. 예외도 Debug 이벤트의 한 종류이다.

만약 디버기 프로세스에서 예외가 발생하면 WaitForDebugEvent() API가 리턴하고, lpDebugEvent 포인터가 가리키는 DEBUG_EVENT 객체에 해당 예외에 대한 정보가 채워진다.

typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode;
DWORD dwProcessId;
DWORD dwThreadId;
union {
EXCEPTION_DEBUG_INFO Exception;
CREATE_THREAD_DEBUG_INFO CreateThread;
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
EXIT_THREAD_DEBUG_INFO ExitThread;
EXIT_PROCESS_DEBUG_INFO ExitProcess;
LOAD_DLL_DEBUG_INFO LoadDll;
UNLOAD_DLL_DEBUG_INFO UnloadDll;
OUTPUT_DEBUG_STRING_INFO DebugString;
RIP_INFO RipInfo;
} u;
} DEBUG_EVENT, *LPDEBUG_EVENT;

출처 : MSDN

출처 : MSDN

dwDebugEventCode 멤버는 Debug 이벤트의 종류(타입)이다. 9가지의 이벤트가 존재하며, u 멤버는 각각의 Debug 이벤트에 해당하는 구조체가 저장된다(u 멤버는 union이며 9개의 structure를 하나로 묶어버린다).

4011CA 주소까지 디버깅하면 pDebugEvent가 가리키는 주소는 18F9D8이다.

WaitForDebugEvent() API 호출 이전의 DEBUG_EVENT 구조체

WaitForDebugEvent() API 호출을 실행한 후 18F9D8을 보면 dwDebugEventCode 값은 3(CREATE_PROCESS_DEBUG_EVENT)이다.

WaitForDebugEvent() API 호출 이후의 DEBUG_EVENT 구조체

트레이싱을 하다 보면 조건 분기 코드가 나온다. 4011F0 주소의 [ESP+68]은 [18F9D8](dwDebugEventCode)이다. 이 값을 EAX 레지스터에 저장한 후 4011F4 주소에서 1(EXCEPTION_DEBUG_EVENT)과 비교한다. 현재 dwDebugEventCode3이므로 4011F7 주소의 JNZ 명령에 의해 4012C0 주소로 점프한다.

dwDebugEventCode 값의 비교를 통한 조건 분기 1

4012C0 주소에서 다시 EAX(dwDebugEventCode) 값을 5(EXIT_PROCESS_DEBUG_EVENT)와 비교한다. 현재 EAX 값은 3이므로 4012C3 주소의 JE 명령어를 그대로 통과한다. 4012D4 주소의 ContinueDebugEvent() API를 호출하면서 실행이 멈춰진 디버기 프로세스를 계속 실행시켜 준다. 그리고 4012F2 주소의 WaitForDebugEvent() API를 호출하여 디버기 프로세스의 Debug 이벤트를 기다리는 상태가 된다. 다시 Debug 이벤트가 발생하면 4011F0 주소로 가게 된다..

dwDebugEventCode 값의 비교를 통한 조건 분기 2

4011F0~4011F7 주소의 명령어를 보면, 이 루프는 디버기로부터 EXCEPTION_DEBUG_EVENT(1) 예외를 기다리는 것을 알 수 있다. 이 경우를 집중적으로 분석하자. 가장 간단한 방법은 4011FDBP를 설치하고 실행하는 것이다. 루프는 디버기의 EXCEPTION_DEBUG_EVENT(1)가 발생했을 때 4011FD 주소에 정확히 멈출 것이다. 또 다른 방법은 4011FDConditional Log Break Point[SHIFT+F4]를 설치하여 사용자 로그를 출력하고 조건에 따라 실행을 멈추도록 할 수 있다.

 

● 4차 시도

OllyDbgCLBP(Conditional Log Break Point) 기능을 이용하여 4011F0~4012FA 루프의 동작 흐름을 살펴보자.

우선 디버기로부터 발생하는 모든 Debug 이벤트를 출력해 보자. OllyDbg를 재실행해서 Go To Expression[CTRL+G] 명령으로 4011F0 주소로 이동한다.

4011F0 주소로 이동

4011F0 주소에 커서를 위치한 후 Conditional Log Break Point[SHIFT+F4] 명령을 내린다.

 

  • Condition 항목에 필요한 조건을 입력 (생략 가능)
  • Explanation과 Expression 항목에는 Log 창에 출력하고 싶은 내용 입력
  • Pause program 항목은 언제까지 실행할지 선택
  • Log value of expression 항목은 어떤 상황에 로그를 출력할지를 선택
    - Pause program 항목은 ‘Never’ 옵션을 지정하여
    프로그램이 끝날 때까지 실행되도록 하였고, Log
    value of expression 항목은 ‘Alway’ 옵션을 지정하여
    주어진 조건과 상관없이 항상 로그를 출력

 

 

CLBP가 설치되면 해당 주소는 분홍색으로 표시되어 일반적인 BP의 붉은색과 구분된다. 그리고 Breakpoints 창의 ‘Active’ 항목에 ‘Explanation’에 기록한 내용이 표시된다.

4011F0 주소에 설치된 Conditional Log Break Point

이 상태로 OllyDbg에 실행 명령을 내리면 DebugMe4.exe가 정상적으로 실행되면서 로그를 출력한다. 출력된 로그를 보면 디버기 프로세스에서 총 3회의 EXCEPTION_DEBUG_EVENT(1)가 발생한 것을 알 수 있다. (로그의 대부분은 LOAD_DL_DEBUG_EVENT(6)이다.)

실행 로그

 

● 5차 시도

EXCEPTION_DEBUG_EVENT(1)의 경우를 집중적으로 디버깅한다. OllDbg를 재실행하고 4011F0 주소에 설치된 CLBP를 수정한다. ‘Pause program’ 항목을 ‘On condition’ 옵션으로 변경하여 조건에 맞는 경우 프로그램 실행을 멈추도록 한다. 그리고 ‘Log value of expression’ 항목 역시 ‘On condition’ 옵션으로 변경하여 조건에 맞는 경우만 로그를 출력하도록 한다.

○ System Break Point

OllyDbg에서 실행 명령을 내리면 dwDebugEventCode = 1(EXCEPTION_DEBUG_EVENT)의 경우에 4011F0 주소에서 멈추게 된다. 4011FD 주소까지 트레이싱한다.

ExceptionCode 비교

사용되는 메모리는 다음과 같다.

[ESP+68] = [18F9D8] = DEBUG_EVENT.dwDebugEventCode
[ESP+74] = [18F9E4] = DEBUG_EVENT.EXCEPTION_DEBUG_INFO.EXCEPTION_RECODE.ExceptionCode
[ESP+80] = [18F9F0] = DEBUG_EVENT.EXCEPTION_DEBUG_INFO.EXCEPTION_RECODE.ExceptionAddress

현재 디버거 프로세스는 77A0114C(ExceptionAddress) 주소에서 80000003(ExceptionCode - “EXCEPTION_BREAKPOINT”) 예외가 발생했다는 의미이다. 77A0114C 주소의 명령어를 따라가 보면, ntdll.dll 모듈의 ‘System Break Point’인 것을 알 수 있다. , 프로그램을 디버기 모드로 실행시킬 때 무조건 발생하는 예외이다.

System Break Point

typedef struct _EXCEPTION_DEBUG_INFO {
EXCEPTION_RECORD ExceptionRecord;
DWORD dwFirstChance;
} EXCEPTION_DEBUG_INFO, *LPEXCEPTION_DEBUG_INFO;
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation
[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;

출처 : MSDN

○ EXCEPTION_ILLEGAL_INSTRUCTION (1)

4011F0 주소에서 다시 실행이 멈춘다. ExceptionCode = C000001D(EXCEPTION_ILLEGAL_INSTRUCTION)이고, ExceptionAddress = 40103F이다. 40103F2차 시도에서 확인한 ‘LEA EAX, EAX’명령어가 위치한 주소이다.

40103F 주소에서 EXCEPTION_ILLEGAL_INSTRUCTION 예외 발생

4011F0 주소의 루프에는 3개의 조건 분기 코드가 있다.

004011F0
004011F4
004011F7
004011FD

00401205
0040120B
00401212

00401217
0040121D
...
MOV EAX, DWORD PTR SS:[ESP+68]
CMP EAX, 1
JNZ 004012C0
CMP DWORD PTR SS:[ESP+74], C000001D

JNZ 004012C5
MOV EAX, DWORD PTR SS;[ESP+80]
CMP EAX, 40103F

JNZ 00401299
MOV EAX, DWORD PTR SS:[ESP+10]


; 1) dwDebugEventCode==1

; 2) ExceptionCode == C000001D



;3) ExceptionAddress == 40103F



3가지 조건이 모두 만족되면 40121D 주소의 코드를 실행한다.

첫 번째 EXCEPTION_ILLEGAL_INSTRUCTION 예외가 발생했을 때, 40121D 주소의 코드가 실행되는 것을 확인했다. 따라서 디버기 프로세스의 예외를 처리해서 재실행하는 코드가 나타날 것이라 생각된다.

 

○ EXCEPTION_ILLEGAL_INSTRUCTION (2)

세 번째 EXCEPTION_DEBUG_EVENT401048 주소에서 나타난다. ExceptionCode는 이전과 마찬가지로 EXCEPTION_ILLEGAL_INSTRUCTION이다. 세 번의 조건 분기로 인해 401299 주소로 점프한다. , 두 번째 EXCEPTION_ILLEGAL_INSTRUCTION이 발생하였을 때 401299 주소의 코드가 실행된다는 것을 알 수 있다.

401048 주소에서 EXCEPTION_ILLEGAL_INSTRUCTION 예외 발생

이 상태에서 OllyDbg의 실행[F9]을 하면 자식 프로세스의 메시지 박스가 나타나며 정상적으로 실행되는 것을 확인할 수 있다.

※ 5차 시도에서 알아낸 사실

  • 디버기 프로세스에서 두 번의 EXCEPTION_ILLEGAL_INSTRUCTION 예외가 발생
  • EXCEPTION_ILLEGAL_INSTRUCTION 예외가 발생한 경우 디버거 프로세스의 40121D, 401299 주소의 코드가 실행

 

● 6차 시도

DebugMe4.exe의 핵심 코드(40121D, 401299)를 디버깅한다.

 

○ 40121D (첫 번째 예외)

40121D 주소의 코드를 디버깅해 보자. 401233 주소의 ReadProcessMemory() API 호출 코드는 디버기 프로세스의 401041 주소에서 14바이트 크기만큼 읽어오는 명령이다.

ReadProcessMemory() API 호출

401041 주소의 첫 번째 EXCEPTION_ILLEGAL_INSTRUCTION 예외가 발생하는 40103F 주소 명령(LEA EAX, EAX)바로 다음 주소이다.

ReadProcessMemory() API의 pBaseAddress

ReadProcessMemory() API 호출을 넘기면 ReadProcessMemory() API에서 읽어온 Byte들을 7F와 XOR하는 코드를 발견할 수 있다. XORXOR 디코딩 루프(Decoding Loop), 401041~401054 주소를 디코딩하여 정상적인 코드로 변환하는 역할을 한다.

Decidong Loop
Decodeing Code

디코딩 루프에서 디코딩된 코드를 WriteProcessMemory() API를 사용하여 401041~401054 주소에 덮어쓴다. 디버기 프로세스 입장에서는 암호화된 주소영역이 디버거에 의해 복호화된 것이다.

WriteProcessMemory() API

이후에는 메인 ThreadContext 구조체를 읽어와서 EIP를 변경하는 코드를 확인할 수 있다. 이로 인해 디버기 프로세스의 40103F 주소의 에서 발생한 EXCEPTION_ILLEGAL_INST-RUCTION 예외가 해결된다. (40103F 주소의 LEA EAX, EAX의 명령어는 2byte)

디버기 프로세스의 EIP 변경

변경한 EIPSetThreadProcess() API 호출하여 적용한다. 4012D4 주소의 ContinueDebugEvent() API를 호출하여 실행이 중지된 디버기 프로세스를 다시 실행되도록 한다. 이후 4012F2 주소의 WaitForDebugEvent() API를 다시 호출하여 디버기 프로세스의 Debug 이벤트를 기다린다.

40121D 실행 코드 흐름

1. Generate Exception(디버기)

디버기 프로세스의 40103F 주소에서 EXCEPTION_IELLAGER_INSTRUCTION 예외가 발생한다. 이 순간 디버기 프로세스는 실행이 중지되고, 디버거 프로세스에게 제어가 넘어온다.(정확히는 디버거 프로세스에서 호출한 WaitForDebugEvent() API가 리턴된다.)

2. Decoding Loop(디버거)

디버거는 디버기 프로세스의 401041~401054 메모리 영역을 읽어 들여XOR(7F) 명령으로 디코딩한다. 그리고 디코딩된 실제 코드를 디버기 프로세스의 동일한 메모리 영역에 덮어쓴다.

3.Change EIP(디버거)

디버기의 코드 실행 주소(EIP)를 변경(40103F->401041)한다.

4. Countinue Debuggee(디버거)

40103F의 예외를 처리했으므로 디버기 프로세스를 실행하고, 다시 디버거는 디버기의 예외를 기다리는 대기모드로 전환된다.

 

○ 401299 (두 번째 예외)

두 번째 EXCEPTION_ILLEGAL_INSTRUCTION 예외는 401048에서 발생한다. 401299 주소에서는 4010482byte 명령어를 변경(8DC0(LEA EAX, EAX) -> 681C(PUSH XXXX))하는 WriteProcessMemory() API를 호출한다.

WriteProcessMemory() API

2byte가 수정되며 그 이후의 명령어도 정상적으로 해석되고, EXCEPTION_ILLEGAL_INSTRUCTION 예외도 해결된다.

401299 실행 코드 흐름

 1. Generate Exception(디버기)

디버기 프로세스의 401048 주소에서 EXCEPTION_ILLEGAL_INSTRUCTION 예외가 발생한다.

2. Code Patch(디버거)

디버기 프로세스의 401048~401049 메모리 영역에 ‘681C’를 덮어쓴다.

3. Countinue Debuggee(디버거)

예외가 처리되었으므로 디버기 프로세스를 실행시킨다. 그리고 디버거를 다시 대기모드로 전환한다.

 


 

4. 마무리

Debug Blocker 기법은 상당히 까다로운 기법이다. 아주 단순한 형태로 구현된 DebugMe4.-exe파일도 디버깅 설명이 길다. 디버거-디버기 관계를 강제로 끊고 OllyDbgAttach 시킨다는 개념만 이해하면 큰 문제없이 디버깅할 수 있을 것이다.