본문 바로가기

High Level Technique/Reversing

Debug Blocker

Debug Blocker


Debug Blocker 기법은 자기 자신을 디버깅 모드로 실행하는 기법입니다.


부모 프로세스와 자식 프로세스간의 관계가 디버거 - 디버기 관계로 이루어져 있기 때문에 디버기인 자식프로세스에 중요 코드가 존재한다면 자식 프로세스를 디버깅해야 합니다.

하지만 디버거 - 디버기 관계가 안티 디버깅 역할을 하기 때문에 디버깅하기가 어렵습니다.








중점사항


1. 부모 - 자식 관계


디버거 - 디버기 관계는 부모 - 자식 프로세스 관계이기 때문에 Self Creation 기법에서와 같이 디버깅을 하기 어려워 집니다.



2. 디버기 프로세스는 다른 디버거에서 디버깅 불가


Windows 운영체제에서 여러 디버거로 동시에 같은 프로세스를 디버깅 할 수 없습니다.



3. 디버거 프로세스를 종료하면 디버기 프로세스도 종료


여러 디버거로 디버깅을 하지 못하니 디버거 - 디버기 관계를 끊어서 디버깅을 시도하고자 할때 디버거 프로세스가 종료되면 디버기 프로세스도 종료되므로 디버깅을 할 수 없습니다.



4. 디버거에서 디버기 코드를 조작


Debug Blocker 기법에서 디버거의 역할이 디버기 프로세스의 실행 분기를 조작하고 코드를 생성하고 변경시키빈다. 



5. 디버기의 예외를 디버거에서 처리


디버거 -디버기 관계의 프로세스는 디버기 프로세스에서 발생한 모든 예외를 디버거에서 처리하도록 되어있습니다.











분석


실습예제인 DebugMe4.exe를 분석해 보도록 하겠습니다.




일단 그냥 실행해본 결과 콘솔에서는 부모 프로세스로 작동하고 메시지 박스가 나타나면서 자식 프로세스로 작동합니다.


OllyDBG로 DebugMe4.exe를 실행시킵니다.



CreateMutexW API를 호출하게 되면 ReverseCore:DebugMe4라는 이름의 Mutex 객체를 생성합니다.


트레이싱을 하다보면 40102A의 GetLastError로 이동하게 됩니다. 401030의 CMP 명령에 의해서 0xB7과 값을 비교합니다.


DebugMe4.exe 파일이 부모 프로세스로 실행되면 ReverseCore:DebugMe4라는 이름의 Mutex가 생성되고 Last Error가 0이됩니다. 그런데 자식 프로세스로 실행이되면 부모 프로세스에서 이미 Mutex 객체가 존재하므로 return 값이 0xB7이 됩니다.


이러한 과정을 통해서 부모 프로세스인지 자식프로세스인지 판단을 할 수 있습니다.


Last Error가 0이므로 점프를 하지 않고 401037로 이동하게 됩니다. 해당 주소를 들어가면 아래와 같은 루틴이 나타나게 됩니다.



CreateProcessW API가 존재하는 것을 볼 수 있는데 여기서 CreationFlags를 보면 DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS인 것을 볼 수 있습니다.

이것은 자기 자신을 디버그 모드로 실행하는 것입니다.


Debug 모드로 실행시켜서 부모 프로세스는 디버거가 되고 자식 프로세스는 디버기로 동작을 합니다.
















Debug Blocker 에서는 보통 자식 프로세스에 핵심 코드가 실행되는데 Mutex를 이용해서 조건 분기를 조작하여 자식 프로세스 모드에서 실행될 코드를 디버깅 합니다.



앞서 분석했을 때 JE 명령어에서 점프를 하지 않고 진행이 되었기 때문에 이번에는 다시 실행시켜서 해당 명령어에서 멈춰 ZF를 1로 세팅하고 넘어가 보도록 하겠습니다.


넘어가서 실행을 시켜보니 아래와 같은 에러창이 나타나게 됩니다.



해당 명령어를 보면 LEA EAX, EAX로 되어있는데 LEA 명령어는 주소를 이동하는 것이죠.


LEA를 사용할 때 보면 LEA EAX, [EAX]와 같은 꼴로 사용됩니다. 주소가 아닌 레지스터가 존재하기 때문에 에러가 발생한 것입니다.


자식 프로세스로 넘어가기 위해서 조건 분기를 조작했지만 이 방법으로는 더이상 진행을 할 수 없습니다.














그러면 이제 다시 원래 부모프로세스 쪽을 분석해 봅니다.


앞서 처음에 분석했을 때 CreatProcessW에서 자기 자신을 Debug Mode로 실행시킨 것을 봤었습니다.  더 트레이싱을 해보면 WaitForDebugEvent가 나타나게 됩니다.



WaitForDebugEvent는 디버기 프로세스에서 Debug Event가 발생할 때까지 기다립니다. 


WaitForDebugEvent를 실행시켜서 pDebugEvent 값을 확인해 봅니다.




19F9D0의 값이 0x3으로 바뀐 것을 알 수 있습니다.


