静态反调试技术(3)

ZwSetInformationThread

强制分离(Detach)被调试者和调试器的技术。利用ZwSetInformationThread()API,被调试者可将自身从调试器中分离出来

	typedef enum _THREAD_INFORMATION_CLASS {
        ThreadBasicInformation,
        ThreadTimes,
        ThreadPriority,
        ThreadBasePriority,
        ThreadAffinityMask,
        ThreadImpersonationToken,
        ThreadDescriptorTableEntry,
        ThreadEnableAlignmentFaultFixup,
        ThreadEventPair,
        ThreadQuerySetWin32StartAddress,
        ThreadZeroTlsCell,
        ThreadPerformanceCount,
        ThreadAmILastThread,
        ThreadIdealProcessor,
        ThreadPriorityBoost,
        ThreadSetTlsArrayAddress,
        ThreadIsIoPending,
       ThreadHideFromDebugger           // 17 (0x11)
    } THREAD_INFORMATION_CLASS, *PTHREAD_INFORMATION_CLASS;
 

	typedef NTSTATUS (WINAPI* ZWSETINFORMATIONTHREAD)(
        HANDLE ThreadHandle,
        THREAD_INFORMATION_CLASS ThreadInformationClass,
        PVOID ThreadInformation,
        ULONG ThreadInformationLength
    );

ZwSetInformationThread()函数是一个系统原生API(System Native API),它是用来为线程设置信息的。该函数有2个参数,第一个参数ThreadHandle用来接收当前线程的句柄,第二个参数ThreadInformationClass表示线程信息类型,若其值设置为ThreadHideFromDebugger(0x11),调用该函数后,调试进程就会被分离出来。ZwSetInformationThread() API不会对正常的程序(非调试运行)产生任何影响,但若运行的是调试程序,调用该API将使调试器终止运行,同时终止自身进程。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

破解方法

简单的破解思路就是:调用0x00DA1823 地址处的ntdll.ZwSetInformationThreadAPI前,查找存储在栈中的第二个参数ThreadInformationClass值,若值为ThreadHideFromDebugger(0x11),则修改为0后继续运行即可
在这里插入图片描述
当然也可以勾取ZwSetInformationThread()API,并以同样的方式操作函数的参数。

提示:
利用ZwSetInformationThread()进行反调试的工作原理是:将线程隐藏起来,调试器就接受不到信息,从而无法调试。另外,Windows XP 以后新增了 DebugActiveProcessStop()API

BOOL WINAPI DebugActiveProcessStop( 
__in DWORD dwProcessId 
);

DebugActiveProcessStop()API用来分离调试器和被调试进程从而停止调试。而前面介绍的ZwSetInformationThread()API则是用来隐藏当前线程,使调试器无法再接收到该线程的调试事件,最终停止调试。

调试程序代码

#include "stdio.h"
#include "windows.h"
#include "tchar.h"
 
void DetachDebugger()
{
    //强制分离被调试者和调试器的技术。调试器与被调试进程同时终止
    typedef enum _THREAD_INFORMATION_CLASS {
        ThreadBasicInformation,
        ThreadTimes,
        ThreadPriority,
        ThreadBasePriority,
        ThreadAffinityMask,
        ThreadImpersonationToken,
        ThreadDescriptorTableEntry,
        ThreadEnableAlignmentFaultFixup,
        ThreadEventPair,
        ThreadQuerySetWin32StartAddress,
        ThreadZeroTlsCell,
        ThreadPerformanceCount,
        ThreadAmILastThread,
        ThreadIdealProcessor,
        ThreadPriorityBoost,
        ThreadSetTlsArrayAddress,
        ThreadIsIoPending,
       <span style="color:#ff0000;"> ThreadHideFromDebugger           // 17 (0x11)</span>
    } THREAD_INFORMATION_CLASS, *PTHREAD_INFORMATION_CLASS;
 
    typedef NTSTATUS (WINAPI* ZWSETINFORMATIONTHREAD)(
        HANDLE ThreadHandle,
        THREAD_INFORMATION_CLASS ThreadInformationClass,
        PVOID ThreadInformation,
        ULONG ThreadInformationLength
    );
 
    ZWSETINFORMATIONTHREAD pZwSetInformationThread = NULL;
    pZwSetInformationThread = (ZWSETINFORMATIONTHREAD)
                              GetProcAddress(GetModuleHandle(L"ntdll.dll"), 
                                             "ZwSetInformationThread");
 
    pZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
 
    printf("ZwSetInformationThread() -> Debugger detached!!!\n\n");
}
 
int _tmain(int argc, TCHAR* argv[])
{
    DetachDebugger();
 
    printf("\npress any key to quit...\n");
    _gettch();
 
    return 0;
}

利用TLS回调函数(详情通过以下链接查看)

https://blog.csdn.net/CSNN2019/article/details/113094488

ETC

判断当前系统是否为逆向分析系统(非常规系统),若是,则直接停止程序。这些技术都能从系统中轻松获取各种信息(进程,文件,窗口,注册表,主机名,计算机名,用户名,环境变量等)。这些都可以借助Win32API获取系统信息来实现,举几个例子:

  1. 检测OllyDbg窗口----->FindWindow()
  2. 检测OllyDbg进程------>CreatToolhelp32Snapshot()
  3. 检查计算机名称是否为“TEST”,“ANALYSIS”等------->GetComputerName()
  4. 检查程序运行路径是否存在“TEST”,“SAMPLE”等名称------->GetCommandLine()
  5. 检测虚拟机是否处于运行状态(查看虚拟机特有的进程名称---->VMWareService.exe,VMWareTray.exe,VMWareUser.exe等)

破解之法

