@author: dlive

TLS (Thread Local Storage 线程局部存储 )回调函数常用于反调试。

TLS回调函数的调用运行要先于EP代码执行,该特性使它可以作为一种反调试技术使用。

TLS是各线程的独立的数据存储空间,使用TLS技术可在线程内部独立使用或修改进程的全局数据或静态数据,就像对待吱声的局部变量一样。

0x01 PE TLS Table

若在编程中启用了TLS功能,PE头文件中就会设置TLS表(IMAGE_NT_HEARDERS->IMAGE_OPTIONAL_HEADER->IMAGE_DATA_DIRECTORY[9])

可以看到TLS Table的RVA是00009310,找到对应位置如下

TLS Table中比较重要的成员为AddressOfCallbacks,该值指向含有TLS回调函数地址(VA)的数据(一个程序中可以注册多个TLS回调函数)

0x02 TLS回调函数

TLS回调函数是指,每当创建/终止进程的线程时会自动调用执行的函数(前后共调用两次)。创建进程的主线程时也会自动调用回调函数,且其调用执行先于EP代码。

TLS回调函数的声明:

void NTAPI TLS_CALLBACK(PVOID DllHandle, DWORD Reason, PVOID Reserved)

DllMain的声明:

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)

可以看到两者声明非常相似,第一个参数为模块句柄,即加载地址,第二个参数为调用原因

调用原因有四种

#define DLL_PROCESS_ATTACH 1
#define DLL_THREAD_ATTACH 2
#define DLL_THREAD_DETACH 3
#define DLL_PROCESS_ATTACH 0

TlsTest.cpp

#include <windows.h>

//告知连接器使用TLS
#pragma comment(linker, "/INCLUDE:__tls_used") void print_console(char* szMsg)
{
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
//先于主线程调用执行的TLS回调函数中使用printf可能会发生Runtime Error,可直接调用WriteConsole API
WriteConsoleA(hStdout, szMsg, strlen(szMsg), NULL, NULL);
} void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
char szMsg[80] = {0,};
wsprintfA(szMsg, "TLS_CALLBACK1() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
print_console(szMsg);
} void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
char szMsg[80] = {0,};
wsprintfA(szMsg, "TLS_CALLBACK2() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
print_console(szMsg);
}
/*
注册TLS函数
.CRT$XLX的作用
CRT表示使用C Runtime 机制
X表示表示名随机
L表示TLS Callback section
X也可以换成B~Y任意一个字符
*/
#pragma data_seg(".CRT$XLX")
//存储回调函数地址
PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1, TLS_CALLBACK2, 0 };
#pragma data_seg() DWORD WINAPI ThreadProc(LPVOID lParam)
{
print_console("ThreadProc() start\n"); print_console("ThreadProc() end\n"); return 0;
} int main(void)
{
HANDLE hThread = NULL; print_console("main() start\n");
//创建子线程
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
//等待子线程结束
WaitForSingleObject(hThread, 60*1000);
CloseHandle(hThread); print_console("main() end\n"); return 0;
}

主线程调用main前调用TLS回调函数,调用原因为DLL_PROCESS_ATTACH

子线程启动前调用TLS,原因为DLL_THREAD_ATTACH

子线程结束后调用TLS,原因为DLL_THREAD_DETACH

主线程结束后调用TLS的原因为DLL_PROCESS_DETACH

0x03 调试TLS回调函数

在OD调试器的默认设置下调试器会在EP处暂停,WinDbg调试器默认在系统启动断点暂停。

调试TLS回调函数时,因为回调函数代码在EP之前就已经执行了,所以调试选项需要设置暂停于系统断点(system breakpoint), 设置后调试器会在ntdll.dll模块内部的“system startup breakpoint‘处暂停

然后在PE中找到回调函数的地址,下断点调试即可

OD2.0中直接提供暂停在TLS函数的选项

