逆向1.检测程序是否被调试

检测程序是否被调试

BeingDebug

// 通过检查 PEB.BeingDebuged 字段判断是否被调试
// 可以在目标程序运行之前修改对应的字段为 0 进行反反调试
bool CheckBeingDebugged()
{
    __asm 
    {
        ; 获取到 PEB 的内容
        mov eax, fs:[0x30]
        ; 获取 PEB 内偏移为 2 大小为 1 字节的字段
        movzx eax, byte ptr[eax + 2]
    }
}
​
​
int main()
{
    if (CheckBeingDebugged())
        printf("当前处于[被]调试状态
");
    else
        printf("当前处于[非]调试状态
");
​
    system("pause");
    return 0;
}

PEB.NtGlobalFlag

#include <iostream>
#include <windows.h>// 通过 PEB.NtGlobalFlag 判断是否被调试,当处于被调试状态时
// PEB.NtGlobalFlag 保存的是 0x70,可以通过修改标志反反调试
bool CheckNtGlobalFlag()
{
    __asm
    {
        ; 获取到 PEB,保存在 FS : [0x30]
        mov eax, fs : [0x30]
        ; 获取到 NtGlobalFlag 字段的内容
        mov eax, [eax + 0x68];
    }
}
​
int main()
{
    if (CheckNtGlobalFlag())
        printf("当前处于[被]调试状态
");
    else
        printf("当前处于[非]调试状态
");
​
    system("pause");
    return 0;
}

通过判断 PEB.ProcessHeap 中具体的标志位来确定是否被调试

#include <iostream>
#include <windows.h>// 通过判断 PEB.ProcessHeap 中具体的标志位来确定是否被调试
// 对应不同的系统,标志位所在的偏移可能也不同,通过 dt _HEAP 
// 可以查看到当前系统中具体所在的位置
// Flags 与 ForceFlags 在正常情况下应为 2 与 0
// 如果是被附加状态,那么无法判断当前是否被调试
// 通过修改具体的字段进行反反调试,注意不同版本
bool CheckProcessHeap()
{
    UINT Flags = 0, ForceFlags = 0;
​
    __asm
    {
        ; 1. 获取到 PEB
        mov eax, fs:[0x30]
        ; 2. 获取到 ProcessHeap
        mov eax, [eax + 0x18]
        ; 3. 获取到 Flags
        mov ecx, [eax + 0x40]
        mov Flags, ecx
        ; 4. 获取到 ForceFlags
        mov eax, [eax + 0x44]
        mov ForceFlags, eax
    }
​
    return (Flags == 2 && ForceFlags == 0) ? false : true;
}
​
int main()
{
    if (CheckProcessHeap())
        printf("当前处于[被]调试状态
");
    else
        printf("当前处于[非]调试状态
");
​
    system("pause");
    return 0;
}

原理就是查询 EPROCESS 结构体中相关的ProcessDebugPort字段

#include <iostream>
#include <windows.h>
​
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")
​
​
// 原理就是查询 EPROCESS 结构体中相关的字段,
// 因为不能在 R3 修改 R0 的数据,所以只能 HOOK
bool CheckProcessDebugPort() 
{
    int nDebugPort = 0;
    NtQueryInformationProcess(
        GetCurrentProcess(),    // 目标进程句柄
        ProcessDebugPort,       // 查询信息类型(7)
        &nDebugPort,            // 输出查询信息
        sizeof(nDebugPort),     // 查询类型大小
        NULL);                  // 实际返回数据大小
// 如果返回的是 -1 ,那么就被调试了
    return nDebugPort == 0xFFFFFFFF ? true : false;
}
​
​
int main()
{
    if (CheckProcessDebugPort())
        printf("当前处于[被]调试状态
");
    else
        printf("当前处于[非]调试状态
");
​
    system("pause");
    return 0;
}

原理就是查询 EPROCESS 结构体中相关的0x1E字段,

#include <iostream>
#include <windows.h>
​
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")// 原理就是查询 EPROCESS 结构体中相关的字段,
// 因为不能在 R3 修改 R0 的数据,所以只能 HOOK
bool CheckProcessDebugObjectHandle()
{
    HANDLE hProcessDebugObjectHandle = 0;
​
    NtQueryInformationProcess(
        GetCurrentProcess(),                // 目标进程句柄
        (PROCESSINFOCLASS)0x1E,             // 查询信息类型(0x1E)
        &hProcessDebugObjectHandle,         // 输出查询信息
        sizeof(hProcessDebugObjectHandle),  // 查询类型大小
        NULL);                              // 实际返回大小
// 如果它的值是非空的,就说明被调试了
    return hProcessDebugObjectHandle ? true : false;
​
}
​
​
int main()
{
    if (CheckProcessDebugObjectHandle())
        printf("当前处于[被]调试状态
");
    else
        printf("当前处于[非]调试状态
");
​
    system("pause");
    return 0;
}

原理就是查询 EPROCESS 结构体中相关的0x1F字段,

#include <iostream>
#include <windows.h>
​
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")// 原理就是查询 EPROCESS 结构体中相关的字段,
// 因为不能在 R3 修改 R0 的数据,所以只能 HOOK
// 通过 HOOK 函数 NtQueryInformationProcess 可以
// 直接的解决所有的关于这个 API 的反调试,但是,由于
// 这个函数的功能非常的多,所以需要经过特定的筛选,筛
// 选的值就是需要查询的类型 0x07, 0x1E, 0x1F
bool CheckProcessDebugFlag()
{
    BOOL bProcessDebugFlag = 0;
    NtQueryInformationProcess(
        GetCurrentProcess(),        // 目标进程句柄
        (PROCESSINFOCLASS)0x1F,     // 查询信息类型
        &bProcessDebugFlag,         // 输出查询信息
        sizeof(bProcessDebugFlag),  // 查询类型大小
        NULL);                      // 实际返回大小
// 值为 0 的话处于一个被调试的状态
    return bProcessDebugFlag ? false : true;
}
​
​
int main()
{
    if (CheckProcessDebugFlag())
        printf("当前处于[被]调试状态
");
    else
        printf("当前处于[非]调试状态
");
​
    system("pause");
    return 0;
}

如果一个进程被正常打开,那么它的父进程应该是 Explorer.exe, 也就是资源管理器,如果判断不是,就可能被调试,提供参考

#include <iostream>
#include <windows.h>
​
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")// 如果一个进程被正常打开,那么它的父进程应该是 Explorer.exe,
// 也就是资源管理器,如果判断不是,就可能被调试,提供参考
bool CheckParentProcess()
{
​
    struct PROCESS_BASIC_INFORMATION {
        ULONG ExitStatus;           // 进程返回码
        PPEB  PebBaseAddress;       // PEB 地址
        ULONG AffinityMask;         // CPU 亲和性掩码
        LONG  BasePriority;         // 基本优先级
        ULONG UniqueProcessId;      // 本进程PID
        ULONG InheritedFromUniqueProcessId; // 父进程PID
    }stcProcInfo;
​
    // 查询到目标进程的对应信息,主要是 父进程 ID
    NtQueryInformationProcess(
        GetCurrentProcess(), 
        ProcessBasicInformation, 
        &stcProcInfo,
        sizeof(stcProcInfo), 
        NULL);
​
    // 查询资源管理器对应的 PID
    DWORD ExplorerPID = 0;
    DWORD CurrentPID = stcProcInfo.InheritedFromUniqueProcessId;
    GetWindowThreadProcessId(FindWindow(L"Progman", NULL), &ExplorerPID);
    
    // 比对两个 PID 的值,相同就OK,不同就可能被调试
    return ExplorerPID == CurrentPID ? false : true;
}
​
​
int main()
{
    if (CheckParentProcess())
        printf("当前处于[被]调试状态
");
    else
        printf("当前处于[非]调试状态
");
​
    system("pause");
    return 0;
}

查询当前的系统是否处于被调试状态,同样可以 HOOK

#include <iostream>
#include <windows.h>
​
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")// 查询当前的系统是否处于被调试状态,同样可以 HOOK
bool CheckSystemKernelDebuggerInformation() 
{
    struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION {
        BOOLEAN KernelDebuggerEnabled;
        BOOLEAN KernelDebuggerNotPresent;
    }DebuggerInfo = { 0 };
​
    NtQuerySystemInformation(
        (SYSTEM_INFORMATION_CLASS)0x23,         // 查询信息类型
        &DebuggerInfo,                          // 输出查询信息
        sizeof(DebuggerInfo),                   // 查询类型大小
        NULL);                                  // 实际返回大小
    return DebuggerInfo.KernelDebuggerEnabled;
}
​
​
int main()
{
    if (CheckSystemKernelDebuggerInformation())
        printf("当前处于[被]调试状态
");
    else
        printf("当前处于[非]调试状态
");
​
    system("pause");
    return 0;
}

隐藏线程让调试器找不到

#include <iostream>
#include <windows.h>
​
typedef enum THREAD_INFO_CLASS {
    ThreadHideFromDebugger = 17
};
​
// 这个函数被用于设置线程的信息
typedef NTSTATUS(NTAPI* ZW_SET_INFORMATION_THREAD)(
    IN  HANDLE          ThreadHandle,
    IN  THREAD_INFO_CLASS   ThreadInformaitonClass,
    IN  PVOID           ThreadInformation,
    IN  ULONG           ThreadInformationLength);
​
// 可以通过 HOOk 的方式进行反反调试
void ZSIT_DetachDebug()
{
    ZW_SET_INFORMATION_THREAD Func;
​
    // 获取到函数,因为这是一个未公开函数
    Func = (ZW_SET_INFORMATION_THREAD)GetProcAddress(
        LoadLibrary(L"ntdll.dll"), "ZwSetInformationThread");
​
    // 需要设置的线程,需要设置的类型,不考虑,不考虑
    Func(GetCurrentThread(), ThreadHideFromDebugger, NULL, NULL);
}
​
​
int main()
{
    ZSIT_DetachDebug();
    printf("runnning...
");
    system("pause");
    return 0;
}

找窗口名

#include <iostream>
#include <windows.h>// 原理是通过查找窗口类或窗口名称对应的窗口是否存在来
// 进行调试器或其它分析工具的检查。[缺点是窗口的的名字
// 通常不是固定的,检查起来非常的不方便]
// 还可以通过遍历进程的方式来检查调试器或其它工具是否
// 存在。[缺点是可以通过修改 exe 的名字修改进程名]
int main()
{
    if (FindWindow(NULL, L"OllyDbg"))
        printf("存在调试器
");
    else
        printf("没检测到调试器
");
​
    return 0;
}

 

原文地址:https://www.cnblogs.com/ltyandy/p/11166195.html