Source code for cle.backends.tls.pe_tls
from cle.address_translator import AT
from .tls_object import InternalTLSRelocation, ThreadManager, TLSObject
[docs]class PEThreadManager(ThreadManager):
[docs] def register_object(self, obj):
if not super().register_object(obj):
return False
# The PE TLS header says to write its index into a given address
if hasattr(obj, "tls_index_address"):
obj.memory.pack_word(AT.from_lva(obj.tls_index_address, obj).to_rva(), obj.tls_module_id)
return True
@property
def _thread_cls(self):
return PETLSObject
[docs]class PETLSObject(TLSObject):
"""
This class is used when parsing the Thread Local Storage of a PE binary. It
represents both the TLS array and the TLS data area for a specific thread.
In memory the ``PETLSObj`` is laid out as follows::
+----------------------+---------------------------------------+
| TLS array | TLS data area |
+----------------------+---------------------------------------+
A more detailed description of the TLS array and TLS data areas is given
below.
**TLS array**
The TLS array is an array of addresses that points into the TLS data area.
In memory it is laid out as follows::
+-----------+-----------+-----+-----------+
| address | address | ... | address |
+-----------+-----------+-----+-----------+
| index = 0 | index = 1 | | index = n |
+-----------+-----------+-----+-----------+
The size of each address is architecture independent (e.g. on X86 it is
4 bytes). The number of addresses in the TLS array is equal to the number
of modules that contain TLS data. At load time (i.e. in the ``finalize``
method), each module is assigned an index into the TLS array. The address
of this module's TLS data area is then stored at this location in the
array.
**TLS data area**
The TLS data area directly follows the TLS array and contains the actual
TLS data for each module. In memory it is laid out as follows::
+----------+-----------+----------+-----------+-----+
| TLS data | zero fill | TLS data | zero fill | ... |
+----------+-----------+----------+-----------+-----+
| module a | module b | ... |
+---------------------------------------------------+
The size of each module's TLS data area is variable and can be found in the
module's ``tls_data_size`` property. The same applies to the zero fill. At
load time (i.e in the ``finalize`` method), the initial TLS data values are
copied into the TLS data area. Because a TLS index is also assigned to each
module, we can access a module's TLS data area using this index into the
TLS array to get the start address of the TLS data.
"""
[docs] def __init__(self, thread_manager: PEThreadManager):
super().__init__(loader=thread_manager.loader, arch=thread_manager.arch)
self.used_modules = len(thread_manager.modules)
self.data_start = self.arch.bytes * thread_manager.max_modules
self.used_data = 0
self.memory.add_backer(0, bytes(self.data_start))
self.pic = True
for obj in thread_manager.modules:
image = thread_manager.initialization_image(obj)
if image is None:
continue
image_offset = self.data_start + self.used_data
index_offset = obj.tls_module_id * self.arch.bytes
self.memory.pack_word(index_offset, image_offset)
self.relocs.append(InternalTLSRelocation(image_offset, index_offset, self))
self.memory.add_backer(image_offset, image)
self.used_data += len(image)
[docs] def get_tls_data_addr(self, tls_idx):
"""
Get the start address of a module's TLS data area via the module's TLS
index.
From the PE/COFF spec:
The code uses the TLS index and the TLS array location (multiplying
the index by the word size and using it as an offset into the
array) to get the address of the TLS data area for the given
program and module.
"""
if 0 <= tls_idx < self.used_modules:
return self.memory.unpack_word(tls_idx * self.arch.bytes)
else:
raise IndexError("TLS index out of range")
@property
def max_addr(self):
return self.mapped_base + self.data_start + self.used_data - 1
# PE is MUCH simpler in terms of what's the pointer to the thread data. Add these properties for compatibility.
@property
def thread_pointer(self):
return self.mapped_base
@property
def user_thread_pointer(self):
return self.mapped_base