TLS回调函数以及反调试简单使用
TLS回调函数以及反调试简单使用
0x00 TLS介绍
TLS(Thread Local Storage,线程局部储存),主要用于给线程独立的传值,由于线程不拥有进程的资源,所以几个同一进程的几个线程需要独立赋值时的需要通过TLS技术。每个线程创建时都会分配一个index所以,这个索引index是全局变量,线程根据index来获取其他线程传过来的返回值。TLS有一个特点,就是它通常在程序EP前就要运行,所以起始TLS才是个程序真正的开始。利用这一特点,可以用来进行的程序的反调试。
0x01 TLS调用过程
下面我们来创建一个TLS调用实例来看看这个过程会发生什么。
程序如下:
#include "pch.h"
#include <iostream>
#include<windows.h>
//声明要使用TLS
#pragma comment(linker,"/INCLUDE:__tls_used")
//#pragma comment(linker,"/INCLUDE:__tls_used")
//
void print_consoleA(const char *szMsg)
{
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(hStdout, szMsg, strlen(szMsg), NULL, NULL);
}
//定义两个TLS回调函数
//注意一下这个回调函数的参数表,和DLLmain一样的
//Reason的值分别有四种
// #define DLL_PROCESS_ATTACH 1
// #define DLL_THREAD_ATTACH 2
//#define DLL_THREAD_DETACH 3
//#define DLL_PROCESS_DETACH 0
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_consoleA(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_consoleA(szMsg);
}
//在数据段注册两个TLS回调函数
#pragma data_seg(".CRT$XLX")
PIMAGE_TLS_CALLBACK pTLS_CALLBACK[] = { TLS_CALLBACK1,TLS_CALLBACK2 ,0 };
#pragma data_seg()
//新建线程
DWORD WINAPI ThreadProc(LPVOID lParam)
{
print_consoleA("ThreadProc() start\n");
print_consoleA("ThreadProc() end\n");
return 0;
}
int main()
{
HANDLE hThread = NULL;
print_consoleA("main() start\n");
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
WaitForSingleObject(hThread, 60 * 1000);
CloseHandle(hThread);
print_consoleA("main() end\n");
}
编译程序,记住要在debug模式下编译,不然无法打印出完整的日志信息。
Cmd运行得到如下:
D:\>TLSTest1.exe
TLS_CALLBACK1():DllHandle =BE0000,Reason=1
TLS_CALLBACK2():DllHandle =BE0000,Reason=1
main() start
TLS_CALLBACK1():DllHandle =BE0000,Reason=2
TLS_CALLBACK2():DllHandle =BE0000,Reason=2
ThreadProc() start
ThreadProc() end
TLS_CALLBACK1():DllHandle =BE0000,Reason=3
TLS_CALLBACK2():DllHandle =BE0000,Reason=3
main() end
TLS_CALLBACK1():DllHandle =BE0000,Reason=0
TLS_CALLBACK2():DllHandle =BE0000,Reason=0
由上述log信息可以知道TLS回调函数是在进程或者线程开始前就已经执行了,在进程或者线程结束后也结束。
0x00 使用c++编写简单的反调试程序
程序如下:
#include "pch.h"
#include <iostream>
#include<windows.h>
//声明使用TLS回调函数
#pragma comment(linker,"/INCLUDE:__tls_used")
//定义回调函数
void NTAPI TLS_CallBack(PVOID Dllhandle, DWORD Reason, PVOID Reserved)
{
//发现被调试就退出,使用这个API来侦察反调试已经完全不管用了
//吾爱的OD和看雪的OD已经可以屏蔽这个API了,所以起不到反调试的作用,API仅做原理演示用
if (IsDebuggerPresent())
{
MessageBoxA(NULL, "Debugger detect!!", "TLS_CALLBACK", MB_OK);
ExitProcess(1);
}
}
//注册TLS回调函数
#pragma data_seg(".CRT$XLX")
PIMAGE_TLS_CALLBACK pTls_CallBack[] = { TLS_CallBack,0 };
#pragma data_seg()
int main(void)
{
MessageBoxA(NULL, "hello !", "main()", MB_OK);
}
在debug模式下编译生成TLSantidubug.exe文件。我们将其拖入吾爱的od,f9运行。如下图:

我们发现并没有弹出调试信息,看来吾爱的OD已经绕过去了,没办法,使用原版OD再来测试以下,如下图:

上图可以看出,TLS已经先调用来检测是否被调试。
0x03 使用汇编来编写TLS反调试程序。
我们先编写一个没有调用TLS的程序,程序的功能很简单,就是弹出一个messageBox。
代码如下:
#include "windows.h"
void main()
{
MessageBoxA(NULL, "Hello :)", "main()", MB_OK);
}
编译生成Hello.exe。运行如下图:

下面使用工具PEview和hexworkshop以及od来编写TLS程序。编写程序前,先用PEview来查看没有使用TLS的文件结构。我们在IMAGE_OPTIONAL_HEADER.DIRECTORY[9]找到TLS选项。如下图:

两个值都是零。添加TLS步骤如下:
1)选址,一共有三个地址可选,最后段尾,段中的空白区域或者新建分段。我们就选择在段尾插入一块空白区域,来实现。下图为最后段.rsrc分段信息:

由上图可以知道整个文件大小为9200。我们将.rsrc段增到400h,即在91ff处插入200h空白区域。如下图:



记得把.rsrc的大小改为400h,并把属性改为可读写,可执行即E00060。
2)修改IMAGE_OPTIONAL_HEADER.DIRECTORY[9]D的TLS的地址和大小。如下图:

地址RVA=C200就是fileoffset=9200处大小为18h。
3)编写IMAGE_TLS_DIRECTORY结构体。
IMAGE_TLS_DIRECTORY结构的定义如下:
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
PDWORD AddressOfIndex;
PIMAGE_TLS_CALLBACK *AddressOfCallBacks;
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32;
其中PIMAGE_TLS_CALLBACK如下:
typedef VOID
(NTAPI *PIMAGE_TLS_CALLBACK) (
PVOID DllHandle,
DWORD Reason,
PVOID Reserved
);
编写的位置就是刚刚插入的位置即9200处,如下图:

这里函数就先写一个无功能的RERN 0C 具体的汇编指令要等到保存后使用OD编写。
4)使用OD编写具体的汇编指令
如果没有编写错误的话,od载入修改后的程序将会停在40c230处,因为程序先运行TLS,而40c230正是我们编写的TLS函数的EP处。
将下列代码填入以40c230开始处:
0040C230 837C24 08 01 cmp dword ptr ss:[esp+0x8],0x1
0040C235 75 28 jnz short 0040C25F
0040C237 64:A1 30000000 mov eax,dword ptr fs:[0x30]
0040C23D 8078 02 00 cmp byte ptr ds:[eax+0x2],0x0
0040C241 74 1C je short 0040C25F
0040C243 6A 00 push 0x0
0040C245 68 70C24000 push 0x40C270 ; ASCII "TLS Callback"
0040C24A 68 80C24000 push 0x40C280 ; ASCII "DebuggerDetected!"
0040C24F 6A 00 push 0x0
0040C251 FF15 E8804000 call dword ptr ds:[0x4080E8]
0040C257 6A 01 push 0x1
0040C259 FF15 28804000 call dword ptr ds:[0x408028]
0040C25F C2 0C00 retn 0xC
0040C262 90 nop
如下图:

保存修改,至此完成。
5)载入原版的od。如下图:

