안티 디버깅(Anti-debugging, 디버거 탐지) – INT3 예외 처리

How to debugging detect?

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

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

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

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

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

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

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

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

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

INT3 (예외 처리)

디버깅을 진행할 때 디버거들은 브레이크 포인트를 인식하도록 구성되어 있는데, 실제 소프트웨어 브레이크 포인트는 기계어 코드를 INT3(0xCC)으로 변경하여 실행하게 된다. 이 때 디버거는 분석자가 혼돈하지 않도록 INT3으로 변경 전 값을 계속 유지하여 보여줘 분석을 원활하게 할 수 있도록 돕게 된다.

↓0xF8 코드에 소프트웨어 브레이크 포인트를 설치하면, 아래와 같이 실제 코드는 0xCC로 변경된다.

[그림] 소프트웨어 브레이크 포인트 설정

그리고 프로그램은 실행 중에 이 INT3 코드를 만나게 되면, 운영체제에게 이벤트를 알리고 프로그램을 멈추게 된다(2부 13장 Exception Dispatching를 확인해 보면 브레이크 포인트의 예외 처리에 대해 나온다). 이를 이용해 프로그램 디버깅시 브레이크 포인트를 지정해가며 분석을 하는데, 여기에서 이와 같은 조건을 이용하여 브레이크 코드인 INT 3을 만났을 때, 디버거가 예외를 잡아서 처리하였는지를 감시하는 코드이다. 그럼 안티 디버깅 코드를 확인해 보자.

int3.cpp
#include
<stdio.h>#include
<windows.h>bool Debug = TRUE;bool Check()

{

__try

{

__asm    INT 3;

}

// 예외가 발생하면, 디버깅중이지 않음을 암시한다. 만약 디버깅 중이라면 예외가 발생하기전에 디버거가 이벤트를 잡아 처리하게 된다.

__except(true)

{

Debug = FALSE;

}

}

void main()

{

while(true)

{

Sleep(1000);

// 예외처리 체크, 만약 Check를 거치지 않고 통과한 경우 if 조건으로 차단한다.

Check();

if(Debug == TRUE)

{

printf(“Debugging Detected\n”);

// 임의의 주소에서 종료

exit( 0x0ADE0005 );

}

else

printf(“Safe\n”);

}

return;

}

[예제] 브레이크 포인트에 대한 예외 동작을 통해 탐지하는 안티 디버깅 코드

그럼 위 프로그램을 비주얼 스튜디오 2010 C++ Express 버전에서 빈 프로젝트를 만들고 새 항목à
소스파일(Cpp)를 추가하여 컴파일 하고 Ollydbg로 연결하여 분석을 진행해 보자.

프로그램을 Ollydbg로 열면 아래와 같이 여러 함수 중 main 함수에 브레이크 포인트(F2키)를 설정하고 프로그램을 실행(F9키)하자.

[그림] main 함수에 브레이크 포인트를 설정하자

프로그램이 main 함수 처리를 위해 위 0x010D1136 주소에 접근하였을 때 프로그램은 멈추게 된다. 이후 Step into(F7키)를 이용하여, 우리가 확인하고자 하는 코드에 접근하자.

013E1450 i> 55 PUSH EBP

013E1451 8BEC MOV EBP,ESP

013E1453 81EC C0000000 SUB ESP,0C0

013E1459 53 PUSH EBX

013E145A 56 PUSH ESI

013E145B 57 PUSH EDI

013E145C 8DBD 40FFFFFF LEA EDI,DWORD PTR SS:[EBP-C0]

013E1462 B9 30000000 MOV ECX,30

013E1467 B8 CCCCCCCC MOV EAX,CCCCCCCC

013E146C F3:AB REP STOS DWORD PTR ES:[EDI]

013E146E B8 01000000 MOV EAX,1

013E1473 85C0 TEST EAX,EAX

013E1475 74 6B JE SHORT int3.013E14E2

013E1477 8BF4 MOV ESI,ESP

013E1479 68 E8030000 PUSH 3E8

013E147E FF15 98813E01 CALL DWORD PTR DS:[<&KERNEL32.Sleep>] ; kernel32.Sleep

013E1484 3BF4 CMP ESI,ESP

013E1486 E8 A6FCFFFF CALL int3.013E1131

013E148B E8 42FCFFFF CALL int3.013E10D2 ß Check() 함수를 호출하여 INT 3처리 유무를 확인한다.

013E1490 0FB605 00703E01 MOVZX EAX,BYTE PTR DS:[Debug]

013E1497 83F8 01 CMP EAX,1 ß 1과 같은지 비교한다. 1인 경우 예외상황을 감감지하지 못한 것이 된다.

013E149A 75 2D JNZ SHORT int3.013E14C9 ß 조건분기점

013E149C 8BF4 MOV ESI,ESP

013E149E 68 44573E01 PUSH int3.013E5744 ; ASCII “Debugging Detected\n”

013E14A3 FF15 A8823E01 CALL DWORD PTR DS:[<&MSVCR100D.printf>] ; MSVCR100.printf

013E14A9 83C4 04 ADD ESP,4

013E14AC 3BF4 CMP ESI,ESP

013E14AE E8 7EFCFFFF CALL int3.013E1131

013E14B3 8BF4 MOV ESI,ESP

013E14B5 68 0500DE0A PUSH 0ADE0005

013E14BA FF15 AC823E01 CALL DWORD PTR DS:[<&MSVCR100D.exit>] ; MSVCR100.exit

[실습] Ollydbg로 확인한 INT 3 코드 흐름

그럼 실제 INT 3 유무를 확인하는 명령은 어떻게 이루어 지는지 확인해 보자. CALL int3.013E10D2을 Step into(F7키)를 이용하여 들어가자.

013E1390 i> 55 PUSH EBP

013E1391 8BEC MOV EBP,ESP

013E1393 6A FE PUSH -2

013E1395 68 486B3E01 PUSH int3.013E6B48

013E139A 68 6E103E01 PUSH int3.013E106E

013E139F 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]

013E13A5 50 PUSH EAX

013E13A6 81C4 38FFFFFF ADD ESP,-0C8

013E13AC 53 PUSH EBX

013E13AD 56 PUSH ESI

013E13AE 57 PUSH EDI

013E13AF 8DBD 28FFFFFF LEA EDI,DWORD PTR SS:[EBP-D8]

013E13B5 B9 30000000 MOV ECX,30

013E13BA B8 CCCCCCCC MOV EAX,CCCCCCCC

013E13BF F3:AB REP STOS DWORD PTR ES:[EDI]

013E13C1 A1 04703E01 MOV EAX,DWORD PTR DS:[__security_cookie]

013E13C6 3145 F8 XOR DWORD PTR SS:[EBP-8],EAX

013E13C9 33C5 XOR EAX,EBP

013E13CB 50 PUSH EAX

013E13CC 8D45 F0 LEA EAX,DWORD PTR SS:[EBP-10]

013E13CF 64:A3 00000000 MOV DWORD PTR FS:[0],EAX

013E13D5 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP

013E13D8 C745 FC 00000000 MOV DWORD PTR SS:[EBP-4],0

013E13DF CC INT3 ß INT 3을 이용하여 예외 처리 여부를 확인한다.

013E13E0 C745 FC FEFFFFFF MOV DWORD PTR SS:[EBP-4],-2

//예외 상황이 실행되지 않으면 다음 명령인 MOV EAX,1를 이용하여 EAX에 1을 입력해 반환한다.

013E13E7 EB 17 JMP SHORT int3.013E1400

013E13E9 B8 01000000 MOV EAX,1

…중략

[실습] Ollydbg를 이용한 INT3 체크 어셈블리 분석

이 안티 디버깅은 Ollydbg의 경우 간단히 우회할 수 있다.

옵션 화면에서 INT3 break를 무시하거나, 사용자 예외 설정의 경우 사전에 무시하도록 설정해 놓으면, 이 함정은 손쉽게 우회된다(EAX에 1를 입력하지 않도록 하여도 우회된다).

[그림] 옵션 조작만으로 손쉽게 우회가 가능하다

Facebook Comments

Leave A Reply

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