一开始想这节的名字叫做加密TLS表,后来感觉TLS表的加密实在是有些不符合。主要是出于TLS节加密的必要性考虑。所以改成处理TLS表了。我自身喜欢把TLS利用成模糊入口点来利用。此节规划是这样的,我们先理解一下什么是TLS以及它的结构。接下来汇编一个TLS节及相关在MASM中的应用。最后,我们讲解如何修改现有的TLS以及制作TLS入口点。

什么是TLS表
这个问题最有力的参考莫过于MSDN的文档了。这种源的东西比起用我的语言归纳要清楚的多。
MSDM文档的连接为
ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_vclang/html/a0f1b109-c953-4079-aa10-e47f5483173d.htm
引用自MSDN

引用:
线程本地存储 (TLS) 是一个方法,通过该方法,给定的多线程进程中的每个线程都可以分配存储线程特定数据的位置。动态绑定(运行时)线程特定数据是通过 TLS API(TlsAlloc、TlsGetValue、TlsSetValue 和 TlsFree)的方式支持的。除了现有的 API 实现,Win32 和 Visual C++ 编译器现在还支持静态绑定(加载时间)基于线程的数据。

TLS 的 API 实现
线程本地存储是通过 Win32 API 层和编译器实现的。有关更多信息,请参见 Win32 API 文档中的 TlsAlloc、TlsGetValue、TlsSetValue 和 TlsFree。

Visual C++ 编译器提供了一个关键字(而不通过 API 层)使 TLS 操作更加自动化。将在下一节(TLS 的编译器实现)描述此语法。

TLS 的编译器实现
为了支持 TLS,已将新属性 thread 添加到了 C 和 C++ 语言,并由 Visual C++ 编译器支持。此属性是一个扩展存储类修饰符,如上一节中所述。使用 __declspec 关键字声明 thread 变量。例如,以下代码声明了一个整数线程局部变量,并用一个值对其进行初始化:
__declspec( thread ) int tls_i = 1;
其实我们把TLS理解成为可以在预先给每条线程所使用并预先规划出一片区域的数据。
文档中还有些关于TLS变量的使用地点这里总结一下:
TLS变量只能定义变量
TLS变量只能声明为公共静态的
TLS变量不能修饰函数
不能在函数内定义,不能作为函数的参数
在正常代码内不能引用TLS变量
这些编译方面上的限制,应该是为代码安全与稳定性上设定的。毕竟,TLS是作为多线程一部分使用的。在内存访问上没有明确的限制(在运行代码中自行定位TLS变量并引用是没有问题的)。
下面我们了解一下TLS表的结构。
TLS表的目录索引处在数据目录的第9个索引上,可以使用微软提供的宏IMAGE_DIRECTORY_ENTRY_TLS来直接定位。
这个目录的VirtualAddress指向一个IMAGE_TLS_DIRECTORY32的结构,如下所示:
IMAGE_TLS_DIRECTORY32 STRUCT
    StartAddressOfRawData dd    ?
    EndAddressOfRawData dd      ?
    AddressOfIndex dd           ?
    AddressOfCallBacks dd       ?
    SizeOfZeroFill dd           ?
    Characteristics dd          ?
IMAGE_TLS_DIRECTORY32 ENDS
这里解释一下
StartAddressofRawData为TLS数据起始的地址
EndAddressOfRawData为结束的地址
AddressOfIndex是TLS索引的地址
AddressOfCallBacks指向一组以NULL结尾的IMAGE_TLS_CALLBACK函数的地址数组
以上都为地址,这里使用的是VA,而不是RVA了。当修改时这是要注意的地方。
SizeOfZeroFill表明填充TLS变量区域的大小
Characteristics用作保留,可能用作TLS标志。
在一个EXE程序访问TLS变量时要经过以下步骤。
(这里需要注意的是DLL访问TLS变量除非这个DLL是静态链接到这个EXE中,否则使用LoadLibrary动态加载的DLL是不会加载TLS变量的)
1.链接时。连接器设置TLS目录的AddressOfIndex字段。这个字段指向一个地址。在这个地址保存了程序所用的TLS索引。
2.当线程创建的时候,通常在TEB开得2ch处位置防止一个指向TLS数组的指针。TEB的地址放入FS中保存所以可以使用FS[2ch]引用此TLS数组。
3.我们在可执行代码中使用1提供的TLS索引与2提供的TLS数组的地址来使用TLS所提供的数据。
需要注意的是TLS数组在每条线程都有维护的一个区域。这个数组的每个地址指出了程序中给定模块的TLS数据区的位置。TLS索引指出了这个数组的哪个元素。
这里给出一段使用TLS的代码。读者可以自行参阅MSDN的文档深入理解。
代码:
Start:
    invoke MainFunc
    invoke ExitProcess, 1
