一种基于tls的高级反调试技术(代码片段)

lxykl lxykl     2023-01-30     461

关键词:

盗版行为日益猖獗,严重影响到软件开发者和开发商的知识产权及利益,反盗版技术的重要性也越来越引起人们的重视。在反盗版技术中,起最大作用的当属反调试技术。然而传统的反调试技术都存在一个弱点:他们都在程序真正开始执行之后才采取反调试手段。实际上在反调试代码被执行前,调试器有大量的时间来影响程序的执行,甚至可以在程序入口处插入断点命令来调试程序。对于使用C/C++语言编译的程序来说,问题通常会更严重,在执行到main()函数之前,会执行C/C++编译器插入的很大一段代码,这也给调试器带来影响程序执行的机会。

本文讨论一种利用Windows提供的线程访问互斥机制,来实现一种在程序入口之前就执行反调试代码的技术。技术本身不会影响程序的执行,但能有效地防止调试器的调试。

1  TLS技术简介

TLS全称为Thread Local Storage,是Windows为解决一个进程中多个线程同时访问全局变量而提供的机制。TLS可以简单地由操作系统代为完成整个互斥过程,也可以由用户自己编写控制信号量的函数。当进程中的线程访问预先制定的内存空间时,操作系统会调用系统默认的或用户自定义的信号量函数,保证数据的完整性与正确性[1]

1.1  TLS回调函数

当用户选择使用自己编写的信号量函数时,在应用程序初始化阶段,系统将要调用一个由用户编写的初始化函数以完成信号量的初始化以及其他的一些初始化工作。此调用必须在程序真正开始执行到入口点之前就完成,以保证程序执行的正确性。

TLS回调函数具有如下的函数原型:

void NTAPI TlsCallBackFunction(PVOID Handle, DWORD Reason, PVOID Reserve);

1.2  TLS的数据结构

Windows的可执行文件为PE格式,在PE格式中,专门为TLS数据开辟了一段空间,具体位置为IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]。其中DataDirectory的元素具有如下结构:

typedef struct _IMAGE_DATA_DIRECTORY 
    DWORD   VirtualAddress;
    DWORD   Size;
 IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

 

  对于TLS的DataDirectory元素,VirtualAddress成员指向一个结构体,结构体中定义了访问需要互斥的内存地址、TLS回调函数地址以及其他一些信息[2]

2  具体实现及原理

充分利用TLS回调函数在程序入口点之前就能获得程序控制权的特性,在TLS回调函数中进行反调试操作比传统的反调试技术有更好的效果。

2.1  在程序中使用TLS

Microsoft提供的VC编译器都支持直接在程序中使用TLS,下文都将使用VC进行操作。

要在程序中使用TLS,必须为TLS数据单独建一个数据段,用相关数据填充此段,并通知链接器为TLS数据在PE文件头中添加数据。为此,需要在程序源文件中添加如下代码[3]

#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma data_seg(".CRT$XLB")
       PIMAGE_TLS_CALLBACK TlsCallBackArray[] = 
TlsCallBackFunction1,
TlsCallBackFunction2,
......
NULL
;
#pragma data_seg()

 

其中TlsCallBackArray数组中保存了所有的TLS回调函数指针。值得指出的是,数组必须以NULL指针结束,且数组中的每一个回调函数在程序初始化时都会被调用,程序员可按需要添加。但程序员不应当假设操作系统已何种顺序调用回调函数。如此则要求在TLS回调函数中进行反调试操作需要一定的独立性。

2.2  回调函数的具体实现

2.2.1  检测调试器在程序入口插入的断点

PE可执行文件在初始化阶段,PE文件都会被完整地加载进入内存。通过分析PE文件头来获取程序的入口点,并对入口点的前一个或多个字节进行判断,以阻止调试器在程序入口点下断。在下面代码中,使用GetModuleHandle(NULL)来获得应用程序的加载基址;也可以模拟GetModuleHandle()手动读取程序的PEB来得到。但为了程序的通用性,这里仍然使用GetModuleHandle()。获得程序加载基址后,将其强制类转换为相关指针,并进行计算,最终得到程序的入口点在内存中的具体地址[3]

