Skip to content

Commit

Permalink
Initial disassembler support
Browse files Browse the repository at this point in the history
  • Loading branch information
romainthomas committed Nov 2, 2024
1 parent 86e1769 commit f7a127e
Show file tree
Hide file tree
Showing 57 changed files with 1,408 additions and 38 deletions.
35 changes: 35 additions & 0 deletions api/python/examples/disassembler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python
'''
This script disassembles an ELF/PE/Mach-O binary using LIEF's extended API
Note: this script is only working with the extended version of LIEF
'''
import argparse
import lief

from pathlib import Path

def disassemble(target: lief.Binary, addr: int):
for inst in target.disassemble(addr):
print(inst)

def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("file", help='Target file', type=Path)
parser.add_argument("address", help='Address to disassemble',
type=lambda x: int(x,0))

args = parser.parse_args()

target_file = args.file
addr = args.address

target = lief.parse(target_file)
if target is None:
print(f"Can't load: {target_file}")
return 1

disassemble(target, addr)

if __name__ == "__main__":
raise SystemExit(main())
12 changes: 10 additions & 2 deletions api/python/lief/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any, ClassVar, Optional, Union
from typing import Any, ClassVar, Iterator, Optional, Union

from . import ART, Android, DEX, ELF, MachO, OAT, PE, VDEX, dsc, dwarf, logging, objc, pdb # type: ignore
from . import ART, Android, DEX, ELF, MachO, OAT, PE, VDEX, assembly, dsc, dwarf, logging, objc, pdb # type: ignore
from typing import overload
import io
import lief # type: ignore
Expand All @@ -11,6 +11,7 @@ import lief.Function # type: ignore
import lief.Header # type: ignore
import lief.MachO # type: ignore
import lief.PE # type: ignore
import lief.assembly # type: ignore
import lief.logging # type: ignore
import os

Expand Down Expand Up @@ -66,6 +67,13 @@ class Binary(Object):
def __len__(self) -> int: ...
def __next__(self) -> lief.Symbol: ...
def __init__(self, *args, **kwargs) -> None: ...
@overload
def disassemble(self, address: int) -> Iterator[Optional[lief.assembly.Instruction]]: ...
@overload
def disassemble(self, address: int, size: int) -> Iterator[Optional[lief.assembly.Instruction]]: ...
@overload
def disassemble(self, function_name: str) -> Iterator[Optional[lief.assembly.Instruction]]: ...
def disassemble_from_bytes(self, buffer: bytes, address: int = ...) -> Iterator[Optional[lief.assembly.Instruction]]: ...
def get_content_from_virtual_address(self, virtual_address: int, size: int, va_type: lief.Binary.VA_TYPES = ...) -> memoryview: ...
def get_function_address(self, function_name: str) -> Union[int,lief.lief_errors]: ...
def get_int_from_virtual_address(self, address: int, interger_size: int, type: lief.Binary.VA_TYPES = ...) -> Optional[int]: ...
Expand Down
14 changes: 14 additions & 0 deletions api/python/lief/assembly.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Engine:
def __init__(self, *args, **kwargs) -> None: ...

class Instruction:
def __init__(self, *args, **kwargs) -> None: ...
def to_string(self) -> str: ...
@property
def address(self) -> int: ...
@property
def mnemonic(self) -> str: ...
@property
def raw(self) -> bytes: ...
@property
def size(self) -> int: ...
81 changes: 81 additions & 0 deletions api/python/src/Abstract/pyBinary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@

#include "LIEF/Abstract/DebugInfo.hpp"

#include "LIEF/asm/Engine.hpp"
#include "LIEF/asm/Instruction.hpp"

