PE结构-3
9. TLS
数据目录表索引:#define IMAGE_DIRECTORY_ENTRY_TLS 9
,我查看了一下位于rdata段,不知道是不是固定的。
- 为了解决多线程变量同步问题
- 声明为TLS变量后,当线程去访问这个全局变量是,会将这个变量拷贝到自己线程中的TLS空间中。线程只能修改自己的空间tls变量,不会修改到全局变量
- TLS经常会用于反调试,抢占式执行。
__declspec (thread) int g_nNum;
正常用法:定义一个线程内的全局数据,回调函数做初始化工作。
非正常用法:安全上,将反调试代码写在回调函数里。抢占执行等。
另外,还可以提高壳的兼容性。
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
DWORD AddressOfIndex; // PDWORD
DWORD AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *
DWORD SizeOfZeroFill;
union {
DWORD Characteristics;
struct {
DWORD Reserved0 : 20;
DWORD Alignment : 4;
DWORD Reserved1 : 8;
} DUMMYSTRUCTNAME;
} DUMMYUNIONNAME;
} IMAGE_TLS_DIRECTORY32;
- StartAddressOfRawData:数据起始位置,在TLS节;
- EndAddressOfRawData:数据结束位置,在TLS节;
- AddressOfCallBacks:回调函数地址表位置,在rdata节。
数据起始位置和结束位置中间是TLS数据初始值。
回调函数触发事件:
- DLL_PROCESS_ATTACH 1
- DLL_THREAD_ATTACH 2
- DLL_THREAD_DETACH 3
- DLL_PROCESS_DETACH 0
注册TLS回调函数,".CRT$XLB"
的含义是:
- CRT表明使用C RunTime机制
- X表示标识名随机
- L表示TLS callback section
- B其实也可以为B-Y的任意一个字母
TLS回调函数在.text段中,但回调函数地址保存在.tls段。
#include<iostream>
#include<windows.h>
#pragma comment(linker, "/INCLUDE:__tls_used")
__declspec (thread) int g_nNum = 0xdeadbeef;
__declspec (thread) char g_szStr[] = "TLS g_nNum : 0x%x at 0x%p ... \n";
void NTAPI tTlsCallBack_A(PVOID DllHandle, DWORD Reason, PVOID red)
{
if (DLL_THREAD_DETACH == Reason) // 如果线程退出则打印信息
printf("tTlsCallBack_A -> ThreadDetach!\r\n");
return;
}
void NTAPI tTlsCallBack_B(PVOID DllHandle, DWORD Reason, PVOID red)
{
if (DLL_THREAD_DETACH == Reason) // 如果线程退出则打印信息
printf("tTlsCallBack_B -> ThreadDetach!\r\n");
return;
}
#pragma data_seg(".CRT$XLB")
PIMAGE_TLS_CALLBACK pThreadCallBack[] =
{
tTlsCallBack_A,
tTlsCallBack_B,
NULL
};
#pragma data_seg()
DWORD WINAPI t_ThreadFun(PVOID pParam) {
printf("t_Thread -> first printf:");
printf(g_szStr, g_nNum);
g_nNum = 0x22222222; // 注意这里
printf("t_Thread -> second printf:");
printf(g_szStr, g_nNum);
return 0;
}
int main()
{
CreateThread(NULL, 0, t_ThreadFun, NULL, 0, 0);
Sleep(100);
printf("\n");
CreateThread(NULL, 0, t_ThreadFun, NULL, 0, 0);
system("pause");
return 0;
}
10. 异常
PE文件的异常目录位于.pdata
区段,数据目录中的IMAGE_DIRECTORY_ENTRY_EXCEPTION
指向此结构。
平台不同,异常结构也不同。x64的异常结构如下:
typedef struct _IMAGE_RUNTIME_FUNCTION_ENTRY {
DWORD BeginAddress;
DWORD EndAddress;
union {
DWORD UnwindInfoAddress;
DWORD UnwindData;
} DUMMYUNIONNAME;
} _IMAGE_RUNTIME_FUNCTION_ENTRY, *_PIMAGE_RUNTIME_FUNCTION_ENTRY;
typedef _IMAGE_RUNTIME_FUNCTION_ENTRY IMAGE_IA64_RUNTIME_FUNCTION_ENTRY;
typedef _PIMAGE_RUNTIME_FUNCTION_ENTRY PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY;
BeginAddress
和EndAddress
是SEH相关代码的起始和末尾偏移地址,这一部分的代码差异属性由UNWIND_INFO
结构描述。
这个结构可以译为展开处理程序(unwind handler)结构,记录函数对堆栈指针的影响以及非易失寄存器在堆栈中的保存位置。
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes;
UBYTE FrameRegister : 4;
UBYTE FrameOffset : 4;
UNWIND_CODE UnwindCode[1];
union {
OPTIONAL ULONG ExceptionHandler;
OPTIONAL ULONG FunctionEntry;
};
OPTIONAL ULONG ExceptionData[];
} UNWIND_INFO, *PUNWIND_INFO;
11. 安全
数据目录的IMAGE_DIRECTORY_ENTRY_SECURITY
指向安全目录结构,该结构一般存有映像文件的数字签名。
该结构位于wintrust.h
,已经弃用。
//
//
// support for old calling convention: *** DO NOT USE ***
//
#ifdef WT_DEFINE_ALL_APIS
typedef struct _WIN_CERTIFICATE
{
DWORD dwLength;
WORD wRevision;
WORD wCertificateType; // WIN_CERT_TYPE_xxx
BYTE bCertificate[ANYSIZE_ARRAY];
} WIN_CERTIFICATE, *LPWIN_CERTIFICATE;
12. 调试
数据目录表的 IMAGE_DIRECTORY_ENTRY_DEBUG
指向调试目录结构,该结构位于.debug
区段,主要协助第三方应用调试本程序,如提供调试数据块的位置与大小。
//
// Debug Format
//
typedef struct _IMAGE_DEBUG_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Type;
DWORD SizeOfData;
DWORD AddressOfRawData; //RVA in memory, 0 means no mapping
DWORD PointerToRawData;
} IMAGE_DEBUG_DIRECTORY, *PIMAGE_DEBUG_DIRECTORY;
//Type
#define IMAGE_DEBUG_TYPE_UNKNOWN 0
#define IMAGE_DEBUG_TYPE_COFF 1
#define IMAGE_DEBUG_TYPE_CODEVIEW 2 //vc++
#define IMAGE_DEBUG_TYPE_FPO 3 //框架指针忽略信息,引导调试器解释非标准框架结构
#define IMAGE_DEBUG_TYPE_MISC 4 //dbg文件位置
#define IMAGE_DEBUG_TYPE_EXCEPTION 5 //copy of .pdata
#define IMAGE_DEBUG_TYPE_FIXUP 6 //保留
#define IMAGE_DEBUG_TYPE_OMAP_TO_SRC 7 //将此映像的RVA映射到源映像的RVA
#define IMAGE_DEBUG_TYPE_OMAP_FROM_SRC 8 //将源映像的RVA映射到此映像的RVA
#define IMAGE_DEBUG_TYPE_BORLAND 9 //为Borland公司保留
#define IMAGE_DEBUG_TYPE_RESERVED10 10 //保留
#define IMAGE_DEBUG_TYPE_CLSID 11