Drltrace is a dynamic API calls tracer for Windows and Linux applications designed primarily for malware analysis. Drltrace is built on top of DynamoRIO dynamic binary instrumentation framework.
The release build can be downloaded here.
Why Drltrace Rock?
- Fast enough to perform analysis of malicious samples without being detected by time-based anti-research techniques.
- Supports both x86 and x64 (ARM in future).
- Supports both Windows and Linux (macOS in future).
- Supports self-modifying code.
- Supports all types of library linkage (static and dynamic).
- Not-detectable by standard anti-research approaches (anti-hooking, anti-debugging and anti-emulation).
- User can easily add a new function prototype to tell drltrace how to print more details about previously unknown API calls (even about non-system DLLs). External configuration file is used.
- Easy-to-use and modify for your own purposes (no additional package requirements, no heavy-weight GUI interface).
- Open-source, code is clear and well-documented. You can freely build & use your own advanced solution on top of drltrace.
The usage of drltrace is very simple. A user needs to specify a log directory and a name of a target process in the following way:
drltrace -logdir . -- calc.exe
That’s all, the tool will inject required DLLs in the target process, starts instrumentation and in parallel will log information about all library calls which are executed in the target process:
~~43600~~ msvcrt.dll!__wgetmainargs arg 0: 0x010d2364 arg 1: 0x010d2368 and return to module id:0, offset:0x193a ~~43600~~ ntdll.dll!EtwEventRegister arg 0: 0x002ff994 arg 1: 0x010d1490 and return to module id:0, offset:0x157e ~~43600~~ ntdll.dll!EtwEventSetInformation arg 0: 0x007b4b40 arg 1: 0x00000033 and return to module id:0, offset:0x15a1 ~~43600~~ SHELL32.dll!ShellExecuteW arg 0: <null> (type=<unknown>, size=0x0) arg 1: <null> (type=wchar_t*, size=0x0) arg 2: calculator:// (type=wchar_t*, size=0x0) arg 3: <null> (type=wchar_t*, size=0x0) arg 4: <null> (type=wchar_t*, size=0x0) arg 5: 0x1 (type=int, size=0x4) and return to module id:0, offset:0x167d
The format of the output is simple and can be easily parsed by an external script:
~~[thread id]~~ [dll name]![api call name] arg [arg #]: [value] (type=[Windows type name], size=[size of arg]) and return to module id:[module unique id], offset:[offset in memory]
The module unique identifiers table is printed at the end of the log file:
Module Table: version 3, count 70 Columns: id, containing_id, start, end, entry, checksum, timestamp, path 0, 0, 0x010d0000, 0x010da000, 0x010d1b80, 0x0000f752, 0xb5fe3575, C:\Windows\SysWOW64\calc.exe 1, 1, 0x6d4c0000, 0x6d621000, 0x6d563940, 0x00136d65, 0x59ce1b0b, C:\Users\Max\Downloads\drltrace\drltrace\dynamorio\lib32\release\dynamorio.dll 2, 2, 0x73800000, 0x73975000, 0x7380dbf7, 0x00000000, 0x59ce1b0f, C:\Users\Max\Downloads\drltrace\drltrace\bin\release/drltracelib.dll 3, 3, 0x742f0000, 0x742fa000, 0x742f2a00, 0x0000c877, 0x0adc52c1, C:\Windows\System32\CRYPTBASE.dll 4, 4, 0x74300000, 0x74320000, 0x7430c9b0, 0x0002c617, 0x245970b4, C:\Windows\System32\SspiCli.dll 5, 5, 0x74410000, 0x74431000, 0x74416900, 0x0002a940, 0x88a53c1d, C:\Windows\System32\GDI32.dll 6, 6, 0x74440000, 0x74500000, 0x7446fb20, 0x000cc410, 0xd343d532, C:\Windows\System32\RPCRT4.dll 7, 7, 0x74500000, 0x74525000, 0x745047d0, 0x00026737, 0xa39c8991, C:\Windows\System32\IMM32.DLL 8, 8, 0x74550000, 0x745c7000, 0x7456e8a0, 0x00081857, 0x73b971e1, C:\Windows\System32\advapi32.dll 9, 9, 0x748f0000, 0x74929000, 0x748febd0, 0x00045303, 0xa58be652, C:\Windows\System32\cfgmgr32.dll 10, 10, 0x74930000, 0x75c78000, 0x74aa09d0, 0x01377aa6, 0x4b39926b, C:\Windows\System32\SHELL32.dll
Drltrace can easily filter out interlibrary calls and print only API calls performed from the main module (or from a heap) of a target application by specifying
-only_from_app option which is very useful in case of applications that generate huge logs. Drltrace also has several useful external scripts to filter API calls for certain library, print only potentially interesting API calls and strings.
Command line options
-logdir [ .] Log directory to print library call data -only_from_app [ false] Reports only library calls from the app -follow_children [ true] Trace child processes -print_ret_addr [ false] Print library call's return address -num_unknown_args [ 2] Number of unknown libcall args to print -num_max_args [ 6] Maximum number of arguments to print -default_config [ true] Use default config file. -config [ ""] The path to custom config file. -ignore_underscore [ false] Ignores library routine names starting with "_". -only_to_lib [ ""] Only reports calls to the library <lib_name>. -help [ false] Print this message. -version [ false] Print version number. -verbose [ 1] Change verbosity. -use_config [ true] Use config file
Configuration file syntax
Drltrace supports external configuration files where a user can describe how drltrace should print arguments for certain API calls.
Each function argument should be separated by
|. The first argument is return type, the second argument is a function name itself and the rest are the function arguments. A token
__out is used to mark output arguments and
___inout is used to mark input+output arguments.
To make the work with log files easier, we have implemented a script called
api_calls_viz.py which can be used to generate RGB images where each pixel color represents unique API call. For example, the picture below represents log file of WannaCry malware.
The large green areas on the picture represent API calls (
wcscmp/wcsicmp) which are used to select files with interesting extensions (e.g. docx, xls, py) to encrypt them. The purple areas represent API calls (
FindFirstFile/FindNextFile/CryptEncrypt) which are used to enumerate and encrypt files and folders on the disk.
The script can also generate an HTML representation of generated RGB image where each element can be selected to show a name of API call.