首页
社区
课程
招聘
[转帖]TLS回调函数,Anti-od原理分析
发表于: 2010-9-22 12:29 6369

[转帖]TLS回调函数,Anti-od原理分析

2010-9-22 12:29
6369

本文转载链接:

线程本地存储器可以将数据与执行的特定线程联系起来,一个进程中的每个线程在访问同一个线程局部存储时,访问到的都是独立的绑定于该线程的数据块。动态绑定(运行时)线程特定数据是通过 TLS API(TlsAlloc、TlsGetValue、TlsSetValue 和 TlsFree)的方式支持的。除了现有的 API 实现,Win32 和 Visual C++ 编译器现在还支持静态绑定(加载时间)基于线程的数据。当使用_declspec(thread)声明的TLS变量时,编译器把它们放入一个叫.tls的区块里。当应用程序加载到内存时,系统寻找可执行文件中的.tls区块,并动态的分配一个足够大的内存块,以便存放TLS变量。系统也将一个指向已分配内存的指针放到TLS数组里,这个数组由FS:[2CH]指向。

数据目录表中第9索引的IMAGE_DIRECTORY_ENTRY_TLS条目的VirtualAddress指向TLS数据,如果非零,这里是一个IMAGE_TLS_DIRECTORY结构,如下:

IMAGE_TLS_DIRECTORY32    STRUC

   StartAddressOfRawData DWORD ?   ; 内存起始地址,用于初始化新线程的TLS

   EndAddressOfRawData   DWORD ?   ; 内存终止地址

AddressOfIndex         DWORD ?   ; 运行库使用该索引来定位线程局部数据

AddressOfCallBacks     DWORD ?   ; PIMAGE_TLS_CALLBACK函数指针数组的地址

SizeOfZeroFill          DWORD ?   ; 用0填充TLS变量区域的大小

Characteristics           DWORD ?   ; 保留,目前为0

IMAGE_TLS_DIRECTORY32    ENDS

AddressOfCallBacks 是线程建立和退出时的回调函数,包括主线程和其它线程。当一个线程创建或销毁时,在列表中的每一个函数被调用。一般程序没有回调函数,这个列表是空的。TLS数据初始化和TLS回调函数调用都在入口点之前执行,也就是说TLS是程序最开始运行的地方。程序退出时,TLS回调函数再被执行一次。回调函数:

TLS_CALLBACK proto Dllhandle : LPVOID, Reason : DWORD, Reserved : LPVOID

参数如下:

Dllhandle : 为模块的句柄

Reason可取以下值:

DLL_PROCESS_ATTACH 1 : 启动一个新进程被加载

DLL_THREAD_ATTACH 2 : 启动一个新线程被加载

DLL_THREAD_DETACH 3 : 终止一个新线程被加载

DLL_PROCESS_DETACH 0 : 终止一个新进程被加载

Reserverd:用于保留,设置为0

IMAGE_TLS_DIRECTORY结构中的地址是虚拟地址,而不是RVA。这样,如果可执行文件不是从基地址装入,则这些地址会通过基址重定位修正。而且IMAGE_TLS_DIRECTORY本身不在.TLS区块中,而在.rdata里。

TLS回调可以使用诸如pedump之类的PE文件分析工具来识别。如果可执行文件中存在TLS条目,数据条目将会显示出来。

Data directory

EXPORT                                rva:00000000      size:00000000

IMPORT                                rva:00061000      size:000000E0

:::

TLS                                         rva:000610E0      size:00000018

:::

IAT                                          rva:00000000      size:00000000

DELAY_IMPORT                 rva:00000000      size:00000000

COM_DESCRPTR                rva:00000000      size:00000000

unused                                    rva:00000000      size:00000000

接着显示TLS条目的实际内容。AddressOfCallBacks成员指向一个以null结尾的回调函数数组。

TLS directory:

StartAddressOfRawData:                          00000000

EndAddressOfRawData:                           00000000

AddressOfIndex:                              004610F8

AddressOfCallBacks:                       004610FC

SizeOfZeroFill:                                          00000000

Characteristics:                                          00000000

在这个例子中,RVA 0x4610fc指向回调函数指针(0x490f43和0x44654e):

默认情况下OllyDbg载入这个例子将会暂停在入口点。由于TLS回调函数是在实际的入口点执行之前被调用的,OllyDbg应该配置一下使其在TLS回调被调用之前中断在实际的loader。

可以通过选择选项->调试选项->事件->第一次中断于->系统断点来设置中断于ntdll.dll内的实际loader代码。这样设置以后,OllyDbg将会中断在位于执行TLS回调的ntdll!LdrpRunInitializeRoutines()之前的ntdll!_LdrpInitializeProcess(),这时就可以在回调例程中下断并跟踪了。如,在内存映像的.text代码段上设置内存访问断点,可以断在TLS回调函数。

实例代码看下一篇:

参考自《加密与解密》及看雪论坛

使用Thread Local Storage (TLS)回调函数可以实现在实际的入口点之前执行反调试的代码,这也是OD载入程序就退出的原因所在。


[培训]科锐逆向工程师培训第53期2025年7月8日开班!

最后于 2025-2-12 14:01 被kanxue编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回