65.9K
CodeProject 正在变化。 阅读更多。
Home

Windows 反调试介绍

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (38投票s)

2008年9月18日

CPOL

3分钟阅读

viewsIcon

106196

Windows Anti-Debugging 技术简要介绍。

引言

近年来,用于逆向工程的工具如雨后春笋般涌现。对于那些对此领域感兴趣的人来说,有很多资源网站,而且这个领域非常值得投入时间。我发现学习 C++ 的同时接触逆向工程和汇编语言,极大地帮助我理解代码的工作原理,并同时提升了我的 C/C++ 和 ASM 编码能力。然而,逆向工程也有其阴暗的一面。Cracker 是那些利用逆向工程知识来逆向他人代码的人,通常是为了解码序列号的处理方式,或者移除试用版的保护。理所当然,开发者会希望保护他们的投资;这可以通过工具实现,例如 ThemidaExecryptorArmadillo,甚至是由 CodeProject resident Jim Charles 编写的一个名为 Eagle Protector 的保护系统。本文旨在向个人介绍一些反调试技术,并不打算涵盖所有内容,也不涉及商业保护器使用的一些更复杂的例程。

背景

阅读本文的读者应扎实掌握 ASM、计算机内存处理、Win32 调试 API,并至少了解一些 Windows 内部知识。由于操作系统的根本差异,这些代码在任何 *nix 平台上的运行可能性不大。在逆向工程领域拥有任何其他知识也将是加分项。学习和实现反调试的一个好处是,你也能提高你的逆向能力,这对于任何对该领域感兴趣的人来说都是一个巨大的优势。除了前面提到的主题,感兴趣的读者还应该熟悉用于二进制应用程序逆向的工具,如 OllyDBG、WinDBG、SoftICE、IDA Pro 等。以下是一些在阅读正文之前读者应熟悉的重点信息链接:

IsDebuggerPresent

这可能是最简单的调试器保护方法。此函数是 Win32 调试 API 的一部分,其参考页面可在此处找到:MSDN

if(IsDebuggerPresent())
{
    MessageBox(NULL, TEXT("Please close your debugging application" + 
               " and restart the program"), 
               TEXT("Debugger Found!"), 0);
    ExitProcess(0);
}
// Normal Code Here....

通过 PEB 使用 IsDebuggerPresent

IsDebuggerPresent 函数实际上是围绕这段代码的一个包装器。它直接访问进程的 PEB,并读取一个字节值,该值表示进程是否正在被调试。

char IsDbgPresent = 0;
__asm {
     mov eax, fs:[30h]
     mov al, [eax + 2h]
     mov IsDbgPresent, al
}

if(IsDbgPresent)
{
    MessageBox(NULL, TEXT("Please close your debugging " + 
               "application and restart the program"), 
               TEXT("Debugger Found!"), 0);
    ExitProcess(0);
}
// Normal Execution

CheckRemoteDebuggerPresent

这是另一个 Win32 调试 API 函数;它可以用于检查远程进程是否正在被调试。然而,我们也可以将其作为检查自身进程是否被调试的另一种方法。对于想知道此函数如何不同的读者来说,CheckRemoteDebuggerPresent 调用 NTDLL 的导出函数 NtQueryInformationProcess,并将 SYSTEM_INFORMATION_CLASS 设置为 7(ProcessDebugPort)。该函数的 MSDN 参考可在以下网址找到:MSDN

BOOL IsDbgPresent = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &IsDbgPresent);
if(IsDbgPresent)
{
        MessageBox(NULL, TEXT("Please close your debugging" + 
                   " application and restart the program"), 
                   TEXT("Debugger Found!"), 0);
        ExitProcess(0);
}
// Normal Execution

NtQueryInformationProcess

一个人也可以直接调用 NtQueryInformationProcess 而不是调用 CheckRemoteDebuggerPresent。MSDN 不鼓励使用任何 NtxXx 函数,因为其行为可能会发生改变,因此在决定使用该函数之前可能需要仔细考虑。该函数的 MSDN 参考可在以下网址找到:MSDN

// Function Pointer Typedef for NtQueryInformationProcess
typedef unsigned long (__stdcall *pfnNtQueryInformationProcess)(IN  HANDLE, 
        IN  unsigned int, OUT PVOID, IN ULONG, OUT PULONG);

 
// ProcessDebugPort
const int ProcessDbgPort = 7;
 
// We have to import the function
pfnNtQueryInformationProcess NtQueryInfoProcess = NULL;
 
// Other Vars
unsigned long Ret;
unsigned long IsRemotePresent = 0;
 
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
if(hNtDll == NULL)
{
    // Handle however.. chances of this failing
    // is essentially 0 however since
    // ntdll.dll is a vital system resource
}
 
NtQueryInfoProcess = (pfnNtQueryInformationProcess)
   GetProcAddress(hNtDll, "NtQueryInformationProcess");
if(NtQueryInfoProcess == NULL)
{
    // Handle however it fits your needs but as before,
    // if this is missing there are some SERIOUS issues with the OS
}
 
// Time to finally make the call
Ret = NtQueryInfoProcess(GetCurrentProcess(), ProcessDbgPort, 
      &IsRemotePresent, sizeof(unsigned long), NULL);
if(Ret == 0x00000000 && IsRemotePresent != 0)
{
    // Debugger is present
    MessageBox(NULL, TEXT("Please close your debugging " + 
               "application and restart the program"), 
               TEXT("Debugger Found!"), 0);
    ExitProcess(0);
}

NtGlobalFlag

NtGlobalFlag 是进程 PEB 中的一个 DWORD 值。此值包含操作系统设置的许多标志,这些标志会影响进程的运行方式。当进程被调试时,会为该进程设置 FLG_HEAP_ENABLE_TAIL_CHECK (0x10)、FLG_HEAP_ENABLE_FREE_CHECK(0x20) 和 FLG_HEAP_VALIDATE_PARAMETERS(0x40) 标志,我们可以利用这一点来识别我们的进程是否正在被调试。

unsigned long NtGlobalFlags = 0;

__asm {

    mov eax, fs:[30h]
    mov eax, [eax + 68h]
    mov NtGlobalFlags, eax
}


if(NtGlobalFlags & 0x70)
// 0x70 =  FLG_HEAP_ENABLE_TAIL_CHECK |
//         FLG_HEAP_ENABLE_FREE_CHECK | 
//         FLG_HEAP_VALIDATE_PARAMETERS
{
    // Debugger is present
    MessageBox(NULL, TEXT("Please close your debugging " + 
               "application and restart the program"), 
               TEXT("Debugger Found!"), 0);
    ExitProcess(0);
}
// Normal execution

关注点

Windows 中还有更多反调试技术,一些方法,如使用 CloseHandleOutputDebugStringA,在 Windows Vista 中已被修补,因此未包含在本文中。我计划撰写其他关于其他反调试技术的文章,许多可用的方法仅凭内联汇编很难实现,需要使用其他 ASM 风格/编译器来更有效地实现。

参考文献

历史

  • 2008 年 9 月 18 日 - 原始修订。
© . All rights reserved.