안티 디버깅(Anti-debugging, 디버거 탐지) – HardwareBreakpoint

안티 디버깅(Anti-debugging, 디버거 탐지) – HardwareBreakpoint

프로그램 역분석, 즉 프로그램 디버깅 방법을 배웠다면, 해당 프로그램의 코드를 그대로 보는 것과 같아서, 숙달되면 프로그램의 모든 코드를 손쉽게 확인할 수 있다. 이를 통해 개발자가 원하지 않았던 방식으로 처리 흐름을 바꾸거나 데이터를 바꿔 치기 하는 등 악용할 수 있는 소지가 많다.

이를 방지하기 위해 개발된 기술이 바로 안티 디버깅으로, 말 그대로 디버깅 방지를 위한 기술들이라고 할 수 있다. 디버깅 자체를 막는 기술은 많이 개발되었으나, 이에 맞춰 안티-안티 디버깅 역시 함께 발전하면서 서로 뚫고 막고를 반복하고 있는 상황이다.

그리고 안티 디버깅 방법들은 대부분 이미 여러 포럼에서 공개해 놓은 상태이다. 아래는 시만텍에서 공개한 안티 디버깅 코드 참조용 내용이니 학습 전에 먼저 들러보기 바란다.

http://www.symantec.com/connect/articles/windows-anti-debug-reference

이외에도 많은 사이트에서 안티 디버깅과 관련된 참조 문서들을 많이 확인할 수 있는 만큼 안티 디버깅은 프로그램 보안 기술 중 기본 중 기본이라 할 수 있다(실제 대부분의 안티 디버깅 역시 방어 방법이 이미 나와 있다).

여기서는 안티 디버깅 중 유용한 몇 가지 방법에 대해 소개하여, 여러분이 안티 디버깅이라는 기술의 시각을 넓힐 수 있는 기본 지식을 다질 수 있도록 원리와 기법을 여러분들에게 소개하는 데 목적을 두고 진행하도록 하겠다.

디버거 탐지는 사용자가 프로그램을 분석하기 위해서 디버깅을 진행하는지에 대해 탐지하여 분석을 할 수 없도록 프로그램을 오동작하도록 하거나 중지, 함정에 빠뜨리는 방법이다.

이렇게 컴파일한 기계어인 프로그램의 보호가 필요한 이유는 3부에서 배웠듯이 역분석을 진행하면, 해당 프로그램의 동작 상황을 소설책을 읽어 내려가듯 분석하게 되면, 원하는 문장을 고쳐, 처리 흐름을 바꿔놓거나, 해당 프로그램의 목적과 반대로 동작하도록 수정할 수도 있다.+

따라서 디버깅을 진행하는 프로그램에 대한 제재를 진행해야 하는데, 이를 실행하기 위해 프로그램 자체에 디버깅을 방지하는 보호 코드를 삽입하여야 한다. 이러한 안티 디버깅 코드들은 유명 개발 커뮤니티에도 다수 공개되어 있으므로, 전체적인 내용을 진행하는 것에는 큰 의미 없으므로, 여기서 에서는 몇 가지 개념 이해를 돕기에 유용한 분석을 진행해보도록 하겠다.

HardwareBreakpoint (브레이크 포인트)

우리는 2부에서 CPU에서 이용하는 레지스터에 대해 공부한 적이 있다. 그 중 하드웨어 브레이크포인트를 저장할 때 DR 레지스터를 이용한다고 배웠는데, 디버깅 유무를 판단하는 방식으로 이 DR 레지스터의 내용 유무를 확인하여 현재 디버깅 중인지를 파악할 수 있다. 하드웨어 브레이크 포인트는 CPU에서 제공하기 때문에 프로그램을 역분석을 재시작해도 브레이크 포인트 유지가 가능하여 해당 지점에서 다시 분석을 재개할 수 있다(소프트웨어 브레이크 포인트를 리셋된다). 이외에도 소프트웨어 브레이크 포인트는 메모리상의 프로그램을 기계어를 0xCC로 변경하여 인식하는데, 이는 변경을 CRC 체크와 같은 무결성 검사시 프로그램 코드를 변경하였음을 감지하는 경우에도 유용하다고 할 수 있다(언패킹시 하드웨어 브레이크 포인트를 종종 이용한다. 패킹 부분에서 다루겠다). 그럼 소스를 통해 브레이크 포인트를 이용해 디버깅 유무를 파악해 보자.

