The Mysterious Life of an Exception

Presented at REcon 2022, June 5, 2022, 1 p.m. (60 minutes)

Error handling is often done using exceptions. Because error code paths represent abnormal program behavior, they are an interesting target for reverse engineering. Try, catch and throw semantics are straight-forward to use when programming, but what’s happening behind the scenes? On a quest to figure this out, we will follow an exception on its journey from the throw statement all the way to it being caught by its handler. On its way it will interact with many different parts of the program’s runtime, which will use DWARF information to unwind the stack, determining the handler using Language-Specific Data Areas (LSDA), personality routines, and more. Using what we learnt, we’ll introduce a reverse-engineering plugin for Binary Ninja that extracts exception handling information from ELF and Mach-O binaries. Exceptions are an important control flow mechanism used in many modern programming languages. They allow for control flow to be diverted directly to an exception handler, and are generally used for handling error conditions. It is hard to reverse engineer binaries containing exception handling since the control flow transitions are not obvious and hidden behind layers of indirection. While some off-the-shelf reverse engineering frameworks support automated recovery of exception information, this process is generally not well-understood and documentation about this process is spread out over multiple standards and varies between implementations. In this talk, we will approach reverse engineering exceptions from scratch. After a primer on the Intel Itanium C++ ABI, which specifies how language runtimes implement exceptions on different architectures, we will focus on the exception handling data emitted by the compiler. We will show how to parse and understand the call frame information used for unwinding the stack and restoring register state, which will lead to a small journey into DWARF internals. Similarly, we dissect the Language Specific Data Areas (LSDA) encoding not only the locations of exception handlers but also provide information on which exceptions they can handle. Lastly, we will also shine light on the exception handlers themselves, and discuss different calling conventions and assumed architectural state on handler entry. While the main focus of the talk will be on x86_64 ELF binaries, we will briefly highlight the differences in Windows C++ exception handling and Apple’s Compact Unwind information used in Mach-O binaries. To provide practical examples of decoding the exception metadata, we will release and discuss an open-source Binary Ninja plugin applying the techniques discussed in this talk. This plugin decodes and displays the corresponding structures and aims to ease the identification and reverse engineering of exception handlers.

Presenters:

  • Marius Muench
    Marius is a postdoctoral researcher at Vrije Universiteit Amsterdam. His research interests cover (in-)security of embedded systems, as well as binary and microarchitectural exploitation. He obtained his PhD from Sorbonne University in cooperation with EURECOM. He developed and maintains avatar2, a framework for analyzing embedded systems firmware. Most recently, he used the framework in the scope of the FirmWire project for automated security testing of cellular baseband implementations.
  • Fabian Freyer
    Fabian is a CTF player, reverse engineer, and security researcher.

Links:

Similar Presentations: