进程注入Process Injection之Thread Local Storage——常用在恶意软件反调试,在OEP前检测是否有调试器存在进而exit程序
线程本地存储
攻击者可能会通过线程本地存储 (TLS) 回调将恶意代码注入进程,以逃避基于进程的防御以及可能提升的权限。TLS 回调注入是一种在单独的实时进程的地址空间中执行任意代码的方法。
TLS 回调注入涉及操作可移植可执行文件 (PE) 内的指针,以在到达代码的合法入口点之前将进程重定向到恶意代码。操作系统通常使用 TLS 回调来设置和/或清理线程使用的数据。可以通过使用其他进程注入技术(例如 Process Hollowing)分配和写入进程内存空间内的特定偏移量来操作 TLS 回调。
在另一个进程的上下文中运行代码可能允许访问该进程的内存、系统/网络资源以及可能提升的权限。通过 TLS 回调注入执行,因为执行被隐藏在合法进程下。
例子
线程本地存储回调是 Windows 加载程序提供的机制,允许程序在进程启动时执行特定于线程的初始化任务。TLS 回调的有趣之处在于它们在应用程序的入口点之前执行,即在 main() 函数之前执行。由于以下几个原因,这是有问题的:
· 调试器通常停在主函数处,因此会丢失任何额外的 TLS 代码
· 反汇编程序和静态分析工具首先呈现主要功能,再次导致可能隐藏的代码。
要使用它们,我们需要声明原型
回调定义如下:
现在让我展示示例代码
我们在上面看到我们声明了 TLS Callback,下面有我们的 main 函数,一旦执行,main 函数将永远不会被执行,因为 TLSCallback 声明中的 ExitProcess() ,当然我们可以删除它并将其打印在控制台中,但这里将仅演示 TLS,然后让它按照预期的方式终止。
一旦我们点击“确定”,该过程就会按预期终止
我们在上面看到代码的主要部分,它只是应该在控制台中打印一些文本,但没有执行,因为我们的 TLS 回调是在到达主要部分之前第一个执行并终止进程的
在某些示例中,这些 TLSCallbacks 甚至在加载到调试器之前就会执行
代码如下:
#include "windows.h" #include <stdio.h> void NTAPI __stdcall TLSCallbacks(PVOID DllHandle, DWORD dwReason, PVOID Reserved); //linker spec #ifdef _M_IX86 #pragma comment (linker, "/INCLUDE:__tls_used") #pragma comment (linker, "/INCLUDE:__tls_callback") #else #pragma comment (linker, "/INCLUDE:_tls_used") #pragma comment (linker, "/INCLUDE:_tls_callback") #endif EXTERN_C #ifdef _M_X64 #pragma const_seg (".CRT$XLB") const #else #pragma data_seg (".CRT$XLB") #endif //end linker //tls import PIMAGE_TLS_CALLBACK _tls_callback = TLSCallbacks; #pragma data_seg () #pragma const_seg () //end // tls declaration void NTAPI __stdcall TLSCallbacks(PVOID DllHandle, DWORD dwReason, PVOID Reserved) { MessageBox(nullptr, L"TLS Callback before main :)", L"dZkyXj - Debugger Owned!", 0); ExitProcess(0); } // end declaration int main(int argc, char* argv[]) { printf("wow i'm main function but i'll never be executed :("); }
实际效果:
不会执行到main!
补充下:
1、TLS回调函数的调用时机:
TLS回调其实是再process启动、停止,dll attach、detach的时候都会执行,参考《逆向工程核心原理:第45章 TLS回调函数》 如下部分说明:
2、正是因为可以在main OEP执行前执行,所以可以利用它来检测是否有调试器存在,进而达到反调试的效果。例如利用peb里的调试标识检测调试器:
3、如何调试tls:
以之前的例子为例,xdb里调试看到的,xdb比较友好,可直接跳到tls callback,如下:
callstack:
如果是正常执行到main(上面的代码remove exit部分),则callstack长成这样:
4、如何查看一个PE文件是否存在可疑的TLS反调试呢?如下:
我们实践下吧,针对前面的tls生成的代码,放到IDA里去看看:可直接看到tls的回调
所以要验证是否为tls反调试还是比较方便的!
如果要检测tls,用以下思路:
1、PE中有tls和没有tls的差别:
2、运行时加hook,在这里:
list = 。。。
for (in list ...) {
module = get_module();
func = get_func();
ImageTlsCallbackCaller(module, 1, func);
}
就可确定是否有可疑的tls回调了!