Binary Abstraction


Introduction

ELF, PE, Mach-O binaries share similar characteristics like an entrypoint, imported/exported functions, etc.

These shared characteristics are represented in an abstract layer that is represented by an inheritance relationship in C++/Python and by a trait in Rust.

In Python/C++, one can access an abstract binary object by using the generic function:
target: lief.Binary = lief.parse("/tmp/some.elf")

target: lief.Binary = lief.parse("/Users/demo/some.macho")

target: lief.Binary = lief.parse(r"C:\some.pe.exe")

Because of the dynamical polymorphism aspect of Python, the return value of lief.parse() is automatically casted into either: lief.ELF.Binary, lief.PE.Binary or lief.MachO.Binary. To upcast this object into a lief.Binary object, one can use the attribute: lief.Binary.abstract which effectively returns a lief.Binary instance:

import lief

target = lief.parse("some.elf")
assert type(target) is lief.ELF.Binary

abstract = target.abstract
assert type(abstract) is lief.Binary

In C++, one can downcast a LIEF::Binary instance into its effective type using the classof idiom:

std::unique_ptr<LIEF::Binary> target = LIEF::Parser::parse("some.elf");

if (LIEF::ELF::Binary::classof(target.get())) {
  auto& elf = static_cast<LIEF::ELF::Binary&>(*target);
}