IMAGE_DOS_HEADER *dos_head=(IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
PIMAGE_NT_HEADERS32 nt_head=(PIMAGE_NT_HEADERS32)((DWORD)dos_head+(DWORD)dos_head->e_lfanew);
BYTE*OEP=(BYTE*)(nt_head->OptionalHeader.AddressOfEntryPoint+(DWORD)dos_head);
下面的代码则通过扫描程序入口点的20字节,判断其中有无调试断点,如有,则退出进程。
for(unsigned long index=0;index<20;index++)
If(OEP[index]==0xcc)
ExitProcess(0);

 

需要指出的是,在TLS回调函数执行时,VC运行库msvcrt.dll,mfc.dll等并未载入,不能使用C库的函数。如果有需要使用,应该使用LoadLibrary()函数载入相应的库并使用GetProcAddress()获得函数地址。但此类操作可能会导致调试器的相关事件触发,不建议进行此类操作。

2.2.2  使调试器窗口无效

此处使用FindWindow()查找指定的窗口,并使用SetWindowsLong()将其超类化为不可用,具体代码见下:

HWND hd_od=FindWindow("ollydbg",NULL);

SetWindowLong(hd_od,GWL_STYLE,WS_DISABLED);

.........处理其他类型的调试器

这里需要说明的是,此类方法仅仅对ring3调试器才起作用。如SoftICE,WinDebug之类的内核调试器,因为根本不存在窗口,此种方法对其无效。

2.2.3  为程序执行所必需的元素进行初始化或分配空间

这个操作的目的是为了防止盗版者通过直接将TLS数据清除的方法来避开反调试。当程序正常运行所需要的内存空间或数据必需经过TLS回调函数初始化时,盗版者不可以将程序的TLS数据清除。因为那样做带来的后果是程序运行根本不正常。

另外,如果这种初始化或分配空间的操作分散在各个TLS回调函数中完成,效果会更好。

2.2.4  堵塞输入

此功能主要是为了应对一些未知调试器和一些不在程序入口点下断的调试器。

函数将首先检查user32.dll导出的BlockInput()函数,如果函数代码是被调试器修改过的,那么将直接退出,代码如下[4]

BYTE *address=(BYTE *)GetProcAddress(LoadLibrary("user32.dll"),"BlockInput");;
bool modify=true;
for(int x=0;x<20;x++)
if(address[x]==0xff&&address[x+1]!=0xff)
              modify=false;
              break;

if(modify) ExitProcess(0);

 

检查过BlockInput()函数正确性之后,函数将调用BlockInput(TRUE),阻塞用户的鼠标和键盘输入。但函数接下来并不立即取消阻塞,而在main()函数中发起一个异常后再取消阻塞。

这么处理的理由很充分,在main()函数中发起异常将导致调试器捕获异常,并暂停等待用户输入。而此时用户输入是被锁定的,那么程序就相当于被变相锁死了,没有办法继续调试。而当调试器不存在时,代码中的__except()部分将直接获得执行权,并取消阻塞,程序正常运行。main函数中的具体代码如下:

__try
       __asm
              xor eax,eax
             div eax,eax
            xor eax,eax
       
      ExitProcess(0);

__except(1,1)
       BlockInput(FALSE);
    

 

值得说明的是,此次在main()函数中调用BlockInput()而不检查也是有原因的。如果此时的BlockInput()函数被调试器修改过,那么取消输入锁定将完全不能工作,那么之后的整个过程也是无法调试的。

如果进一步深入,还可以测试异常返回所用的时间,不论过长或者过短,都能够说明调试器的存在。此处不继续展开了。

2.2.5  创建监视线程

此步目的是为了防止OllyDbg等调试器以进程附加的形式对程序进行调试。程序此步将创建一个子线程,监视调试器窗口的出现,如果发现调试器窗口,将其超类化为不可用。子线程的代码如下:

DWORD WINAPI Monitor(LPVOID s)

       while(sign==TRUE)

              HWND hd=FindWindow("ollydbg",NULL);

              SetWindowLong(hd,GWL_STYLE,WS_DISABLED);

.......处理其它调试器窗口

              Sleep(50);

       

       return 0;

3  实际测试

3.1  测试直接执行

测试方法为直接在资源管理器中双击执行。

技术分享图片

3.2  测试用调试器加载

测试方法为分别使用VC自带的调试器加载调试和使用OllyDebug加载生成的程序进行调试。其中OllyDbg使用从看雪论坛下载的OllyDebug 1.10 CHS,并打开了所有的隐藏插件。

测试结果:两者调试的进程都直接退出,且没有输出任何信息。

技术分享图片

图2  使用VC自带调试器调试,程序直接退出

技术分享图片

图3  使用OllyDebug 1.10,打开全部反反调试插件调试,程序直接退出

3.3  测试用调试器附加进程

测试方法为先在资源管理器中双击程序运行,然后打开OllyDebug试图附加进程。

测试结果:OllyDebug窗口直接失效。

技术分享图片

 

 

4  总  结

通过使用TLS技术作为反调试技术的载体,可以大大增强程序软件的反盗版能力。如果能结合传统技术,将使反调试技术发展至一新高度。

win10x64下通过tls实现反调试

目录(?)[-]TLS技术简介1TLS回调函数2TLS的数据结构具体实现及原理1VS2015X64release下的demo2回调函数的具体实现21使用IsDebuggerPresent检测调试器22使调DebugPort检测调试器实际测试1测试直接执行2测试用调试器加载总结 1TLS技术简介Thread... 查看详情

《逆向工程核心原理》学习笔记:反调试技术(代码片段)

目录前言一、反调试技术概况二、静态反调试技术1、PEB2、NtQueryInformationProcess()(1)ProcessDebugPort(0x7)(2)ProcessDebugObjectHandle(0x1E)(3)ProcessDebugFlags(0x1F)(4)例子 查看详情

《逆向工程核心原理》学习笔记:反调试技术(代码片段)

目录前言一、反调试技术概况二、静态反调试技术1、PEB2、NtQueryInformationProcess()(1)ProcessDebugPort(0x7)(2)ProcessDebugObjectHandle(0x1E)(3)ProcessDebugFlags(0x1F)(4)例子 查看详情

windows下反(反)调试技术汇总(代码片段)

反调试技术,恶意代码用它识别是否被调试,或者让调试器失效。恶意代码编写者意识到分析人员经常使用调试器来观察恶意代码的操作,因此他们使用反调试技术尽可能地延长恶意代码的分析时间。为了阻止调试器的分析,当... 查看详情

《逆向工程核心原理》学习笔记:高级逆向分析技术(代码片段)

...《逆向工程核心原理》,本篇笔记是第六部分:高级逆向分析技术,包括TLS、TEB、PEB、SEH和IA-32指令等内容一、TLS回调函数TLS(ThreadLocalStorage,线性局部存储)回调函数要先于EP代码执行,故可作为反调... 查看详情

《逆向工程核心原理》学习笔记:高级逆向分析技术(代码片段)

...《逆向工程核心原理》,本篇笔记是第六部分:高级逆向分析技术,包括TLS、TEB、PEB、SEH和IA-32指令等内容一、TLS回调函数TLS(ThreadLocalStorage,线性局部存储)回调函数要先于EP代码执行,故可作为反调... 查看详情

反调试手法之createprocess反调试(代码片段)

              反调试手法之CreateProcess反调试在学习Win32创建进程的时候.我们发现了有一个进程信息结构体.STARTUPINFO.这个结构体可以实现反调试.具体CreateProcess可以参考上一篇博客.:  https://www.cnblogs.com/iBi... 查看详情

android签名验证与反调试机制的对抗技术(代码片段)

...章目录前言签名验证1.1签名机制1.2签名验签1.3签名绕过反反调试2.1tracerPid检测2.2进程名称检测2.3关键文件检测2.4调试端口检测2.5ptrace值检测2.6时间差异检测2.7内置函数检测2.8调试断点检测总结前言Android的APK文件为了防止被篡改... 查看详情

c++反调试(基于seh的setunhandledexceptionfilter)

终于!终于把静态反调试串了一遍,虽然没有包含全部但是也对反调试的轮廓有了初步的认识。但是这一切才刚刚开始,翻过一座山(坑),还有另一座山(坑)在等着你?刚发现csdn还有个发表情的功能,这就是传说中的彩蛋吗... 查看详情

过反调试

...二者本为一体破解技术就不要我多介绍了,下面我来介绍反调试技术也就是所谓的防破解技术反调试技术可以简单通俗的理解为:防止OD分析软件的技术,也就是反调试技术那么反调试技术又有几种呢?下面我介绍几种常用反调... 查看详情

android签名验证与反调试机制的对抗技术(代码片段)

...章目录前言签名验证1.1签名机制1.2签名验签1.3签名绕过反反调试2.1tracerPid检测2.2进程名称检测2.3关键文件检测2.4调试端口检测2.5ptrace值检测2.6时间差异检测2.7内置函数检测2.8调试断点检测总结前言Android的APK文件为了防止被篡改... 查看详情

javascript加密代码反调试

...JS代码,可能被他人分析,为了对抗分析调试,本文分享一种反调试技术。功能效果使函数名不可修改,修改则代码无法运行技术原理将JS代码用可逆算法进行加密。公开或发布的代码时,只提供这部分“密文”。运行代码中包... 查看详情

javascript加密代码反调试

...JS代码,可能被他人分析,为了对抗分析调试,本文分享一种反调试技术。功能效果使函数名不可修改,修改则代码无法运行技术原理将JS代码用可逆算法进行加密。公开或发布的代码时,只提供这部分“密文”。运行代码中包... 查看详情

android反调试实践(代码片段)

(一)xposed检测1.每一个被hook的进程,都会将xposed的相关库文件和jar文件加载到相应的进程空间中,如图:这里看到进程空间中加载了1)app_process32_xposed2)libxposed_art.so3)XResourcesSuperClass.dex既然能... 查看详情

