TLS

线程局部存储,TLS

image-20230214145111183

如何使用TLS

有静态方法和动态方法

image-20230215013708036

静态方法 把全局变量数据类型前添加 __declspec(thread) 关键字,同一变量在不同的线程中各自使用自己的那一份,不同的线程间不会互相影响

使用这种方法,生成的PE文件在节头中会有.tls头

动态方法 存放索引 使用一个普通的全局变量,在创建线程前创建一个独立的索引 __ v2=TlsAlloc(),根据索引设置数组的值 TlsSetValue( __ v2, 0) ,使用时使用a = TlsGetValue(__ v2)获取

理论上没有.tls头出现,但是测试失败

TLS回调

image-20230215013255890

image-20230215013357895

调用约定为NTAPI,即_stdcall

只要有线程启动或销毁会自动调用线程回调,比EP进程初始化还要早(比主函数启动还早),许多逆向分析人员把这个特性用于反调试

TLS目录

使用内存映射的方法进行分析 相当于系统自动申请内存,不用自己手动申请内存。获取内存映射句柄,使用MapViewOfFile获取虚拟地址位置

都是文件粒度对齐

对大文件操作效率更高

image-20230215014825488

将内存粒度转换为文件粒度

image-20230215015918708

image-20230215020146408

DumpTlsDirectory是自己实现的

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
void DumpTlsDirectory(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader, 
LPBYTE ImageBase, DWORD Offset)
{
BOOL IsWow64 = FALSE;
//判断PE文件的位数
if (!IsOptionalHeaderPE32Plus(ImageOptionalHeader, &IsWow64))
{
return;
}

if (IsWow64 == FALSE)
{
//x64
PIMAGE_TLS_DIRECTORY64 ImageTlsDirectory = (PIMAGE_TLS_DIRECTORY64)((DWORD_PTR)ImageBase + (DWORD_PTR)Offset);
_tprintf(_T("StartAddressOfRawData: %p \r\nEndAddressOfRawData: %p \r\n"),
ImageTlsDirectory->StartAddressOfRawData, ImageTlsDirectory->EndAddressOfRawData);

_tprintf(_T("AddressOfIndex: %p \r\nAddressOfCallBacks:%p \r\n"),
ImageTlsDirectory->AddressOfIndex, ImageTlsDirectory->AddressOfCallBacks);

_tprintf(_T("SizeOfZeroFill: %u \r\nCharacteristics: %p \r\n"),
ImageTlsDirectory->SizeOfZeroFill, ImageTlsDirectory->Characteristics);
}
else
{
PIMAGE_TLS_DIRECTORY32 ImageTlsDirectory = (PIMAGE_TLS_DIRECTORY32)((DWORD_PTR)ImageBase + (DWORD_PTR)Offset);
_tprintf(_T("StartAddressOfRawData: %p \r\nEndAddressOfRawData: %p \r\n"),
ImageTlsDirectory->StartAddressOfRawData, ImageTlsDirectory->EndAddressOfRawData);

_tprintf(_T("AddressOfIndex: %p \r\nAddressOfCallBacks:%p \r\n"),
ImageTlsDirectory->AddressOfIndex, ImageTlsDirectory->AddressOfCallBacks);

_tprintf(_T("SizeOfZeroFill: %u \r\nCharacteristics: %p \r\n"),
ImageTlsDirectory->SizeOfZeroFill, ImageTlsDirectory->Characteristics);
}

return;
}

完整代码

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
#include "pch.h"
void Sub_1(); //文件粒度
void Sub_2(); //内存粒度
BOOL RtlImageDirectoryEntryToData(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader, int DirectoryIndex, PIMAGE_DATA_DIRECTORY ImageDataDirectory);
DWORD Rva2Offset(DWORD Rva, UINT_PTR ImageBaseAddress);
BOOL IsOptionalHeaderPE32Plus(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader, BOOL* IsWow64);
void DumpTlsDirectory(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader,
LPBYTE ImageBase, DWORD Offset);
void DumpTlsDirectory2(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader, LPBYTE ImageBase, DWORD VirtualAddress);
LONGLONG AlignmentData(LONGLONG OperateLength, LONGLONG AlignmentLength);
BOOL IsPEFile(LPVOID ImageBase, PIMAGE_NT_HEADERS& ImageNtHeaders);
int _tmain(int argc, TCHAR* argv[],TCHAR *envp[])
{
setlocale(LC_ALL,"Chinese-simplified");


Sub_1(); //内存映射的方法分析TLS目录


Sub_2(); //文件读写的方法分析TLS目录

_tprintf(_T("Input AnyKey To Exit\r\n"));
_gettchar();

return 0;
}