CommonFunc proc
    LOCAL lpvData : LPVOID
    LOCAL dwThreadId : DWORD
    
    invoke TlsGetValue, dwTlsIndex
    .IF eax == 0
        ret
    .ENDIF
    mov lpvData, eax
    invoke Sleep, 5000
    ;; 显示信息
    invoke GetCurrentThread
    mov dwThreadId, eax
    invoke PrintLine, offset g_szOutThread, addr dwThreadId
    invoke PrintLine, offset g_szOutData, addr lpvData
    ret
    
CommonFunc endp 
  
ThreadFunc proc
    LOCAL lpvData : LPVOID
    LOCAL dwThreadId : DWORD
    invoke LocalAlloc, LPTR, 256
    mov lpvData, eax
    invoke TlsSetValue, dwTlsIndex, eax
    .IF eax == 0
        ret
    .ENDIF
    invoke GetCurrentThread
    mov dwThreadId, eax
    invoke PrintLine, offset g_szOutThread, addr dwThreadId
    invoke PrintLine, offset g_szOutData, addr lpvData  
    ;; 调用公共函数
    invoke CommonFunc
    ;; 释放内存
    invoke TlsGetValue, dwTlsIndex
    .IF eax != 0
        invoke LocalFree, lpvData
    .ENDIF
    ret

ThreadFunc endp  
  
MainFunc proc
    LOCAL IDThread : DWORD
    LOCAL hThread[THREADCOUNT] : HANDLE
    
    invoke TlsAlloc
    .IF eax == TLS_OUT_OF_INDEXES
        ret
    .ENDIF
    mov dwTlsIndex, eax
    
    xor ecx, ecx
    mov ebx, THREADCOUNT
CreateThreadLoop:
    cmp ecx, ebx
    jz EndCreateThreadLoop
    push ecx
    invoke CreateThread, NULL, 0, ThreadFunc, NULL, 0, addr IDThread
    pop ecx
    mov hThread[ecx*sizeof HANDLE], eax
    inc ecx
    jmp CreateThreadLoop           
EndCreateThreadLoop:    

    xor ecx, ecx
    mov ebx, THREADCOUNT
WaitThreadLoop:
    cmp ecx, ebx
    jz EndWaitThreadLoop
    push ecx
    invoke WaitForSingleObject, hThread[ecx*sizeof HANDLE], INFINITE
    pop ecx
    inc ecx
    jmp WaitThreadLoop
    ;; 是否TLS索引
    invoke TlsFree, dwTlsIndex
    ret    
    
EndWaitThreadLoop:

    ret

MainFunc endp

PrintLine proc uses eax ebx ecx edx edi esi pFormat : LPSTR, pBuffer : LPSTR
;; it's must be include masm32.inc and masm32.lib
    LOCAL szBuf[MAX_PATH] : BYTE
    
    mov ebx, pFormat
    mov edx, pBuffer
    invoke wsprintf, addr szBuf, ebx, edx
    invoke StdOut, addr szBuf
    mov ebx, offset CRLF
    invoke StdOut, ebx
    ret
    
