re_base2

  1. TLS回调
    1. TLS结构体:
  2. 调试TLS回调函数
  • keyboard hooking
    1. 调试hook
  • TLS and keyboard hooking

    TLS回调

    TLS(Thread Local Storage) 线程本地存储,主要用于提供本地线程访问局部数据的句柄。

    下面来一段官方解释:

    使用线程本地存储 (TLS),可以为进程可以使用全局索引访问的每个线程提供唯一数据。一个线程分配索引,其他线程可以使用它来检索与索引关联的唯一数据

    原理图:

    image-20221221220303818

    ==TLS主要用于程序的反调试检测==

    它在运行EP(entrypoint 程序入口代码)代码前执行

    TLS结构体:

    image-20221221220612843

    可以看到TLS结构体中保存着回调函数的地址

    可以打开CFF explore软件直接查看存放TLS回调函数数组的地址

    image-20221221221105861

    打开od查看它的地址为401000

    image-20221221221841170

    汇编直接定位过去就出来了

    image-20221221221931378

    TLS回调函数是在进程或线程创建或终止时调用,如果某个进程存在一个线程,那从程序的创建到销毁需要4次调用回调函数

    TLS回调函数定义:

    image-20221221222309417

    注册TLS程序示例代码:

    #include <windows.h>
    
    #pragma comment(linker, "/INCLUDE:__tls_used")
    
    void print_console(char* szMsg)
    {
        HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    
        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);
    }
    
    #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;
    }
    
    调试TLS回调函数

    直接在运行前设置调试选项

    image-20221221222931558

    添加TLS这里不做阐述


    keyboard hooking

    消息钩取工作原理

    image-20221222102644542

    使用SetWindowsHookEx()可以实现消息钩子,钩子函数需要存在于dll文件中,由操作系统调用回调函数

    API定义:

    image-20221222103023824

    在某进程拥有消息,操作系统就会将dll注入进程序中,实现消息钩取

    只需要一个程序加载安装钩子所在的dll程序,然后SetWindowsHookEx()注册钩子,操作系统就会检测每一个程序的行为,一旦发生键盘输入,就将dll文件注入。

    加载dll注册hook实例:

    #include "stdio.h"
    #include "conio.h"
    #include "windows.h"
    
    #define	DEF_DLL_NAME		"KeyHook.dll"
    #define	DEF_HOOKSTART		"HookStart"
    #define	DEF_HOOKSTOP		"HookStop"
    
    typedef void (*PFN_HOOKSTART)();
    typedef void (*PFN_HOOKSTOP)();
    
    void main()
    {
        HMODULE			hDll = NULL;
        PFN_HOOKSTART	HookStart = NULL;
        PFN_HOOKSTOP	HookStop = NULL;
        char			ch = 0;
    
        // KeyHook.dll 肺爹
        hDll = LoadLibraryA(DEF_DLL_NAME);
        if( hDll == NULL )
        {
            printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
            return;
        }
    
        // export 窃荐 林家 掘扁
        HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
        HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
    
        // 饶欧 矫累
        HookStart();
    
        // 荤侩磊啊 'q' 甫 涝仿且 锭鳖瘤 措扁
        printf("press 'q' to quit!\n");
        while( _getch() != 'q' )	;
    
        // 饶欧 辆丰
        HookStop();
        
        // KeyHook.dll 攫肺爹
        FreeLibrary(hDll);
    }
    

    dll代码:

    #include "stdio.h"
    #include "windows.h"
    
    #define DEF_PROCESS_NAME		"notepad.exe"
    
    HINSTANCE g_hInstance = NULL;
    HHOOK g_hHook = NULL;
    HWND g_hWnd = NULL;
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
    {
        switch( dwReason )
        {
            case DLL_PROCESS_ATTACH:
                g_hInstance = hinstDLL;
                break;
    
            case DLL_PROCESS_DETACH:
                break;	
        }
    
        return TRUE;
    }
    
    LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        char szPath[MAX_PATH] = {0,};
        char *p = NULL;
    
        if( nCode >= 0 )
        {
            // bit 31 : 0 => press, 1 => release
            if( !(lParam & 0x80000000) )
            {
                GetModuleFileNameA(NULL, szPath, MAX_PATH);
                p = strrchr(szPath, '\\');
    
                // 泅犁 橇肺技胶 捞抚阑 厚背秦辑 父距 notepad.exe 扼搁 0 酒囱 蔼阑 府畔窃
                // => 0 酒囱 蔼阑 府畔窍搁 皋矫瘤绰 促澜栏肺 傈崔登瘤 臼澜
                if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
                    return 1;
            }
        }
    
        // 老馆利牢 版快俊绰 CallNextHookEx() 甫 龋免窍咯
        //   览侩橇肺弊伐 (趣篮 促澜 扰) 栏肺 皋矫瘤甫 傈崔窃
        return CallNextHookEx(g_hHook, nCode, wParam, lParam);
    }
    
    #ifdef __cplusplus
    extern "C" {
    #endif
        __declspec(dllexport) void HookStart()
        {
            g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
        }
    
        __declspec(dllexport) void HookStop()
        {
            if( g_hHook )
            {
                UnhookWindowsHookEx(g_hHook);
                g_hHook = NULL;
            }
        }
    #ifdef __cplusplus
    }
    #endif
    
    调试hook

    用字符串搜索直接定位到main函数

    image-20221222110634215

    上面得到hookStart地址,然后call,F7直接步入就能进入hookstart函数

    image-20221222111024672

    这里就是dll中注册键盘hook

    直接根据注册时得到的地址进行跳转就能得到hook函数

    image-20221222111311777

    也可以打开notepad,进行附加attach,先摁F9,让hook注册成功

    然后在新加一个模块处停止,option设置

    image-20221222111708005

    附加后在键盘输入字母,od会立马断住。


    转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jaytp@qq.com

    ×

    喜欢就点赞,疼爱就打赏