龙空技术网

简单了解反调试技术

创易栈 154

前言:

此时大家对“如何设置反向解析函数”都比较着重,你们都需要学习一些“如何设置反向解析函数”的相关知识。那么小编同时在网络上汇集了一些关于“如何设置反向解析函数””的相关内容,希望你们能喜欢,看官们一起来学习一下吧!

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

反调试

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()内部执行的代码相同,获取方式如下:

//x86PPEB pPeb = (PPEB)__readfsdword(0x30);//x64PPEB 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; //0x0UCHAR ReadImageFileExecOptions; //0x1UCHAR BeingDebugged; //0x2union{UCHAR BitField; //0x3struct{UCHAR ImageUsesLargePages:1; //0x3UCHAR IsProtectedProcess:1; //0x3UCHAR IsImageDynamicallyRelocated:1; //0x3UCHAR SkipPatchingUser32Forwarders:1; //0x3UCHAR IsPackagedProcess:1; //0x3UCHAR IsAppContainer:1; //0x3UCHAR IsProtectedProcessLight:1; //0x3UCHAR IsLongPathAwareProcess:1; //0x3};};ULONG Mutant; //0x4ULONG ImageBaseAddress; //0x8ULONG Ldr; //0xcULONG ProcessParameters; //0x10ULONG SubSystemData; //0x14ULONG ProcessHeap; //0x18....}

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

HeapFlags

ForceFlags

如果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, // 0x1EProcessDebugFlags, // 0x1FProcessHandleTracing, 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,REDSTONE3ProcessCaptureTrustletLiveDump,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;

测试结果如下

总结

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

声明:本文内容及配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。

标签: #如何设置反向解析函数