CRLF db 0Dh,0Ah,0    
PrintLine endp 
end Start
这段代码很简单是TLS函数相关使用C++的代码可以从MSDN文档处获取。
接下来我们介绍一下关于TLS回调函数这个东西,这个东西对于编写病毒来说直安逸的。大多数调试器是从PE加载器开始加载,几乎都跳过读取此入口的。呵呵。话说壳技术也是从病毒技术发展而来的。是先有病毒后有壳的,他们两者有太多相似的地方。不过貌似安全圈里很多的技术都是从Anti-安全到安全过渡的。想想rootkit的技术不也一样吗!
这个回调函数的地址是由AddressOfCallBacks字段指出的。指向的地址是一个由IMAGE_TLS_CALLBACK函数组成的数组并以 NULL做结束符。回调函数的顺序是依次调用。这个TLS回调函数是在加载引入表之后,所以在引入表中的函数都可以直接使用。如果加密了引入表,在这里就需要做一些手段了。不过要注意的是切不可在这里解密引入表。否则,加密引入表就没有意义了。
回调函数的原型与DLL启动函数的样子很像。
TLS_CALLBACK proto Dllhandle : LPVOID, Reason : DWORD, Reserved : LPVOID
参数如下:
Dllhandle : 为模块的句柄
Reason可取以下值:
DLL_PROCESS_ATTACH 1 : 启动一个新进程被加载
DLL_THREAD_ATTACH 2 : 启动一个新线程被加载
DLL_THREAD_DETACH 3 : 终止一个新线程被加载
DLL_PROCESS_DETACH 0 : 终止一个新进程被加载
Reserverd:用于保留,设置为0
函数的参数是被加载器设置。我们的函数只管判断就可以。
下面就让我们使用MASM打造一个带有回调功能的TLS节的程序,最初使用见到使用MASM编写TLS相关是在一个俄国人的论坛的代码里。随后的附件中也会附带此代码名为:TlsInAsm。这里我们还是自己打造一个。在TlsInAsm中我学习到了一些关于MASM的编译选项。
代码如下
代码:
.data?
dwTLS_Index dd  ?

 ;; 这里的OPTION DOTNAME的意思为让ML可以使用以“.”开头的变量,结构,联合,标号等
;; 开启这个选项也可以在ML中使用/Zm选项来启动。
OPTION    DOTNAME
 ;; 定义一个TLS节
;; SEGMENT标志在ML里是用于定义一个新节。
.tls  SEGMENT                        
TLS_Start LABEL  DWORD
 dd    0100h    dup ("slt.")
TLS_End   LABEL  DWORD
.tls   ENDS
OPTION    NODOTNAME

.data
TLS_CallBackStart   dd TlsCallBack0
TLS_CallBackEnd     dd 0

g_szTitle           db "Hello TLS",0
g_szInTls           db "我在TLS里",0
g_szInNormal        db "我在正常代码内",0

 ;; 这里需要注意的是,
;; 必须要将此结构声明为PUBLIC,用于让连接器连接到指定的位置,
;; 其次结构名必须为_tls_uesd这是微软的一个规定。编译器引入的位置名称也如此。
PUBLIC _tls_used
_tls_used IMAGE_TLS_DIRECTORY <TLS_Start, TLS_End, dwTLS_Index, TLS_CallBackStart, 0, ?>

.code
Start:
    ;; 在正常代码中使用
    invoke MessageBox, NULL, addr g_szInNormal, addr g_szTitle, MB_OK
    invoke ExitProcess, 1
    ret

;; TLS的回调函数
TlsCallBack0 proc Dllhandle : LPVOID, dwReason : DWORD, lpvReserved : LPVOID
   ;; 这里可以判断dwReason发生的条件
   mov eax, dwReason
   ;; 在进行加载时被调用
   cmp eax, DLL_PROCESS_ATTACH
   jnz ExitTlsCallBack0
   invoke MessageBox, NULL, addr g_szInTls, addr g_szTitle, MB_OK
   mov dword ptr[TLS_Start],0  
   xor eax, eax
   inc eax
ExitTlsCallBack0:   
   ret
TlsCallBack0   ENDP

end Start
以上代码给出了如何汇编一个具有回调函数的TLS节程序。代码很简单有关MASM的选项可参照masm32中help的自带文档。可惜没中文也没有例子,此文档显的非常鸡肋。不过有总比没好。如果有对 MASM语法熟悉的大哥,请赐教小弟一二。我使用MASM也是从今年开始,纯属小白一个。
下面该轮到修改TLS了。这段也可以作为加解密TLS用,但是个人感觉不到TLS加解密的必要,纯属一个可选的选项。不过用作Anti-debug是个用途。yC壳中也是将TLS进行了一个简单的移动。而在加密时掠过以.tls为名的节,这个就会造成不稳定性。而完美的处理TLS比较麻烦,如果TLS回调函数不止一个,我们必须要读取所有的TLS回调函数并判断它的结束当然这需要一个反汇编引擎来做。而有些加入Anti-反汇编代码的也会产生读取错误,最后还要做一些重定位的工作。考虑至此我们此刻的代码功能是这样的,首先复制TLS表,然后进行一个TLS入口点的替换。最后跳转回TLS真正的回调函数中。由于TLS程序并不常见,读者可以使用以上提供的程序进行测试。
现在我们来描述一下算法:
现在我们有这样几种情况
1.有TLS表
  1.1有TLS回调函数
     修改AddressOfCallBacks字段指向区域的第一个回调函数的VA为我们自己回调函数的VA
  1.2无TLS回调函数
     设置一个新的TLS回调函数地址表,并将新的回调函数地址表的VA设置给它
