前言
这是我们VIP2会员内部视频:PE文件结构的内部课件的部分内容,鉴于网络上没有比较详尽、通俗易懂的科普文章,本篇内部课件,在此分享给大家,如果您喜欢本篇文章,想深入学习内部视频,想学好软件安全逆向技术,想学好逆向破解技术:欢迎联系小迪老师微信ixiaodi668,我们在这里等你。
本篇文章为内部课件的一部分:如下所示
![图片[1]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/c161c8a99110942498d39f59ec8f82f65b9b614573f71f495a07a3460679cc3701cf91cb7eb25d21dd985f751943c2c7?pictype=scale&from=30013&version=3.3.3.3&fname=Snipaste_2025-11-19_17-51-37%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
内部视频教学试看
TLS 简介
TLS (Thread Local Storage) = 线程本地存储
就是为每个线程单独分配的一块数据区,使得不同线程有自己的独立变量副本。
参考文档:
![图片[2]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/8f7076c4e37f3384dc27bc16aba4c3ec5e55844670cf2201f172757149856a2d7acde9ea281015e4ba8146129b537747?pictype=scale&from=30013&version=3.3.3.3&fname=11.png&size=750)
TLS 执行时机
PE 支持设置“TLS 回调函数”(TLS Callback)。当进程或线程启动时,这些函数会被系统自动调用。
执行顺序大概如下:
EXE加载
当 EXE 被加载到内存时:
操作系统 loader →
执行 TLS Callback →
执行 C 运行库初始化 →
执行 main/WinMain
DLL 加载
当 DLL 被 LoadLibrary 或被系统加载时:
操作系统 loader →
TLS Callback(DLL_PROCESS_ATTACH) →
DllMain(DLL_PROCESS_ATTACH) →
返回
当创建新线程时
线程创建 →
TLS Callback(DLL_THREAD_ATTACH) →
DllMain(DLL_THREAD_ATTACH)
在这里大家只需要认识到一点:就是
TLS 回调函数的执行时机,比 exe 或者 dll 两者的入口位置,还要早
因为它的加载时机最靠前,所以可以作很多用途
TLS Callback 的用途
1. 反调试隐藏点
恶意软件常把反调试代码写在 TLS Callback 中,因为:
- 你来不及下
main()入口断点 - 你来不及下
DllMain()入口断点 - TLS 回调在调试器入口断点反应前就执行了

大部分人的xdbg配置都会只勾选一个“入口断点”
反调试常常阻止 OllyDbg / x64dbg 运行, 在 TLS 里崩溃调试器或者结束程序进程。
反调试代码示例:
mov eax, fs:[30h] ; PEB
cmp byte ptr [eax+2], 1 ; BeingDebugged
je EXIT_PROCESS
2. 壳(packer)/恶意软件(Malware)用于解密重要代码
某些软件保护壳如 VMProtect 会在 TLS Callback 中:
- 解密代码段
- 修改 IAT
- 修改代码段权限
某些恶意软件也会在 TLS Callback 中:
- 解密 .text 或 .data 段:让执行的代码在 TLS 回调里动态恢复。
- 解密 IAT / Import table:绕过杀软静态分析。
- 解密资源(如字符串、配置数据):延迟到运行时才解密,静态分析不到。
- 解密自身 DLL 代码段:用于保护 DLL 注入后的第一段代码。
- 解密配置信息(远控服务器C2地址、密钥):恶意软件常用。
- 自动自毁/修补:反分析时自动破坏自身以防逆向。
3. 软件保护:初始化 HOOK、补丁
一些程序会在 TLS 回调里进行:
- Inline Hook 自身函数
- 禁用调试器
- 注入代码
- 检测硬件断点、软件断点
4.恶意软件行为
- 在 main() 之前执行恶意逻辑,防止分析工具进入程序入口。
- 隐藏恶意行为,分析者下断到 main 还没看到恶意行为。
- 创建新的线程: 恶意线程在 TLS 阶段启动,不容易被发现。
- 重定向执行流(自定义入口): 把真正的 main 隐藏起来。
总结:
在逆向和恶意软件Malware分析(病毒木马分析)中
TLS Callback 是常见的隐藏点。
什么时候 强烈推荐 把某些代码放进 TLS 执行 ?
- 做软件保护(反破解)
- 做壳
- 做反调试
- 做加密逻辑
- 做木马(恶意软件)
- 做防注入
- 做关键代码的完整性检查
- 需要在 OEP 之前执行
如果你正在研究逆向、HOOK、注入、防调试,那么 TLS 是神器。
PE 文件结构之解析TLS
TLS 结构位置
在扩展PE 头Optional Header 的 Data Directory 项中有一个:
IMAGE_DIRECTORY_ENTRY_TLS (索引(下标):9)
![图片[4]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/329b3022d873ca866295135c91c914164eb77c25275aa1b5bbd1b46346bffce0a24cbc02fbae7ff5bfb09ff7747eb774?pictype=scale&from=30013&version=3.3.3.3&fname=33%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
010Editor 工具可以看到它的 RVA 和 Size![图片[5]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/34c3400790859c13ddf9cbb714aaf7be0dd8d266dbb48c1c4153985db9947da6edb7050e1e4f831fbede83b9a085b199?pictype=scale&from=30013&version=3.3.3.3&fname=44%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
使用 studyPE 也可以查看,如果存在 TLS,那么 RVA、FOA、Size 肯定不会为 0
![图片[6]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/40b255ddf126806e7a4173e178ea66eac946bb4a9493ef4f6fd25d048f0d054368d30c1aaa6a816c3c806da32c52b800?pictype=scale&from=30013&version=3.3.3.3&fname=55%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
TLS 结构
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
DWORD AddressOfIndex;
DWORD AddressOfCallBacks; // 指向 TLS Callback 数组
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32;
成员解析:一共 24 个字节
|
字段 |
说明 |
|
StartAddressOfRawData |
TLS 数据区起始地址(VA) |
|
EndAddressOfRawData |
TLS 数据区结束地址(VA) |
|
AddressOfIndex |
TLS 索引变量(编译器用) |
|
AddressOfCallBacks |
指向 TLS 回调函数数组,数组以 0 结尾 |
|
SizeOfZeroFill |
要清零的字节数 |
|
Characteristics |
保留 |
如何解析TLS 回调函数
- 在
DataDirectory[9].VirtualAddress找到TLS Directory的RVA - 转 VA / 文件偏移得到真正的
IMAGE_TLS_DIRECTORY - 读取
AddressOfCallBacks
(这是一个 指针数组 VA,每个元素都是回调函数地址)
格式:
AddressOfCallBacks → [Callback1][Callback2][Callback3]...[0]
例如:
401020 → 00401234
401024 → 00404567
401028 → 00000000 // 数组结束
示例解析
![图片[7]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/6eb19641b905864d398d6249da37aec1c48f015bf6b6200b127b4fe2ad41dff808c329761ff92e242db31ef47c46c6c0?pictype=scale&from=30013&version=3.3.3.3&fname=image%20%287%29.png&size=750)
把 RVA 转换为 FOA,Size 是 18h == 24 个字节
![图片[8]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/a228f489527ac23f2cb4ac80dbb514e72c94f4ecf18efa47ba6436feea46907e279e2ff4dd688404d583c1fe031cbbd1?pictype=scale&from=30013&version=3.3.3.3&fname=66%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
![图片[9]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/ccb2948032f30d61b403075381946fa570b038b0267eca8dbf8880f6980a80ddf5e41a8686e21104cc9a8af7522028fc?pictype=scale&from=30013&version=3.3.3.3&fname=77%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
最重要的是第 4 个成员:AddressOfCallBacks == 4FA834
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
DWORD AddressOfIndex;
DWORD AddressOfCallBacks; // 指向 TLS Callback 数组
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32;
我们讲一个极端情况,这个掌握了,其他的就都会分析了:
1. 跳转到 dbg 发现内存地址4FA834无效
![图片[10]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/56b02131df0142256d9bc6d5ce6f2c9267d225facf0a10ed28b67704692506298407b9578c567cf3bf7454ae03bac4ab?pictype=scale&from=30013&version=3.3.3.3&fname=88.png&size=750)
2.这个时候我们要思考程序是否存在重定位的情况
存在重定位表
![图片[11]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/ccd21edd4d7fbe7c99a440970f782f1f7e87740aade6e3d4a89a9d1643b893f628accac03acc46f6f56a089168bde76a?pictype=scale&from=30013&version=3.3.3.3&fname=99%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
存在 reloc 段
![图片[12]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/d8e1afe5f001e59c9aeb3716c97664faf32c0918e21221f77e44da6ebcc56acaf98944ddf1a2ce234104a00aac38422f?pictype=scale&from=30013&version=3.3.3.3&fname=1010%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
是动态基址
![图片[13]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/ef13b08c0ed44a370ed8b3a0142960157f2890aee96ec7e1ed2f42fe195cfd072217cba033489036c594b539ccb415a1?pictype=scale&from=30013&version=3.3.3.3&fname=1011%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
3.计算重定位后的 TLS 回调函数数组地址
4FA834 – ImageBase = 0xFA834
其中ImageBase = 00400000、NewBase = 1A0000
TLS 回调数组地址 = 1A0000 + 0xFA834 = 29A834
![图片[14]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/692710d596ad1d064ae903ac96581efb0cc77f7fd562e44a76477f588dfe1c291b47c339a692a60e9fced15e97d6252d?pictype=scale&from=30013&version=3.3.3.3&fname=image11.png&size=750)
4.验证计算结果
数组的结构是这样的:
AddressOfCallBacks → [29A834]=1F33DB、[29A838]=1F2549、[29A83C]=0
![图片[15]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/b8ae6a313f5f40fa4acd1666ce8e36bbdf4fe38acbe5d4125dedd71c27642e88dd301d046739cd4c767b70e70e1c8b85?pictype=scale&from=30013&version=3.3.3.3&fname=1013%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
![图片[16]-PE结构:TLS与TLS回调函数精讲解析-软件安全逆向社区论坛-技术社区-学技术网](https://picabstract-preview-ftn.weiyun.com/ftn_pic_abs_v3/a021451ba4ad95feac6b3fc138caaae9e903ed85d9549c621875e1f8907641192ca09214da626abc82f4ce8cc2aef6cb?pictype=scale&from=30013&version=3.3.3.3&fname=1012%E6%B0%B4%E5%8D%B0%E7%89%88.jpg&size=750)
5.解析大致代码
IMAGE_TLS_DIRECTORY32* tlsDir =
(IMAGE_TLS_DIRECTORY32*)((BYTE*)base + RvaToOffset(dataDir.VirtualAddress));
DWORD* callbacks = (DWORD*)tlsDir->AddressOfCallBacks;
while (*callbacks)
{
printf("TLS Callback: %p\n", (void*)*callbacks);
callbacks++;
}
总结
TLS 是线程局部存储机制,但对逆向工程更重要的是 TLS Callback。
- 在 main/DllMain 之前执行
- 可用于反调试、解密、初始化 HOOK
- 常见于壳与恶意软件
- 在 PE 文件中:
DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]→ TLS Directory → Callback Pointer Array(回调函数数组) - TLS 执行时机:
操作系统 loader →
执行 TLS Callback →(全部) →
C/C++ 静态初始化(全局对象构造等) →
mainCRTStartup →
main() 或 WinMain()
DLL 被加载 →
加载器初始化(LdrInitialize) →
TLS Callback 执行(全部 TLS 回调) →
C/C++ CRT 静态初始化(全局对象构造、全局变量初始化) →
DLL入口点 DllMain(DLL_PROCESS_ATTACH)



没有回复内容