tlsloader.py is python script that can be used as a PyCommand within Immunity Debugger to load a PE file, set breakpoints on all TLS callback functions, and stop execution at the first TLS callback function. Just place it within the PyCommands directory and type
!tlsloader <path_to_file> in the command box to run it. Additional details will be written to the Log window (Alt + L).
Usage of this PyCommand requires you to configure the debugger to make its first pause at the system breakpoint (which not the default setting). To make this change, go to “Options” -> “Debugging options” -> [Events]. I don’t believe that the python API allows me to do this automatically (or at least I didn’t find it).
If the PE file doesn’t contain any TLS callback functions, execution will be paused at the entry point as defined in the PE header. Note: this script does not currently work on DLLs.
Oversimplified, Microsoft’s PE COFF Spec implements Thread Local Storage (TLS) callback functions in order to allow separate threads of a process to maintain different values for variables. TLS callback functions are executed by the loader before the entry point defined in the PE header (which is where debuggers typically set their initial breakpoint). Some malware authors have taken advantage of this capability by creating TLS callback functions that execute code to complicate reverse engineering.
Lenny Zeltser posted an ISC diary entry that better describes how this technique works and provides a process to analyze the callback function(s) using IDA Pro and OllyDbg. The
tlsloader.py PyCommand was written as an exercise to help me better understand TLS code and to further simplify the debugging process so that you won’t have to manually identify the callback function(s) in IDA Pro and set breakpoints in the debugger.
How it works
Ero Carrera’s pefile is used to parse the PE header, locate the TLS directory, and identify all of the callback functions. The PE is opened with the debugger and pauses at the system breakpoint (as configured in debugging options). Breakpoints are set on all of the TLS callback functions that were identified, then the PE executes to the first breakpoint. Opening the Breakpoints window (Alt + B) will allow you to see the callback functions:
A note on DLLs - Since they are not loaded directly by the debugger, this approach does not work on DLLs. When asked to debug a DLL, the debugger invokes loaddll.exe which uses kernel32.LoadLibrary to get the Windows loader to load the DLL into memory; the pause at system breakpoint debugging option does not work here. As a safeguard, tlsloader will exit before the file is loaded if it is asked to load a DLL.
edit: This script is now hosted on GitHub.