一篇文章带你了解线程局部存储(TLS)技术

线程局部存储技术:

      线程局部存储技术(Thread Local Storage)很好的解决了多线程程序设计中变量的同步问题。同时,TLS回调函数也适用于反调试技术。使用TSL技术,可以让进程拥有同一个全局变量,但是在不同的线程却对应着不同的值,这些变量只是对应特定的线程才是有意义的

两个前提知识:

线程环境块(TEB):

      TEB指的是线程环境块,该结构体里面包含一个线程的基本信息,进程的每一个线程都对应的一个这样的TEB数据结构。

TEB的数据结构:

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
typedef struct _TEB
{
NT_TIB Tib; /* 00h */
PVOID EnvironmentPointer; /* 1Ch */
CLIENT_ID Cid; /* 20h */
PVOID ActiveRpcHandle; /* 28h */
PVOID ThreadLocalStoragePointer; /* 2Ch */
struct _PEB *ProcessEnvironmentBlock; /* 30h */
ULONG LastErrorValue; /* 34h */
ULONG CountOfOwnedCriticalSections; /* 38h */
PVOID CsrClientThread; /* 3Ch */
struct _W32THREAD* Win32ThreadInfo; /* 40h */
ULONG User32Reserved[0x1A]; /* 44h */
ULONG UserReserved[5]; /* ACh */
PVOID WOW32Reserved; /* C0h */
LCID CurrentLocale; /* C4h */
ULONG FpSoftwareStatusRegister; /* C8h */
PVOID SystemReserved1[0x36]; /* CCh */
LONG ExceptionCode; /* 1A4h */
struct _ACTIVATION_CONTEXT_STACK *ActivationContextStackPointer; /* 1A8h */
UCHAR SpareBytes1[0x28]; /* 1ACh */
GDI_TEB_BATCH GdiTebBatch; /* 1D4h */
CLIENT_ID RealClientId; /* 6B4h */
PVOID GdiCachedProcessHandle; /* 6BCh */
ULONG GdiClientPID; /* 6C0h */
ULONG GdiClientTID; /* 6C4h */
PVOID GdiThreadLocalInfo; /* 6C8h */
ULONG Win32ClientInfo[62]; /* 6CCh */
PVOID glDispatchTable[0xE9]; /* 7C4h */
ULONG glReserved1[0x1D]; /* B68h */
PVOID glReserved2; /* BDCh */
PVOID glSectionInfo; /* BE0h */
PVOID glSection; /* BE4h */
PVOID glTable; /* BE8h */
PVOID glCurrentRC; /* BECh */
PVOID glContext; /* BF0h */
NTSTATUS LastStatusValue; /* BF4h */
UNICODE_STRING StaticUnicodeString; /* BF8h */
WCHAR StaticUnicodeBuffer[0x105]; /* C00h */
PVOID DeallocationStack; /* E0Ch */
PVOID TlsSlots[0x40]; /* E10h */
LIST_ENTRY TlsLinks; /* F10h */
PVOID Vdm; /* F18h */
PVOID ReservedForNtRpc; /* F1Ch */
PVOID DbgSsReserved[0x2]; /* F20h */
ULONG HardErrorDisabled; /* F28h */
PVOID Instrumentation[14]; /* F2Ch */
PVOID SubProcessTag; /* F64h */
PVOID EtwTraceData; /* F68h */
PVOID WinSockData; /* F6Ch */
ULONG GdiBatchCount; /* F70h */
BOOLEAN InDbgPrint; /* F74h */
BOOLEAN FreeStackOnTermination; /* F75h */
BOOLEAN HasFiberData; /* F76h */
UCHAR IdealProcessor; /* F77h */
ULONG GuaranteedStackBytes; /* F78h */
PVOID ReservedForPerf; /* F7Ch */
PVOID ReservedForOle; /* F80h */
ULONG WaitingOnLoaderLock; /* F84h */
ULONG SparePointer1; /* F88h */
ULONG SoftPatchPtr1; /* F8Ch */
ULONG SoftPatchPtr2; /* F90h */
PVOID *TlsExpansionSlots; /* F94h */
ULONG ImpersionationLocale; /* F98h */
ULONG IsImpersonating; /* F9Ch */
PVOID NlsCache; /* FA0h */
PVOID pShimData; /* FA4h */
ULONG HeapVirualAffinity; /* FA8h */
PVOID CurrentTransactionHandle; /* FACh */
PTEB_ACTIVE_FRAME ActiveFrame; /* FB0h */
PVOID FlsData; /* FB4h */
UCHAR SafeThunkCall; /* FB8h */
UCHAR BooleanSpare[3]; /* FB9h */
} TEB, *PTEB;