2.无TLS表
  汇编一个新的TLS表
在我们自己的回调函数中也有不一样的地方。
如果是1.1的情况那么要记录原先第一个回调函数的VA并且最后进行跳转
如果是1.2的情况那么直接返回即可.(最后不要忘记清除参数所造成的堆栈开销)
代码:
ModifyTLS proc szFileName : LPSTR
    LOCAL hFile : HANDLE
    LOCAL hMap : HANDLE
    LOCAL pMem : LPVOID
    LOCAL dwOrigFileSize : DWORD
    LOCAL dwNTHeaderAddr : DWORD
    LOCAL dwNewTlsSectionOffset : DWORD
    
    ;; init data
    xor eax, eax
    mov g_bError, al
    mov eax, OWNTLS_SIZE
    mov g_dwNewSectionSize, eax
    
    ;; open file
    invoke CreateFile, szFileName,\
                      GENERIC_WRITE + GENERIC_READ,\
                      FILE_SHARE_WRITE + FILE_SHARE_READ,\
                      NULL,\
                      OPEN_EXISTING,\
                      FILE_ATTRIBUTE_NORMAL,\
                      0
    .IF eax == INVALID_HANDLE_VALUE
        jmp OpenFileFailed
    .ENDIF
    mov hFile, eax 
    invoke GetFileSize, hFile, NULL
    .IF eax == 0
        invoke CloseHandle, hFile  
        jmp GetFileSizeFailed
    .ENDIF
    mov dwOrigFileSize, eax
    add eax, APPEND_SIZE
    xchg eax, ecx
    ;; create memory map
    xor ebx, ebx     
    invoke CreateFileMapping, hFile, ebx, PAGE_READWRITE, ebx, ecx, ebx
    .IF eax == 0
        invoke CloseHandle, hFile
        jmp CreateMapFailed                
    .ENDIF
    mov hMap, eax
    ;; map file to memory
    invoke MapViewOfFile, hMap,
                       FILE_MAP_WRITE+FILE_MAP_READ+FILE_MAP_COPY, 
                       ebx, ebx, ebx
    .IF eax == 0
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp MapFileFailed
    .ENDIF
    mov pMem, eax                               
    ;; check it's PE file or not ?
    xchg eax, esi
    assume esi : ptr IMAGE_DOS_HEADER
    .IF [esi].e_magic != 'ZM'
        invoke UnmapViewOfFile, pMem
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp InvalidPE        
    .ENDIF       
    add esi, [esi].e_lfanew
    assume esi : ptr IMAGE_NT_HEADERS   
    .IF word ptr [esi].Signature != 'EP'
        invoke UnmapViewOfFile, pMem
        invoke CloseHandle, hMap
        invoke CloseHandle, hFile
        jmp InvalidPE        
    .ENDIF
    mov dwNTHeaderAddr, esi
    
    ;; 增加一个新节,此节为我们自己的TLS节
    invoke AddSection, pMem, offset g_szNewSectionName, g_dwNewSectionSize
    mov dwNewTlsSectionOffset, eax    
    
    ;; 寻找原始的TLS节并且记录它
    mov eax, dword ptr [esi].OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS * sizeof IMAGE_DATA_DIRECTORY].VirtualAddress
    .IF eax == 0
        ;; 如果没有TLS节则汇编一个新的
        ;; 设置标志变量
        mov eax, offset OwnTLS_OrigYes
        push 0
        pop dword ptr [eax]
        jmp NoTLSCallBack
    .ENDIF
    invoke RVA2Offset, pMem, eax
    add eax, pMem
    ;; 复制这个表到我们自己的内存
    mov esi, eax
    lea edi, Orig_TLS
    mov ecx, sizeof IMAGE_TLS_DIRECTORY
    cld
    rep movsb
    ;; 汇编一个新的TLS节并设置回调函数   
    ;; 这里检验是否存在回调函数
    mov eax, offset Orig_TLS
    assume eax : ptr IMAGE_TLS_DIRECTORY
    mov eax, dword ptr [eax].AddressOfCallBacks
    ;; 首先将VA转换为RVA然后转换为FVA
    mov esi, dwNTHeaderAddr
    assume esi : ptr IMAGE_NT_HEADERS
    sub eax, dword ptr [esi].OptionalHeader.ImageBase
    invoke RVA2Offset, pMem, eax
    add eax, pMem
    mov ebx, dword ptr [eax]
    test ebx, ebx
    jz NoTLSCallBack
    ;; 保存原回调函数的地址
    mov edx, offset OwnTLS_OrigCallBackEntry
    mov dword ptr [edx], ebx
    ;; 有回调函数这修改,修改位置为eax指向的地方
    mov ebx, dwNewTlsSectionOffset
    assume ebx : ptr IMAGE_SECTION_HEADER
    mov ebx, dword ptr [ebx].VirtualAddress
    add ebx, dword ptr [esi].OptionalHeader.ImageBase
    mov dword ptr [eax], ebx
    ;; 设置TLS回调跳转标志
    mov eax, offset OwnTLS_OrigYes
    push 1
    pop dword ptr [eax]
    jmp ContinueNoTLSCallBack
