Jaa


TIB와 IsDebuggerPresent

 Win32 API 중에 IsDebuggerPresent()라는 함수가 제공되는데 이는 현재 실행 중인 프로세스에 디버거가 붙은 있는지, 즉 현재 디버거에 의해서 디버깅 되고 있는지에 대한 결과를 TRUE/FALSE로 리턴해 주는 함수입니다. 디버거에서는 디버깅되는 프로세스의 메모리에 접근해서 ReadProcessMemory, WriteProcessMemory와 같은 함수를 호출해서 자유롭게 메모리 내용을 읽고 쓰는 것이 가능하기 때문에 개발된 프로그램에 대하여 디버깅 되는 것을 원치 않을 수도 있습니다. 흔히 이를 Anti-debugging 기술이라고 불리우는데 현재 디버깅이 되고 있느냐 아니냐를 판단하는 것부터 시작을 하게 됩니다. 이때 가장 기본적으로 사용될 수 있는 것이 바로 IsDebuggerPresent()라는 함수를 호출해서 리턴값을 검사해보는 것입니다.

BOOL WINAPI IsDebuggerPresent(void);

간단한 방법으로 작성된 프로그램이 초기화 되는 부분에서 이 함수를 호출하고 리턴값이 TRUE 이면 프로그램이 종료되도록 구현할 수 있을 것입니다.

디버깅을 하려는 입장에서는 이러한 루틴이 있을 경우 디버깅을 하지 못하게 되므로 이를 다시 회피할 방법을 찾게 되는데, 가장 간단한 방법으로 IsDebuggerPresent() 함수가 무조건 FALSE를 리턴하도록 변조하는 방법을 사용합니다. 모든 해킹은 동작 원리를 알고 이를 역으로 이용하고, 보안은 다시 해킹의 동작 원리를 이해하고 이를 방어하는 방법을 찾는 과정입니다.

자, 그럼. IsDebuggerPresent() 함수가 무조건 FALSE를 리턴하도록 변조된 상황이라고 가정하고 이를 다시 감지할 방법을 찾아보도록 하겠습니다. IsDebuggerPresent()라는 함수는 현재 프로세스가 디버깅되고 있는지를 어떻게 판단할까요? 우리가 직접 해당 로직을 구현해서 판단한다면 이러한 문제를 해결할 수 있을 것 같습니다.

실제 아래에 소개하려는 내용은 Debugging Applications for Microsoft .NET and Microsoft Windows(by John Robbins) 책의 160-161 page에서 소개되고 있는 방법입니다. IsDebuggerPresent() 함수를 직접 구현해보는 과정을 설명하고 있는데, 이렇게 구현을 하더라도 다시 쉽게 해당 루틴이 변조될 것으로 예상되지만 일단 시간을 지연시킬 수 있다는 측면에서 교육적인 예제라고 생각되어서 소개하려고 합니다.

먼저 핵심부터 말씀드리자면 'TIB 구조체 중에 디버깅 중인지에 대한 정보를 저장하고 있는 멤버 변수가 있다는 것이고, IsDebuggerPresent 함수가 이 값을 참조해서 결과를 리턴해주는 함수'라는 것입니다.

자. 그럼 우선 TIB(Thread Information Block)이라는 구조체에 대해서 알아보도록 하겠습니다. TIB(Thread Information Block)은 단어 의미 그대로 특정 쓰레드에 대한 정보를 가지고 있는 구조체입니다. Windows 운영체제에서 실행의 주체가 Thread인 만큼 내부적으로 관리되어야 하는 정보가 많기 때문에 매우 많은 멤버를 가지고 있는 구조체입니다. 이 구조체 변수에 접근하면 내부적으로 해당 쓰레드가 가지고 있는 많은 정보를 참조할 수 있는데, 아래 wikipedia 사이트에서 확인이 가능합니다.

Win32 Thread Information Block 
https://en.wikipedia.org/wiki/Win32_Thread_Information_Block

실제 이 구조체 정보는 undocumented 된 내용이었는데, Windows Internals 책에서도 소개를 하고 있고 이미 인터넷 상에서도 쉽게 관련 정보를 찾을 수 있는 것이 사실입니다.

위 문서의 내용에 의하면 FS 레지스터의 0x18h에 TIB가 위치한 주소의 위치 정보가 저장되어 있음을 알 수 있습니다. 이를 응용하면 프로그램 코드에서 다음과 같은 방법으로 간단히 TIB에 접근할 수 있게 됩니다.

 void *getTib()
{
    void *pTib;
    __asm {
        mov EAX, FS:[18h]
        mov [pTib], EAX
    }
    return pTib;
}

TIB 구조체에서 0x30h offset 위치에 해당하는 값이 현재 프로세스의 디버깅 여부에 대한 값을 가지고 있는데, 이를 다음과 같이 구현할 수 있습니다.

BOOL AntiHackIsDebuggerPresent (void)
{
    BOOL bRet = TRUE;
    __asm
    {
           // TIB(Thread Information Block)의 위치 얻기
           MOV     EAX, FS:[00000018H]

           // TIB에서 0x30 위치에 해당하는 것이 debugging과 관련된 구조체에 대한 포인터임
           MOV     EAX, DWORD PTR [EAX+030H]

           // 해당 구조체에서 두번째 WORD에 저장된 값이 현재 프로세스가 디버깅 중인지에 대한 값이다.
           MOVZX  EAX, BYTE PTR [EAX+002H]

           // 결과 리턴
           MOV     bRet, EAX
    }
    return bRet;
}

현재 Anti-debugging 기술이 많이 사용되고 있고, 또한 이렇게 많이 사용되는 Anti-debugging 기술을 회피할 수 있는 방법 또한 많이 연구되고 있습니다. 따라서 기술적인 측면에서 높은 수준의 보안책은 아니지만, 이렇게 IsDebuggerPresent()라는 알려진 Win32 함수 대신에 우리가 직접 작성한 함수를 호출해서 디버깅 여부를 감지하는 것만으로도 책의 저자인 John Robbins은 어느 정도의 효과를 볼 수 있다고 설명하고 있습니다.

undocumented 된 내용이므로 TIB의 세부 구조 또는 Anti-debugging 기술에 대해서 자세히 알고 싶으신 분은 관련 책이나 다른 문서를 참고하시기 바랍니다.