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回调函数以及反调试简单使用的更多相关文章

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

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

  2. TLS回调函数

    @author: dlive TLS (Thread Local Storage 线程局部存储 )回调函数常用于反调试. TLS回调函数的调用运行要先于EP代码执行,该特性使它可以作为一种反调试技术使 ...

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

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

  4. 基于TLS的反调试技术

    TLS(Thread Local Storage 线程局部存储) 一个进程中的每个线程在访问同一个线程局部存储时,访问到的都是独立的绑定于该线程的数据块.在PEB(进程环境块)中TLS存储槽共64个( ...

  5. WIN10 X64下通过TLS实现反调试

    目录(?)[-] TLS技术简介 1 TLS回调函数 2 TLS的数据结构 具体实现及原理 1 VS2015 X64 release下的demo 2 回调函数的具体实现 21 使用IsDebugger ...

  6. TLS反调试

    0x01 TLS反调试简介 TLS(Thread Local Storage)原本的目的是解决多线程程序中变量同步的问题.线程本身有独立于其他线程的栈空间,因此线程中的局部变量不用考虑同步问题.多线程 ...

  7. C++ 回调函数简单示例

    回调函数其实就是以函数指针做函数参数传递给另一个函数,在另一个函数执行的时候可以根据函数指针执行回调函数的代码.简单示例,便于理解,防止遗忘. #include <iostream> ty ...

  8. C/C++ 程序反调试的方法

    C/C++ 要实现程序反调试有多种方法,BeingDebugged,NtGlobalFlag,ProcessHeap,CheckRemoteDebuggerPresent,STARTUPINFO,Is ...

  9. Windows反调试技术(下)

    OD的DBGHELP模块 检测DBGHELP模块,此模块是用来加载调试符号的,所以一般加载此模块的进程的进程就是调试器.绕过方法也很简单,将DBGHELP.DLL改名. #include <Wi ...

随机推荐

  1. SpringBoot 2.3 整合最新版 ShardingJdbc + Druid + MyBatis

    今天项目不忙,想搞一下shardingJDBC分库分表看看,主要想实现以下几点: 舍弃xml配置,使用.yml或者.properties文件+java的方式配置spring. 使用 Druid 作为数 ...

  2. Python 读取和输出到txt

    读txt文件 python常用的读取文件函数有三种read().readline().readlines() read() #一次性读取文本中全部的内容,以字符串的形式返回结果 with open(& ...

  3. Spting:基于注解的组件化管理

    @Component,@Controller(控制层),@Service(业务层),@Repository(持久层) 以上四个注解的功能完全相同,不过在实际开发中,要在不同功能的类上加上响应的注解 1 ...

  4. @gym - 100958J@ Hyperrectangle

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 给定一个大小为 \(l_1\times l_2 \dots l_ ...

  5. Quartz.Net系列(二):介绍、简单使用、对比Windows计划任务

    1.介绍 Quartz是功能强大的开源作业调度库,几乎可以集成到任何Java应用程序中-从最小的独立应用程序到最大的电子商务系统.Quartz可用于创建简单或复杂的计划,以执行数以万计,数以万计的工作 ...

  6. shutil模块的使用

    shutil模块 高级的文件,文件夹,压缩包处理模块 shutil.copyfileobj(fsrc,fdst,length) 将文件内容拷贝到另外一个文件中,可以部分.fdst目标length长度( ...

  7. Jmeter(十一) - 从入门到精通 - JMeter逻辑控制器 - 下篇(详解教程)

    1.简介 Jmeter官网对逻辑控制器的解释是:“Logic Controllers determine the order in which Samplers are processed.”. 意思 ...

  8. demo项目开发(Python+flask+mysql+redis只包含后端接口)

    [demo项目开发需求] 用户信息管理,可以注册.登录.添加用户.删除用户 注册:任何用户可以注册,对用户提交的注册信息进行校验,返回对应的信息,其中: 用户名:必填,唯一 密码:必填,只能6-12位 ...

  9. WeChair项目Alpha冲刺(2/10)

    团队项目进行情况 1.昨日进展    Alpha冲刺第二天 昨日进展: 前端完成小程序首页的html+css设计 后端springboot项目搭建完成 详情参见github 数据库也初步建成一些表格, ...

  10. 三文搞懂学会Docker容器技术(中)

    接着上面一篇:三文搞懂学会Docker容器技术(上) 6,Docker容器 6.1 创建并启动容器 docker run [OPTIONS] IMAGE [COMMAND] [ARG...] --na ...