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

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

3天内不再提示

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

蛇矛实验室 来源:蛇矛实验室 2024-01-15 09:53 次阅读

本期作者/牛杰

前言

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

反调试

1.NtSetInformationThread

NtSetInformationThread 是 Windows 操作系统提供的一个函数,用于设置线程的信息,该函数通常用来设置线程的优先级,此外通过设置不同的 ThreadInformationClass 参数,可以实现隐藏线程、禁止调试、设置调试状态等操作,从而增加程序的安全性和防御性,该函数原型与枚举信息如下。

__kernel_entry NTSYSCALLAPI NTSTATUS NtSetInformationThread(
[in] HANDLE ThreadHandle,
[in] THREADINFOCLASS ThreadInformationClass,
[in] PVOID ThreadInformation,
[in] ULONG ThreadInformationLength
);

typedef enum_THREADINFOCLASS { 
ThreadBasicInformation, 
ThreadTimes, 
ThreadPriority, 
ThreadBasePriority, 
ThreadAffinityMask, 
ThreadImpersonationToken, 
ThreadDescriptorTableEntry, 
ThreadEnableAlignmentFaultFixup, 
ThreadEventPair_Reusable, 
ThreadQuerySetWin32StartAddress, 
ThreadZeroTlsCell, 
ThreadPerformanceCount, 
ThreadAmILastThread, 
ThreadIdealProcessor, 
ThreadPriorityBoost, 
ThreadSetTlsArrayAddress, 
ThreadIsIoPending, 
ThreadHideFromDebugger, 
ThreadBreakOnTermination, 
MaxThreadInfoClass 
} THREADINFOCLASS;

NtSetInformationThread通过ThreadInformationClass中ThreadHideFromDebugger来反调试 是 NtSetInformationThread 函数的一个参数,用于将当前线程隐藏起来,使得调试器无法对其进行调试。这个参数的作用是防止调试器附加到被隐藏的线程上进行调试操作。

当线程被隐藏后,调试器无法访问和控制该线程的执行。这可以用作一种反调试技术,防止恶意用户或恶意软件使用调试器来分析、修改或干扰程序的执行。

通过隐藏线程,程序可以增加自身的安全性,防止被调试器进行逆向工程、代码分析、内存调试等操作。这对于一些需要保护知识产权、防止恶意调试的应用程序或安全软件来说特别有用。

NtSetInformationThread反调试demo如下。

#include
#include

typedefenum_THREADINFOCLASS {
ThreadBasicInformation,
ThreadTimes,
ThreadPriority,
ThreadBasePriority,
ThreadAffinityMask,
ThreadImpersonationToken,
ThreadDescriptorTableEntry,
ThreadEnableAlignmentFaultFixup,
ThreadEventPair_Reusable,
ThreadQuerySetWin32StartAddress,
ThreadZeroTlsCell,
ThreadPerformanceCount,
ThreadAmILastThread,
ThreadIdealProcessor,
ThreadPriorityBoost,
ThreadSetTlsArrayAddress,
ThreadIsIoPending,
ThreadHideFromDebugger,
ThreadBreakOnTermination,
MaxThreadInfoClass
} THREADINFOCLASS;
typedefNTSTATUS(WINAPI* pNtSetInformationThread)(HANDLE, THREADINFOCLASS, PVOID, ULONG);
voidHideFromDebugger(){
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
pNtSetInformationThread NtSetInformationThread = (pNtSetInformationThread)GetProcAddress(hNtDll, "NtSetInformationThread");
NTSTATUS status = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
}

voidThreadHid(){
HideFromDebugger();
intx = 1;
while(1) {
printf("%d",x);
Sleep(1000);
x++;
};
}

intmain()
{
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadHid, 0, 0, 0);

getchar();

return0;
}

使用调试器执调试该程序,可以看到程序正常执行。

0419ab90-b132-11ee-8b88-92fbcf53809c.png

然后在线程中下断点,程序会自动关闭。

043e5792-b132-11ee-8b88-92fbcf53809c.png

需要注意的是,虽然使用 ThreadHideFromDebugger 可以增加程序的安全性,但它并不是绝对的安全措施,因为仍然存在其他方法可以绕过或检测到线程隐藏。因此,在设计安全应用程序时,应综合考虑多种防护措施,并定期进行安全评估和更新。

2.SeDebugPrivilege

默认情况下,进程没有调试权限(SeDebugPrivilege),除非自己主动开启,但是调试器启动程序并调试时,会从调试器继承该权限。使用该方式需要先调用CsrGetProcessId获取csrss.exe的pid,该函数在ntdll。获取pid后,通过OpenProcess读取句柄csrss.exe,如果能获取则说明被调试,代码如下。

