0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

简单了解反调试技术

蛇矛实验室 来源:蛇矛实验室 2023-11-13 11:10 次阅读

本期作者/牛杰

前言

反调试技术,是一种防止逆向的方案。逆向人员如果遇到复杂的代码混淆,有时会使用调试器动态分析代码逻辑简化分析流程。例如恶意软件通常会被安全研究人员、反病毒厂商和其他安全专业人员分析和调试,以了解其行为和功能,并开发相应的安全措施来保护系统,这时,恶意软件开发人员就会使用反调试技术阻碍逆向人员的分析,以达到增加自己恶意代码的存活时间。此外,安全人员也需要了解反调试技术,当遇到反调试代码时,可以使用相对应的反反调试。

反调试

1. IsDebuggerPresent

IsDebuggerPresent 用于检测当前进程是否正在被调试。该函数属于 Windows 调试辅助功能,可以帮助开发人员在程序运行过程中进行调试。

IsDebuggerPresent 函数的原型如下:

BOOL IsDebuggerPresent(void);

该函数返回一个布尔值,如果当前进程正在被调试,则返回 TRUE;否则返回 FALSE。

检查进程环境块(PEB)中是否设置了正在调试的标志。

这实际上与IsDebuggerPresent()内部执行的代码相同。

x86的PEB指针从DWORD FS:[0x30]中获取,x64在QWORD GS:[0x60]中获取。

IsDebuggerPresent 函数只能检测当前进程是否正在被调试,而不能检测其他进程的调试状态。此外,安全研究人员和反病毒厂商可以使用各种技术和工具来绕过 IsDebuggerPresent 函数的检测,因此它并不是一个绝对可靠的方法来判断系统是否正在进行调试。

2. CheckRemoteDebuggerPresent

CheckRemoteDebuggerPresent 用于检测当前进程是否被远程调试器附加。该函数可以检测当前进程是否正在被远程调试器(如远程调试器工具或调试代理程序)监视和调试,恶意软件可以使用该函数来判断自身是否处于被远程调试的环境中,并根据检测结果采取相应的措施,如崩溃、隐藏关键代码等,以防止被分析和调试。

CheckRemoteDebuggerPresent 函数的原型如下:

BOOL CheckRemoteDebuggerPresent(
HANDLE hProcess,
PBOOL pbDebuggerPresent
);

该函数接受两个参数

hProcess:要检查的进程的句柄。通常使用 GetCurrentProcess() 函数获取当前进程的句柄。

pbDebuggerPresent:一个指向 BOOL 类型的变量的指针,用于接收检测结果。如果检测到远程调试器附加,则该变量被设置为 TRUE;否则设置为 FALSE。

3. 断点检测

断点是一种调试技术,用于在特定的内存地址上设置断点,以便在程序执行到该地址时触发中断,因此可以通过判断断点的存在与否来确认程序是否被调试,断点分为硬件断点与软件断点,检测的方式不同。

硬件断点检测

x86架构,DR0到DR3寄存器用于设置硬件断点的地址,DR4和DR5寄存器在x86架构中没有特定的用途,DR6寄存器是一个状态寄存器,用于指示硬件断点的触发情况。因此我们需要判断DR0-DR3的值,如果有值不为0,则处于调试状态,x64架构引入了新的调试寄存器,称为DR7寄存器,用于控制硬件断点和其他调试功能,但是判断是否被调试的方式与x86架构相同。获取值的代码如下:

BOOL HardwareBreakpoints()
{
BOOL bResult = FALSE;
PCONTEXT ctx = PCONTEXT(VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT, PAGE_READWRITE));

if(ctx) {

SecureZeroMemory(ctx, sizeof(CONTEXT));
ctx->ContextFlags = CONTEXT_DEBUG_REGISTERS;
if(GetThreadContext(GetCurrentThread(), ctx)) {
if(ctx->Dr0 != 0|| ctx->Dr1 != 0|| ctx->Dr2 != 0|| ctx->Dr3 != 0)
bResult = TRUE;
}

VirtualFree(ctx, 0, MEM_RELEASE);
}

returnbResult;
}

软件断点检测

