[转]TLS callback科普小知识
本文转自http://hi.baidu.com/ayarei/blog/item/1cfc9d0262c5900a4bfb511f.html
前言:最初想了解TLS callback是来自于team509翻译的一篇关于 TLS callback反ida的文章。当时问了几个朋友对TLS callback都不太了解,但是问到dummy牛,他给了一个他曾经写过的使用TLS callback anti-debugger的壳,于是就看了下TLS callback机制。如果你是新手,并对TLS callback机制感兴趣,那么这篇基础文章也许对你有用。不过,了解的就勿看了,没有什么东西 :)。另外,文章没有进行排版修改.....= =|||
======================我是邪恶的分割线=========================
每个线程拥有自己的线程局部存储,互补干扰。系统中线程局部存储是存放在线程的TEB中,每个线程都有自己的TEB因此互相独立。见下面的TEB结构中的ThreadLocalStoragePointer、TlsSlots、TlsLinksTlsExpansionSlots域。对TLS的访问通过 TlsAlloc、TlsSetValue和TlsGetValue以及TlsFree几个API进行。这些API也是对TEB中Tls相关域的访问。跟踪Tls*等API函数发现,系统通过PEB中的TlsBitmap来保存Tls的使用记录,并据此分配Tls索引,另外PEB还有 TlsExpansionCounter和TlsBitmapBits来跟踪Tls的使用情况。
选择子fs所对应的段用来存储TEB和PEB等信息,由ring3下FS选择子如下:
selector 3B, DATA32, BaseAddress 7FFDF000, Limit 00000FFF, DPL 3, Present, RW
因此在ring3下是可读写的。fs:[0]指向_NT_TIB中的ExceptionList,fs:[18]指向_NT_TIB自身,也就是指向_TEB结构;
mov eax, dword ptr fs:[18]
mov esi, dword ptr [eax+30]
此时esi指向PEB,dword ptr[esi+40]即为TlsBitmap,假设ecx为分配的索引则
写
mov eax, dword ptr fs:[18]
mov [eax+ecx*4+e10], data
读
mov eax, dword ptr fs:[18]
mov data, [eax+ecx*4+e10]
结构如下所示(windows XP professional SP2)
kd> dt !_NT_TIB
+0×000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0×004 StackBase : Ptr32 Void
+0×008 StackLimit : Ptr32 Void
+0×00c SubSystemTib : Ptr32 Void
+0×010 FiberData : Ptr32 Void
+0×010 Version : Uint4B
+0×014 ArbitraryUserPointer : Ptr32 Void
+0×018 Self : Ptr32 _NT_TIB
//SEH和栈信息
kd> dt !_TEB
+0×000 NtTib : _NT_TIB
+0×01c EnvironmentPointer : Ptr32 Void
+0×020 ClientId : _CLIENT_ID
+0×028 ActiveRpcHandle : Ptr32 Void
+0×02c ThreadLocalStoragePointer : Ptr32 Void
+0×030 ProcessEnvironmentBlock : Ptr32 _PEB
+0×034 LastErrorValue : Uint4B
+0×038 CountOfOwnedCriticalSections : Uint4B
+0×03c CsrClientThread : Ptr32 Void
+0×040 Win32ThreadInfo : Ptr32 Void
+0×044 User32Reserved : [26] Uint4B
+0×0ac UserReserved : [5] Uint4B
+0×0c0 WOW32Reserved : Ptr32 Void
+0×0c4 CurrentLocale : Uint4B
+0×0c8 FpSoftwareStatusRegister : Uint4B
+0×0cc SystemReserved1 : [54] Ptr32 Void
+0×1a4 ExceptionCode : Int4B
+0×1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
+0×1bc SpareBytes1 : [24] UChar
+0×1d4 GdiTebBatch : _GDI_TEB_BATCH
+0×6b4 RealClientId : _CLIENT_ID
+0×6bc GdiCachedProcessHandle : Ptr32 Void
+0×6c0 GdiClientPID : Uint4B
+0×6c4 GdiClientTID : Uint4B
+0×6c8 GdiThreadLocalInfo : Ptr32 Void
+0×6cc Win32ClientInfo : [62] Uint4B
+0×7c4 glDispatchTable : [233] Ptr32 Void
+0xb68 glReserved1 : [29] Uint4B
+0xbdc glReserved2 : Ptr32 Void
+0xbe0 glSectionInfo : Ptr32 Void
+0xbe4 glSection : Ptr32 Void
+0xbe8 glTable : Ptr32 Void
+0xbec glCurrentRC : Ptr32 Void
+0xbf0 glContext : Ptr32 Void
+0xbf4 LastStatusValue : Uint4B
+0xbf8 StaticUnicodeString : _UNICODE_STRING
+0xc00 StaticUnicodeBuffer : [261] Uint2B
+0xe0c DeallocationStack : Ptr32 Void
+0xe10 TlsSlots : [64] Ptr32 Void
+0xf10 TlsLinks : _LIST_ENTRY
+0xf18 Vdm : Ptr32 Void
+0xf1c ReservedForNtRpc : Ptr32 Void
+0xf20 DbgSsReserved : [2] Ptr32 Void
+0xf28 HardErrorsAreDisabled : Uint4B
+0xf2c Instrumentation : [16] Ptr32 Void
+0xf6c WinSockData : Ptr32 Void
+0xf70 GdiBatchCount : Uint4B
+0xf74 InDbgPrint : UChar
+0xf75 FreeStackOnTermination : UChar
+0xf76 HasFiberData : UChar
+0xf77 IdealProcessor : UChar
+0xf78 Spare3 : Uint4B
+0xf7c ReservedForPerf : Ptr32 Void
+0xf80 ReservedForOle : Ptr32 Void
+0xf84 WaitingOnLoaderLock : Uint4B
+0xf88 Wx86Thread : _Wx86ThreadState
+0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
+0xf98 ImpersonationLocale : Uint4B
+0xf9c IsImpersonating : Uint4B
+0xfa0 NlsCache : Ptr32 Void
+0xfa4 pShimData : Ptr32 Void
+0xfa8 HeapVirtualAffinity : Uint4B
+0xfac CurrentTransactionHandle : Ptr32 Void
+0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
+0xfb4 SafeThunkCall : UChar
+0xfb5 BooleanSpare : [3] UChar
kd> dt !_PEB
+0×000 InheritedAddressSpace : UChar
+0×001 ReadImageFileExecOptions : UChar
+0×002 BeingDebugged : UChar
+0×003 SpareBool : UChar
+0×004 Mutant : Ptr32 Void
+0×008 ImageBaseAddress : Ptr32 Void
+0×00c Ldr : Ptr32 _PEB_LDR_DATA
+0×010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0×014 SubSystemData : Ptr32 Void
+0×018 ProcessHeap : Ptr32 Void
+0×01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0×020 FastPebLockRoutine : Ptr32 Void
+0×024 FastPebUnlockRoutine : Ptr32 Void
+0×028 EnvironmentUpdateCount : Uint4B
+0×02c KernelCallbackTable : Ptr32 Void
+0×030 SystemReserved : [1] Uint4B
+0×034 AtlThunkSListPtr32 : Uint4B
+0×038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0×03c TlsExpansionCounter : Uint4B
+0×040 TlsBitmap : Ptr32 Void
+0×044 TlsBitmapBits : [2] Uint4B
+0×04c ReadOnlySharedMemoryBase : Ptr32 Void
+0×050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0×054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0×058 AnsiCodePageData : Ptr32 Void
+0×05c OemCodePageData : Ptr32 Void
+0×060 UnicodeCaseTableData : Ptr32 Void
+0×064 NumberOfProcessors : Uint4B
+0×068 NtGlobalFlag : Uint4B
+0×070 CriticalSectionTimeout : _LARGE_INTEGER
+0×078 HeapSegmentReserve : Uint4B
+0×07c HeapSegmentCommit : Uint4B
+0×080 HeapDeCommitTotalFreeThreshold : Uint4B
+0×084 HeapDeCommitFreeBlockThreshold : Uint4B
+0×088 NumberOfHeaps : Uint4B
+0×08c MaximumNumberOfHeaps : Uint4B
+0×090 ProcessHeaps : Ptr32 Ptr32 Void
+0×094 GdiSharedHandleTable : Ptr32 Void
+0×098 ProcessStarterHelper : Ptr32 Void
+0×09c GdiDCAttributeList : Uint4B
+0×0a0 LoaderLock : Ptr32 Void
+0×0a4 OSMajorVersion : Uint4B
+0×0a8 OSMinorVersion : Uint4B
+0×0ac OSBuildNumber : Uint2B
+0×0ae OSCSDVersion : Uint2B
+0×0b0 OSPlatformId : Uint4B
+0×0b4 ImageSubsystem : Uint4B
+0×0b8 ImageSubsystemMajorVersion : Uint4B
+0×0bc ImageSubsystemMinorVersion : Uint4B
+0×0c0 ImageProcessAffinityMask : Uint4B
+0×0c4 GdiHandleBuffer : [34] Uint4B
+0×14c PostProcessInitRoutine : Ptr32
+0×150 TlsExpansionBitmap : Ptr32 Void
+0×154 TlsExpansionBitmapBits : [32] Uint4B
+0×1d4 SessionId : Uint4B
+0×1d8 AppCompatFlags : _ULARGE_INTEGER
+0×1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0×1e8 pShimData : Ptr32 Void
+0×1ec AppCompatInfo : Ptr32 Void
+0×1f0 CSDVersion : _UNICODE_STRING
+0×1f8 ActivationContextData : Ptr32 Void
+0×1fc ProcessAssemblyStorageMap : Ptr32 Void
+0×200 SystemDefaultActivationContextData : Ptr32 Void
+0×204 SystemAssemblyStorageMap : Ptr32 Void
+0×208 MinimumStackCommit : Uint4B
PE和编译器支持:
PE文件结构的目录表(directory table)中有一个是IMAGE_DIRECTORY_ENTRY_TLS (9)线程级局部存储目录,如果此处非零的话,PE将会有一个tls节,tls节以结构IMAGE_TLS_DIRECTORY(winnt.h)开始
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
PDWORD AddressOfIndex;
PIMAGE_TLS_CALLBACK *AddressOfCallBacks;
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32;
其中PIMAGE_TLS_CALLBACK如下:
typedef VOID
(NTAPI *PIMAGE_TLS_CALLBACK) (
PVOID DllHandle,
DWORD Reason,
PVOID Reserved
);
为了方便使用TLS,编译器支持用__declspec(thread)来修饰全局或静态变量,这样的变量最终存储在TLS中,对这些变量的访问与普通变量一样,编译器负责处理其中细节分配索引和初始化等。
IMAGE_TLS_DIRECTORY32 中的StartAddressOfRawData和EndAddressOfRawData指向的区域保存__declspec(thread)变量的初始值,AddressOfIndex指向的区域保存动态分配的Tls索引。AddressOfCallBacks指向一个回调函数,此函数与 DllMain有同样的形式,它会在线程创建前被调用,多用来完成__declspec(thread)变量的初始化等,也被用于anti- debugger(如blacklight)。
Visual Studio C++中对此的支持请参考Thread Local Storage - The C++ Way。
MASM中可以将tls节中的数据放到data节中,然后使用LordPE等工具改变PE中目录表中的IMAGE_DIRECTORY_ENTRY_TLS为对应的地址和大小即可,具体请见
TLS-CallBack +IsDebuggerPresent() Debugger Detection。
参考文献
MSDN
Thread Local Storage: http://www.windowsitlibrary.com/Content/356/11/5.html
Thread Local Storage - The C++ Way:http://www.codeproject.com/threads/tls.asp
TLS-CallBack +IsDebuggerPresent() Debugger Detection:
http://www.openrce.org/reference_library/anti_reversing_view/26/TLS-CallBack%20+IsDebuggerPresent()%20Debugger
======================我是邪恶的分割线=========================
每个线程拥有自己的线程局部存储,互补干扰。系统中线程局部存储是存放在线程的TEB中,每个线程都有自己的TEB因此互相独立。见下面的TEB结构中的ThreadLocalStoragePointer、TlsSlots、TlsLinksTlsExpansionSlots域。对TLS的访问通过 TlsAlloc、TlsSetValue和TlsGetValue以及TlsFree几个API进行。这些API也是对TEB中Tls相关域的访问。跟踪Tls*等API函数发现,系统通过PEB中的TlsBitmap来保存Tls的使用记录,并据此分配Tls索引,另外PEB还有 TlsExpansionCounter和TlsBitmapBits来跟踪Tls的使用情况。
选择子fs所对应的段用来存储TEB和PEB等信息,由ring3下FS选择子如下:
selector 3B, DATA32, BaseAddress 7FFDF000, Limit 00000FFF, DPL 3, Present, RW
因此在ring3下是可读写的。fs:[0]指向_NT_TIB中的ExceptionList,fs:[18]指向_NT_TIB自身,也就是指向_TEB结构;
mov eax, dword ptr fs:[18]
mov esi, dword ptr [eax+30]
此时esi指向PEB,dword ptr[esi+40]即为TlsBitmap,假设ecx为分配的索引则
写
mov eax, dword ptr fs:[18]
mov [eax+ecx*4+e10], data
读
mov eax, dword ptr fs:[18]
mov data, [eax+ecx*4+e10]
结构如下所示(windows XP professional SP2)
kd> dt !_NT_TIB
+0×000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0×004 StackBase : Ptr32 Void
+0×008 StackLimit : Ptr32 Void
+0×00c SubSystemTib : Ptr32 Void
+0×010 FiberData : Ptr32 Void
+0×010 Version : Uint4B
+0×014 ArbitraryUserPointer : Ptr32 Void
+0×018 Self : Ptr32 _NT_TIB
//SEH和栈信息
kd> dt !_TEB
+0×000 NtTib : _NT_TIB
+0×01c EnvironmentPointer : Ptr32 Void
+0×020 ClientId : _CLIENT_ID
+0×028 ActiveRpcHandle : Ptr32 Void
+0×02c ThreadLocalStoragePointer : Ptr32 Void
+0×030 ProcessEnvironmentBlock : Ptr32 _PEB
+0×034 LastErrorValue : Uint4B
+0×038 CountOfOwnedCriticalSections : Uint4B
+0×03c CsrClientThread : Ptr32 Void
+0×040 Win32ThreadInfo : Ptr32 Void
+0×044 User32Reserved : [26] Uint4B
+0×0ac UserReserved : [5] Uint4B
+0×0c0 WOW32Reserved : Ptr32 Void
+0×0c4 CurrentLocale : Uint4B
+0×0c8 FpSoftwareStatusRegister : Uint4B
+0×0cc SystemReserved1 : [54] Ptr32 Void
+0×1a4 ExceptionCode : Int4B
+0×1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
+0×1bc SpareBytes1 : [24] UChar
+0×1d4 GdiTebBatch : _GDI_TEB_BATCH
+0×6b4 RealClientId : _CLIENT_ID
+0×6bc GdiCachedProcessHandle : Ptr32 Void
+0×6c0 GdiClientPID : Uint4B
+0×6c4 GdiClientTID : Uint4B
+0×6c8 GdiThreadLocalInfo : Ptr32 Void
+0×6cc Win32ClientInfo : [62] Uint4B
+0×7c4 glDispatchTable : [233] Ptr32 Void
+0xb68 glReserved1 : [29] Uint4B
+0xbdc glReserved2 : Ptr32 Void
+0xbe0 glSectionInfo : Ptr32 Void
+0xbe4 glSection : Ptr32 Void
+0xbe8 glTable : Ptr32 Void
+0xbec glCurrentRC : Ptr32 Void
+0xbf0 glContext : Ptr32 Void
+0xbf4 LastStatusValue : Uint4B
+0xbf8 StaticUnicodeString : _UNICODE_STRING
+0xc00 StaticUnicodeBuffer : [261] Uint2B
+0xe0c DeallocationStack : Ptr32 Void
+0xe10 TlsSlots : [64] Ptr32 Void
+0xf10 TlsLinks : _LIST_ENTRY
+0xf18 Vdm : Ptr32 Void
+0xf1c ReservedForNtRpc : Ptr32 Void
+0xf20 DbgSsReserved : [2] Ptr32 Void
+0xf28 HardErrorsAreDisabled : Uint4B
+0xf2c Instrumentation : [16] Ptr32 Void
+0xf6c WinSockData : Ptr32 Void
+0xf70 GdiBatchCount : Uint4B
+0xf74 InDbgPrint : UChar
+0xf75 FreeStackOnTermination : UChar
+0xf76 HasFiberData : UChar
+0xf77 IdealProcessor : UChar
+0xf78 Spare3 : Uint4B
+0xf7c ReservedForPerf : Ptr32 Void
+0xf80 ReservedForOle : Ptr32 Void
+0xf84 WaitingOnLoaderLock : Uint4B
+0xf88 Wx86Thread : _Wx86ThreadState
+0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
+0xf98 ImpersonationLocale : Uint4B
+0xf9c IsImpersonating : Uint4B
+0xfa0 NlsCache : Ptr32 Void
+0xfa4 pShimData : Ptr32 Void
+0xfa8 HeapVirtualAffinity : Uint4B
+0xfac CurrentTransactionHandle : Ptr32 Void
+0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
+0xfb4 SafeThunkCall : UChar
+0xfb5 BooleanSpare : [3] UChar
kd> dt !_PEB
+0×000 InheritedAddressSpace : UChar
+0×001 ReadImageFileExecOptions : UChar
+0×002 BeingDebugged : UChar
+0×003 SpareBool : UChar
+0×004 Mutant : Ptr32 Void
+0×008 ImageBaseAddress : Ptr32 Void
+0×00c Ldr : Ptr32 _PEB_LDR_DATA
+0×010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0×014 SubSystemData : Ptr32 Void
+0×018 ProcessHeap : Ptr32 Void
+0×01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0×020 FastPebLockRoutine : Ptr32 Void
+0×024 FastPebUnlockRoutine : Ptr32 Void
+0×028 EnvironmentUpdateCount : Uint4B
+0×02c KernelCallbackTable : Ptr32 Void
+0×030 SystemReserved : [1] Uint4B
+0×034 AtlThunkSListPtr32 : Uint4B
+0×038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0×03c TlsExpansionCounter : Uint4B
+0×040 TlsBitmap : Ptr32 Void
+0×044 TlsBitmapBits : [2] Uint4B
+0×04c ReadOnlySharedMemoryBase : Ptr32 Void
+0×050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0×054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0×058 AnsiCodePageData : Ptr32 Void
+0×05c OemCodePageData : Ptr32 Void
+0×060 UnicodeCaseTableData : Ptr32 Void
+0×064 NumberOfProcessors : Uint4B
+0×068 NtGlobalFlag : Uint4B
+0×070 CriticalSectionTimeout : _LARGE_INTEGER
+0×078 HeapSegmentReserve : Uint4B
+0×07c HeapSegmentCommit : Uint4B
+0×080 HeapDeCommitTotalFreeThreshold : Uint4B
+0×084 HeapDeCommitFreeBlockThreshold : Uint4B
+0×088 NumberOfHeaps : Uint4B
+0×08c MaximumNumberOfHeaps : Uint4B
+0×090 ProcessHeaps : Ptr32 Ptr32 Void
+0×094 GdiSharedHandleTable : Ptr32 Void
+0×098 ProcessStarterHelper : Ptr32 Void
+0×09c GdiDCAttributeList : Uint4B
+0×0a0 LoaderLock : Ptr32 Void
+0×0a4 OSMajorVersion : Uint4B
+0×0a8 OSMinorVersion : Uint4B
+0×0ac OSBuildNumber : Uint2B
+0×0ae OSCSDVersion : Uint2B
+0×0b0 OSPlatformId : Uint4B
+0×0b4 ImageSubsystem : Uint4B
+0×0b8 ImageSubsystemMajorVersion : Uint4B
+0×0bc ImageSubsystemMinorVersion : Uint4B
+0×0c0 ImageProcessAffinityMask : Uint4B
+0×0c4 GdiHandleBuffer : [34] Uint4B
+0×14c PostProcessInitRoutine : Ptr32
+0×150 TlsExpansionBitmap : Ptr32 Void
+0×154 TlsExpansionBitmapBits : [32] Uint4B
+0×1d4 SessionId : Uint4B
+0×1d8 AppCompatFlags : _ULARGE_INTEGER
+0×1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0×1e8 pShimData : Ptr32 Void
+0×1ec AppCompatInfo : Ptr32 Void
+0×1f0 CSDVersion : _UNICODE_STRING
+0×1f8 ActivationContextData : Ptr32 Void
+0×1fc ProcessAssemblyStorageMap : Ptr32 Void
+0×200 SystemDefaultActivationContextData : Ptr32 Void
+0×204 SystemAssemblyStorageMap : Ptr32 Void
+0×208 MinimumStackCommit : Uint4B
PE和编译器支持:
PE文件结构的目录表(directory table)中有一个是IMAGE_DIRECTORY_ENTRY_TLS (9)线程级局部存储目录,如果此处非零的话,PE将会有一个tls节,tls节以结构IMAGE_TLS_DIRECTORY(winnt.h)开始
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
PDWORD AddressOfIndex;
PIMAGE_TLS_CALLBACK *AddressOfCallBacks;
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32;
其中PIMAGE_TLS_CALLBACK如下:
typedef VOID
(NTAPI *PIMAGE_TLS_CALLBACK) (
PVOID DllHandle,
DWORD Reason,
PVOID Reserved
);
为了方便使用TLS,编译器支持用__declspec(thread)来修饰全局或静态变量,这样的变量最终存储在TLS中,对这些变量的访问与普通变量一样,编译器负责处理其中细节分配索引和初始化等。
IMAGE_TLS_DIRECTORY32 中的StartAddressOfRawData和EndAddressOfRawData指向的区域保存__declspec(thread)变量的初始值,AddressOfIndex指向的区域保存动态分配的Tls索引。AddressOfCallBacks指向一个回调函数,此函数与 DllMain有同样的形式,它会在线程创建前被调用,多用来完成__declspec(thread)变量的初始化等,也被用于anti- debugger(如blacklight)。
Visual Studio C++中对此的支持请参考Thread Local Storage - The C++ Way。
MASM中可以将tls节中的数据放到data节中,然后使用LordPE等工具改变PE中目录表中的IMAGE_DIRECTORY_ENTRY_TLS为对应的地址和大小即可,具体请见
TLS-CallBack +IsDebuggerPresent() Debugger Detection。
参考文献
MSDN
Thread Local Storage: http://www.windowsitlibrary.com/Content/356/11/5.html
Thread Local Storage - The C++ Way:http://www.codeproject.com/threads/tls.asp
TLS-CallBack +IsDebuggerPresent() Debugger Detection:
http://www.openrce.org/reference_library/anti_reversing_view/26/TLS-CallBack%20+IsDebuggerPresent()%20Debugger