wDebugEventCode의 값이 3인데, CREATE_PROCESS_DEBUG_EVENT 입니다. 디버기 프로세스가 실행될 때 처음 발생하는 이벤트입니다.



좀 더 트레이싱을 해보면 4011F0에서 ESP+68의 값을 EAX에 넣습니다. 그리고 이 값을 1과 비교하고 있습니다. ESP+68의 값이 dwDebugEventCode인데 이 값을 1과 비교하고 있는 것입니다.

앞서 DebugEventCode의 값이 3이였으므로 JNZ 명령어를 통해서 4012C0로 이동하게 됩니다.



4012C0에서 다시 5와 비교하고 있습니다. 앞서 값이 3이라는 것을 알았기 때문에 JE 명령어는 실행되지 않습니다. 4012D4에서 ContinueDebugEvent를 호출해서 실행이 멈춰진 디버기 프로세스를 계속 실행시킵니다. 그리고 다시 4012F2에서 WaitForDebugEvent를 호출해서 디버기 프로세스로 Debug Event를 기다리게 됩니다.


그리고 이벤트가 발생하게 되면 4012FA에서 4011F0로 이동하게 됩니다. 



4011F0로 이동해서 또다시 같은 작업을 반복하게 됩니다. JNZ 명령어가 실행이된다는 것이죠. 그러면 JNZ가 실행이 안되고 4011FD로 이동하는 것을 보아야 합니다.

해당 주소에 BreakPoint를 걸고 진행시킵니다.


다른 방법으로는 4011FD에 SHITF + F4를 눌러서 Conditional Log Break Point(이하 CLBP)를 설치하는 것입니다.

사용자 로그를 출력하고 조건에 따라 실행을 멈추도록 하는 것입니다.








다시 디버거를 실행시켜서 4011FD로 이동합니다. 그리고 해당 주소에서 SHITF + F4를 눌러서 CLBP를 설정합니다.



Condition 항목은 필요한 조건을 입력하는 것입니다. dwDebugEventCode가 1과 같은지 체크하는 것이빈다.

Explanation과 Experssion 항목에는 Log 창에 출력하고 싶은 내용을 입력하는 것입니다.


Pause program에는 Never를 선택해서 프로그램이 끝까지 실행되도록 하고, Log value of expression 항목은 Always 옵션을 지정해서 주어진 조건과 상관없이 항상 로그를 출력하도록 합니다.



CLBP를 설치하게 되면 핑크색으로 BreakPoint가 설정됩니다. 그리고 BreakPoint 정보를 보면 WFDE()가 보이게 됩니다.


그리고 프로그램을 진행시킨 후 Log를 보면 아래와 같이 나타나게 됩니다.












EXCEPTION_DEBUG_EVENT(0x1)인 경우를 디버깅 합니다.


Pasue program과 Log valueof expression 항목을 On condition에 체크를 해서 해당 조건만 맞는 경우 로그를 출력합니다. (그런데 저는 애초에 맞는 조건만 출력이 되었습니다.)



다시 재실행을 해보면 4011FD에 멈추게 됩니다.  해당 값을 살펴보도록 하겠습니다.





디버기 프로세스는 7712CCBC 주소에서  80000003 예외가 발생했다는 뜻입니다. 7712CCBC 주소를 따라가보면 System BreakPoint라는 것을 알 수 있습니다.



이 부분은 중요하지 않기 때문에 다시 F9를 눌러서 실행을 시킵니다.



다시 실행시켜서 값을 확인해 보면 아래와 같습니다.



이번에는 Exception Code는 C000001D이고 Exception Address는 0040103F 입니다. 디버기 프로세스에서 40103F 주소에서 EXCEPTION_IILLEGAL_INSTRUCTION 에러가 발생했다는 것입니다.


40103F는 처음에 분석했을 때 LEA EAX, EAX 명령어가 있던 주소이빈다. 디버기 프로세스가 40103F 주소에서 잘못된 명령어를 만나서 예외가 발생한 것입니다.



CMP 가 총 3번에 걸쳐서 값을 확인하고 있습니다. 위 3가지 조건이 만족해야 40121D로 넘어가게 됩니다.


한번 더 실행해서 값을 확인해 보겠습니다.



401048 주소에서 C000001D 에러가 발생했습니다. 401048 주소의 값은 정상적인 명령어가 아닙니다. 



401217, 401217 명령에 의해서 EXCEPTION_ILLEGAL_INSTRUCTION 예외가 발생하면 401299로 이동한다는 것을 알 수 있습니다.
















여기까지 진행했을 때 총 2번의 예외가 발생했습니다. 하나는 40121D로 넘어가는 것과 다른 하나는 401299로 넘어가는 것이였습니다.

그럼 먼저 40121D 주소의 코드를 디버깅 해보겠습니다.



스택의 값을 보면 pBaseAddress의 값이 401041입니다. 디버기 프로세스의 401041주소에서 14바이트 만큼 읽어드리는 명령어입니다.



트레이싱을 하면 401240에서 XOR 7F를 하는 것을 볼 수 있습니다.