编写的TLS反调试起作用了。
TLS回调函数以及反调试简单使用的更多相关文章
- 通过TLS回调函数的反调试
下面是TLS数据结构的定义 typedef struct _IMAGE_TLS_DIRECTORY { DWORD StartAddressOfRawData; DWORD EndAddressOfR ...
- TLS回调函数
@author: dlive TLS (Thread Local Storage 线程局部存储 )回调函数常用于反调试. TLS回调函数的调用运行要先于EP代码执行,该特性使它可以作为一种反调试技术使 ...
- 《逆向工程核心原理》——TLS回调函数
pe中TLS(thread local storage)中函数的执行时机早于入口函数(entry point), 相关结构: // // Thread Local Storage // typedef ...
- 基于TLS的反调试技术
TLS(Thread Local Storage 线程局部存储) 一个进程中的每个线程在访问同一个线程局部存储时,访问到的都是独立的绑定于该线程的数据块.在PEB(进程环境块)中TLS存储槽共64个( ...
- WIN10 X64下通过TLS实现反调试
目录(?)[-] TLS技术简介 1 TLS回调函数 2 TLS的数据结构 具体实现及原理 1 VS2015 X64 release下的demo 2 回调函数的具体实现 21 使用IsDebugger ...
- TLS反调试
0x01 TLS反调试简介 TLS(Thread Local Storage)原本的目的是解决多线程程序中变量同步的问题.线程本身有独立于其他线程的栈空间,因此线程中的局部变量不用考虑同步问题.多线程 ...
- C++ 回调函数简单示例
回调函数其实就是以函数指针做函数参数传递给另一个函数,在另一个函数执行的时候可以根据函数指针执行回调函数的代码.简单示例,便于理解,防止遗忘. #include <iostream> ty ...
- C/C++ 程序反调试的方法
C/C++ 要实现程序反调试有多种方法,BeingDebugged,NtGlobalFlag,ProcessHeap,CheckRemoteDebuggerPresent,STARTUPINFO,Is ...
- Windows反调试技术(下)
OD的DBGHELP模块 检测DBGHELP模块,此模块是用来加载调试符号的,所以一般加载此模块的进程的进程就是调试器.绕过方法也很简单,将DBGHELP.DLL改名. #include <Wi ...
随机推荐
- iOS -实现imageView中的button响应点击事件的方法
<pre name="code" class="cpp" style="font-size: 13px;">/** imagev ...
- 基于华为云IOT及无线RFID技术的智慧仓储解决方案最佳实践系列一
[摘要]仓储管理存在四大细分场景:出入库管理.盘点.分拣和货物跟踪.本系列将介绍利用华为云IOT全栈云服务,端侧采用华为收发分离式RFID解决方案,打造端到端到IOT智慧仓储解决方案的最佳实践. 仓储 ...
- Logstash下字段以及嵌套Json字段类型转换
前言 从filebeat传输到Logstash的数据,某个字段需要由string类型装换成float类型.但是不管怎么改logstash的配置文件都不生效,其实官方文档都有,但是具体细节方面的东西就得 ...
- HashMap(三)之源码分析
通过分析HashMap来学习源码,那么通过此过程我们要带着这几个问题去一起探索 为什么要学习源码 怎么去学习 0.1 为什么要学习源码 这个问题,直接给出结论,学习源码肯定是有好处的,比如: 学习优秀 ...
- 【php】 jsonp转数组函数jsonp_decode
分享一个可以跟json一样用的函数jsonp_decode,能把jsonp格式数据转为php数组或对象. /** * 把jsonp转为php数组 * @param string $jsonp js ...
- 使用nodejs的puppeteer库爬取瓜子二手车网站
const puppeteer = require('puppeteer'); (async () => { const fs = require("fs"); const ...
- Winner Winner【模拟、位运算】
Winner Winner 题目链接(点击) 题目描述 The FZU Code Carnival is a programming competetion hosted by the ACM-ICP ...
- 什么是Galil(加利尔)运动控制卡,它是用来干嘛的呢?galil开发文件dmc32.dll,动态链接库,API
什么是Galil(加利尔)运动控制卡,它是用来干嘛的呢?运动控制卡是基于PC总线,利用高性能微处理器(如 DSP)及大规模可编程器件实现多个伺服电机的多轴协调控制的一种高性能的步进/伺服电机运动控制卡 ...
- phpstorm设置xdebug调试
phpstorm设置xdebug调试# wamp开发环境安装完成以后,打开网页,输入 :localhost 检测xdebug是否开启 3.若xdebug已开启,请找到你wamp或者phpstudy的安 ...
- 入门大数据---Spark_Transformation和Action算子
一.Transformation spark 常用的 Transformation 算子如下表: Transformation 算子 Meaning(含义) map(func) 对原 RDD 中每个元 ...