PE学习(九)第九章:TLS 动态TLS与静态TLS

来源:互联网 发布:深入浅出java 云盘 编辑:程序博客网 时间:2024/05/04 20:36

第九章:线程局部存储

PEB,在NT中,该结构可以从进程空间的FS:[0x30]处找到,PEB描述的信息主要包括:进程状态、进程堆、PE映像信息等,其中Ldr记录了进程加载进内存的所有模块的基地址。
TLS技术:
动态线程局部存储技术、静态线程局部存储技术
OS动态申请通过四个API,静态则通过预先在PE文件中声明数据存储空间。
TlsAlloc函数一旦得到一个可用的索引值后,还会遍历进程中的每个线程,并将对应的TLS存储槽全部清0,再返回。
TlsSetValue/TlsGetValue为了速度没有参数验证的错误检查,可以传不是TlsAlloc返回的值作为参数。
TLS存储槽实际对应于TEB中TlsSlot字段,用户可以直接操作此字段

静态线程局部存储预先将变量定义在PE文件内部,一般使用.tls节存储,在Visual C++中只要如下声明
_declspec (thread) int tlsFlag = 1;
静态线程局部存储只有用于静态加载,静态连接到该DLL的其他DLL也要确保不被动态加载。

难过

//动态TLS实例    .386    .model flat,stdcall    option casemap:noneinclude    windows.incinclude    user32.incincludelib user32.libinclude    kernel32.incincludelib kernel32.libMAX_THREAD_COUNT equ 4;数据段    .datahTlsIndex  dd  ?dwThreadID dd  ?hThreadID  dd  MAX_THREAD_COUNT dup(0)dwCount    dd  ?szBuffer   db  500 dup(0)szOut1     db  '线程%d终止,用时:%d毫秒。',0szErr1     db  '读取TLS槽数据时失败!',0szErr2     db  '写入TLS槽数据时失败!',0;代码段    .code;----------; 初始化;----------_initTime  proc     local @dwStart   pushad   ;获得当前时间,   ;将线程的创建时间与线程对象相关联   invoke GetTickCount   mov @dwStart,eax   invoke TlsSetValue,hTlsIndex,@dwStart   .if eax==0     invoke MessageBox,NULL,addr szErr2,\                                NULL,MB_OK   .endif   popad   ret_initTime endp;----------; 获取用时;----------_getLostTime  proc     local @dwTemp   pushad   ;获得当前时间,   ;返回当前时间和线程创建时间的差值   invoke GetTickCount   mov @dwTemp,eax   invoke TlsGetValue,hTlsIndex   .if eax==0     invoke MessageBox,NULL,addr szErr2,\                                NULL,MB_OK   .endif   sub @dwTemp,eax   popad   mov eax,@dwTemp   ret_getLostTime endp;----------; 线程函数;----------_tFun   proc  uses ebx ecx edx esi edi,lParam   local @dwCount   local @tID   pushad   invoke _initTime   ;模拟耗时操作   mov @dwCount,1000*10000   mov ecx,@dwCount   .while ecx>0     dec @dwCount     dec ecx   .endw    invoke GetCurrentThreadId   mov @tID,eax   invoke _getLostTime   invoke wsprintf,addr szBuffer,\                    addr szOut1,@tID,eax   invoke MessageBox,NULL,addr szBuffer,\                               NULL,MB_OK   popad   ret_tFun   endpstart:  ;通过在进程位数组中申请一个索引,  ;初始化线程运行时间记录系统  invoke TlsAlloc  mov hTlsIndex,eax  mov dwCount,MAX_THREAD_COUNT  mov edi,offset hThreadID  .while  dwCount>0     invoke  CreateThread,NULL,0,\                offset _tFun,NULL,\                NULL,addr dwThreadID     mov dword ptr [edi],eax     add edi,4     dec dwCount  .endw    ;等待结束线程  mov dwCount,MAX_THREAD_COUNT  mov edi,offset hThreadID  .while  dwCount>0     mov eax,dword ptr [edi]     mov dwThreadID,eax     push edi     invoke WaitForSingleObject,eax,\                              INFINITE     invoke CloseHandle,dwThreadID     pop edi     add edi,4     dec dwCount  .endw  ;通过释放线程局部存储索引,  ;释放时间记录系统占用的资源  invoke TlsFree,hTlsIndex  end start


 

//静态TLS实例    .386    .model flat,stdcall    option casemap:noneinclude    windows.incinclude    user32.incincludelib user32.libinclude    kernel32.incincludelib kernel32.lib    .dataszText  db 'HelloWorldPE',0,0,0,0  ; 构造IMAGE_TLS_DIRECTORYTLS_DIR     dd offset Tls1            dd offset Tls2            dd offset Tls3            dd offset TlsCallBack            dd 0            dd 0Tls1        dd 0Tls2        dd 0Tls3        dd 0TlsCallBack dd  offset TLS            dd     0            dd     0    .data?TLSCalled db ?   ;重进标志    .codestart:     invoke ExitProcess,NULL    RET    ; 以下代码将会在.code之前执行一次TLS:    ; 变量TLSCalled是一个防重进标志。正常情况下该部分代码    ; 会被执行两次,但使用了该标识后,该代码只在开始运行前    ; 执行一次        cmp byte ptr [TLSCalled],1    je @exit    mov byte ptr [TLSCalled],1    invoke MessageBox,NULL,addr szText,NULL,MB_OK@exit:    RET    end start  

静态TLS实例生成的exe需要手动修改下数据目录表
根据 .data节     800文件偏移地址   3000内存偏移,szText占10字节
所有TLS数据目录起始RVA修改为3010,大小为一个TLS数据目录大小。能170文件偏移位置修改为10 30 00 00 18 00...

0 0
原创粉丝点击