• 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 ", 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 ", 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 ");

    print_consoleA("ThreadProc() end ");

    return 0;

    }

    int main()

    {

    HANDLE hThread = NULL;

    print_consoleA("main() start ");

    hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

    WaitForSingleObject(hThread, 60 * 1000);

    CloseHandle(hThread);

    print_consoleA("main() end ");

    }

    编译程序,记住要在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文件。我们将其拖入吾爱的odf9运行。如下图:

    我们发现并没有弹出调试信息,看来吾爱的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反调试起作用了。

  • 相关阅读:
    手机浏览器的viewport(视觉窗口)
    google开源了google chrome android
    Yii 直接执行SQL语句(转)
    WebKit学习网址收集
    Yii CDbCriteria的常用方法
    现货黄金入门知识普及一:图形分析之K线理论
    java 获取当前函数名
    yii url生成
    android 判断屏幕是否关闭
    yii yiiplayground
  • 原文地址:https://www.cnblogs.com/2f28/p/10051042.html
Copyright © 2020-2023  润新知