吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8575|回复: 20
收起左侧

[调试逆向] 【原创】反调试实战系列二 TLS反调试+CheckRemoteDebuggerPresent原理

  [复制链接]
lyl610abc 发表于 2021-8-8 20:30
本帖最后由 lyl610abc 于 2021-8-8 20:31 编辑

系列

【原创】反调试实战系列一 x64dbg+IDA 过IsDebuggerPresent

【原创】反调试实战系列二 TLS反调试+CheckRemoteDebuggerPresent原理


前言

在反调试系列开篇中介绍了简单的反调试的手段:IsDebuggerPresent,这次继续介绍常用的反调试手段:TLS反调试

PS:在第一篇反调试实战中是通过不断创建线程来检测当前进程是否被调试,而使用TLS反调试则无需我们自己去不断创建线程来进行检测,因为TLS也是基于线程的


TLS反调试

什么是TLS

官方版

给出来源于官方的文档释义:Thread Local Storage (TLS) | Microsoft Docs

TLS全称Thread Local Storage,即线程局部存储。TLS是一种方法,通过这种方法,给定多线程进程中的每个线程可以分配位置来存储特定于线程的数据。通过TLS API (TlsAlloc)支持动态绑定(运行时)特定于线程的数据。Win32和Microsoft c++编译器现在除了现有的API实现外,还支持静态绑定(加载时)每个线程数据。

通俗版

抓住官方版中的关键词:方法、存储特定于线程数据、绑定

方法

TLS是一种编程方法

存储特定于线程的数据