TLS回调函数的更多相关文章

  1. 通过TLS回调函数的反调试

    下面是TLS数据结构的定义 typedef struct _IMAGE_TLS_DIRECTORY { DWORD StartAddressOfRawData; DWORD EndAddressOfR ...

  2. TLS回调函数以及反调试简单使用

    TLS回调函数以及反调试简单使用 0x00  TLS介绍 TLS(Thread Local Storage,线程局部储存),主要用于给线程独立的传值,由于线程不拥有进程的资源,所以几个同一进程的几个线 ...

  3. 《逆向工程核心原理》——TLS回调函数

    pe中TLS(thread local storage)中函数的执行时机早于入口函数(entry point), 相关结构: // // Thread Local Storage // typedef ...

  4. Mina、Netty、Twisted一起学(九):异步IO和回调函数

    用过JavaScript或者jQuery的同学都知道,JavaScript特别是jQuery中存在大量的回调函数,例如Ajax.jQuery的动画等. $.get(url, function() { ...

  5. 小兔JS教程(三)-- 彻底攻略JS回调函数

    这一讲来谈谈回调函数. 其实一句话就能概括这个东西: 回调函数就是把一个函数当做参数,传入另一个函数中.传进去的目的仅仅是为了在某个时刻去执行它. 如果不执行,那么你传一个函数进去干嘛呢? 就比如说对 ...

  6. 嵌入式&iOS:回调函数(C)与block(OC)传 参/函数 对比

    C的回调函数: callBack.h 1).声明一个doSomeThingCount函数,参数为一个(无返回值,1个int参数的)函数. void DSTCount(void(*CallBack)(i ...

  7. 嵌入式&iOS:回调函数(C)与block(OC)回调对比

    学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...

  8. 理解 JavaScript 回调函数并使用

    JavaScript中,函数是一等(first-class)对象:也就是说,函数是 Object 类型并且可以像其他一等对象(String,Array,Number等)一样使用.它们可以"保 ...

  9. 关于js的回调函数的一点看法

    算了一下又有好几个月没写博客了,最近在忙公司android的项目,所以也就很少抽时间来写些东西了.刚闲下来,我就翻了翻之前看的东西.做了android之后更加感觉到手机端开发的重要性,现在做nativ ...

随机推荐

  1. 「日常训练」「小专题·图论」 Frogger (1-1)

    题意 分析 变形的dijkstra. 分析题意之后补充. 代码 // Origin: // Theme: Graph Theory (Basic) // Date: 080518 // Author: ...

  2. (Python爬虫01)-本想给随笔加个序号才发现这么不方便

    本想给随机加个序号,才发现还得去返回看看文章的序号.好在cnblog能断点自动保存. 作为一个小程序员,点赞的同时还在想,谁知道咋实现这种实时保存呢?有知道的给个参考文档呗.太感激了! 重点在这里 有 ...

  3. popen和system问题

    popen和system问题 1. 问题描述 C的代码里面去调用命令启动一个shell脚本,分别使用了下面两个途径. 其中一个是: func1(cmd) { popen(cmd,type); pclo ...

  4. Cassandra 常见错误索引

    类型错误 类型错误调试的技巧 有时候,类型错误提示比较不友好,比如不知道哪个字段出错. 在php中可以用 //过滤几个数据进行操作,逐个检查,或者折半查找错误 $data = array_splice ...

  5. day-8 python自带库实现ID3决策树算法

    前一天,我们基于sklearn科学库实现了ID3的决策树程序,本文将基于python自带库实现ID3决策树算法. 一.代码涉及基本知识 1. 为了绘图方便,引入了一个第三方treePlotter模块进 ...

  6. linux下easy_install的安装与使用详解

    Python中的easy_install工具用起来非常好用,它的作用类似于Php中的pear,或者Ruby中的gem,或者Perl中的cpan. 1.easy_install安装 如果想使用easy_ ...

  7. 软工实践 - 第二十八次作业 Beta 冲刺(6/7)

    队名:起床一起肝活队 组长博客:https://www.cnblogs.com/dawnduck/p/10146478.html 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过 ...

  8. hash function比较

    http://blog.csdn.net/kingstar158/article/details/8028635 由于工作需要,针对千万级别的数据,使用stl::map着实存在着效率问题,最后使用bo ...

  9. php函数引用参数解惑《细说php》

  10. 【bzoj2287】[POJ Challenge]消失之物 背包dp

    题目描述 ftiasch 有 N 个物品, 体积分别是 W1, W2, ..., WN. 由于她的疏忽, 第 i 个物品丢失了. “要使用剩下的 N - 1 物品装满容积为 x 的背包,有几种方法呢? ...