namespace LIEF::py {
template<>
void create<Binary>(nb::module_& m) {
Expand Down Expand Up @@ -288,6 +291,84 @@ void create<Binary>(nb::module_& m) {
nb::overload_cast<>(&LIEF::Binary::original_size, nb::const_),
"Original size of the binary"_doc)

.def("disassemble", [] (const Binary& self, uint64_t address) {
auto insts = self.disassemble(address);
return nb::make_iterator(
nb::type<Binary>(), "instructions_it", insts);
}, "address"_a, nb::keep_alive<0, 1>(),
R"doc(
Disassemble code starting a the given virtual address.
.. code-block:: python
insts = binary.disassemble(0xacde, 100);
for inst in insts:
print(inst)
.. seealso:: :class:`lief.assembly.Instruction`
)doc"_doc
)

.def("disassemble", [] (const Binary& self, uint64_t address, size_t size) {
auto insts = self.disassemble(address, size);
return nb::make_iterator(
nb::type<Binary>(), "instructions_it", insts);
}, "address"_a, "size"_a, nb::keep_alive<0, 1>(),
R"doc(
Disassemble code starting a the given virtual address and with the given
size.
.. code-block:: python
insts = binary.disassemble(0xacde, 100);
for inst in insts:
print(inst)
.. seealso:: :class:`lief.assembly.Instruction`
)doc"_doc
)

.def("disassemble", [] (const Binary& self, const std::string& function) {
auto insts = self.disassemble(function);
return nb::make_iterator(
nb::type<Binary>(), "instructions_it", insts);
}, "function_name"_a, nb::keep_alive<0, 1>(),
R"doc(
Disassemble code for the given symbol name
.. code-block:: python
insts = binary.disassemble("__libc_start_main");
for inst in insts:
print(inst)
.. seealso:: :class:`lief.assembly.Instruction`
)doc"_doc
)

.def("disassemble_from_bytes",
[] (const Binary& self, const nb::bytes& buffer, uint64_t address) {
auto insts = self.disassemble(
reinterpret_cast<const uint8_t*>(buffer.c_str()),
buffer.size(), address
);
return nb::make_iterator(
nb::type<Binary>(), "instructions_it", insts);
}, "buffer"_a, "address"_a = 0, nb::keep_alive<0, 1>(), nb::keep_alive<0, 2>(),
R"doc(
Disassemble code from the provided bytes
.. code-block:: python
raw = bytes(binary.get_section(".text").content)
insts = binary.disassemble_from_bytes(raw);
for inst in insts:
print(inst)
.. seealso:: :class:`lief.assembly.Instruction`
)doc"_doc
)

LIEF_DEFAULT_STR(Binary);

}
Expand Down
1 change: 1 addition & 0 deletions api/python/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_subdirectory(DWARF)
add_subdirectory(PDB)
add_subdirectory(ObjC)
add_subdirectory(DyldSharedCache)
add_subdirectory(asm)

if(LIEF_ELF)
add_subdirectory(ELF)
Expand Down
6 changes: 6 additions & 0 deletions api/python/src/asm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target_sources(pyLIEF PRIVATE
init.cpp
pyEngine.cpp
pyInstruction.cpp
)

17 changes: 17 additions & 0 deletions api/python/src/asm/init.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include "asm/init.hpp"
#include "asm/pyAssembly.hpp"


namespace LIEF::assembly {
class Engine;
class Instruction;
}

namespace LIEF::assembly::py {
void init(nb::module_& m) {
nb::module_ mod = m.def_submodule("assembly");

create<LIEF::assembly::Engine>(mod);
create<LIEF::assembly::Instruction>(mod);
}
}
8 changes: 8 additions & 0 deletions api/python/src/asm/init.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef PY_LIEF_ASM_INIT_H
#define PY_LIEF_ASM_INIT_H
#include "pyLIEF.hpp"

namespace LIEF::assembly::py {
void init(nb::module_& m);
}
#endif
12 changes: 12 additions & 0 deletions api/python/src/asm/pyAssembly.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef PY_LIEF_ASM_H
#define PY_LIEF_ASM_H
#include "pyLIEF.hpp"

namespace LIEF::assembly::py {

namespace assembly = LIEF::assembly;

template<class T>
void create(nb::module_&);
}
#endif
16 changes: 16 additions & 0 deletions api/python/src/asm/pyEngine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "LIEF/asm/Engine.hpp"
#include "asm/pyAssembly.hpp"