TLS使用静态(static)或全局局部内存(global memory local)对线程数据进行存储,TLS变量是使用(GS/FS)J扩展段寄存器访问的,而非DS数据段寄存器(有关段寄存器可回顾:保护模式笔记二 段寄存器

PS:是不是还是有点懵逼,不要紧,这部分释义大致看看就好,后面才是重点

绑定

绑定分为动态绑定和静态绑定

动态绑定

给出动态绑定官方文档:Using Thread Local Storage - Win32 apps | Microsoft Docs

所谓动态绑定就是使用:TlsAllocTlsSetValueTlsGetValueTlsFree四个API对创建的线程进行绑定

由于本次的TLS反调试并没有用到动态绑定,并且官方给出的文档还附有实例,故这里不再赘述,有兴趣的小伙伴可以自行研究= ̄ω ̄=

静态绑定

静态绑定则是本次反调试所用到的方法,在之后会具体说明


TLS的意义

TLS主要是为了解决多线程中变量的同步问题

进程中的全局变量和函数内定义的静态(static)变量,是每个线程都可以访问的共享变量。只要有任何一个线程修改了共享变量,其他所有线程中的共享变量也会同步被修改

乍看之下,这种方式使得数据交换十分的便捷,无需额外的通信就可以实现数据之间的交换。但命运赠送的礼物,早已在暗中标好了价格( $ _ $ )。这里的价格就是多线程访问时所耗费的同步开销

比如当共享变量要修改前,需要对该变量上锁,其他要访问该共享变量的线程必须等共享变量修改完成释放锁后才能继续执行。这期间线程等待所耗费的资源就是所谓的开销。关于同步异步、并发并行、互斥信号量等基础知识这里不再赘述


TLS变量

TLS变量即:同一个线程里面调用的各个函数都可以访问、但其他线程无法访问的变量(被称为static memory local to a thread 线程局部静态变量)

举个例子:线程A 修改TLS变量后线程B中的TLS变量不受影响,因为每个线程中都有一个TLS变量副本

TLS变量实例

TLS变量的声明

_declspec(thread) int global=0x610;

TLS变量演示代码

#include <Windows.h>
#include <stdio.h>
//声明TLS变量
_declspec(thread) int global = 0x610;
//线程1,修改TLS变量的值,并输出修改后的结果
DWORD WINAPI threadFunc1(LPVOID lpThreadParameter) {
    global = 0x666;
    printf("global value set to %x\n", global);
    return 0;
}
//线程2,在线程1后执行,输出TLS变量
DWORD WINAPI threadFunc2(LPVOID lpThreadParameter) {
    //让线程2进入睡眠状态,让出执行机会给线程1,以此确保线程1先执行
    Sleep(500);
    printf("global value is %x\n", global);
    return 0;
}
int main()
{  
    HANDLE hThread1=CreateThread(NULL, NULL, threadFunc1, NULL, NULL, NULL);
    HANDLE hThread2 = CreateThread(NULL, NULL, threadFunc2, NULL, NULL, NULL);
    system("pause");
}

TLS演示结果

image-20210726202808017

验证了TLS变量在线程1中被修改,但在线程2中并没有受到影响


TLS静态绑定

所谓的TLS静态绑定主要体现在TLS回调函数

TLS回调函数

官方文档:PE Format - Win32 apps | Microsoft Docs

程序可以提供一个或多个TLS回调函数,以支持TLS数据对象的附加初始化和终止

尽管通常只有一个回调函数,但回调函数是作为数组实现的,以便在需要时可以添加额外的回调函数

如果有多个回调函数,则按其地址在数组中出现的顺序调用每个函数。空指针终止数组。空列表是完全有效的(不支持回调),在这种情况下,回调数组只有一个成员—— null ptr(空指针)


TLS回调函数的使用

下面关于TLS回调函数的使用参考了:c++ - about TLS Callback in windows - Stack Overflow

要在C/C++ 中使用TLS函数步骤如下:

  1. 编译器声明使用TLS
  2. 定义TLS回调函数
  3. 注册TLS回调函数

编译器声明

通过下列代码告诉编译器要使用TLS

#ifdef _WIN64       //64位
     #pragma comment (linker, "/INCLUDE:_tls_used")  
     #pragma comment (linker, "/INCLUDE:tls_callback_func") 
#else               //32位
     #pragma comment (linker, "/INCLUDE:__tls_used") 
     #pragma comment (linker, "/INCLUDE:_tls_callback_func")
#endif

注意64位和32位的TLS编译器声明不同,所以上述代码使用了条件编译


定义TLS回调函数

TLS回调函数的定义如下:

typedef VOID
(NTAPI *PIMAGE_TLS_CALLBACK) (
    PVOID DllHandle,
    DWORD Reason,
    PVOID Reserved
    );

数据类型 数据含义
参数1 PVOID 指向DLL本身的实例句柄
参数2 DWORD 调用原因
参数3 PVOID 保留,固定为0

编写过DLL的同学们不难发现它与DLL的入口点函数:DllMain相同

这里给出第二个参数调用原因的含义:

宏定义 描述
DLL_PROCESS_ATTACH 1 一个新进程已经启动,包括第一个线程
DLL_THREAD_ATTACH 2 创建了一个新线程。此通知已发送给除第一个线程外的所有线程
DLL_THREAD_DETACH 3 线程即将被终止。此通知已发送给除第一个线程外的所有线程
DLL_PROCESS_DETACH 0 进程即将终止,包括原始线程

注册TLS回调函数

#ifdef _WIN64                           //64位
    #pragma const_seg(".CRT$XLF")
    EXTERN_C const
#else
    #pragma data_seg(".CRT$XLF")        //32位
    EXTERN_C
#endif
PIMAGE_TLS_CALLBACK tls_callback_func[] = { tls_callback,0 };
#ifdef _WIN64                           //64位
    #pragma const_seg()
#else
    #pragma data_seg()                  //32位
#endif //_WIN64

注意这里也要使用条件编译来区别对待64位和32位

const_segdata_seg都带了个后缀seg,即segment(段),分别用来指定const段和data段,这部分内容暂时不深入,以后有机会再说(挖坑o(一︿一+)o)

想了解的可参考:

const_seg pragma | Microsoft Docs

data_seg pragma | Microsoft Docs

再来说说括号中的内容:".CRT$XLF"的含义:

CRT表示使用C Runtime 机制

X表示 标识名随机

L表示 TLS Callback section

F也可以替换成B~Y的任意一个字符


TLS回调函数实例

#include <stdio.h>
#include<windows.h>
//编译器声明使用TLS
#ifdef _WIN64       //64位
#pragma comment (linker, "/INCLUDE:_tls_used")  
#pragma comment (linker, "/INCLUDE:tls_callback_func") 
#else               //32位
#pragma comment (linker, "/INCLUDE:__tls_used") 
#pragma comment (linker, "/INCLUDE:_tls_callback_func")
#endif

//定义TLS回调函数
void NTAPI tls_callback(PVOID Dllhandle, DWORD Reason, PVOID Reserved) {
    switch (Reason)
    {
    case DLL_PROCESS_ATTACH: {
        printf("DLL_PROCESS_ATTACH \n");
        break;
    }
    case DLL_THREAD_ATTACH: {
        printf("DLL_THREAD_ATTACH\n");
        break;
    }
    case DLL_THREAD_DETACH: {
        printf("DLL_THREAD_DETACH\n");
        break;
    }
    case DLL_PROCESS_DETACH: {
        printf("DLL_PROCESS_DETACH\n");
        break;
    }
    default:
        break;
    }
}

#ifdef _WIN64                           //64位
#pragma const_seg(".CRT$XLF")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLF")        //32位
EXTERN_C
#endif
PIMAGE_TLS_CALLBACK tls_callback_func[] = { tls_callback,0 };
#ifdef _WIN64                           //64位
#pragma const_seg()
#else
#pragma data_seg()                  //32位
#endif //_WIN64

DWORD WINAPI threadFunc(LPVOID param) {
    while (true) {
        printf("线程运行\n");
        Sleep(200);
    }
    return 0;
}

int main() {
    CreateThread(0, 0, threadFunc, 0, 0, 0);
    while (true)
    {
        Sleep(-1);
    }
}

代码说明

代码看似长,但其实很简单,就是注册了TLS函数,TLS函数输出调用原因

main函数里则创建一个线程,然后主线程(执行main函数的线程)不断Sleep,防止主程序执行完毕后退出


运行结果

image-20210808194249798

可以看到先是主进程加载,然后是线程加载(main函数里的那个CreateThread),最后才是线程运行

也就是使用TLS回调函数后,每个进程/线程 的 启动/退出 都会调用到TLS回调函数,于是在TLS回调函数中检测当前程序是否被调试即可


TLS反调试实例

TLS反调试在了解了TLS回调函数后就十分简单,只需要在TLS回调函数中检测调试就行了

将前面TLS回调函数实例中的TLS回调函数替换成如下即可实现反调试:

void NTAPI tls_callback(PVOID Dllhandle, DWORD Reason, PVOID Reserved) {
    BOOL ret;
    CheckRemoteDebuggerPresent(GetCurrentProcess(), &ret);
    if (ret) {
        ExitProcess(0);
    }   
}

稍微介绍一下CheckRemoteDebuggerPresent这个检测,其检测原理和IsDebuggerPresent差不多,IsDebuggerPresent【原创】反调试实战系列一 x64dbg+IDA 过IsDebuggerPresent 中已经说明,没看过的可以前往回顾

IsDebuggerPresent不同的是CheckRemoteDebuggerPresent可以检测其他进程的调试情况,关于IsDebuggerPresentCheckRemoteDebuggerPresent的检测原理会在后续补充,这里给出CheckRemoteDebuggerPresent的函数原型

BOOL CheckRemoteDebuggerPresent(
  HANDLE hProcess,
  PBOOL  pbDebuggerPresent
);
数据类型 数据说明
参数1 HANDLE 待检测的进程句柄
参数2 PBOOL 用于接收检测的结果
返回值 BOOL 返回检测的执行是否成功

TLS在PE文件中体现

待补充…………


DebuggerPresent检测原理

待补充…………


总结

本篇主要介绍了TLS以及TLS回调函数的使用,反调试在本篇中的占比相对较小

但新引入了CheckRemoteDebuggerPresent这个函数,并补充了IsDebuggerPresentCheckRemoteDebuggerPresent的检测原理,算是将Windows API中提供的两个用于检测是否被调试的函数讲完了

本次的内容也延申到了PE文件结构,旨在拓展各个知识的联系程度,希望能够对大家有所帮助( •̀ ω •́ )✧

PS:最近忙着社畜和自我充电,所以随缘佛系更新,很多大家的回复都没精力去一一细看和回复,这里先说声抱歉啦(>人<;)

如果有实在严重的问题(例如:文章内容有谬误,会给大家带来困扰之类的),可以私信我,指出问题后私信花的CB可以找我报销(ノω<。)ノ))☆.。