软件断点又称int3,在IA-32指令集中用操作码CC (0xCC)表示,因此有时软件点的检测也称为"0xCC"检测,调试器在对应设置断点的位置上修改该地址的字节为0xCC。若是关键位置检测到该指令,可以判断进程处于调试状态。

4. PEB

在Windows操作系统中,PEB(Process Environment Block)是一个数据结构,它存储了进程的环境信息和状态。每个运行的进程都有一个独立的PEB。

1. BeingDebugged

与IsDebuggerPresent()内部执行的代码相同,获取方式如下:

//x86
PPEB pPeb = (PPEB)__readfsdword(0x30);
//x64
PPEB pPeb = (PPEB)__readgsqword(0x60);

2. NtGlobalFlag

NtGlobalFlag 是PEB的一个字段,通常,当进程未被调试时,NtGlobalFlag字段包含值0x0。调试进程时,该字段通常包含值0x70。

该字段在x86越x64架构中的位置不同。

x86在PEB偏移0x68的位置,x64在PEB便0xBC的位置。

Windows内核全局标记,在Windows调试方案中经常用到。这个标记定义了一组系统的调试参数,包括启用或禁用调试技术的开关、造成崩溃的错误代码和处理方式等等。通过改变这个标记,可以在运行时设置和禁用不同的调试技术和错误处理方式,比如调试器只能访问当前进程、只允许用户模式调试、启用特定的错误处理方式等等。但由于NtGlobalFlag标记是内核全局标记,其改变会影响整个系统的行为,需要谨慎处理。

5.ProcessHeap

通过PEB偏移0x18可以找到ProcessHeap,结构体如下:

struct _PEB32
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages:1; //0x3
UCHAR IsProtectedProcess:1; //0x3
UCHAR IsImageDynamicallyRelocated:1; //0x3
UCHAR SkipPatchingUser32Forwarders:1; //0x3
UCHAR IsPackagedProcess:1; //0x3
UCHAR IsAppContainer:1; //0x3
UCHAR IsProtectedProcessLight:1; //0x3
UCHAR IsLongPathAwareProcess:1; //0x3
};
};
ULONG Mutant; //0x4
ULONG ImageBaseAddress; //0x8
ULONG Ldr; //0xc
ULONG ProcessParameters; //0x10
ULONG SubSystemData; //0x14
ULONG ProcessHeap; //0x18
....
}

在ProcessHeap加上偏移可以找到HeapFlags与ForceFlags,偏移的值根据系统版本和位数会有变化,如下表:

HeapFlags

2baedfc0-7fb1-11ee-939d-92fbcf53809c.png

ForceFlags

2bd16c20-7fb1-11ee-939d-92fbcf53809c.png

如果HeapFlags的值大于2,或ForceFlags的值大于0时,说明被调试。

6. INT 2D

int 2d反调试原理很简单,正常运行时int 2d触发异常,进入程序的异常处理函数。而当调试运行时,OD会处理该异常,将eip+1继续运行,因此可以在异常处理函数中添加一些操作,如果没有执行这些代码,说明被调试。这种只能检测原版Ollydbg,x64dbg和一些带有反检测插件的调试器无效。

7. 进程列表

一般情况下,主进程在主线程中启动核心代码

QueryInformationJobObject这个api可以获取当前程序所有的进程列表

不论是主进程还是主线程,他们的ImageFileName应该是都是源程序的文件名filename.exe

8. NtQueryInformationProcess

NtQueryInformationProcess原型如下:

NTSTATUS NTAPI NtQueryInformationProcess(
HANDLE ProcessHandle,// 进程句柄
PROCESSINFOCLASS ProcessInformationClass,// 检索的进程信息类型
PVOID ProcessInformation,// 接收进程信息的缓冲区指针
ULONG ProcessInformationLength,// 缓冲区指针大小
PULONG ReturnLength // 实际接收的进程信息大小
);

PROCESSINFOCLASS原型如下:

typedefenum_PROCESSINFOCLASS
{
ProcessBasicInformation, 
ProcessQuotaLimits, 
ProcessIoCounters, 
ProcessVmCounters, 
ProcessTimes, 
ProcessBasePriority, 
ProcessRaisePriority,
ProcessDebugPort, //0x7 
ProcessExceptionPort, 
ProcessAccessToken, 
ProcessLdtInformation, 
ProcessLdtSize, 
ProcessDefaultHardErrorMode, 
ProcessIoPortHandlers, 
ProcessPooledUsageAndLimits, 
ProcessWorkingSetWatch,
ProcessUserModeIOPL,
ProcessEnableAlignmentFaultFixup, 
ProcessPriorityClass, 
ProcessWx86Information,
ProcessHandleCount, 
ProcessAffinityMask, 
ProcessPriorityBoost, 
ProcessDeviceMap, 
ProcessSessionInformation, 
ProcessForegroundInformation,
ProcessWow64Information, 
ProcessImageFileName, 
ProcessLUIDDeviceMapsEnabled, 
ProcessBreakOnTermination, 
ProcessDebugObjectHandle, // 0x1E
ProcessDebugFlags, // 0x1F
ProcessHandleTracing, 
ProcessIoPriority, 
ProcessExecuteFlags, 
ProcessResourceManagement, 
ProcessCookie, 
ProcessImageInformation, 
ProcessCycleTime, 
ProcessPagePriority, 
ProcessInstrumentationCallback, 
ProcessThreadStackAllocation, 
ProcessWorkingSetWatchEx,
ProcessImageFileNameWin32, 
ProcessImageFileMapping, 
ProcessAffinityUpdateMode, 
ProcessMemoryAllocationMode, 
ProcessGroupInformation,
ProcessTokenVirtualizationEnabled, 
ProcessConsoleHostProcess, 
ProcessWindowInformation, 
ProcessHandleInformation,
ProcessMitigationPolicy,
ProcessDynamicFunctionTableInformation,
ProcessHandleCheckingMode,
ProcessKeepAliveCount,
ProcessRevokeFileHandles,
ProcessWorkingSetControl,
ProcessHandleTable, 
ProcessCheckStackExtentsMode,
ProcessCommandLineInformation,
ProcessProtectionInformation,
ProcessMemoryExhaustion,
ProcessFaultInformation, 
ProcessTelemetryIdInformation, 
ProcessCommitReleaseInformation, 
ProcessDefaultCpuSetsInformation,
ProcessAllowedCpuSetsInformation,
ProcessSubsystemProcess,
ProcessJobMemoryInformation, 
ProcessInPrivate, 
ProcessRaiseUMExceptionOnInvalidHandleClose, 
ProcessIumChallengeResponse,
ProcessChildProcessInformation, 
ProcessHighGraphicsPriorityInformation,
ProcessSubsystemInformation, 
ProcessEnergyValues, 
ProcessActivityThrottleState, 
ProcessActivityThrottlePolicy,
ProcessWin32kSyscallFilterInformation,
ProcessDisableSystemAllowedCpuSets, 
ProcessWakeInformation,
ProcessEnergyTrackingState,
ProcessManageWritesToExecutableMemory,REDSTONE3
ProcessCaptureTrustletLiveDump,
ProcessTelemetryCoverage,
ProcessEnclaveInformation,
ProcessEnableReadWriteVmLogging, 
ProcessUptimeInformation,
ProcessImageSection,
ProcessDebugAuthInformation, 
ProcessSystemResourceManagement,
ProcessSequenceNumber,
ProcessLoaderDetour,
ProcessSecurityDomainInformation, 
ProcessCombineSecurityDomainsInformation, 
ProcessEnableLogging, 
ProcessLeapSecondInformation,
ProcessFiberShadowStackAllocation,
ProcessFreeFiberShadowStackAllocation,
MaxProcessInfoClass
} PROCESSINFOCLASS;

1. ProcessDbgPort

该方式是CheckRemoteDebuggerPresent的另一种调用方式。

通过NTDLL导出NtQueryInformationProcess函数,PROCESSINFOCLASS设置为7,该值是进程调试端口(ProcessDebugPort),该值不为0说明被调试。

2. ProcessDebugObjectHandle

