Introduction
Thread-local storage (TLS) is a computer programming method that uses static or global memory local to a thread. Developers use TLS to provide unique data for each thread that the process can access using a global index.
TLS calls are subroutines that are called by the system before the entry point. There is a .tls
section in the PE file that describes the place of TLS callbacks.
Some malwares employ TLS callbacks to detect debuggers, notwithstanding, this trick has been used for years and modern analysis tools detect it.
TLS callbacks 64-bit
We developed this assembly code to demonstrate the functioning of TLS callbacks.
format PE64 GUI 4.0
entry start
include 'include\win64a.inc'
include 'include\api\user32.inc'
section '.text' code readable executable
proc start
invoke MessageBox, 0, szHelloWorld, szHelloWorld, MB_OK
ret
endp
proc callback1
invoke MessageBox, 0, szCallback1, szTitle, MB_OK
ret
endp
proc callback2
invoke MessageBox, 0, szCallback2, szTitle, MB_OK
ret
endp
section '.rdata' data readable
szTitle db 'Callback Message', 0
szHelloWorld db 'Hello World', 0
szCallback1 db 'This is the first tls callback', 0
szCallback2 db 'This is the second tls callback', 0
section '.tls' data readable writeable
data 9
.StartAddressOfRawData dq 0
.EndAddressOfRawData dq 0
.AddressofIndex dq adress_of_index
.AddressOfCallBacks dq adress_of_callbacks
.SizeOfZeroFill dq 0
.Characteristics dq 0
adress_of_index dq 0
adress_of_callbacks dq callback1, callback2, 0
end data
section '.idata' import data readable
library user32, 'user32.dll'
Compile the code above with Fasm to obtain a 64-bit Windows executable
We can clearly see the structure of the TLS section.
This program will execute three MessageBox
functions in a specific order.
- "This is the first tls callback"
- "This is the second tls callback"
- "Hello World"
As you can see, despite it is the first and only function called after the entry point, the MessageBox
with the "Hello World" message will be executed last.
By default most debuggers break at the entry point and consequently the TLS callbacks function are executed.
An attacker could insert an anti-debugging routine inside the TLS callback function to mislead an analyst.
TLS callbacks 32-bit
The code below is the 32-bit version of the 64-bit code above.
format PE GUI 4.0
entry start
include 'include\win32a.inc'
include 'include\api\user32.inc'
section '.text' code readable executable
proc start
invoke MessageBox, 0, szHelloWorld, szHelloWorld, MB_OK
ret
endp
proc callback1
invoke MessageBox, 0, szCallback1, szTitle, MB_OK
ret
endp
proc callback2
invoke MessageBox, 0, szCallback2, szTitle, MB_OK
ret
endp
section '.rdata' data readable
szTitle db 'Callback Message', 0
szHelloWorld db 'Hello World', 0
szCallback1 db 'This is the first tls callback', 0
szCallback2 db 'This is the second tls callback', 0
section '.tls' data readable writeable
data 9
.StartAddressOfRawData dd 0
.EndAddressOfRawData dd 0
.AddressofIndex dd adress_of_index
.AddressOfCallBacks dd adress_of_callbacks
.SizeOfZeroFill dd 0
.Characteristics dd 0
adress_of_index dd 0
adress_of_callbacks dd callback1, callback2, 0
end data
section '.idata' import data readable
library user32, 'user32.dll'
Compile the code above with Fasm to obtain a 32-bit Windows executable