TEB的重要成员:

1
2
NT_TIB Tib; /* 00h */
struct _PEB *ProcessEnvironmentBlock; /* 30h */
  • ProcessEnvirnmentBlock是一个指向PEB的指针。
  • TEB的第一个成员是_NT_Tib(线程信息块)的结构体。

    • TIB的信息如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      typedef struct _KPCR_TIB
      {
      PVOID ExceptionList; //重点
      PVOID StackBase;
      PVOID StackLimit;
      PVOID SubSystemTib;
      _ANONYMOUS_UNION union
      {
      PVOID FiberData;
      DWORD Version;
      }DUMMYUNIONNAME;
      PVOID ArbitraryUserPointer;
      struct _NT_TIB *Self; //重点
      } KPCR_TIB, *PKPCR_TIB;
    • ExceptionList用于指向EXCEPTION_REGISTRATION_RECORD结构体链表(指向SEH)

    • self成员是TIB的自用指针,指向TIB的第一个成员,也是TEB的第一个成员。
  • TEB的访问方法:

  • FS:[0X00]=SEH的起始地址
  • FS:[0x18]=TEB的起始地址
  • FS:[0x30]=PEB的起始地址

进程环境块(PEB):

      PEB(Process Environment Block,进程环境块)存放进程信息,每个进程都有自己的PEB信息。位于用户地址空间。在Win 2000下,进程环境块的地址对于每个进程来说是固定的,在0x7FFDF000处,这是用户地址空间,所以程序能够直接访问。准确的PEB地址应从系统 的EPROCESS结构的0x1b0偏移处获得,但由于EPROCESS在系统地址空间,访问这个结构需要有ring0的权限。还可以通过TEB结构的偏 移0x30处获得PEB的位置,FS段寄存器指向当前的TEB结构.

PEB的信息如下:

_PEB结构:
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
typedef struct _PEB
{
UCHAR InheritedAddressSpace; /*000*/
UCHAR ReadImageFileExecOptions;/*001*/
UCHAR BeingDebugged; /*002*/ //程序是否处于调试状态
UCHAR SpareBool; /*003*/ // Allocation size
HANDLE Mutant; /*004*/
HINSTANCE ImageBaseAddress; /*008*/ //进程映像基地址
VOID *DllList; /*00C*/ //加载的其他模块信息
PPROCESS_PARAMETERS *ProcessParameters; /*010*/
ULONG SubSystemData; /*014*/
HANDLE DefaultHeap; /*018*/
KSPIN_LOCK FastPebLock; /*01C*/
ULONG FastPebLockRoutine; /*020*/
ULONG FastPebUnlockRoutine; /*024*/
ULONG EnvironmentUpdateCount; /*028*/
ULONG KernelCallbackTable; /*02C*/
LARGE_INTEGER SystemReserved; /*030*/
ULONG FreeList; /*038*/
ULONG TlsExpansionCounter; /*03C*/ //TSL索引计数
ULONG TlsBitmap; /*040*/ //TSL位图指针
LARGE_INTEGER TlsBitmapBits; /*044*/ //TSL进程标志位
ULONG ReadOnlySharedMemoryBase;/*04C*/
ULONG ReadOnlySharedMemoryHeap;/*050*/
ULONG ReadOnlyStaticServerData;/*054*/
ULONG AnsiCodePageData; /*058*/
ULONG OemCodePageData; /*05C*/
ULONG UnicodeCaseTableData; /*060*/
ULONG NumberOfProcessors; /*064*/
LARGE_INTEGER NtGlobalFlag; /*068*/ // Address of a local copy
LARGE_INTEGER CriticalSectionTimeout;/*070*/
ULONG HeapSegmentReserve; /*078*/
ULONG HeapSegmentCommit; /*07C*/
ULONG HeapDeCommitTotalFreeThreshold;/*080*/
ULONG HeapDeCommitFreeBlockThreshold;/*084*/
ULONG NumberOfHeaps; /*088*/
ULONG MaximumNumberOfHeaps; /*08C*/
ULONG ProcessHeaps; /*090*/
ULONG GdiSharedHandleTable; /*094*/
ULONG ProcessStarterHelper; /*098*/
ULONG GdiDCAttributeList; /*09C*/
KSPIN_LOCK LoaderLock; /*0A0*/
ULONG OSMajorVersion; /*0A4*/
ULONG OSMinorVersion; /*0A8*/
USHORT OSBuildNumber; /*0AC*/
USHORT OSCSDVersion; /*0AE*/
ULONG OSPlatformId; /*0B0*/
ULONG ImageSubsystem; /*0B4*/
ULONG ImageSubsystemMajorVersion;/*0B8*/
ULONG ImageSubsystemMinorVersion;/*0BC*/
ULONG ImageProcessAffinityMask;/*0C0*/
ULONG GdiHandleBuffer[0x22]; /*0C4*/
ULONG PostProcessInitRoutine; /*14C*/
ULONG TlsExpansionBitmap; /*150*/
UCHAR TlsExpansionBitmapBits[0x80];/*154*/
ULONG SessionId; /*1D4*/
}PEB, *PPEB;

PEB的重要成员:

  • 1.PEB.BeingDebugged //程序是否处于调试状态
  • 2.ImageBaseAddress //表示进程的基地址
  • 3.PEB.Ldr //指向PEB_LDR_DATA的指针,该结构体可以获取模块(dll)加载的基地址
  • 4.PEB.ProcessHeap&&PEB.NtGlobalFlag //用于反调试

    PEB的访问方法:

  • FS:[0X30]=PEB
  • 先获得TEB的地址,在偏移0x30得到PEB

TLS技术

动态TLS技术

四个API函数:
TLsAlloc //获取索引
TlsGetValue //获取索引值
TlsSetVaule //设置索引值
TisFree //释放索引

静态TLS技术

      预先将变量定义在PE文件内部,一般实验.tls节即可。通过查看TLS节区,而已看到程序是否使用TLS反调试技术

TSL回调函数—-反调试技术的基石

      TSL函数是指,每当创建或者终止进程的线程的时候,就会自动调用的函数,一共调用2次。有意思的是,进程创建主线程的时候也会自动的调用TSL回调函数,并且调用函数优先执行于EP代码(应该是主线程调用后,但是线程函数的尚未执行之前)。一点扩展利用TLS回调函数在调试器加载前执行Anti-Debug函数保护软件不被恶意修改,关键部分依旧是具体实现反调试部分的代码编写。比如传统的检测断点,检测进程,检测调试器等等。反调试技术就是利用这一特征。
      如果使用单纯的调试器(VS),就容易被TSL回调函数检测到,由于od的工具自动免疫了TSL的反调试机制,所以只会出现正常的状态(假的)。

  • 解决反调试的方法:
    • 1)OD插件
    • 2)PE或者IDA查找TSL的回调函数的入口地址和大小,然后转换成文件偏移,最后用工具00填充掉就行。