博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
TLS 处理
阅读量:6077 次
发布时间:2019-06-20

本文共 2909 字,大约阅读时间需要 9 分钟。

TLS(Thread Local Storage,线程局部存储)是一种便利的编程机制。我们通常不使用,因此并不太关心。但是要压缩的原程序可能会用到它。事实上,Delphi 总是使用它,如果我们打算支持 Delphi 程序,最好兼容它。
TLS 基本上是通过 API 实现。大致过程是,你分配一个“ Index(索引)”并存储在一个全局变量中。通过这个 Index 获得针对每个线程的一个双字值。通常使用这个值保存一个为每个线程分配好的内存块的指针。人们认为这样很乏味,一个特殊机制的出现使得实现它更容易些。因此,你可以这样写代码:

None.gif
__declspec ( thread ) 
int
 tls_int_value 
=
 
0
;
None.gif


每个线程可以通过名称访问它独特的实例,就像访问其他变量一样。我不知道这种 TLS 形式是否有官方名称,所以我叫它“简化 TLS”。这种机制与操作系统兼容,并且 PE 文件中有对应的结构。这些结构包含在数据目录的一个块中:


None.gif
origdirinfo[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress
None.gif

问题是, 处理这信息发生在由 OS 在每个线程的创建时刻,在执行到线程开始地址之前。(这句翻译的拗口,原文 The problem is that the processing of this information happens by the OS on the creation of every thread prior to execution being passed to the thread start address. )这通常不牵涉到我们,除了至少一个线程在我们解压数据之前被执行:我们的线程!我们必须设置一个伪 TLS 处理区段捕获 OS 在我们开始之前做的事情,然后在最后一步把这个信息传递给原程序。


为此,我在外部加壳器外部全局结构添加了2个项目:

None.gif
GlobalExternVars
ExpandedBlockStart.gif
{
InBlock.gif
//
(other stuff we previously described)
InBlock.gif
IMAGE_TLS_DIRECTORY tls_original;
InBlock.gifIMAGE_TLS_DIRECTORY tls_proxy;
ExpandedBlockEnd.gif}
;
None.gif

加壳器将会在运行期复制原始数据到 tls_orginal 为我们所用。tls_proxy 几乎是一个精确的副本,除了2个项目将会被修改:

None.gif
tls_proxy.AddressOfIndex
None.giftls_proxy.AddressOfCallBacks

在这个块中我们将要初始化 AddressOfIndex 指向一个正常的全局双字变量,并且我们将初始化 AddressOfCallBacks 指向一个函数指针数组。它是一个线程创建时将会被调用的函数指针列表。用户使用它定义 TLS 对象的初始化。唉,我没见过一个编译器使用它。此外,在 9x 下,这些函数不会被调用。尽管如此,我们还是要支持它以防万一哪天它会被使用。我们令 AddressOfCallbacks 指向一个2个成员的数组,一个试我们将要执行的函数指针,另一个是 NULL 作为列表结束符。


设置一个全局双字存储 TLS slot(槽?)

None.gif
DWORD TLS_slot_index;
None.gif

TLS 回调函数必须是这种原型:

None.gif
extern
 
"
C
"
 
void
 NTAPI TLS_callback ( PVOID DllHandle, DWORD Reason, PVOID Reserved );
None.gif

当然还要添加两个逻辑标志表示是否可以安全地调用原来的回调函数,和是否延期调用。这样初始化它们:

None.gif
bool
 safe_to_callback_tls 
=
 
false
;
None.gif
bool
 delayed_tls_callback 
=
 
false
;
None.gif

再提供一些变量保存延迟调用的数据:

None.gif
PVOID TLS_dll_handle 
=
 NULL;
None.gifDWORD TLS_reason 
=
 
0
;
None.gifPVOID TLS_reserved 
=
 NULL;
None.gif

编写回调函数:

None.gif
extern
 
"
C
"
 
void
 NTAPI TLS_callback ( PVOID DllHandle, DWORD Reason, PVOID Reserved )
ExpandedBlockStart.gif
{
InBlock.gif        
if
 ( safe_to_callback_tls )
ExpandedSubBlockStart.gif        
{
InBlock.gif                PIMAGE_TLS_CALLBACK
*
 ppfn 
=
 g_pkrdat.m_tlsdirOrig.AddressOfCallBacks;
InBlock.gif                
if
 ( ppfn )
ExpandedSubBlockStart.gif                
{
InBlock.gif                        
while
 ( 
*
ppfn )
ExpandedSubBlockStart.gif                        
{
InBlock.gif                        (
*
ppfn) ( DllHandle, Reason, Reserved );
InBlock.gif                        
++
ppfn;
ExpandedSubBlockEnd.gif                        }
ExpandedSubBlockEnd.gif                }
InBlock.gif
ExpandedSubBlockEnd.gif        }
InBlock.gif        
else
ExpandedSubBlockStart.gif        
{
InBlock.gif                delayed_tls_callback 
=
 
true
;
InBlock.gif                TLS_dll_handle 
=
 DllHandle;
InBlock.gif                TLS_reason 
=
 Reason;
InBlock.gif                TLS_reserved 
=
 Reserved;
ExpandedSubBlockEnd.gif        }
ExpandedBlockEnd.gif}
None.gif

这样会为 OS 提供一个存储 slot 信息的地方,我们稍候恢复它,并且如果调用了我们的回调函数我们将捕获参数,在解压缩之后调用原来的回调函数。否则会出错因为 0S 会在我们有机会解压缩之前做这件事情。解压缩之后,我们把参数传递给原来的回调函数。


最后一步是这样的:

None.gif
void
 FinalizeTLSStuff()
ExpandedBlockStart.gif
{
InBlock.gif        
if
 ( origdirinfo[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress 
!=
 
0
 )
ExpandedSubBlockStart.gif        
{
InBlock.gif                
*
gev.tls_original.AddressOfIndex 
=
 TLS_slot_index;
InBlock.gif                
void
*
 TLS_data;
InBlock.gif
InBlock.gif                __asm
ExpandedSubBlockStart.gif                
{
InBlock.gif                mov ecx, DWORD PTR TLS_slot_index;
InBlock.gif                mov edx, DWORD PTR fs:[02ch]
InBlock.gif                mov ecx, DWORD PTR [edx
+
ecx
*
4
]
InBlock.gif                mov pvTLSData, ecx
ExpandedSubBlockEnd.gif                }
InBlock.gif
InBlock.gif                
int
 size 
=
 gev.tls_original.EndAddressOfRawData 
-
 gev.tls_original.StartAddressOfRawData;
InBlock.gif
InBlock.gif                memcpy ( pvTLSData, (
void
*
) gev.tls_original.StartAddressOfRawData, size );
InBlock.gif                memset ( (
void
*
) gev.tls_original.EndAddressOfRawData, 
0
,
InBlock.gif                gev.tls_original.SizeOfZeroFill );
ExpandedSubBlockEnd.gif        }
InBlock.gif
InBlock.gif        safe_to_callback_tls 
=
 
true
;
InBlock.gif        
if
 ( delayed_tls_callback )
ExpandedSubBlockStart.gif        
{
InBlock.gif                TLSCallbackThunk ( TLS_dll_handle TLS_reason TLS_reserved );
ExpandedSubBlockEnd.gif        }
ExpandedBlockEnd.gif}

转载地址:http://vhagx.baihongyu.com/

你可能感兴趣的文章
centos6/7安装 tinyproxy (yum安装)
查看>>
简单选择排序
查看>>
noi2008 志愿者招募
查看>>
Razor及HtmlHelper学习笔记
查看>>
自定义标签
查看>>
Mysql客户端中文乱码问题解决
查看>>
php数组增加元素
查看>>
选择排序算法
查看>>
netmap pkt-gen程序代码分析
查看>>
添加网站QQ客服链接
查看>>
分布式缓存:Velocity之应用实践
查看>>
如何编写方法
查看>>
9.高级控件应用
查看>>
Linux---文件权限
查看>>
postfix+ dovecot搭建邮件服务器
查看>>
GPL、BSD和Apache开源许可证
查看>>
2018-10-16 22:56:13 c language
查看>>
1116: 零起点学算法23——摄氏温度转换
查看>>
【UVA 11181】(条件概率)
查看>>
51nod 1533 && CF538F
查看>>