还有本人暂时没有精力接单,不用私信我了,目前还是以分享知识和个人成长为主(^^ゞ

免费评分

参与人数 15吾爱币 +22 热心值 +14 收起 理由
Nattevak + 2 + 1 我很赞同!
国际豆哥 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
sam喵喵 + 1 谢谢@Thanks!
debug_cat + 2 + 1 热心回复!
nnicetry + 1 + 1 我很赞同!
lllliiii + 1 + 1 我很赞同!
努力加载中 + 1 + 1 谢谢@Thanks!
温柔的一哥 + 1 + 1 我很赞同!
zhczf + 1 我很赞同!
Chenda1 + 1 + 1 谢谢@Thanks!
hjie + 1 + 1 用心讨论,共获提升!
侃遍天下无二人 + 3 + 1 小柠檬教教我py
E式丶男孩 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
XhyEax + 3 + 1 热心回复!
正己 + 3 + 1 先赞再说

查看全部评分

本帖被以下淘专辑推荐:

  • · Aarow|主题: 991, 订阅: 304

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

IBinary 发表于 2021-8-10 09:52
总结下,就是定义了一个表. 表里面有回调. 回调函数里面就是编写我们的检测功能.  更甚者可以触发异常自己来处理. (没尝试)
Tls 局部存储 之前写的,大家可以看看

免费评分

参与人数 1吾爱币 +3 热心值 +1 收起 理由
lyl610abc + 3 + 1 我很赞同!

查看全部评分

jbd666888 发表于 2021-8-22 20:48
hi,这是我用百度网盘分享的文件~复制这段内容打开「百度网盘」APP即可获取。
链接:https://pan.baidu.com/s/1fhnUbMUA3jLWHnlnW4dC2Q
提取码:7Mj7 求更新,无法使用了
侃遍天下无二人 发表于 2021-8-8 23:26
温柔的一哥 发表于 2021-8-9 16:56
感谢分享!辛苦了~吾爱有你更精彩!期待楼主分享更多好文章,好技术!
努力加载中 发表于 2021-8-9 17:20
好东西感谢大佬
cjc3528 发表于 2021-8-10 06:09
太厉害了,膜拜大神啊
sunnyAlvis 发表于 2021-8-10 18:22
学习了很多加油
国际豆哥 发表于 2021-9-15 23:31
久违的上线看看我的好兄弟顺便打上一波评分
11987GENIUS 发表于 2022-6-6 15:39
牛啊牛啊
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-3-28 20:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表