From a11b1b72a95de45be14e79511b0dd4b83880bae9 Mon Sep 17 00:00:00 2001 From: Romain Thomas Date: Mon, 9 Dec 2024 16:58:29 +0100 Subject: [PATCH] Expose `write()` operation in Rust --- api/python/lief/MachO/__init__.pyi | 4 + api/python/src/MachO/objects/pyBinary.cpp | 11 +- api/rust/autocxx_ffi.rs | 10 +- api/rust/cargo/lief/Cargo.toml | 3 +- api/rust/cargo/lief/src/elf.rs | 1 + api/rust/cargo/lief/src/elf/binary.rs | 13 +++ api/rust/cargo/lief/src/elf/builder.rs | 122 ++++++++++++++++++++ api/rust/cargo/lief/src/macho.rs | 1 + api/rust/cargo/lief/src/macho/binary.rs | 13 +++ api/rust/cargo/lief/src/macho/builder.rs | 25 ++++ api/rust/cargo/lief/src/pe/binary.rs | 6 + api/rust/cargo/lief/tests/elf_tests.rs | 14 +++ api/rust/cargo/lief/tests/macho_tests.rs | 20 +++- api/rust/include/LIEF/rust/ELF/Binary.hpp | 51 ++++++++ api/rust/include/LIEF/rust/MachO/Binary.hpp | 13 +++ api/rust/include/LIEF/rust/PE/Binary.hpp | 3 + include/LIEF/MachO/Binary.hpp | 11 ++ src/MachO/Binary.cpp | 4 + 18 files changed, 321 insertions(+), 4 deletions(-) create mode 100644 api/rust/cargo/lief/src/elf/builder.rs create mode 100644 api/rust/cargo/lief/src/macho/builder.rs diff --git a/api/python/lief/MachO/__init__.pyi b/api/python/lief/MachO/__init__.pyi index 222e07a55b..1abdefc31d 100644 --- a/api/python/lief/MachO/__init__.pyi +++ b/api/python/lief/MachO/__init__.pyi @@ -401,8 +401,12 @@ class Binary(lief.Binary): def is_valid_addr(self, address: int) -> bool: ... + @overload def write(self, output: str) -> None: ... + @overload + def write(self, output: str, config: Builder.config_t) -> None: ... + @overload def add(self, dylib_command: DylibCommand) -> LoadCommand: ... diff --git a/api/python/src/MachO/objects/pyBinary.cpp b/api/python/src/MachO/objects/pyBinary.cpp index 087cad167c..6bebfa7faa 100644 --- a/api/python/src/MachO/objects/pyBinary.cpp +++ b/api/python/src/MachO/objects/pyBinary.cpp @@ -499,10 +499,19 @@ void create(nb::module_& m) { .def("write", nb::overload_cast(&Binary::write), - "Rebuild the binary and write and write its content if the file given in parameter"_doc, + "Rebuild the binary and write its content in the file given in the first parameter"_doc, "output"_a, nb::rv_policy::reference_internal) + .def("write", + nb::overload_cast(&Binary::write), + R"doc( + Rebuild the binary and write its content in the file given in the first parameter. + The ``config`` parameter can be used to tweak the building process. + )doc"_doc, + "output"_a, "config"_a, + nb::rv_policy::reference_internal) + .def("add", nb::overload_cast(&Binary::add), "Add a new " RST_CLASS_REF(lief.MachO.DylibCommand) ""_doc, diff --git a/api/rust/autocxx_ffi.rs b/api/rust/autocxx_ffi.rs index c0d2452b53..9779412ab8 100644 --- a/api/rust/autocxx_ffi.rs +++ b/api/rust/autocxx_ffi.rs @@ -53,7 +53,11 @@ include_cpp! { // ELF // ------------------------------------------------------------------------- generate!("ELF_Binary") - block_constructors!("ELF_Binary") + + generate_pod!("ELF_Binary_write_config_t") + block_constructors!("ELF_Binary_write_config_t") + + block_constructors!("Span") generate!("ELF_Binary_it_segments") block_constructors!("ELF_Binary_it_segments") generate!("ELF_Binary_it_sections") @@ -313,6 +317,10 @@ include_cpp! { // ------------------------------------------------------------------------- generate!("MachO_Binary") block_constructors!("MachO_Binary") + + generate_pod!("MachO_Binary_write_config_t") + block_constructors!("MachO_Binary_write_config_t") + generate!("MachO_Binary_it_stubs") block_constructors!("MachO_Binary_it_stubs") generate!("MachO_Binary_it_symbols") diff --git a/api/rust/cargo/lief/Cargo.toml b/api/rust/cargo/lief/Cargo.toml index de63000136..6571a19e8e 100644 --- a/api/rust/cargo/lief/Cargo.toml +++ b/api/rust/cargo/lief/Cargo.toml @@ -21,8 +21,9 @@ bitflags = "2.4" num-traits = "0.2" num-derive = "0.4" num-bigint = "0.4" +tempfile = "3.14.0" lief-ffi = { version = "0.16.0", path = "../lief-ffi" } [features] default = ["rustls-tls"] -rustls-tls = ["lief-ffi/rustls-tls"] \ No newline at end of file +rustls-tls = ["lief-ffi/rustls-tls"] diff --git a/api/rust/cargo/lief/src/elf.rs b/api/rust/cargo/lief/src/elf.rs index 42c1e35ed0..eb333bbdce 100644 --- a/api/rust/cargo/lief/src/elf.rs +++ b/api/rust/cargo/lief/src/elf.rs @@ -11,6 +11,7 @@ //! ``` pub mod binary; +pub mod builder; pub mod dynamic; pub mod hash; pub mod header; diff --git a/api/rust/cargo/lief/src/elf/binary.rs b/api/rust/cargo/lief/src/elf/binary.rs index a1fd46b65f..bd28ef231f 100644 --- a/api/rust/cargo/lief/src/elf/binary.rs +++ b/api/rust/cargo/lief/src/elf/binary.rs @@ -1,11 +1,13 @@ use std::mem::size_of; use std::pin::Pin; +use std::path::Path; use num_traits::{Num, cast}; use lief_ffi as ffi; use crate::Error; +use super::builder::Config; use super::hash::{Sysv, Gnu}; use super::dynamic::{self, DynamicEntries}; use super::header::Header; @@ -287,6 +289,17 @@ impl Binary { Err(Error::NotSupported) } + + /// Write back the current ELF binary into the file specified in parameter + pub fn write(&mut self, output: &Path) { + self.ptr.as_mut().unwrap().write(output.to_str().unwrap()); + } + + /// Write back the current ELF binary into the file specified in parameter with the + /// configuration provided in the second parameter. + pub fn write_with_config(&mut self, output: &Path, config: Config) { + self.ptr.as_mut().unwrap().write_with_config(output.to_str().unwrap(), config.to_ffi()); + } } impl generic::Binary for Binary { diff --git a/api/rust/cargo/lief/src/elf/builder.rs b/api/rust/cargo/lief/src/elf/builder.rs new file mode 100644 index 0000000000..fb870eea21 --- /dev/null +++ b/api/rust/cargo/lief/src/elf/builder.rs @@ -0,0 +1,122 @@ +use lief_ffi as ffi; + +/// Structure used to configure the [`crate::elf::Binary::write_with_config`] operation +#[derive(Debug)] +pub struct Config { + /// Rebuild `DT_HASH` + pub dt_hash: bool, + + /// Rebuild `DT_STRTAB` + pub dyn_str: bool, + + /// Rebuild `PT_DYNAMIC` segment + pub dynamic_section: bool, + + /// Rebuild `DT_FINI_ARRAY` + pub fini_array: bool, + + /// Rebuild `DT_GNU_HASH` + pub gnu_hash: bool, + + /// Rebuild `DT_INIT_ARRAY` + pub init_array: bool, + + /// Rebuild `PT_INTERPRETER` + pub interpreter: bool, + + /// Rebuild `DT_JMPREL` + pub jmprel: bool, + + /// Rebuild notes sections + pub notes: bool, + + /// Rebuild `DT_PREINIT_ARRAY` + pub preinit_array: bool, + + /// Rebuild `DT_RELR` + pub relr: bool, + + /// Rebuild `DT_ANDROID_REL[A]` + pub android_rela: bool, + + /// Rebuild `DT_REL[A]` + pub rela: bool, + + /// Rebuild `.symtab` + pub static_symtab: bool, + + /// Rebuild `DT_VERDEF` + pub sym_verdef: bool, + + /// Rebuild `DT_VERNEED` + pub sym_verneed: bool, + + /// Rebuild `DT_VERSYM` + pub sym_versym: bool, + + /// Rebuild `DT_SYMTAB` + pub symtab: bool, + + /// Rebuild the Coredump notes + pub coredump_notes: bool, + + /// Force to relocating all the ELF structures that are supported by LIEF (mostly for testing) + pub force_relocate: bool, +} + +impl Default for Config { + fn default() -> Config { + Config { + dt_hash: true, + dyn_str: true, + dynamic_section: true, + fini_array: true, + gnu_hash: true, + init_array: true, + interpreter: true, + jmprel: true, + notes: false, + preinit_array: true, + relr: true, + android_rela: true, + rela: true, + static_symtab: true, + sym_verdef: true, + sym_verneed: true, + sym_versym: true, + symtab: true, + coredump_notes: true, + force_relocate: false, + } + } +} + +impl Config { + #[doc(hidden)] + pub fn to_ffi(&self) -> ffi::ELF_Binary_write_config_t { + ffi::ELF_Binary_write_config_t { + dt_hash: self.dt_hash, + dyn_str: self.dyn_str, + dynamic_section: self.dynamic_section, + fini_array: self.fini_array, + gnu_hash: self.gnu_hash, + init_array: self.init_array, + interpreter: self.interpreter, + jmprel: self.jmprel, + notes: self.notes, + preinit_array: self.preinit_array, + relr: self.relr, + android_rela: self.android_rela, + rela: self.rela, + static_symtab: self.static_symtab, + sym_verdef: self.sym_verdef, + sym_verneed: self.sym_verneed, + sym_versym: self.sym_versym, + symtab: self.symtab, + coredump_notes: self.coredump_notes, + force_relocate: self.force_relocate, + } + } +} + + diff --git a/api/rust/cargo/lief/src/macho.rs b/api/rust/cargo/lief/src/macho.rs index 3897411051..ae75c77ef8 100644 --- a/api/rust/cargo/lief/src/macho.rs +++ b/api/rust/cargo/lief/src/macho.rs @@ -34,6 +34,7 @@ pub mod section; pub mod symbol; pub mod header; pub mod stub; +pub mod builder; #[doc(inline)] pub use binary::Binary; diff --git a/api/rust/cargo/lief/src/macho/binary.rs b/api/rust/cargo/lief/src/macho/binary.rs index 0fa04a9c1e..a9ac77bce4 100644 --- a/api/rust/cargo/lief/src/macho/binary.rs +++ b/api/rust/cargo/lief/src/macho/binary.rs @@ -1,8 +1,10 @@ use std::mem::size_of; use std::pin::Pin; +use std::path::Path; use num_traits::{Num, cast}; use crate::Error; +use super::builder::Config; use super::commands::build_version::{BuildVersion, Platform}; use super::commands::code_signature::CodeSignature; use super::commands::code_signature_dir::CodeSignatureDir; @@ -305,6 +307,17 @@ impl Binary { Err(Error::NotSupported) } + + /// Write back the current MachO binary into the file specified in parameter + pub fn write(&mut self, output: &Path) { + self.ptr.as_mut().unwrap().write(output.to_str().unwrap()); + } + + /// Write back the current MachO binary into the file specified in parameter with the + /// configuration provided in the second parameter. + pub fn write_with_config(&mut self, output: &Path, config: Config) { + self.ptr.as_mut().unwrap().write_with_config(output.to_str().unwrap(), config.to_ffi()); + } } impl generic::Binary for Binary { diff --git a/api/rust/cargo/lief/src/macho/builder.rs b/api/rust/cargo/lief/src/macho/builder.rs new file mode 100644 index 0000000000..7abd18b433 --- /dev/null +++ b/api/rust/cargo/lief/src/macho/builder.rs @@ -0,0 +1,25 @@ +use lief_ffi as ffi; + +/// Structure used to configure the [`crate::macho::Binary::write_with_config`] operation +#[derive(Debug)] +pub struct Config { + /// Rebuild the `__LINKEDIT` segment + pub linkedit: bool, +} + +impl Default for Config { + fn default() -> Config { + Config { + linkedit: true, + } + } +} + +impl Config { + #[doc(hidden)] + pub fn to_ffi(&self) -> ffi::MachO_Binary_write_config_t { + ffi::MachO_Binary_write_config_t { + linkedit: self.linkedit, + } + } +} diff --git a/api/rust/cargo/lief/src/pe/binary.rs b/api/rust/cargo/lief/src/pe/binary.rs index c134f2e95a..aa2dd0c4da 100644 --- a/api/rust/cargo/lief/src/pe/binary.rs +++ b/api/rust/cargo/lief/src/pe/binary.rs @@ -3,6 +3,7 @@ use lief_ffi as ffi; use num_traits::{cast, Num}; use std::mem::size_of; use std::pin::Pin; +use std::path::Path; use super::data_directory::{DataDirectories, DataDirectory}; use super::debug; @@ -307,6 +308,11 @@ impl Binary { Err(Error::NotSupported) } + + /// Write back the current PE binary into the file specified in parameter + pub fn write(&mut self, output: &Path) { + self.ptr.as_mut().unwrap().write(output.to_str().unwrap()); + } } impl std::fmt::Debug for Binary { diff --git a/api/rust/cargo/lief/tests/elf_tests.rs b/api/rust/cargo/lief/tests/elf_tests.rs index 71185239cc..700508071b 100644 --- a/api/rust/cargo/lief/tests/elf_tests.rs +++ b/api/rust/cargo/lief/tests/elf_tests.rs @@ -1,5 +1,6 @@ mod utils; use std::env; +use lief::elf::builder::Config; use lief::logging; use lief::elf::dynamic; use lief::elf::dynamic::DynamicEntry; @@ -224,3 +225,16 @@ fn test_api() { test_with("simple-gcc-c.bin"); } +#[test] +fn test_mut_api() { + let path = utils::get_elf_sample("elf_reader.mips.elf").unwrap(); + let Binary::ELF(mut bin) = Binary::parse(path.to_str().unwrap()).unwrap() else { panic!("Expecting an ELF"); }; + let tmpfile = tempfile::NamedTempFile::new().unwrap(); + bin.write(tmpfile.path()); + + let path = utils::get_elf_sample("ELF_Core_issue_808.core").unwrap(); + let Binary::ELF(mut bin) = Binary::parse(path.to_str().unwrap()).unwrap() else { panic!("Expecting an ELF"); }; + let tmpfile = tempfile::NamedTempFile::new().unwrap(); + bin.write_with_config(tmpfile.path(), Config::default()); +} + diff --git a/api/rust/cargo/lief/tests/macho_tests.rs b/api/rust/cargo/lief/tests/macho_tests.rs index 4f32d0f792..df5a8c1085 100644 --- a/api/rust/cargo/lief/tests/macho_tests.rs +++ b/api/rust/cargo/lief/tests/macho_tests.rs @@ -2,6 +2,7 @@ mod utils; use std::path::Path; use std::env; use lief::logging; +use lief::macho::builder::Config; use lief::macho::Relocation; use lief::generic::Binary as GenericBinary; use lief::macho::binding_info::{self, AsGeneric}; @@ -310,7 +311,6 @@ fn test_with_fullpath(name: &str, suffix: &str) { #[test] fn test_api() { - let mut dir = env::temp_dir(); dir.push("lief_macho_test.log"); logging::set_path(dir.as_path()); @@ -330,3 +330,21 @@ fn test_api() { test_with("liblog_srp.dylib"); test_with_fullpath("CoreFoundation", "private/MachO/CoreFoundation"); } + +#[test] +fn test_mut_api() { + let path = utils::get_macho_sample("FAT_MachO_x86_x86-64_library_libc++abi.dylib").unwrap(); + let Binary::MachO(fat) = Binary::parse(path.to_str().unwrap()).unwrap() else { panic!("Expecting an ELF"); }; + for mut bin in fat.iter() { + let tmpfile = tempfile::NamedTempFile::new().unwrap(); + bin.write(tmpfile.path()); + } + + let path = utils::get_macho_sample("json_api.cpp_1.o").unwrap(); + let Binary::MachO(fat) = Binary::parse(path.to_str().unwrap()).unwrap() else { panic!("Expecting an ELF"); }; + for mut bin in fat.iter() { + let tmpfile = tempfile::NamedTempFile::new().unwrap(); + bin.write_with_config(tmpfile.path(), Config::default()); + } +} + diff --git a/api/rust/include/LIEF/rust/ELF/Binary.hpp b/api/rust/include/LIEF/rust/ELF/Binary.hpp index fe0e2d5148..af7059508b 100644 --- a/api/rust/include/LIEF/rust/ELF/Binary.hpp +++ b/api/rust/include/LIEF/rust/ELF/Binary.hpp @@ -35,6 +35,30 @@ #include "LIEF/rust/error.hpp" +class ELF_Binary_write_config_t { + public: + bool dt_hash; + bool dyn_str; + bool dynamic_section; + bool fini_array; + bool gnu_hash; + bool init_array; + bool interpreter; + bool jmprel; + bool notes; + bool preinit_array; + bool relr; + bool android_rela; + bool rela; + bool static_symtab; + bool sym_verdef; + bool sym_verneed; + bool sym_versym; + bool symtab; + bool coredump_notes; + bool force_relocate; +}; + class ELF_Binary : public AbstractBinary { public: using lief_t = LIEF::ELF::Binary; @@ -332,6 +356,33 @@ class ELF_Binary : public AbstractBinary { return impl().is_targeting_android(); } + void write(std::string output) { impl().write(output); } + void write_with_config(std::string output, ELF_Binary_write_config_t config) { + impl().write(output, LIEF::ELF::Builder::config_t { + config.dt_hash, + config.dyn_str, + config.dynamic_section, + config.fini_array, + config.gnu_hash, + config.init_array, + config.interpreter, + config.jmprel, + config.notes, + config.preinit_array, + config.relr, + config.android_rela, + config.rela, + config.static_symtab, + config.sym_verdef, + config.sym_verneed, + config.sym_versym, + config.symtab, + config.coredump_notes, + config.force_relocate, + }); + } + private: const lief_t& impl() const { return as(this); } + lief_t& impl() { return as(this); } }; diff --git a/api/rust/include/LIEF/rust/MachO/Binary.hpp b/api/rust/include/LIEF/rust/MachO/Binary.hpp index 24c39e5435..1b17b0e51e 100644 --- a/api/rust/include/LIEF/rust/MachO/Binary.hpp +++ b/api/rust/include/LIEF/rust/MachO/Binary.hpp @@ -54,6 +54,11 @@ #include "LIEF/rust/ObjC/Metadata.hpp" +class MachO_Binary_write_config_t { + public: + bool linkedit; +}; + class MachO_Binary : public AbstractBinary { public: using lief_t = LIEF::MachO::Binary; @@ -278,10 +283,18 @@ class MachO_Binary : public AbstractBinary { bool is_ios() const { return impl().is_ios(); } bool is_macos() const { return impl().is_macos(); } + void write(std::string output) { impl().write(output); } + void write_with_config(std::string output, MachO_Binary_write_config_t config) { + impl().write(output, { + config.linkedit + }); + } + static bool is_exported(const MachO_Symbol& symbol) { return lief_t::is_exported(static_cast(symbol.get())); } private: const lief_t& impl() const { return as(this); } + lief_t& impl() { return as(this); } }; diff --git a/api/rust/include/LIEF/rust/PE/Binary.hpp b/api/rust/include/LIEF/rust/PE/Binary.hpp index d2962bd6c5..03a5f1ef47 100644 --- a/api/rust/include/LIEF/rust/PE/Binary.hpp +++ b/api/rust/include/LIEF/rust/PE/Binary.hpp @@ -253,6 +253,9 @@ class PE_Binary : public AbstractBinary { return make_span(impl().get_content_from_virtual_address(virtual_address, size)); } + void write(std::string output) { impl().write(output); } + private: const lief_t& impl() const { return as(this); } + lief_t& impl() { return as(this); } }; diff --git a/include/LIEF/MachO/Binary.hpp b/include/LIEF/MachO/Binary.hpp index 9102e08d0c..bbdce8a01f 100644 --- a/include/LIEF/MachO/Binary.hpp +++ b/include/LIEF/MachO/Binary.hpp @@ -27,6 +27,7 @@ #include "LIEF/MachO/BindingInfoIterator.hpp" #include "LIEF/MachO/BuildVersion.hpp" #include "LIEF/MachO/Stub.hpp" +#include "LIEF/MachO/Builder.hpp" #include "LIEF/visibility.h" #include "LIEF/utils.hpp" @@ -320,6 +321,16 @@ class LIEF_API Binary : public LIEF::Binary { /// @param filename Path to write the reconstructed binary void write(const std::string& filename) override; + + /// Reconstruct the binary object and write the result in the given + /// `filename`. + /// + /// The second `config` parameter is used to tweak the building process + /// + /// @param filename Path to write the reconstructed binary + /// @param config Builder configuration + void write(const std::string& filename, Builder::config_t config); + /// Reconstruct the binary object and write the result in the given `os` stream /// /// @param os Output stream to write the reconstructed binary diff --git a/src/MachO/Binary.cpp b/src/MachO/Binary.cpp index 6cca31dcc5..1ed35f2964 100644 --- a/src/MachO/Binary.cpp +++ b/src/MachO/Binary.cpp @@ -340,6 +340,10 @@ void Binary::write(const std::string& filename) { Builder::write(*this, filename); } +void Binary::write(const std::string& filename, Builder::config_t config) { + Builder::write(*this, filename, config); +} + void Binary::write(std::ostream& os) { Builder::write(*this, os); }