ida动态调试破解alicrackme与反调试对抗(代码片段)

文章目录前言APK破解(上)1.1静态分析1.2动态分析反调试对抗2.1Ptrace2.2全局调试2.3反反调试APK破解(下)3.1重新编译3.2动态调试总结前言在前面的文章中IDA动态调试破解EXE文件与分析APK流程介绍了IDA对APK进行动态调试分析的简单流程&#... 查看详情

学习:反调试之检测类名与标题名(代码片段)

反调试之检测类名与标题名:通过FindWindow函数来进行反调试HWNDFindWindowA(LPCSTRlpClassName,LPCSTRlpWindowName);返回值类型:类型:HWND如果函数成功,则返回值是具有指定类名和窗口名的窗口的句柄。如果函数失败,则返回值为NULL。要获... 查看详情

修改android手机内核,绕过反调试(代码片段)

本文博客链接:http://blog.csdn.net/qq1084283172/article/details/570864860x1.手机设备环境Modelnumber:Nexus5OSVersion:Android4.4.4KTU84PKernelVersion:3.4.0-gd59db4e0x2.Android内核提取查找Android设备的boot分区文件。高通芯片的设备可 查看详情

反调试技术常用api,用来对付检测od和自动退出程序

在调试一些病毒程序的时候,可能会碰到一些反调试技术,也就是说,被调试的程序可以检测到自己是否被调试器附加了,如果探知自己正在被调试,肯定是有人试图反汇编啦之类的方法破解自己。为了了解如何破解反调试技术... 查看详情