HMODULEhMod = LoadLibrary(TEXT("ntdll.dll"));
typedefint(*CSRGETPROCESSID)();
CSRGETPROCESSIDCsrGetProcessId = (CSRGETPROCESSID)GetProcAddress(hMod, "CsrGetProcessId");
DWORDpid = CsrGetProcessId();

HANDLEhandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if(handle) {
returntrue;
}
else{
returnfalse;
}

3.时间检测

通过计算时间差,如果时间间隔过长,判断当前进程被反调试,常用API有API有:QueryPerformanceCounter、GetTickCount、GetSystemTime、GetLocalTime,这些API使用方法相似,我们使用GetTickCount举例。

DWORDstarttime = GetTickCount();
DWORDendtime = GetTickCount();
if(endtime - starttime > 500) {
returntrue;
}
else{
returnfalse;
}

4.父进程

通过检测自身父进程来判定是否被调试,原理非常简单,我们的系统在运行程序的时候,绝大多数应用程序都是由explorer.exe这个父进程派生而来的子进程,也就是说如果没有被调试其得到的父进程就是explorer.exe的进程PID,而如果被调试则该进程的父进程PID就会变成调试器的PID值,通过对父进程的检测即可实现检测是否被调试的功能。

#include
#include
#include

intIsDebug()
{
DWORD ExplorerId = 0;
PROCESSENTRY32 pe32 = { 0};
DWORD ProcessId = GetCurrentProcessId();

GetWindowThreadProcessId(FindWindow(L"Progman", NULL), &ExplorerId);

HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if(hProcessSnap != INVALID_HANDLE_VALUE)
{
pe32.dwSize = sizeof(PROCESSENTRY32);
Process32First(hProcessSnap, &pe32);
do
{
if(ProcessId == pe32.th32ProcessID)
{
// 判断父进程是否是 Explorer.exe
if(pe32.th32ParentProcessID != ExplorerId)
{
returnTRUE;
}
}
} while(Process32Next(hProcessSnap, &pe32));
}
returnFALSE;
}