void Sub_1()
{
TCHAR ImageName[MAX_PATH] = _T("Test.exe");
_tprintf(_T("ImageName: %s \n"), ImageName);

HANDLE FileHandle = NULL;
HANDLE MappingHandle = NULL;
LPVOID ImageBase = NULL;
PIMAGE_NT_HEADERS ImageNtHeaders = NULL;

DWORD Offset = 0;
IMAGE_DATA_DIRECTORY ImageDataDirectory = { 0 };

//打开文件并将其映射到内存中
FileHandle = CreateFile(ImageName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (FileHandle == INVALID_HANDLE_VALUE)
{
_tprintf(_T("CreateFile() Error:%d \r\n"), GetLastError());
goto Exit;
}
//根据文件句柄创建内存映射对象获得内存映射句柄
MappingHandle = CreateFileMapping(FileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
if (MappingHandle == NULL)
{
_tprintf(_T("CreateFileMapping() Error:%d \r\n"), GetLastError());
goto Exit;
}
ImageBase = MapViewOfFile(MappingHandle, FILE_MAP_READ, 0, 0, 0); //文件粒度对齐
if (ImageBase == NULL)
{
_tprintf(_T("MapViewOfFile() Error:%d \r\n"), GetLastError());
goto Exit;
}


//是否为有效的PE可执行文件
if (!IsPEFile(ImageBase, ImageNtHeaders))
{
_tprintf(_T("IsPEFile() Error:%d \r\n"), GetLastError());
goto Exit;
}



//查看TLS目录
if (!RtlImageDirectoryEntryToData(&ImageNtHeaders->OptionalHeader, IMAGE_DIRECTORY_ENTRY_TLS,&ImageDataDirectory))
{
_tprintf(_T("GetTlsDirectoryRVA() Error\r\n"));
goto Exit;
}

//将内存粒度转换成文件粒度
Offset = Rva2Offset(ImageDataDirectory.VirtualAddress, (UINT_PTR)ImageBase);

if (Offset<=0)
{
_tprintf(_T("Rva2Offset() Error\r\n"));
goto Exit;
}
//显示TLS目录信息
DumpTlsDirectory(&ImageNtHeaders->OptionalHeader, (LPBYTE)ImageBase, Offset);

Exit:

//关闭句柄
if (ImageBase != NULL)
{
UnmapViewOfFile(ImageBase);
ImageBase = NULL;
}
if (MappingHandle != NULL)
{
CloseHandle(MappingHandle);
MappingHandle = NULL;
}
if (FileHandle != INVALID_HANDLE_VALUE)
{

CloseHandle(FileHandle);
FileHandle = INVALID_HANDLE_VALUE;
}
_tprintf(_T("Please Input Anykey to Continue\r\n"));
_gettchar();
return;
}
BOOL IsPEFile(LPVOID ImageBase, PIMAGE_NT_HEADERS& ImageNtHeaders)
{
if (ImageBase == NULL)
{
return FALSE;
}

PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
return FALSE;
}
ImageNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)ImageDosHeader + (DWORD_PTR)ImageDosHeader->e_lfanew);
if (ImageNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
return FALSE;
}

return TRUE;
}
BOOL RtlImageDirectoryEntryToData(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader,int DirectoryIndex,PIMAGE_DATA_DIRECTORY ImageDataDirectory)
{
if (ImageOptionalHeader == NULL)
{
return FALSE;
}

BOOL IsWow64 = FALSE;
if (!IsOptionalHeaderPE32Plus(ImageOptionalHeader, &IsWow64))
{
return FALSE;
}

if (IsWow64==FALSE)
{

PIMAGE_OPTIONAL_HEADER64 ImageOptionalHeader64 = (PIMAGE_OPTIONAL_HEADER64)ImageOptionalHeader;
memcpy(ImageDataDirectory, &ImageOptionalHeader64->DataDirectory[DirectoryIndex], sizeof(IMAGE_DATA_DIRECTORY));

}
else
{
PIMAGE_OPTIONAL_HEADER32 ImageOptionalHeader32 = (PIMAGE_OPTIONAL_HEADER32)ImageOptionalHeader;
memcpy(ImageDataDirectory, &ImageOptionalHeader32->DataDirectory[DirectoryIndex], sizeof(IMAGE_DATA_DIRECTORY));
}

if ((ImageDataDirectory->VirtualAddress == 0) && (ImageDataDirectory->Size == 0))
{
//这个目录不存在
return TRUE;
}
else if ((ImageDataDirectory->VirtualAddress == 0) || (ImageDataDirectory->Size == 0))
{
//数据目录中的数据不一致
return FALSE;
}
else if (ImageDataDirectory->Size < (IsWow64 ? sizeof(IMAGE_TLS_DIRECTORY32) : sizeof(IMAGE_TLS_DIRECTORY64)))
{
//查看目录大小是否合理
return FALSE;
}

return TRUE;
}

DWORD Rva2Offset(DWORD Rva, UINT_PTR ImageBaseAddress)
{
WORD i = 0;
WORD NumberOfSections = 0;
PIMAGE_SECTION_HEADER ImageSectionHeader = NULL;

PIMAGE_NT_HEADERS ImageNtHeaders = (PIMAGE_NT_HEADERS)(ImageBaseAddress + ((PIMAGE_DOS_HEADER)ImageBaseAddress)->e_lfanew);
if (ImageNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) // PE32
{
PIMAGE_NT_HEADERS32 ImageNtHeaders32 = (PIMAGE_NT_HEADERS32)ImageNtHeaders;

ImageSectionHeader = (PIMAGE_SECTION_HEADER)((UINT_PTR)(&ImageNtHeaders32->OptionalHeader) + ImageNtHeaders32->FileHeader.SizeOfOptionalHeader);
NumberOfSections = ImageNtHeaders32->FileHeader.NumberOfSections;
}
else if (ImageNtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) // PE64
{
PIMAGE_NT_HEADERS64 ImageNtHeaders64 = (PIMAGE_NT_HEADERS64)ImageNtHeaders;
ImageSectionHeader = (PIMAGE_SECTION_HEADER)((UINT_PTR)(&ImageNtHeaders64->OptionalHeader) + ImageNtHeaders64->FileHeader.SizeOfOptionalHeader);
NumberOfSections = ImageNtHeaders64->FileHeader.NumberOfSections;
}
else
{
return 0;
}

if (Rva < ImageSectionHeader[0].PointerToRawData)
return Rva;

for (i = 0; i < NumberOfSections; i++)
{
if (Rva >= ImageSectionHeader[i].VirtualAddress && Rva < (ImageSectionHeader[i].VirtualAddress + ImageSectionHeader[i].SizeOfRawData))
{
return (Rva - ImageSectionHeader[i].VirtualAddress + ImageSectionHeader[i].PointerToRawData);
}
}

return 0;
}
BOOL IsOptionalHeaderPE32Plus(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader, BOOL* IsWow64)
{
//#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107 ROM映像不处理
if (ImageOptionalHeader->Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
// PE32
*IsWow64 = TRUE;
}
else if (ImageOptionalHeader->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
// PE64
*IsWow64 = FALSE;
}
else
{
// 无法判断 返回失败
*IsWow64 = FALSE;
return FALSE;
}

return TRUE;
}
void DumpTlsDirectory(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader,
LPBYTE ImageBase, DWORD Offset)
{
BOOL IsWow64 = FALSE;
//判断PE文件的位数
if (!IsOptionalHeaderPE32Plus(ImageOptionalHeader, &IsWow64))
{
return;
}

if (IsWow64 == FALSE)
{
//x64
PIMAGE_TLS_DIRECTORY64 ImageTlsDirectory = (PIMAGE_TLS_DIRECTORY64)((DWORD_PTR)ImageBase + (DWORD_PTR)Offset);
_tprintf(_T("StartAddressOfRawData: %p \r\nEndAddressOfRawData: %p \r\n"),
ImageTlsDirectory->StartAddressOfRawData, ImageTlsDirectory->EndAddressOfRawData);

_tprintf(_T("AddressOfIndex: %p \r\nAddressOfCallBacks:%p \r\n"),
ImageTlsDirectory->AddressOfIndex, ImageTlsDirectory->AddressOfCallBacks);

_tprintf(_T("SizeOfZeroFill: %u \r\nCharacteristics: %p \r\n"),
ImageTlsDirectory->SizeOfZeroFill, ImageTlsDirectory->Characteristics);
}
else
{
PIMAGE_TLS_DIRECTORY32 ImageTlsDirectory = (PIMAGE_TLS_DIRECTORY32)((DWORD_PTR)ImageBase + (DWORD_PTR)Offset);
_tprintf(_T("StartAddressOfRawData: %p \r\nEndAddressOfRawData: %p \r\n"),
ImageTlsDirectory->StartAddressOfRawData, ImageTlsDirectory->EndAddressOfRawData);

_tprintf(_T("AddressOfIndex: %p \r\nAddressOfCallBacks:%p \r\n"),
ImageTlsDirectory->AddressOfIndex, ImageTlsDirectory->AddressOfCallBacks);

_tprintf(_T("SizeOfZeroFill: %u \r\nCharacteristics: %p \r\n"),
ImageTlsDirectory->SizeOfZeroFill, ImageTlsDirectory->Characteristics);
}

return;
}

//内存对齐
void Sub_2()
{
TCHAR ImageName[MAX_PATH] = _T("Test.exe");
_tprintf(_T("ImageName: %s \r\n"), ImageName);

HANDLE FileHandle = INVALID_HANDLE_VALUE;
LPVOID v1 = NULL;
LPVOID ImageBase = NULL;
PIMAGE_NT_HEADERS ImageNtHeaders = NULL;
PIMAGE_SECTION_HEADER ImageSectionHeader = NULL;

DWORD FileLength = -1;
DWORD ReturnLength = -1;
DWORD NumberOfSections = -1;
DWORD ImageSize = -1;
DWORD SizeOfImage = -1;


IMAGE_DATA_DIRECTORY ImageDataDirectory = { 0 };

//将整个文件内容读取到内存中
FileHandle = CreateFile(ImageName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, 0);
if (FileHandle == INVALID_HANDLE_VALUE)
{
_tprintf(_T("CreateFile() Error:%d \r\n"), GetLastError());
goto Exit;
}
FileLength = GetFileSize(FileHandle, NULL);
//FileSize = 67072
if (FileLength == INVALID_FILE_SIZE) //#define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF)
{
_tprintf(_T("GetFileSize() Error:%d \r\n"), GetLastError());
goto Exit;
}
v1 = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, FileLength);
if (v1 == NULL)
{
_tprintf(_T("GlobalAlloc() Error:%d \r\n"), GetLastError());
goto Exit;
}
if (!ReadFile(FileHandle, v1, FileLength, &ReturnLength, NULL))
{
_tprintf(_T("ReadFile() Error:%d \r\n"), GetLastError());
goto Exit;
}
if (FileLength != ReturnLength)
{
//FileSize = 67072
//RealReadSize = 67072
_tprintf(_T("ReadFile() Error:%d \r\n"), GetLastError());
goto Exit;
}

//获得Nt头地址
ImageNtHeaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)v1 + ((PIMAGE_DOS_HEADER)v1)->e_lfanew);

//获得节表起始地址
ImageSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)&ImageNtHeaders->OptionalHeader +
(DWORD_PTR)ImageNtHeaders->FileHeader.SizeOfOptionalHeader);

//获得PE文件中节的个数
NumberOfSections = ImageNtHeaders->FileHeader.NumberOfSections;
//NumberOfSections = 11

//最后一个节的RVA 加上 该节数据相对内存对齐粒度对齐后的值 -> 该PE文件加载到内存后所占内存的总大小
ImageSize = ImageSectionHeader[NumberOfSections - 1].VirtualAddress +
AlignmentData(ImageSectionHeader[NumberOfSections - 1].SizeOfRawData, ImageNtHeaders->OptionalHeader.SectionAlignment);

//ImageOptionalHeader的成员SizeOfImage 内存中PE文件的总大小(必须保证内存对齐)
SizeOfImage = AlignmentData(ImageNtHeaders->OptionalHeader.SizeOfImage,
ImageNtHeaders->OptionalHeader.SectionAlignment);

//通常以上两种方法得到的两个值是相等的,如果不等则取较大的一个
if (SizeOfImage < ImageSize)
{
//ImageSize = 163840
//CalculateImageSize = 163840
SizeOfImage = ImageSize;
}