在这里插入图片描述
要想让FindWindow()失效,只需要在调用FindWindow()把栈中0xC7F668这个名称字符串用NULL来覆盖即可,那么FindWindow()API将无法探测到相应的调试器
修改后:
在这里插入图片描述
接下来要让GetWindowText() API失效,调用GetWindowText() API的代码在0xDA192D处。若想正常调用GetWindowText(),就不能执行0xDA1912地址处的条件跳转指令。要实现这点,可以直接操作条件跳转语句,也可以将其上GetDesktopWindow()GetWindow()API的返回值修改为NULL
在这里插入图片描述

在这里插入图片描述
总之呢,见招拆招,加油

调试程序代码

#include "stdio.h"
#include "windows.h"
#include "tchar.h"
 
void FindDebuggerWindow()
{
    BOOL bDebugging = FALSE;
 
    // using ClassName
    if( FindWindow(L"OllyDbg", NULL) ||                  // OllyDbg
        FindWindow(L"TIdaWindow", NULL) ||               // IDA Pro
        FindWindow(L"WinDbgFrameClass", NULL) )          // Windbg
        bDebugging = TRUE;
 
    printf("FindWindow()\n");
    if( bDebugging )    printf("  => Found a debugger window!!!\n\n");
    else                printf("  => Not found a debugger window...\n\n");
 
    // using WindowName
    bDebugging = FALSE;
    TCHAR szWindow[MAX_PATH] = {0,};
 
    HWND hWnd = GetDesktopWindow();
    hWnd = GetWindow(hWnd, GW_CHILD);
    hWnd = GetWindow(hWnd, GW_HWNDFIRST);
    while( hWnd )
    {
        if( GetWindowText(hWnd, szWindow, MAX_PATH) )
        {
            if( _tcsstr(szWindow, L"IDA") ||
                _tcsstr(szWindow, L"OllyDbg") ||
                _tcsstr(szWindow, L"WinDbg") )
            {
                bDebugging = TRUE;
                break;
            }
        }
 
        hWnd = GetWindow(hWnd, GW_HWNDNEXT);
    }
 
    printf("GetWindowText()\n");
    if( bDebugging )    printf("  => Found a debugger window!!!\n\n");
    else                printf("  => Not found a debugger window...\n\n");
}
 
int _tmain(int argc, TCHAR* argv[])
{
    FindDebuggerWindow();
 
    printf("\npress any key to quit...\n");
    _gettch();
 
    return 0;
}

篇章大总结:

1.PEB结构中有几个重要的成员标示了进程是否处于被调试状态。(以下以32位为例)
+0x002 BeingDebugged : UChar
+0x00c Ldr : _PEB_LDR_DATA
+0x018 ProcessHeap : Ptr32 Void
+0x068 NtGlobalFlag : Uint4B

BeingDebugged成员在被调试状态会显示1,正常情况下为0。解决方法:更改该值为0。
Ldr:进程在被调试状态时堆内存区域会出现一些特殊的标志,未使用的堆内存区域全部填充着0xFEEEFEEE,而Ldr正好在堆内存中被创建(只在XP系统中有,Vista之后的系统没有这种标志)。解决方法:将该区域覆盖为NULL即可。
ProcessHeap:ProcessHeap.Flags(+0x00c)在正常情况下值为0x2,ProcessHeap.ForceFlags成员(+0x10)的值为0x0,当调试时,该值会改变。(只在XP中有效)。解决办法:只需将值改回去即可。
NtGlobalFalg:PEB.NtGlobalFlag会在调试情况下显示0x70,该值是以下Flags值or运算的结果:(附加到进程无效,只有启动调试有效)
FLG_HEAP_ENABLE_TAIL_CHECK (0x10)
FLG_HEAP_ENABLE_FREE_CHECK (0x20)
FLG_HEAP_VALIDATE_PARAMETERS (0x40)
解决方法:重置0即可
2.NtQueryInformationProcess()可以查询ProcessDebugPort(0x7),ProcessDebugObjectHandle(0x1E)ProcessDebugFlags(0x1F)
当进程处于调试状态时,系统会给它分配一个调试端口(Debug Port),正常状态dwDebugPort为,调试状态为0xFFFFFFFF
当进程处于调试状态时,ProcessDebugObjectHandle一个句柄值,正常状态为NULL
当进程处于调试状态时,ProcessDebugFlags为0,正常为1
3.NtQueryObject()函数可以枚举系统所有对象,通过观察系统是否有调试对象句柄,就可以知道是否有进程在被调试。(wcscmp(L"DebugObject",pObjectTypeInfo->TypeName.Buffer)==0),解决方法,改变该函数参数
4.ZwSetInformationThread()该函数顾名思义是给线程设置信息的。该函数有两个参数,第一个参数ThreadHandle为线程句柄,第二参数ThreadInformationClass表示线程信息类型,其值设置为ThreadHideFromDebugger(0x11),调用该函数后,调试进程将会与调试器分离开来,使调试器终止调试,同时终止自身进程。该函数对正常运行程序没有影响。
6.TLS(线程局部存储)TLS函数代码会先于main()函数执行,于是我们可以在这里进行对程序是否被调试的判断,例如判断是否被下了int3断点,PEB中的BeingDebugged是否为1等。
7.ETC利用API获得进程窗口,进程,计算机名称,虚拟机是否在运行状态等判断运行环境是否安全。

反调试技术系列:

静态反调试技术(1)https://blog.csdn.net/CSNN2019/article/details/113105292
静态反调试技术(2)https://blog.csdn.net/CSNN2019/article/details/113147820
静态反调试技术(3)https://blog.csdn.net/CSNN2019/article/details/113178232
动态反调试技术 https://blog.csdn.net/CSNN2019/article/details/113181558
高级反调试技术 https://blog.csdn.net/CSNN2019/article/details/113263215

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻梦&之璐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值