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

 

How to debugging detect?

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

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

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

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

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

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

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

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

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

CheckRemoteDebuggerPresent (API)

윈도우에서는 디버깅 유무를 확인할 수 있는 API를 제공하는데 이를 이용하여 확인하는 방법을 알아보고자 한다. CheckRemoteDebuggerPresent API는 디버깅을 진행중인 경우 참(TRUE)을 반환한다. MSDN을 참고해 보자.


[그림] 참/거짓을 통해 디버깅 유무를 파악한다

이외에도 디버깅 유무를 파악할 수 있는 API들이 많으니 MSDN을 적극 이용하자. 그럼 CheckRemoteDebuggerPresentd를 이용해 디버깅 유무를 파악하는 예제를 작성해 보자.

checkremotedebuger.cpp

#include
<stdio.h>#include
<windows.h>void main()

{

BOOL pbDebuggerPresent = TRUE;

while(true)

{

Sleep(1000);

// CheckRemoteDebuggerPresent 리턴결과를 pbDebuggerPresent에 저장한다.

CheckRemoteDebuggerPresent(GetCurrentProcess(), &pbDebuggerPresent);

// pbDebuggerPresent가 참인경우 디버깅으로 감지 된다. CheckRemoteDebuggerPresent를 우회한 경우에도 pbDebuggerPresent값을 기본적으로 참이므로 디버깅 유무에 감지 된다.

if(pbDebuggerPresent)

{

printf(“Debugging Detected\n”);

exit(0xF230BE05);

}

else

printf(“Safe\n”);

}

return;

}

[예제] CheckRemoteDebuggerPresent API를 이용하여 디버깅 유무 확인

앞서 여러 번 Ollydbg를 이용해 분석을 진행해 보았으므로 이 부분도 어렵지 않을 거라 판단된다.

핵심 코드만 살펴보자.

013D13E4 3BFC CMP EDI,ESP

013D13E6 E8 46FDFFFF CALL checkrem.013D1131

013D13EB 50 PUSH EAX

013D13EC FF15 9C813D01 CALL DWORD PTR DS:[<&KERNEL32.CheckRemoteDebugge>; kernel32.CheckRemoteDebuggerPresent
CheckRemoteDebuggerPresent를 이용하여 디버깅 유무를 확인한다.

013D13F2 3BF4 CMP ESI,ESP

013D13F4 E8 38FDFFFF CALL checkrem.013D1131

013D13F9 837D F8 00 CMP DWORD PTR SS:[EBP-8],0 값이 0인 비교한다.

013D13FD 74 2D JE SHORT checkrem.013D142C
값이 다른 경우 디버깅으로 판단하는 조건 분기점

013D13FF 8BF4 MOV ESI,ESP 디버깅중 일 때 실행되는 코드

013D1401 68 44573D01 PUSH checkrem.013D5744 ; ASCII “Debugging Detected\n”

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

013D140C 83C4 04 ADD ESP,4

013D140F 3BF4 CMP ESI,ESP

013D1411 E8 1BFDFFFF CALL checkrem.013D1131

013D1416 8BF4 MOV ESI,ESP

013D1418 68 05BE30F2 PUSH F230BE05

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

013D1423 3BF4 CMP ESI,ESP

013D1425 E8 07FDFFFF CALL checkrem.013D1131

013D142A EB 17 JMP SHORT checkrem.013D1443

013D142C 8BF4 MOV ESI,ESP 디버깅중이지 않을 때 실행되는 코드

013D142E 68 3C573D01 PUSH checkrem.013D573C ; ASCII “Safe\n”

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

[실습] 분석 내용

우회하는 방법으로는 조건 분기를 항상 우회하도록 코드 패치를 진행하는 방법과, Ollydbg 플러그인인 HideOD를 이용하는 방법도 있다. HideOD는 아래에서 다운로드 할 수 있다. http://www.pediy.com/tools/Debuggers/ollydbg/plugin.htm


[그림] 주요 디버깅 체크 API를 감출 수 있다

Facebook Comments

Leave A Reply

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