#include <nanobind/stl/string.h>
#include <nanobind/stl/unique_ptr.h>

namespace LIEF::assembly::py {
template<>
void create<assembly::Engine>(nb::module_& m) {
nb::class_<assembly::Engine> obj(m, "Engine",
R"doc(
This class interfaces the assembler/disassembler support
)doc"_doc
);
}
}
45 changes: 45 additions & 0 deletions api/python/src/asm/pyInstruction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <ostream>
#include <sstream>
#include "pyLIEF.hpp"
#include "LIEF/asm/Instruction.hpp"
#include "asm/pyAssembly.hpp"

#include <nanobind/stl/string.h>
#include <nanobind/stl/unique_ptr.h>

namespace LIEF::assembly::py {
template<>
void create<assembly::Instruction>(nb::module_& m) {
nb::class_<assembly::Instruction> obj(m, "Instruction",
R"doc(
This class represents an assembly instruction
)doc"_doc
);

obj
.def_prop_ro("address", &Instruction::address,
R"doc(Address of the instruction)doc"_doc
)

.def_prop_ro("size", &Instruction::size,
R"doc(Size of the instruction in bytes)doc"_doc
)

.def_prop_ro("mnemonic", &Instruction::mnemonic,
R"doc(Instruction mnemonic (e.g. ``br``))doc"_doc
)

.def("to_string", &Instruction::to_string,
R"doc(Representation of the current instruction in a pretty assembly way)doc"_doc
)

.def_prop_ro("raw", [] (const Instruction& inst) {
const std::vector<uint8_t>& raw = inst.raw();
return nb::bytes((const char*)raw.data(), raw.size());
}, R"doc(Raw bytes of the current instruction)doc"_doc
)

LIEF_DEFAULT_STR(Instruction)
;
}
}
2 changes: 2 additions & 0 deletions api/python/src/pyLIEF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "PDB/init.hpp"
#include "ObjC/init.hpp"
#include "DyldSharedCache/init.hpp"
#include "asm/init.hpp"

#if defined(LIEF_ELF_SUPPORT)
#include "ELF/init.hpp"
Expand Down Expand Up @@ -268,6 +269,7 @@ void init(nb::module_& m) {
LIEF::pdb::py::init(m);
LIEF::objc::py::init(m);
LIEF::dsc::py::init(m);
LIEF::assembly::py::init(m);

#if defined(LIEF_ELF_SUPPORT)
LIEF::ELF::py::init(m);
Expand Down
12 changes: 12 additions & 0 deletions api/rust/autocxx_ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ include_cpp! {
generate!("AbstracDebugInfo")
block_constructors!("AbstracDebugInfo")

generate!("AbstractBinary_it_instructions")
block_constructors!("AbstractBinary_it_instructions")

// -------------------------------------------------------------------------
// ELF
// -------------------------------------------------------------------------
Expand Down Expand Up @@ -624,5 +627,14 @@ include_cpp! {
generate!("dsc_SubCache")
block_constructors!("dsc_SubCache")

// -------------------------------------------------------------------------
// ASM Support
// -------------------------------------------------------------------------
generate!("asm_Engine")
block_constructors!("asm_Engine")

generate!("asm_Instruction")
block_constructors!("asm_Instruction")

safety!(unsafe)
}
19 changes: 19 additions & 0 deletions api/rust/cargo/lief/src/assembly.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Assembly/Disassembly Module
//!
//! ### Getting Started
//!
//! One can start disassembling code by using the different functions exposes in the
//! [`crate::generic::Binary`] trait:
//!
//! ```
//! fn disassemble(target: &dyn lief::generic::Binary) {
//! for inst in target.disassemble_symbol("_entrypoint") {
//! println!("{}", inst.to_string());
//! }
//! }
//! ```

pub mod instruction;

#[doc(inline)]
pub use instruction::{Instructions, Instruction};
Loading

0 comments on commit f7a127e

Please sign in to comment.