通过NTDLL导出NtQueryInformationProcess函数,PROCESSINFOCLASS设置为0x1E,该值是进程的调试对象句柄(ProcessDebugObjectHandle),当该值存在且函数返回值不为NULL,说明进程处于调试状态,当返回值为NULL,或该值不存在,说明处于非调试状态。

3. ProcessDebugFlags

通过NTDLL导出NtQueryInformationProcess函数,PROCESSINFOCLASS设置为0x1f,该值获取了EPROCESS中的成员NoDebugInherit,该值为0说明被调试。

9. WUDFPlatform.dll模块

WUDFPlatform.dll模块中,有三个导出函数 WudfIsAnyDebuggerPresent,WudfIsKernelDebuggerPresent,WudfIsUserDebuggerPresent,分别为任何调试器、0环调试器和3环调试器,该模块只有x64。

通过调用这三个函数,如果返回值不为0,则正在被调试。

代码如下:

HMODULE h_wudf = LoadLibrary(L"WUDFPlatform.dll");
if(h_wudf == NULL) {
cout << "fail" << endl;

    }

    // WudfIsAnyDebuggerPresent
    pWudfIsAnyDebuggerPresent WudfIsAnyDebuggerPresent = (pWudfIsAnyDebuggerPresent)GetProcAddress(h_wudf, "WudfIsAnyDebuggerPresent");
    if (WudfIsAnyDebuggerPresent == NULL) {
        cout << "未发现调试器" << endl;

    }
    if (WudfIsAnyDebuggerPresent() != 0) {
        cout << "发现调试器" << endl;
    }

    // WudfIsKernelDebuggerPresent
    pWudfIsKernelDebuggerPresent WudfIsKernelDebuggerPresent = (pWudfIsKernelDebuggerPresent)GetProcAddress(h_wudf, "WudfIsKernelDebuggerPresent");
    if (WudfIsKernelDebuggerPresent == NULL) {
        cout << "未发现3环调试器"" << endl;
    }
    if (WudfIsKernelDebuggerPresent() != 0) {
        cout << "发现0环调试器" << endl;
    }

    // pWudfIsUserDebuggerPresent
    pWudfIsUserDebuggerPresent WudfIsUserDebuggerPresent = (pWudfIsUserDebuggerPresent)GetProcAddress(h_wudf, "WudfIsUserDebuggerPresent");
    if (WudfIsUserDebuggerPresent == NULL) {
        cout << "未发现3环调试器" << endl;
    }
    if (WudfIsUserDebuggerPresent() != 0) {
        cout << "发现3环调试器" << endl;

测试结果如下

2bee6bd6-7fb1-11ee-939d-92fbcf53809c.png

总结

本篇介绍了部分反调试的方法,在自己的代码中使用反调试技术,可以增加逆向人员的分析难度,或是通过了解这些技术的原理,在分析恶意代码时进行反反调试,在后续的文章中,将会介绍更多的反调试方法。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • WINDOWS
    +关注

    关注

    3

    文章

    3541

    浏览量

    88624
  • 调试
    +关注

    关注

    7

    文章

    578

    浏览量

    33923
  • 调试技术
    +关注

    关注

    0

    文章

    7

    浏览量

    6627
  • 代码
    +关注

    关注

    30

    文章

    4779

    浏览量

    68524
  • 调试器
    +关注

    关注

    1

    文章

    303

    浏览量

    23716

原文标题:反调试技术-上

文章出处:【微信号:蛇矛实验室,微信公众号:蛇矛实验室】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    介绍6种常见的反调试方法

    开发相应的安全措施来保护系统,这时,恶意软件开发人员就会使用反调试技术阻碍逆向人员的分析,以达到增加自己恶意代码的存活时间。此外,安全人员也需要了解反调试
    的头像 发表于 01-15 09:53 3796次阅读
    介绍6种常见的<b class='flag-5'>反调试</b>方法

    动态反调试技术总结

    ;windows.h"#include "tchar.h"void DynAD_RDTSC(){//Timing Check 技术通过计算运行时间的差异来反调试,反模拟
    发表于 07-10 06:21

    shell编程的简单命令语句了解

    shell(脚本:批处理)适合职业:Linux系统运维工程师1.命令解析器(将用户输入的命令解析,调用相应的命令的可执行文件)2.脚本语言我们需要了解的是shell编程的简单命令语句一个简单的shell编程实现的程序:记录100
    发表于 11-04 08:00

    学PLC技术简单

    学PLC技术简单 作者:蔡杏山 主编 出版时间:2013 《学技能超简单:学PLC技术简单》是一本介绍PLC
    发表于 11-05 10:52 0次下载

    labview怎么调试?基于labview的USB调试教程案例分析

    labview怎么调试是我们在使用NI LabVIEW软件时候经常会碰到的问题,当软件不能按照预期的情况运行时,我们可能会需要有一个自己的调试技巧和技术工具箱。这里给大家分享一个为了查看收发数据而编写的基于labview的USB
    发表于 01-16 09:40 1.4w次阅读

    何为调试调试为何?

    一、何为调试调试为何 这并不是废话,作为一个菜鸟而言,面对一块熟悉又陌生的板子如何下手调试也许并不是一件So easy的事。什么是调试呢?简单
    发表于 01-18 14:48 3186次阅读

    STM32调试DEBUG时需要了解那些知识相关资料概述

    学习STM32开发,肯定少不了DEBUG调试这一步骤。那么,本文带你了解一下这个调试相关的知识。本文以STM
    的头像 发表于 11-11 11:39 7057次阅读
    STM32<b class='flag-5'>调试</b>DEBUG时需要<b class='flag-5'>了解</b>那些知识相关资料概述

    如何使用Xilinx SDK创建Linux应用程序,并进行开发和调试

    了解如何使用Xilinx SDK创建Linux应用程序。 我们还将重点介绍和演示支持Linux应用程序开发和调试的不同方面的SDK功能。 整个过程快速而简单
    的头像 发表于 11-20 07:03 1.1w次阅读

    下载调试接口SWD和JTAG的区别

    作为嵌入式工程师,下载调试器都应该知道,但你真正了解其 SWD 和 JTAG 接口的含义和区别吗? 1、什么是下载调试简单来说,下载调试
    的头像 发表于 11-28 11:44 4590次阅读

    RISC-V的调试标准及跟踪技术

    本文主要详细介绍了RISC-V的调试标准以及RISC-V的跟踪技术,感兴趣的小伙伴跟小编一起了解一下吧。
    的头像 发表于 06-23 17:19 2928次阅读
    RISC-V的<b class='flag-5'>调试</b>标准及跟踪<b class='flag-5'>技术</b>

    220kv电力变压器正反调和粗细调冲击电压分析

    220kv电力变压器正反调和粗细调冲击电压分析(通信电源技术期刊是什么级别)-220kv电力变压器正反调和粗细调冲击电压分析             
    发表于 09-24 09:20 1次下载
    220kv电力变压器正<b class='flag-5'>反调</b>和粗细调冲击电压分析

    基于OpenOCD和GDB的简单且廉价的步进调试

    电子发烧友网站提供《基于OpenOCD和GDB的简单且廉价的步进调试器.zip》资料免费下载
    发表于 08-02 09:47 0次下载
    基于OpenOCD和GDB的<b class='flag-5'>简单</b>且廉价的步进<b class='flag-5'>调试</b>器

    了解 1-Wire 的简单

    做得更好:了解 1-Wire 的简单
    的头像 发表于 12-29 10:02 1119次阅读
    <b class='flag-5'>了解</b> 1-Wire 的<b class='flag-5'>简单</b>性

    FPGA在线调试的方法简单总结

    Xilinx被AMD收购的事情把我震出来了,看了看上上一篇文章讲了下仿真的文件操作,这篇隔了很久远,不知道该从何讲起,就说说FPGA的在线调试的一些简单的操作方法总结。
    发表于 06-19 15:52 2041次阅读
    FPGA在线<b class='flag-5'>调试</b>的方法<b class='flag-5'>简单</b>总结

    简单了解SDK与APK的区别

    不少小伙伴在开发软件中会提到两个词:API和SDK。虽然它们看起来很专业,但其实背后的概念并不复杂。这篇文章能简单的帮你了解SDK与APK的区别。
    的头像 发表于 10-11 10:08 1165次阅读