NoTLSCallBack:
    ;; 如果没有回调函数,则重新建立一个回调函数地址表
    ;; 并且设置新的TLS目录的字段
    mov ebx, dwNewTlsSectionOffset
    assume ebx : ptr IMAGE_SECTION_HEADER
    mov edx, dword ptr [ebx].VirtualAddress
    add edx, offset OwnTLS_CallBackStart - offset OwnTLS_Section
    add edx, dword ptr [esi].OptionalHeader.ImageBase
    mov eax, offset Orig_TLS
    assume eax : ptr IMAGE_TLS_DIRECTORY
    mov dword ptr [eax].AddressOfCallBacks, edx
    ;; 设置AddressOfCallBacks
    sub edx, offset OwnTLS_CallBackStart - offset OwnTLS_CallBack
    mov ebx, offset OwnTLS_CallBackStart
    mov dword ptr [ebx], edx
    ;; 设置AddressOfIndex
    add edx, offset OwnTLS_Index - offset OwnTLS_CallBack
    mov dword ptr [eax].AddressOfIndex, edx
    ;; 设置StartAddressOfRawData
    add edx, offset OwnTLS_Start - offset OwnTLS_Index
    mov dword ptr [eax].StartAddressOfRawData, edx
    ;; 设置EndAddressOfRawData
    add edx, offset OwnTLS_End - offset OwnTLS_Start
    mov dword ptr [eax].EndAddressOfRawData, edx
    ;; 设置SizeOfZeroFill
    push 0
    pop dword ptr[eax].SizeOfZeroFill
ContinueNoTLSCallBack:
    ;; 这里复制TLS到新的TLS表中
    mov eax, dwNewTlsSectionOffset
    assume eax : ptr IMAGE_SECTION_HEADER
    ;; 将数据目录设置到我们自己的TLS节内
    mov eax, dword ptr [eax].VirtualAddress
    add eax, offset Orig_TLS - offset OwnTLS_Section
    mov dword ptr [esi].OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS * sizeof IMAGE_DATA_DIRECTORY].VirtualAddress, eax
    push sizeof IMAGE_TLS_DIRECTORY
    pop dword ptr [esi].OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS * sizeof IMAGE_DATA_DIRECTORY].isize
    
    ;; 复制内容到新的TLS节
    mov edi, dwNewTlsSectionOffset
    assume edi : ptr IMAGE_SECTION_HEADER
    mov edi, dword ptr [edi].PointerToRawData
    add edi, pMem
    mov esi, offset OwnTLS_Section
    mov ecx, g_dwNewSectionSize
    cld
    rep movsb
    
    ;; close handle & write it
    invoke UnmapViewOfFile, pMem
    invoke CloseHandle, hMap
    invoke CloseHandle, hFile                           
 LogicShellExit:
     .IF g_bError == 0
         ;; show success message  
         invoke MessageBox, NULL, offset g_szDone, offset g_szDoneCap, MB_ICONINFORMATION
     .ENDIF        
     assume eax : nothing
     assume ebx : nothing
     assume edx : nothing
     assume esi : nothing
     assume edi : nothing
     ret