hwbpcheck.cpp
#include
<stdio.h>
#include
<windows.h>
bool Hwbpcheck()

{

CONTEXT ctx;

HANDLE    thread;

// 현재 스레드의 핸들값을 구한다.

ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;

thread = GetCurrentThread();

GetThreadContext(thread, &ctx);

// 만약 DR레지스터가 0x00이 아닌 경우 레지스터를 초기화하고 TRUE를반환한다 (분석에 혼선을 주기 DR 레지스터에 엉뚱한 값을 더하거나 전혀 다른 방향으로 프로그램이 전개되도록 하는 방법도 좋다).

if ((ctx.Dr0 != 0) || (ctx.Dr1 != 0) || (ctx.Dr2 != 0) || (ctx.Dr3 != 0) || (ctx.Dr6 != 0) || (ctx.Dr7 != 0))

{

ctx.Dr0 = 0;

ctx.Dr1 = 0;

ctx.Dr2 = 0;

ctx.Dr3 = 0;

ctx.Dr6 = 0;

ctx.Dr7 = 0;

// 처리결과를 TRUE를 반환한다.

return TRUE;

}

else

return FALSE;

}

void main()

{

while(true)

{

Sleep(1000);

// 예외처리체크, 만약Check를 거치지 않고 통과한 경우 if 조건으로 차단하므로 보안성이 향상 된다.

if(Hwbpcheck() == TRUE)

{

printf(“Debugging Detected\n”);

// 임의의 주소에서 종료

exit(0x0ADE0005);

}

else

printf(“Safe\n”);

}

return;

}

[예제] 하드웨어 브레이크 포인트가 설정되었는지 확인한다

이 역시 Ollydbg를 이용하여, main 함수를 찾고 해당 코드에서 Hwbpcheck 함수 위치에 브레이크 포인트를 설정하자. 그리고 하드웨어 감지가 정상적으로 체크되는지 확인하기 위하여, 하드웨어 브레이크 포인트도 지정해 보자.


[그림] Hwbpcheck 함수 위치와 디버깅 메세지에 브레이크 포인트를 설정하였다

이제 하드웨어 브레이크 포인트를 정상적으로 체크하는지 확인을 위해 Debug

Hardware breakpoints 에 정상적으로 하드웨어 브레이크 포인트가 등록된 것을 확인할 수 있다.


[그림] 하드웨어 브레이크 포인트를 설정하였다

이제 Hwbpcheck 함수의 처리 내용을 확인해 보자.

012013A0 h> 55 PUSH EBP

012013A1 8BEC MOV EBP,ESP

012013A3 81EC A4030000 SUB ESP,3A4

012013A9 53 PUSH EBX

012013AA 56 PUSH ESI

012013AB 57 PUSH EDI

012013AC 8DBD 5CFCFFFF LEA EDI,DWORD PTR SS:[EBP-3A4]

012013B2 B9 E9000000 MOV ECX,0E9

012013B7 B8 CCCCCCCC MOV EAX,CCCCCCCC

012013BC F3:AB REP STOS DWORD PTR ES:[EDI]

012013BE A1 00702001 MOV EAX,DWORD PTR DS:[__security_cookie]

012013C3 33C5 XOR EAX,EBP

012013C5 8945 FC MOV DWORD PTR SS:[EBP-4],EAX

012013C8 C785 2CFDFFFF 10000>MOV DWORD PTR SS:[EBP-2D4],10010

012013D2 8BF4 MOV ESI,ESP

012013D4 FF15 A8812001 CALL DWORD PTR DS:[<&KERNEL32.GetCurrentThread>] ; kernel32.GetCurrentThread

012013DA 3BF4 CMP ESI,ESP

012013DC E8 4BFDFFFF CALL hardware.0120112C

012013E1 8985 20FDFFFF MOV DWORD PTR SS:[EBP-2E0],EAX

012013E7 8BF4 MOV ESI,ESP

012013E9 8D85 2CFDFFFF LEA EAX,DWORD PTR SS:[EBP-2D4]

012013EF 50 PUSH EAX

012013F0 8B8D 20FDFFFF MOV ECX,DWORD PTR SS:[EBP-2E0]

012013F6 51 PUSH ECX ; KERNELBA.75E0189F

012013F7 FF15 A4812001 CALL DWORD PTR DS:[<&KERNEL32.GetThreadContext>] ; kernel32.GetThreadContext DR 레지스터의 값을 가져온다.