해당 디코딩 부분을 넘어간 후 다시 살펴보도록 하겠습니다.


19FD00의 주소를 가보면 아래와 같이 DebugMe4라는 문자열이 나타난 것을 볼 수 있습니다.




401264에서 WriteProcessMemory가 호출됩니다. 


트레이싱을 계속 진행하면 메인 쓰레드 컨텍스트 EIP를 변경하는 부분이 나타납니다.



Hex Dump의 파란표시로 된 부분이 CONTEXT.Eip 입니다.


40127E에서 GetThreadContext를 호출해서 디버기 프로세스의 메인 쓰레드 컨텍스트 구조체를 읽어드립니다. 제 환경에서는 19FA30 입니다.


401284에서 해당 값을 +2를 해서 401041로 값을 바꿉니다. 그래서 40103F(LEA EAX, EAX 크기가 2바이트)에서 발생한 EXCEPTION_ILLEGAL_INSTRUCTION 예외를 해결합니다.



트레이싱을 계속해보면 ContinueDebugEvent, WaitForDebugEvent가 호출됩니다. 중지된 프로세스를 다시 실행하도록 하고 Debug Event를 기다립니다. 












이제 401299에서 발생한 예외를 디버깅 해보겠습니다.


401299에 BreakPoint를 걸고 진행해서 해당 주소까지 실행합니다.




4012BC에서 WriteProcessMemory를 호출하게 됩니다.




401299에서 EAX의 값은 EXCEPTION_ILLEGAL_INSTRUCTION이 발생한 주소 401048입니다. 해당 주소에는 LEA EAX, EAX가 있죠.


디버기 프로세스가 해당 명령어를 수행하다가 예외가 발생했기 때문에 나타난 것입니다.


4012BC 에서 WriteProcessMemory를 호출하는데 이때 401048 주소의  2바이트 8DC0(LEA EAX, EAX)를 681C로 변경하는 것입니다.


그 후 ContinueDebugEvent와 WaitForDebugEvent가 호출이 됩니다.













이번에는 자식 프로세스를 디버깅하는 방법에 대해서 알아보겠습니다.



먼저 Static 방식입니다.


디버거 프로세스를 분석해서 디코딩 코드를 얻은 후에 PE 파일 이나 메모리에 직접 패치를 시킨 후 OllyDBG로 디버깅을 하면 됩니다.


제일 처음에 분석을 했을 때 401035 위치에서 JE 명령어를 조작해서 40103F까지 이동했었습니다. 이 방식으로 40103F까지 이동시킵니다.



먼저 LEA EAX, EAX 예외가 발생하지 않도록 NOP으로 변경하고 401041부터 401054를 다음 코드로 패치합니다.



이렇게 코드를 패치하고 실행하면 됩니다.





두 번째로는 Dynamic 방법입니다.


앞서 분석을 해서 적절한 위치를 찾았습니다. 401041부터 디버깅을 하면 자식프로세스를 디버깅 할 수 있습니다.

분석을 하기위해서는 디코딩 부분까지 디버깅을 진행해야 합니다.


401233에 BreakPoint를 설치해 두고 진행합니다.




hProcess의 값을 보면 1E8 입니다. 이 값은 뒤에서 사용됩니다.


4012D4 주소의 ContinueDebugEvent 부분까지 진행합니다.




ThreadId는 1890 ProcessID는 1150 입니다. 이 값도 뒤에서 사용됩니다.



ContinueDebugEvent가 실행이되면 자식 프로세스가 실행이 되버리기 때문에 OllyDBG에 붙여서 디버깅을 할 수 없습니다.


따라서 그전에 무한루프를 설치해 둡니다.


4012D2 주소에 무한루프 EBFE로 코드를 패치합니다.


그리고 4012D4 부터 4012E4까지 아래와 같이 코드를 변경합니다.




WriteProcessMemory 주소까지 실행시킨 뒤에 추가로 코드를 변경하여 무한루프가 설치된 자식 프로세스를 실행 시킵니다.




이제 디버거 - 디버기 프로세스 관계를 끊어야 합니다.


DebugActiveProcessStop을 이용해서 디버거에서 디버기를 때어낼 수 있습니다.






4010305의 주소까지 실행하고 나면 디버거에서 디버기가 떨어지게 됩니다.


이제 자식 프로세스를 Attach 해야 합니다. 새로 OllyDBG를 켭니다.



하하 역시 에러가 발생하네요.... 여튼 정상적으로 붙게되면 무한루프가 걸린 부분을 다시 원본 코드로 바꿔 분석을 하면됩니다.



실패한 이유는 Windows 10에서 사용되는 API가 다르거나 x64 환경이기 때문일 수도 있습니다.


이 부분은 다음에 더 해보는 걸로...




이렇게 핵심원리 한권이 끝났다!!

'High Level Technique > Reversing' 카테고리의 다른 글

Native API  (0) 2016.08.24
C++ 분석  (0) 2016.08.13
PE Image Switching  (2) 2016.08.10
Self Creation Debugging  (0) 2016.08.09
Service Debugging  (0) 2016.08.09