;; ----- Show error message ----- 
OpenFileFailed:
    lea eax, g_szOpenFileFailed
    jmp ShowErr
GetFileSizeFailed:
    lea eax, g_szGetFileSizeFailed
    jmp ShowErr    
CreateMapFailed:
    lea eax, g_szCreateMapFailed
    jmp ShowErr
MapFileFailed:
    lea eax, g_szMapFileFailed
    jmp ShowErr        
InvalidPE:          
    lea eax, g_szInvalidPE
    jmp ShowErr
ShowErr:
     invoke MessageBox, NULL, eax, offset g_szErr, MB_ICONERROR
     mov al, 1
     mov g_bError, al
     jmp LogicShellExit

;; ----- 自己的TLS数据 -----
OwnTLS_Section:
;; 我们自己的TLS回调函数
OwnTLS_CallBack:
    ;; 只判断它的启动状态
    OwnTLS_CallBackArg_Reason           equ 08h
    ;; 这里没有做堆栈帧所以esp指向的是返回地址和
    ;; 而我们所用的参数Reason是第2个所以这里加08h
    mov eax, dword ptr [esp+OwnTLS_CallBackArg_Reason]
    cmp eax, DLL_PROCESS_ATTACH
    jnz ExitOwnTLS_CallBack
    ;; 在这里停下,把注释停掉,可以在ollydbg里调试查看
    ;int 3
ExitOwnTLS_CallBack:        
    ;; 这里做一个判断,如果是新的TLS则直接退出如果是修改的TLS则跳入原来的
    call GetEip
    GetEip:
    pop eax
    mov edx, eax
    add eax, offset OwnTLS_OrigYes - offset GetEip
    mov eax, dword ptr [eax]
    test eax, eax   ; 0 为原来没有TLS,1为有TLS回调
    ;; 这里取出原始的TLS回调函数入口并跳入
    jz NotHaveOrigTlsCallBack
    add edx, offset OwnTLS_OrigCallBackEntry - offset GetEip
    jmp dword ptr [edx]      ; 跳入正常的回调函数入口
NotHaveOrigTlsCallBack:
    retn 03h                 ; 清除参数造成的堆栈开销    
    ;; ----- 原始回调函数入口 -----         
;; 原来的程序是否有回调函数
OwnTLS_OrigYes              dd  0  
;; 原TLS回调函数入口点
OwnTLS_OrigCallBackEntry    dd  0
;; TLS变量索引
OwnTLS_Index                dd  0
;; TLS回调函数地址表
OwnTLS_CallBackStart        dd  0
OwnTLS_CallBackEnd          dd  0
;; TLS变量区域
OwnTLS_Start:
    db 80h dup ("tls")
OwnTLS_End:
;; 保存原始的TLS节目录
Orig_TLS IMAGE_TLS_DIRECTORY <0>
EndOwnTLS_Section:

ModifyTLS endp
到这里TLS相关就讲解完毕了。在TLS回调函数中可以用异常链的方法取出ntdll.dll的句柄并用过引出表找到你想用的函数并使用它。例如加载驱动。呵呵!!!
这个程序在加有TLS节的程序会修改TLS回调函数地址。
对无TLS节的程序会给他添加一个。

这节对TLS节本身的使用并没有过多的探讨。TLS回调函数与加密解思路是在这里了。怎样使用就看读者的想象力了。
上传的附件 UseTLS_src.rar
UseTLS_bin.rar
TestTLS_src.rar
TestTLS_bin.rar
ModifyTLS_src.rar
ModifyTLS_bin.rar

  • 标 题:TlsInAsm
  • 作 者:玩命
  • 时 间:2008-07-01 05:35:14

一个国外玩家关于TLS的代码。
最初汇编一个TLS节就是从中学习的。。。 这里也上传到此

上传的附件 TlsInAsm.rar