intmain(intargc, char* argv[])
{
if(IsDebug())
{
printf("正在被调试 
");
}

system("pause");
return0;
}

044be114-b132-11ee-8b88-92fbcf53809c.png

5.NtYieldExecution

NtYieldExecution让当前线程主动放弃其剩余的时间片,并执行下一个等待的线程。如果没有线程被安排执行或在使用调试器单步调试时线程无法切换,NtYieldExecution函数返回为STATUS_NO_YIELD_PERFORMED (0x40000024)。

#defineSTATUS_NO_YIELD_PERFORMED 0x40000024 
typedefNTSTATUS(WINAPI* pNtYieldExecution)();
boolisDebug()
{
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
pNtYieldExecution NtYieldExecution = (pNtYieldExecution)GetProcAddress(hNtDll, "NtSetInformationThread");
INT iDebugged = 0;

for(inti = 0; i < 0x20; i++)
    {
        Sleep(0xf);

        if (NtYieldExecution() != STATUS_NO_YIELD_PERFORMED)
            iDebugged++;
    }

    if (iDebugged <= 3)
        return false;
    else
        return true;

    system("pause");
}

但是这种方法其实并不可靠,因为它只显示当前进程中是否有一个高优先级的线程。然而,它可以作为一种反跟踪技术。

6.DbgUiRemoteBreakin

DbgUiRemoteBreakin打补丁可以反OD附加调试,当我们用OD附加调试时,CreateRemoteThread函数在目标程序中创建了一个远程线程,然后在远程线程中调用DbgUiRemoteBreakin函数,DbgUiRemoteBreakin内部调用了DbgBreakPoint函数,DbgBreakPoint函数内部下了一个int 3断点,触发异常让操作系统运行异常处理程序,然后操作系统把控制权交管给调试器,因此可以创建TLS回调的方式hook DbgUiRemoteBreakin,内部直接调用ExitProcess()退出程序,测试代码如下。

# include
# include
# include
intmain(intargc, char* argv[])
{
BYTE bBuffer[0x10] = { 0};
DWORD dwBreakAddress; 
DWORD dwOldProtect;
DWORD dwNum;

dwBreakAddress = (DWORD)GetProcAddress(LoadLibrary(L"ntdll.dll"), "DbgUiRemoteBreakin");
bBuffer[0] = 0xE9; 
*((DWORD*)(bBuffer + 1)) = (DWORD)ExitProcess - dwBreakAddress; 

VirtualProtect((LPVOID)dwBreakAddress, 0x10, PAGE_EXECUTE_READWRITE, &dwOldProtect);
WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwBreakAddress, bBuffer, 5, &dwNum);
VirtualProtect((LPVOID)dwBreakAddress, 0x10, dwOldProtect, &dwOldProtect);

while(1)
{
staticintx = 0;
printf("%d
",x);
Sleep(1000);
x++;
}
return0;
}

当OD附加,程序终止。

04613956-b132-11ee-8b88-92fbcf53809c.png

总结

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

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

    关注

    3

    文章

    3440

    浏览量

    87144
  • 操作系统
    +关注

    关注

    37

    文章

    6284

    浏览量

    121883
  • 调试
    +关注

    关注

    7

    文章

    527

    浏览量

    33625
  • 函数
    +关注

    关注

    3

    文章

    3868

    浏览量

    61309

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

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

收藏 人收藏

    评论

    相关推荐

    简单了解反调试技术

    反调试技术,是一种防止逆向的方案。逆向人员如果遇到复杂的代码混淆,有时会使用调试器动态分析代码逻辑简化分析流程。例如恶意软件通常会被安全研究人员、反病毒厂商和其他安全专业人员分析和调试,以了解其行为
    的头像 发表于 11-13 11:10 682次阅读
    简单了解<b class='flag-5'>反调试</b>技术

    常见的金属冶炼方法

    常见的金属冶炼方法本文介绍常用的方法,希望大家能用得到! 
    发表于 03-03 17:14

    嵌入式arm调试方法介绍

    开发基于 arm 的应用软件,包括编辑、编译、汇编、链接等工作全部在 PC 机上即可完成,调试工作则需要配合其他的模块或产品方可完成,目前常见调试方法有以下几种:  1. 指令集模拟
    发表于 05-11 16:53

    【北京】奇虎360诚聘车联网安全研究员

    ;5.至少能够熟练使用C、C++、python等一编程语言;6.熟悉常见的软件保护方法和协议加密算法。 有整车厂软件开发经验、破解程序POC实例、汽车破解经验者优先。 有意向者请发送
    发表于 08-17 17:27

    动态反调试技术总结

    :1. CC int3 指令(注意两者之间没有空格)。2.CD 2D int 2d 指令3.pushfdor [esp],100 popfd使EFlags寄存器TF==1.此3种方法,都是利用了调试器特有
    发表于 07-10 06:21

    ARM的三中断调试方法介绍

    址处的代码,实现动态改变中断处理函数。具体方法是:嵌入式技巧:ARM的三中断调试方法介绍嵌入式软件开发流程参照嵌入式软件的开发流程。第一步
    发表于 10-18 09:28

    介绍几种常见电阻器的测试方法

    本文介绍了几种常见电阻器的测试方法
    发表于 05-10 07:12

    介绍常见的CPU故障及维护方法

    我们使用电脑的时候有一个CPU的硬件,大家很多对这个硬件不是很了解,遇到状况也很难解决,下面给大家介绍常见的CPU故障及维护方法,欢迎阅读!常见的CPU故障及维护
    发表于 09-08 06:09

    介绍goahead的调试手段

    这里介绍goahead的调试手段:(1)终端调试(2)日志调试1 终端调试终端
    发表于 12-16 06:21

    安防监控系统常见故障及解决方法

    本文介绍监控系统进入调试、试运行及交付使用阶段,可能出现的常见故障现象及其解决方法参考。
    发表于 12-18 11:45 4423次阅读

    TR5001T设备介绍及程序调试方法

    TR5001设备介绍及程序调试方法和程序调试技巧。
    发表于 06-16 18:21 0次下载

    GOE219硬件调试常见问题及检修方法

    GOE219硬件调试常见问题及检修方法
    发表于 12-27 16:26 0次下载

    MATLAB程序调试方法及工具介绍

    MATLAB程序设计之MATLAB程序调试方法及工具介绍
    的头像 发表于 07-13 17:50 6155次阅读
    MATLAB程序<b class='flag-5'>调试</b>的<b class='flag-5'>方法</b>及工具<b class='flag-5'>介绍</b>

    单片机常用的调试方法

    在单片机程序调试过程中,串口打印调试方法是非常重要的手段,在使用串口调试时,我们更多的是使用printf。但是下面我们不介绍printf,
    的头像 发表于 04-04 14:58 3744次阅读

    EtherCAT运动控制卡的辅助调试工具与方法介绍

    EtherCAT运动控制卡的辅助调试工具与方法介绍
    的头像 发表于 11-15 18:52 1725次阅读
    EtherCAT运动控制卡的辅助<b class='flag-5'>调试</b>工具与<b class='flag-5'>方法</b><b class='flag-5'>介绍</b>