//分配内存空间,并将其全部初始化为0
ImageBase = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, ImageSize);
if (ImageBase == NULL)
{
_tprintf(_T("GlobalAlloc() Error:%d \r\n"), GetLastError());
goto Exit;
}

//将 头+节表 复制到申请的内存中
memcpy(ImageBase, v1, ImageNtHeaders->OptionalHeader.SizeOfHeaders);

//将各个节数据复制到内存中
for (int i = 0; i < NumberOfSections; i++)
{
memcpy((PBYTE)ImageBase + ImageSectionHeader[i].VirtualAddress, //节在内存中的起始处(目标)
(PBYTE)v1 + ImageSectionHeader[i].PointerToRawData, //节在文件中的起始处(源数据)
ImageSectionHeader[i].SizeOfRawData); //节数据的大小
}



//查找TLS目录的RVA和大小
if (!RtlImageDirectoryEntryToData(&ImageNtHeaders->OptionalHeader, IMAGE_DIRECTORY_ENTRY_TLS,
&ImageDataDirectory))
{
_tprintf(_T("GetTlsDirectoryRVA() Error\r\n"));
goto Exit;
}

//显示TLS目录信息
DumpTlsDirectory2(&ImageNtHeaders->OptionalHeader, (LPBYTE)ImageBase,ImageDataDirectory.VirtualAddress);

Exit:

//关闭句柄 释放内存
if (ImageBase != NULL)
{
GlobalFree(ImageBase);
ImageBase = NULL;
}
if (v1 != NULL)
{
GlobalFree(v1);
v1 = NULL;
}
if (FileHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(FileHandle);
FileHandle = INVALID_HANDLE_VALUE;
}
_tprintf(_T("Please Input Anykey to Exit\r\n"));
_gettchar();
return;

}
void DumpTlsDirectory2(PIMAGE_OPTIONAL_HEADER ImageOptionalHeader, LPBYTE ImageBase, DWORD VirtualAddress)
{
BOOL IsWow64 = FALSE;
if (!IsOptionalHeaderPE32Plus(ImageOptionalHeader, &IsWow64))
{
return;
}

if (IsWow64==FALSE)
{
PIMAGE_TLS_DIRECTORY64 ImageTlsDirectory = (PIMAGE_TLS_DIRECTORY64)((DWORD_PTR)ImageBase + (DWORD_PTR)VirtualAddress);
_tprintf(_T("StartAddressOfRawData: %p \r\nEndAddressOfRawData: %p \r\n"),
ImageTlsDirectory->StartAddressOfRawData, ImageTlsDirectory->EndAddressOfRawData);

_tprintf(_T("AddressOfIndex: %p \r\nAddressOfCallBacks:%p \r\n"),
ImageTlsDirectory->AddressOfIndex, ImageTlsDirectory->AddressOfCallBacks);

_tprintf(_T("SizeOfZeroFill: %u \r\nCharacteristics: %p \r\n"),
ImageTlsDirectory->SizeOfZeroFill, ImageTlsDirectory->Characteristics);
}
else
{
PIMAGE_TLS_DIRECTORY32 ImageTlsDirectory = (PIMAGE_TLS_DIRECTORY32)((DWORD_PTR)ImageBase + (DWORD_PTR)VirtualAddress);
_tprintf(_T("StartAddressOfRawData: %p \r\nEndAddressOfRawData: %p \r\n"),
ImageTlsDirectory->StartAddressOfRawData, ImageTlsDirectory->EndAddressOfRawData);

_tprintf(_T("AddressOfIndex: %p \r\nAddressOfCallBacks:%p \r\n"),
ImageTlsDirectory->AddressOfIndex, ImageTlsDirectory->AddressOfCallBacks);

_tprintf(_T("SizeOfZeroFill: %u \r\nCharacteristics: %p \r\n"),
ImageTlsDirectory->SizeOfZeroFill, ImageTlsDirectory->Characteristics);
}

return;
}

//以AlignmentLength对齐OperateLength, 让OperateLength为AlignmentLength的整数倍
LONGLONG AlignmentData(LONGLONG OperateLength, LONGLONG AlignmentLength)
{
if (AlignmentLength == 0)
{
return OperateLength;
}
int v1 = OperateLength % AlignmentLength;
if (v1 != 0)
{
return OperateLength + AlignmentLength - v1;
}
return OperateLength;
}