【问题标题】:MASM doesn't recognize my TLS callbackMASM 无法识别我的 TLS 回调
【发布时间】:2017-09-21 10:59:32
【问题描述】:

我已经生成了我的.C 源文件的汇编程序列表。在 C 源代码中,我实现了这样的 tls:

char *msg = "callback";
void NTAPI tls_callback(PVOID DllHandle, DWORD dwReason, PVOID lpVd)
{
    MessageBoxA(0,msg,msg,0);

}

#ifdef _WIN64
#pragma comment (linker, "/INCLUDE:_tls_used")
#pragma comment (linker, "/INCLUDE:tls_callback_func") 
#else
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:_tls_callback_func")
#endif

#ifdef _WIN64
#pragma const_seg(".CRT$XLF")
EXTERN_C const
#else
#pragma data_seg(".CRT$XLF")
EXTERN_C
#endif
PIMAGE_TLS_CALLBACK tls_callback_func = tls_callback;
#ifdef _WIN64
#pragma const_seg()
#else
#pragma data_seg()
#endif

__declspec(thread) char *tlsData = "tls static data";

我已经生成了这个 C 文件的汇编列表,tls 现在看起来像这样:

PUBLIC  _tls_callback@12
PUBLIC  _tls_callback_func
PUBLIC  _tlsData

_TLS    SEGMENT
_tlsData 
    DB  00H
    DB  00H
    DB  00H
    DB  00H
    DB  00H
    DB  40H
    DB  30H
    DB  80H
_TLS    ENDS
CRT$XLF SEGMENT
_tls_callback_func DD FLAT:_tls_callback@12
CRT$XLF ENDS


_TEXT   SEGMENT
_DllHandle$ = 8                     ; size = 4
_dwReason$ = 12                     ; size = 4
_lpVd$ = 16                     ; size = 4
_tls_callback@12 PROC                   ; COMDAT

    push    ebp
    mov ebp, esp

    mov edx, DWORD PTR _msg

    push 0
    push edx
    push edx
    push 0
    call DWORD PTR __imp__MessageBoxA@16
; Line 34
    pop ebp
    ret 12                  ; 0000000cH
_tls_callback@12 ENDP
_TEXT   ENDS

我没有看到生成 tls 模式,但是我在 IDA PRO 中查找了模式应该是:

.rdata:004921A8 __tls_used      dd offset __tls_start  
.rdata:004921AC TlsEnd_ptr      dd offset __tls_end
.rdata:004921B0 TlsIndex_ptr    dd offset __tls_index
.rdata:004921B4 TlsCallbacks_ptr dd offset _tls_callback_func
.rdata:004921B8 TlsSizeOfZeroFill dd 0
.rdata:004921BC TlsCharacteristics dd 100000h

那么我需要定义一个新的 tls 段并将这些模式放在那里吗?还是应该在数据部分?

我是这样编译的:

ml.exe listing.asm /coff

我在 ida pro 中查找了生成的文件,我发现 tls 目录根本没有生成,我如何告诉 masm 或其链接器生成目录?

【问题讨论】:

  • 我找不到链接器或汇编器生成 TLS 目录的任何选项。可能最快的解决方法是将 TLS 结构放在某个段(即部分)中,让链接器生成符号映射并编写读取映射文件并修补可执行文件的脚本。
  • 切勿尝试汇编和使用 Microsoft C/C++ 编译器的汇编输出。不知道这里是不是这样,但是一般微软C/C++编译器生成的汇编输出是不完整的,不正确的。您的目标是将程序集或 C++ 函数称为 TLS 回调吗?
  • @RossRidge 使用 masm 组件中的 tls
  • @RossRidge 我找到了解决方案,稍后我会针对这个问题发布答案

标签: assembly callback linker masm ida


【解决方案1】:

Microsoft 链接器使用符号 __tls_used(在 x86 系统上)或 _tls_used(在非 x86 系统上)作为 TLS 目录的地址。 TLS 目录包含一个指向以零结尾的 TLS 回调数组的指针。因此,通过创建一个合适的 TLS 目录并将其命名为 __tls_used/_tls_used,您可以在汇编代码中拥有一个 TLS 回调函数。

下面是一个演示这个的示例程序:

    PUBLIC  __tls_used
    PUBLIC  start

    EXTERN  __imp__GetStdHandle@4:DWORD, __imp__WriteFile@20:DWORD, __imp__ExitProcess@4:DWORD

_BSS    SEGMENT PUBLIC DWORD FLAT 
tls_index DD    ?
_BSS    ENDS

_RDATA  SEGMENT PUBLIC PARA FLAT ALIAS('.rdata') READ

__tls_used:
    DD  OFFSET tls_start
    DD  OFFSET tls_end
    DD  OFFSET tls_index
    DD  OFFSET tls_callbacks
    DD  tls_bss_end - tls_end
    DD  0   ; chracterstics

tls_callbacks:
    DD  OFFSET tls_callback
    DD  0

main_msg:
    DB  "Main entry called.", 13, 10
main_msg_len = $ - main_msg

_RDATA  ENDS

_DATA   SEGMENT PUBLIC FLAT
tls_cb_msg:
    DB  "TLS callback called. Reason: 0", 13, 10
tls_cb_msg_len = $ - tls_cb_msg
_DATA   ENDS


_TLS    SEGMENT PUBLIC DWORD FLAT ALIAS('.tls')
tls_start:  
    ; put initialized TLS variable definitions here
tls_end:
    ; put uninitialized TLS variable definitions here
tls_bss_end:
_TLS    ENDS

_TEXT   SEGMENT PUBLIC PARA 'CODE' FLAT
    ASSUME  DS:FLAT

tls_callback:
    mov eax, [esp + 8]
    add BYTE PTR [tls_cb_msg + tls_cb_msg_len - 3], al

    push    -11 ; nStdHandle
    call    [__imp__GetStdHandle@4]

    push    0   ; lpOverlapped
    push    0   ; lpNumberOfBytesWritten
    pushd   tls_cb_msg_len ; nNumberOfBytesToWrite
    push    tls_cb_msg ; lpBuffer
    push    eax ; hFile
    call    [__imp__WriteFile@20]
    ret 12

start:
    push    -11 ; nStdHandle
    call    [__imp__GetStdHandle@4]

    push    0   ; lpOverlapped
    push    0   ; lpNumberOfBytesWritten
    pushd   main_msg_len ; nNumberOfBytesToWrite
    push    main_msg ; lpBuffer
    push    eax ; hFile
    call    [__imp__WriteFile@20]

    push    0   ; uExitCode
    call    [__imp__ExitProcess@4]
    int 3

_TEXT   ENDS

    END start

请注意,上述代码与 TLS 的 Visual C++ 运行时 (CRT) 实现不兼容。如果您打算使用 C++ 代码,那么您需要让 CRT 提供 TLS 目录和其他信息。您可以通过在.CRT$XL? 部分中放置一个指向函数的指针来告诉它使用您的回调之一,其中问号? 被替换为BY 的字母。使用字母 B 将导致在调用 CRT TLS 回调之前调用它。使用从DY 的字母将在之后调用它。所以你想要的代码是这样的:

_CRT SEGMENT PUBLIC DWORD ALIAS('.CRT$XLD')
     DD OFFSET my_tls_callback
_CRT ENDS

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-26
    • 2019-04-11
    相关资源
    最近更新 更多