012013FD 3BF4 CMP ESI,ESP

012013FF E8 28FDFFFF CALL hardware.0120112C

01201404 83BD 30FDFFFF 00 CMP DWORD PTR SS:[EBP-2D0],0 DR0 레지스터가 0과 같은지 확인한다.


[그림] 우리가 조금 전 등록한 하드웨어 브레이크 포인트 주소가 들어가 있다.

0120140B 75 2D JNZ SHORT hardware.0120143A

0120140D 83BD 34FDFFFF 00 CMP DWORD PTR SS:[EBP-2CC],0 DR1 레지스터가 0과 같은지 확인한다.

01201414 75 24 JNZ SHORT hardware.0120143A

01201416 83BD 38FDFFFF 00 CMP DWORD PTR SS:[EBP-2C8],0 DR2 레지스터가 0과 같은지 확인한다.

0120141D 75 1B JNZ SHORT hardware.0120143A

0120141F 83BD 3CFDFFFF 00 CMP DWORD PTR SS:[EBP-2C4],0 DR3 레지스터가 0과 같은지 확인한다.

01201426 75 12 JNZ SHORT hardware.0120143A

01201428 83BD 40FDFFFF 00 CMP DWORD PTR SS:[EBP-2C0],0 DR6 레지스터가 0과 같은지 확인한다.

0120142F 75 09 JNZ SHORT hardware.0120143A

01201431 83BD 44FDFFFF 00 CMP DWORD PTR SS:[EBP-2BC],0 DR7 레지스터가 0과 같은지 확인한다.

01201438 74 42 JE SHORT hardware.0120147C

// DR 레지스터가 0이 아닌 경우 아래 초기화를 진행한다.

0120143A C785 30FDFFFF 00000>MOV DWORD PTR SS:[EBP-2D0],0

01201444 C785 34FDFFFF 00000>MOV DWORD PTR SS:[EBP-2CC],0

0120144E C785 38FDFFFF 00000>MOV DWORD PTR SS:[EBP-2C8],0

01201458 C785 3CFDFFFF 00000>MOV DWORD PTR SS:[EBP-2C4],0

01201462 C785 40FDFFFF 00000>MOV DWORD PTR SS:[EBP-2C0],0

0120146C C785 44FDFFFF 00000>MOV DWORD PTR SS:[EBP-2BC],0

01201476 B0 01 MOV AL,1 결과값으로 TRUE를 리턴 한다.

01201478 EB 04 JMP SHORT hardware.0120147E

0120147A EB 02 JMP SHORT hardware.0120147E

0120147C 32C0 XOR AL,AL

0120147E 52 PUSH EDX ; ntdll.KiFastSystemCallRet

0120147F 8BCD MOV ECX,EBP

01201481 50 PUSH EAX

01201482 8D15 B0142001 LEA EDX,DWORD PTR DS:[main]

01201488 E8 F0FBFFFF CALL hardware.0120107D

0120148D 58 POP EAX ; hardware._RTC_CheckEsp

0120148E 5A POP EDX ; hardware._RTC_CheckEsp

0120148F 5F POP EDI ; hardware._RTC_CheckEsp

01201490 5E POP ESI ; hardware._RTC_CheckEsp

01201491 5B POP EBX ; hardware._RTC_CheckEsp

01201492 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4]

01201495 33CD XOR ECX,EBP

01201497 E8 78FBFFFF CALL hardware.01201014

0120149C 81C4 A4030000 ADD ESP,3A4

012014A2 3BEC CMP EBP,ESP

012014A4 E8 83FCFFFF CALL hardware.0120112C

012014A9 8BE5 MOV ESP,EBP

012014AB 5D POP EBP ; hardware._RTC_CheckEsp

012014AC C3 RETN 함수 복귀

[실습] Hwbpcheck 함수 분석내용

분석 내용은 어렵지 않게 확인할 수 있을 것이다. 그럼 이를 어떻게 우회할까? 현재 스레드의 하드웨어 브레이크 포인트를 받아오지 못하도록 하거나, 체크 루틴을 무시하는 방법도 있을 것이다.

이 외에도 독자 여러분만의 다양한 방법을 이용하여 무력화를 시도해 보기 바란다.


[그림] 필자는 무조건 FALSE를 반환하도록 수정하였다

Facebook Comments

Leave A Reply

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.