TILIB it's a utility to create a custom type libraries for IDA Pro. You can download it from My Hex-Rays Portal.
This small utility creates type library (til) files for IDA Pro. Its functionality overlaps with "Parse C header file..." from IDA Pro. However, this utility is easier to use and provides more control over the output. Also, it can handle the preprocessor symbols, while the built-in command ignores them.
The utility takes a C header file and extracts all type information from it. The extracted type information is stored in an internal format as a type library file. Type library files can be loaded and used in IDA Pro by opening the Type Library Window and pressing Insert.
TILIB support only C header files. C++ files (classes, templates, etc) are not supported, but some popular C++ keywords are recognized and properly handled. For example, TILIB knows about the inline keyword and properly skips the definition of the inline function.
Just copy the tilib executable file into the IDA Pro directory.
If you prefer to keep it in another directory, you will need to specify the location of the ida.hlp file using the NLSPATH variable:
Please note that you do not need to define this variable if you just copy the tilib executable file to the IDA Pro directory.
If started without parameters, the utility displays a short explanation. Below are some command line samples.
Parse a header file and create a new type library:
-c
means to create a new type library
-h
denotes the name of the input file to read
If you need to parse multiple files into one type library, you may create a text file with #include directives and specify it as the input.
TILIB can handle input files for Visual Studio, Borland, GCC out of the box. Please check the configuration files for them in the archive (*.cfg).
If you need to fine-tune TILIB for another compiler (or unusual compiler settings), feel free to copy the provided configuration files and modify them.
TILIB is not very good at error handling and some error messages may be quite unhelpful (e.g. syntax error). To alleviate this problem, use the -z
switch. If this switch is specified, TILIB will create a .i file with the preprocessed contents. All recognized type names are prepended with @, like this: @my_type_name.
In some urgent cases, the -e
switch may be used to ignore any parsing errors. Please note that the problematic type definitions will be skipped in this mode. This switch may be used together with the -R switch to allow type redeclarations.
If your input file uses another header file (e.g. windows.h), you may opt to use the vc6win.til file instead of parsing it again. For that, just use the -b
switch and specify vc6win.til as the base til. TILIB will load the contents of the specified file into the memory and parse the input file. All definitions from windows.h, including the preprocessor definitions, will be avaible:
TILIB can also be used to list the contents of a til file:
If the -c
switch is not specified, TILIB uses the specified til file as the input and as the output file. This mode allows you to modify existing til files.
TILIB can also convert selected preprocessor macros into enums. If the input file has lines like the following:
they can be converted to enum and used in IDA Pro. This is a three step process. At the first step, we create a .mac file (note the -M
switch):
The second step is to edit the generated MAC file: to remove undesired macro definitions and regroup macros if necessary. Macro group names start at the beginning of a line, symbol definitions are indented:
Feel free to edit the macro file to your taste.
It is also possible to describe the changes to the macro group names and rerun TILIB to regenerate the macro file. There are two ways to describe the changes:
Through the -g[nb]X:Y command-line options. If the regex X matches the macro name (n) or the macro body (b), the group is set to Y. For example:
with this header file:
produces this macro file:
Using an empty regex will prevent the group from being created (useful when you want to leave a group name available from a base til).
Adding a tilib-specific #pragma to the header files:
The third and last step is to specify the macro file as the input for TILIB:
The generated til file will have all normal type definitions and all macro definitions from the .mac file.
Below is the list of supported keywords. If your header files happen to have an unsupported keyword, you take one of the following actions:
edit the input files and remove the unsupported keywords
use #define to replace or hide the unsupported keywords
use the -D command line switch for the same purpose
Learn more about TILIB utility and check Igor's tip of the week about creating custom libraries.
The IDAClang plugin integrates the clang compiler frontend into IDA itself. This allows IDA to parse type information from complex C/C++/Objective-C source code and import it directly into an IDA database.
IDAClang utilizes a specialized build of - the opensource C API for the clang compiler. This custom library is also shipped with IDA alongside the plugin itself, so you do not need to worry about it. The plugin will find and load libclang automatically.
Our build of libclang is from Clang v13.0, so it can handle any Objective-C syntax and anything from C++20 and earlier.
IDAClang was introduced as a more robust alternative to IDA’s built-in source code parser. The built-in parser can handle simple C source code, but naturally it struggles to handle complex C++ and Objective-C syntax. IDAClang solves this problem by outsourcing all the heavy lifting to a third-party library that can handle the ugly parsing operations. The plugin needs only to parse the abstract syntax tree generated by clang.
As a result, IDAClang should be much more flexible. You can even feed it complete .cpp source files. The plugin will extract whatever useful type information it can find, and ignore the rest.
One big advantage of using libclang is that we can take advantage of clang’s internal C++ VTable management. For example, when IDAClang parses a C++ class that looks like this:
The following types will be generated in the database:
To create the C_vtbl type, IDAClang traverses clang’s internal VTableLayout data structure. This data structure is the same mechanism that the clang compiler uses during the actual code generation. Thus, we can be very confident that IDAClang is producing correct vtable types - even in much more complex situations. After all, clang knows what it’s doing in this regard.
Here IDAClang created symbols for the C::func member function, as well as the mangled VTable symbol for the C class.
Another notable advantage of using libclang is it allows us to gracefully handle C++ templates.
For example, consider the following template declarations:
When clang parses the instance_t declaration, internally it will generate a structure that represents the specialized template S<int, void *>. The IDAClang plugin will then use this internal representation to generate a valid type for S<int, void *> in IDA’s type system:
The type with name S<int, void *> represents the fully resolved structure, with all template arguments replaced. This all happens automatically, and it is especially useful in more complex situations - such as template classes containing virtual methods that depend on template parameters, resulting in specialized VTables.
To provide support for third-party parsers, IDA now has a new Source parser field in the Options>Compiler dialog:
To enable the IDAClang parser, select the clang parser from the dropdown menu:
As a quick sanity check, try saving the following declaration in a file called test.h:
Parse the file using menu File>Load file>Parse C header file. IDA should print this to the output window:
The type should now be present in the Local Types view:
Of course, IDAClang is capable of parsing source code that is much more complex. Often times this requires more detailed configuration of the parser invocations.
To support this, the Compiler>Options dialog provides the Arguments field:
In this field you can provide any argument you would typically provide to the clang compiler when invoking it from the command line. For example:
One of the more important clang arguments is the -target option, which specifies the target architecture and platform. This allows clang to properly configure itself to parse macOS/Windows/Linux system headers. Clang calls this the target "triple" because it is often given in the form of:
Some examples:
Note that in the simple test.h example above, we did not specify a target platform. In this case clang will assume that the target platform is the same as the host machine IDA is currently running on. You can print the exact target used by clang by opening Options>Compiler>Parser specific options and enable the following option:
Now when we use IDAClang to parse the test.h file, it will print a message:
Which would be the typical output when IDA is running on macOS. On Windows the default will look something like:
And on Linux:
Such is the default behavior within libclang, but clang supports a wide variety of platforms and architectures. You can almost always specify a target that will match the input binary in the current database.
Now let’s try invoking IDAClang on some more real-world source code.
In this example, assume we are analyzing an x64 binary that makes heavy use of the C++ Standard Template Library. Then assume that at some point we want to create a structure that looks like this:
To parse stl_example.h on macOS, we’ll have to point IDAClang to the macOS SDK as well as the STL system headers:
Copy the text above into the Options>Compiler>Arguments field.
Note that we point IDAClang to the macOS SDK with the -isysroot option and use the -I option to allow IDAClang to find the proper system headers in the Xcode toolchain. Be wary of the last option (ending with usr/lib/clang/11.0.3/include). This path contains the clang version number, so it might be different on your machine. Also make special note of the -x c++ option. This is used to inform libclang that the input source will not be plain C, which is the default syntax for .h files in libclang.
Now we can use File>Load file>Parse C header file to parse stl_example.h. This will generate a useful type for stl_example_t in our database:
On Windows the configuration is a bit different. If you’re using Visual Studio, libclang is normally able to detect common header paths automatically.
Thus you will likely only need to specify the following arguments in Options>Compiler>Arguments:
Ideally this will be enough to parse stl_example.h and generate some useful type info:
If for whatever reason the heuristics within libclang fail to find the headers on your system, it is very easy to specify the header paths manually. Simply open a Visual Studio x64 Command Prompt and run the following command:
This will print a semicolon-separated list of the header paths used on your system:
This list can be copied directly into the Options>Compiler>Include directories field in IDA. IDAClang will automatically process this list and pass the header paths to clang upon invocation of the parser. This is likely enough to handle most Windows-based source code.
On Linux you can determine the header paths used your system by running the following command:
This will print something like:
You can then use these arguments in the Options>Compiler>Arguments field in IDA:
Then use File>Load file>Parse C header file to parse stl_example.h.
Like any good IDA feature, IDAClang can also be invoked from an IDAPython script.
IDA 7.7 introduced the ida_srclang module to provide simple support for invoking third-party parsers from IDAPython. Use the following IDAPython commands for an overview of this new module:
The function ida_srclang.parse_decls_with_parser can notably be used to parse source code snippets:
If the is_path argument is False, this function will assume the input argument is a string that represents a source code snippet. Otherwise it will be considered a path to a source file on disk. Also note the til parameter, which will often times be None. This ensures the parsed types are imported directly into the current database.
IMPORANT NOTE: when libclang parses in-memory strings, it makes no assumptions about the expected syntax. Thus, you must specify the -x option to tell clang which syntax to expect before invoking the parser. Here are the the known syntax directives:
For example, this is how you would use ida_srclang to parse a simple C source string with IDAClang:
We can also handle the same STL example discussed previously, but this time parse stl_example_t as a source snippet:
This should produce an identical result as before when we used File>Load file>Parse C header file for stl_example.h.
In this example we will show how IDAClang can be used in batch mode to improve the analysis of a binary compiled from Boost headers. The experiment will be performed on Debian Linux with gcc 6.3.0.
chat_server.cpp
chat_message.hpp
This script will configure IDAClang to parse the chat_server.cpp source file and extract any type information it finds, then analyze the input with the imported type info, and saves the resulting database in chat_server.i64. You can run the script like this:
You may have noticed this option:
This option is passed to the IDAClang plugin and it enables CLANG_APPLY_TINFO (see idaclang.cfg for more info).
Now let’s open the resulting database chat_server.i64 in IDA, and try decompiling some functions. Immediately we see that the analysis does benefit from the imported type info. For example chat_session::do_write seems somewhat intelligible after some minor simplifications:
Since IDAClang parsed the chat_session class, we now have a correct prototype for chat_session:do_write, as well as a valid chat_session structure. Note that references to chat_session.write_msgs_ (std::deque<chat_message>) and chat_session.socket (boost::asio::ip::tcp::socket) were correctly resolved.
Granted, this is not the most realistic example. It’s not often we have access to the full source code of the target binary, but hopefully this shows that whenever any relevant source code is available, IDAClang can take full advantage.
The IDAClang plugin is useful for enriching your database with complex type information, but often times the imported types are relevant to more than just one database. In this section we discuss how you can use IDAClang to generate rich, generic type libraries for IDA Pro.
After downloading the idaclang binary, copy it to the idabin/ directory of your IDA installation (next to the libclang dll).
For an overview of idaclang’s functionality, run:
For a quick demonstration, save the following source in a file named test.h:
You can compile this header into a type library by invoking idaclang the same way you would typically invoke the clang compiler from the command line:
This will generate a file called test.til that contains all types that were parsed in test.h. Try dumping the TIL with the tilib utility.
The tool also provides extra arguments to configure the til generation. They are given the --idaclang- prefix so they can be easily separated from the clang arguments. For example:
This will create the library at /tmp/test2.til, instead of the default location.
Now let’s try building some type libraries from real-world code. The examples in this section will demonstrate the power of IDAClang by creating TILs from many different opensource C++ projects. They cover a large variety of platforms, architectures, and codebases, so it is best to unify the build system using makefiles.
This makefile defines a simple rule for building a TIL using the idaclang command-line utility. It will be used extensively in the following examples.
To build this TIL we only need to create a single header file that includes all headers from the IDA SDK, and then parse this file with idaclang. See examples/idasdk/idasdk.h, which contains include directives for all files in idasdk77/include (they happen to be in alphabetical order, but the order shouldn’t matter much):
The IDAClang configuration required to parse idasdk.h is highly platform-dependent, so we provide separate makefiles for each of IDA’s supported platforms.
To demonstrate how we might build idasdk.h on MacOSX, see examples/idasdk/idasdk_mac_x64.mak:
You can build the TIL with:
This will generate a type library named idasdk_mac_x64.til, along with a dump of the til contents in idasdk_mac_x64.til.txt. In the text dump we might notice some familiar types:
It’s worth building a separate til for both x64 and arm64 macOS. IDA’s source code is not very architecture dependent, but many system headers might be. So it’s best to be as precise as possible.
To build this TIL on macOS12 for Apple Silicon, the approach is very similar:
Note that we did not provide the path to the C++ STL headers like we did in idasdk_mac_x64.mak. On macOS12 the C++ headers are shipped within MacOSX12.0.sdk, so there is no need to explicitly tell idaclang where to find them.
To parse idasdk.h on Windows, use examples/idasdk/idasdk_win.mak:
Normally we do not need to specify any include paths, since idaclang can find the Visual Studio headers automatically. If it can’t, you can always explicitly provide include paths with the -I option.
Building idasdk.h on Linux is also fairly straightforward. See idasdk_linux.mak:
You can also include the decompiler types from the hexrays SDK in the type library for idasdk77. Simply copy hexrays.hpp from hexrays_sdk/ in your IDA installation to idasdk77/include/, then add this line to idasdk.h:
Then rebuild the TIL. It will likely yield some useful decompiler types:
In this example we will build a type library for the Qt Opensource UI Framework. The example uses Qt 5.15.2, but theoretically it can work for any Qt version. We assume you already have a Qt installation present on your system (See the QTDIR variable in the following makefiles).
Let’s start by creating a file that includes as many Qt headers as we can. Qt makes this easy because they ship "umbrella" headers for the various sub-frameworks, which take care of including most of the critical Qt header files.
This will be more than enough to get started.
To build qt.h on macOS, consider examples/qt/qt_mac.mak:
For the Qt build we must explicitly add the Headers/ directory for each Qt framework to the include paths. Also pay special attention to the -F$(QTDIR)/lib/ option. This option is specific to macOS and informs libclang that the given directory contains .framework bundles. This is necessary for some include directives to be resolved correctly, e.g.:
Now we can build the TIL with:
We might want to check qt_mac.til.txt to see if some core Qt types were correctly added to the TIL:
To build the Qt type library on Windows, use examples/qt/qt_win.mak:
And on Linux, use examples/qt/qt_linux.mak:
This section demonstrates how to build a type library from the Linux Kernel headers.
First you must ensure that you have the kernel headers installed on your system:
As well as the tools necessary to build against them:
The tricky part about building a TIL for the Linux kernel is configuring the correct include paths. The kernel header directory structure is not consistent between different distros, so there is no one configuration that works on all machines. We’ve found that the easiest way to discover the correct kernel header paths on your system is to build a trivial Linux kernel module, then copy the paths used within the kernel build system.
After building, you may notice that the kernel build system added many hidden files to the build directory. One of them being .lkm_example.o.cmd. This file contains the gcc command used to compile the kernel module source file. It will contain many important compiler switches that we will need to copy over to idaclang, including the proper include paths:
These paths are relative to the directory:
We will need to copy them to idaclang as absolute paths. For example, consider examples/linux/linux_kernel_5_11.mak:
This is a makefile we used to successfully generate a Linux kernel TIL on Ubuntu 20.04. The input file linux.h contains include directives for a few interesting kernel header files:
It is not an exhaustive list. You can easily make the TIL more robust by adding more headers to linux.h, but for demonstration purposes this is enough to get the ball rolling.
Inspecting the dump of the til we can see some important kernel types have already been added:
On Debian Linux the configuration is slightly different. The kernel header package tends to be split across multiple root directories. We must let idaclang know about both of them. See examples/linux/linux_kernel_4_9.mak:
We used this makefile to generate a Linux kernel TIL on Debian 9.13. It should produce almost the same result as the Ubuntu example discussed above.
Apple publishes the kernel SDK via Kernel.framework in the macOS SDK:
The IOKit C++ headers are usually present at:
Now consider examples/xnu/xnu_x64.mak:
We can use this makefile to generate an XNU TIL:
Now let’s have a look at the OSMetaClassBase class in the text dump xnu_x64.til.txt:
More specifically the OSMetaClassBase_vtbl type, which has some peculiar members at the end of it:
Apple added several dummy virtual methods to this class so that they can add new methods without breaking binary compatibility. This is a common paradigm in the XNU kernel source, but we must be very careful with it. From the original source code in OSMetaClass.h, we can see that it is heavily platform dependent:
Note that the APPLE_KEXT_VTABLE_PADDING macro is not defined for iOS builds. The iOS kernel does not pad its vtables in order to conserve memory, so these extra vtable members will only be present for macOS builds. Moreover, from the above source we can see that arm64 macOS uses more vtable padding than on x64.
Why is this a big deal? Because almost every single important C++ class in IOKit inherits from OSMetaClassBase. Thus if the vtable type for OSMetaClassBase is not 100% correct, all of our vtable types will be useless. We have no choice but to build separate TILs for each of x64 macOS, arm64 macOS, and iOS, to ensure that we’re working with precise vtables.
See examples/xnu/xnu_m1.mak, which builds the XNU TIL for arm64 macOS:
From the text dump we can see that OSMetaClassBase_vtbl is a bit larger, as expected:
To build the TIL for iOS, use xnu_ios.mak:
Which yields smaller vtables without any padding, as expected:
Hopefully this section demonstrates how easy it is to get in trouble when building type libraries from C++ source, and how the precision of idaclang can help deal with it.
This makefile instructs idaclang to parse examples/mfc/mfc.h, which contains some essential MFC headers:
This is enough to generate a solid type library for MFC. You can build it with:
From the dump of the til in mfc.til.txt, it appears some essential types are successfully created:
There are many more headers that could be included in the build, so don’t hesitate to add more headers to mfc.h if you think they might contain relevant types.
One advantage of using libclang is that we can parse the complex Objective-C syntax used in macOS and iOS Frameworks. Thus we can create type libraries from Apple’s SDKs without much issue.
The input file used here is examples/macsdk/macos12_sdk.h, which simply includes the umbrella header from every Framework present in the SDK:
The Frameworks that have an umbrella header can be easily identified by their module.modulemap file. For example:
Some Frameworks don’t have an umbrella header and they were left out, but this still gives us plenty of useful type info. From macos12_sdk.til.txt we can see that many Objective-C types were successfully parsed:
Also the prototypes for many Objective-C methods were added to the symbol table:
Having such detailed and accurate prototypes for Objective-C Frameworks is particularly useful when analyzing dyld_shared_cache files. The type information parsed in the source headers is much more detailed than the Objective-C type information embedded in the module binaries.
Note that you can build the same TIL for Apple Silicon by simply changing the -target argument to:
Building a TIL for the iPhoneOS.sdk is just as easy. See ios15_sdk.mak and ios15_sdk.h:
Moreover, when using IDAClang to generate a type library (see below), the plugin will take advantage of clang’s name mangling to populate the symbol table:
The various combinations of supported targets is documented in more detail .
This is the contents of stl/stl_example.h from . IDA’s default parser cannot handle such complex C++ syntax, so IDAClang is our only hope of importing this type. The precise configuration of IDAClang will vary between platforms, so we’ll demonstrate them all separately.
Consider the following source files from the boost/ directory in :
These sources were taken directly from the , and we’ll use them to compile a test binary. Begin by downloading the , then compile the chat_server application:
Since Boost is a template library, it will generate a bloated binary that contains thousands of instantiated template functions. Thus, IDA’s initial analysis of chat_server.elf will likely not be very pretty. How can IDAClang help us with this? Consider boost/chat_server.py from .
Hex-Rays also provides a , specifically designed for building custom Type Information Libraries (TILs) that can be loaded into any IDA database.
At the top level of there should be a makefile named idaclang.mak:
Hex-Rays publishes an SDK for developing custom IDA plugins, which is comprised mostly of C++ header files. Thus, it is a perfect use case for IDAClang. In this example we will build a type library for IDA itself, using .
After downloading idasdk77.zip, unzip it into the idasdk subdirectory of .
See examples/qt/qt.h from :
Consider examples/linux/lkm_example/lkm_example.c from :
This is a simple kernel module taken from . It can be built with:
The XNU kernel for macOS and iOS relies heavily on C++ object-oriented development via the . The goal of this section is to create a type library for the XNU kernel, focusing specifically on the C++ type information in the IOKit headers.
See examples/xnu/xnu.h in , which includes some of the important header files from the IOKit/ directory. We’ll use this file to generate our type library.
The makefile examples/mfc/mfc.mak from demonstrates how to build a type library for the Microsoft Foundation Class Library (MFC) on Windows:
See examples/macsdk/macos12_sdk.mak in , which creates a TIL from the macOS12 SDK: