Note
import lief
# Using filepath
macho: lief.MachO.FatBinary | None = lief.MachO.parse("/bin/ls")
# Using a Path from pathlib
macho: lief.MachO.FatBinary | None = lief.MachO.parse(
pathlib.Path(r"C:\Users\test.macho")
)
# Using a io object
with open("/bin/ssh", "rb") as f:
macho: lief.MachO.FatBinary | None = lief.MachO.parse(f)
#include <LIEF/MachO.hpp>
// Using a file path as a std::string
std::unique_ptr<LIEF::MachO::FatBinary> macho =
LIEF::MachO::Parser::parse("/bin/ls");
// Using a vector
std::vector<uint8_t> my_raw_macho;
macho = LIEF::MachO::Parser::parse(my_raw_macho);
let macho: lief::macho::FatBinary = lief::macho::FatBinary::parse("/bin/ls").unwrap();
fat: lief.MachO.FatBinary
# Iterate
for macho in fat:
print(macho.entrypoint)
print(len(macho.commands))
# Pick one at the specified index
macho = fat.at(0)
# Pick one based on the architecture
macho = fat.take(lief.MachO.Header.CPU_TYPE.ARM64)
std::unique_ptr<LIEF::MachO::FatBinary> fat;
// Iterate
for (const LIEF::MachO::Binary& macho : *fat) {
std::cout << macho.entrypoint() << '\n';
std::cout << macho.commands().size() << '\n';
}
// Pick one at the specified index (without taking ownership)
const LIEF::MachO::Binary* macho = fat->at(0);
// Pick one at the specified index and taking ownership
std::unique_ptr<LIEF::MachO::Binary> owned = fat->take(0);
// Pick one with the given arch and taking ownership
std::unique_ptr<LIEF::MachO::Binary> arm64 =
fat->take(LIEF::MachO::Header::CPU_TYPE::ARM64);
let fat: &lief::macho::FatBinary = some_fat;
// Iterate
for macho in fat.iter() {
println!("{}", macho.entrypoint());
}
// Pick one with the given arch
let arm64 = fat
.with_cpu(lief::macho::header::CpuType::ARM64)
.expect("Missing ARM64");
macho: lief.MachO.FatBinary
macho.at(0).write("fit.macho")
macho.write("fat.macho") # write-back the whole FAT binary
std::unique_ptr<LIEF::MachO::FatBinary> macho;
macho->take(LIEF::MachO::Header::CPU_TYPE::ARM64)->write("fit.macho");
macho->write("fat.macho");
let fat: &mut lief::macho::FatBinary = some_fat;
fat.with_cpu(lief::macho::header::CpuType::ARM64)
.unwrap()
.write("fit.macho");
Note
macho: lief.MachO.Binary
new_macho: bytes = macho.write_to_bytes()
std::unique_ptr<LIEF::MachO::Binary> macho;
std::ostringstream os;
macho->write(os);
std::string buffer = os.str();
const auto* start = reinterpret_cast<const uint8_t*>(buffer.data());
size_t size = buffer.size();
let macho: &mut lief::macho::Binary = some_macho;
let bytes: Vec<u8> = macho.write_to_bytes();
Warning
parser_config = lief.MachO.ParserConfig()
parser_config.parse_dyld_bindings = False
fat = lief.MachO.parse("my.macho", parser_config)
assert isinstance(fat, lief.MachO.FatBinary)
macho = fat.at(0)
assert isinstance(fat, lief.MachO.Binary)
builder_config = lief.MachO.Builder.config_t()
builder_config.linkedit = False
macho.write("new.macho", builder_config)
LIEF::MachO::ParserConfig parser_config;
parser_config.parse_dyld_bindings = false;
std::unique_ptr<LIEF::MachO::FatBinary> fat =
LIEF::MachO::Parser::parse("my.macho", parser_config);
LIEF::MachO::Binary* macho = fat->at(0);
LIEF::MachO::Builder::config_t builder_config;
builder_config.linkedit = false;
macho->write("new.macho", builder_config);
let mut parser_config = lief::macho::ParserConfig::default();
parser_config.parse_dyld_bindings = false;
let mut fat = lief::macho::parse_with_config("my.macho", &parser_config).unwrap();
let mut macho = fat.iter().next().unwrap();
let mut builder_config = lief::macho::builder::Config::default();
builder_config.linkedit = false;
macho.write_with_config("new.macho", builder_config);
See also
# 0x11e32c000 is the (absolute) address at which the dump was mapped
fat = lief.MachO.parse_from_dump("module.dump", 0x11E32C000)
assert isinstance(fat, lief.MachO.FatBinary)
macho = fat.at(0)
assert isinstance(fat, lief.MachO.Binary)
for segment in macho.segments:
print(segment.name, hex(segment.virtual_address))
auto fat = LIEF::MachO::Parser::parse_from_dump("module.dump", 0x11e32c000);
const LIEF::MachO::Binary* macho = fat->at(0);
for (const LIEF::MachO::SegmentCommand& segment : macho->segments()) {
std::cout << segment.name() << '\n';
}
let fat = lief::macho::FatBinary::parse_from_dump("module.dump", 0x1_1e32_c000).unwrap();
let macho = fat.iter().next().unwrap();
for segment in macho.segments() {
println!("{} {:#x}", segment.name(), segment.virtual_address());
}
Note
The second parameter must be the (absolute) virtual address at which the dump was mapped. It is used to convert the virtual addresses found in the Mach-O structures back into an offset within the dump.
# Find the module to dump in the current process
mod = lief.runtime.module_from_name("libsystem_c.dylib")
assert isinstance(mod, lief.runtime.osx.Module)
# Dump the module's memory into a file (the raw bytes are also returned) ...
data: bytes = mod.dump("module.dump")
# ... and parse it back using the same imagebase:
macho = lief.MachO.parse_from_dump(data, mod.imagebase)
// Find the module to dump in the current process
auto mod = LIEF::runtime::module_from_name("libsystem_c.dylib");
// Dump the module's memory into a file (the raw bytes are also returned)
std::vector<uint8_t> data = mod->dump("module.dump");
auto fat = LIEF::MachO::Parser::parse_from_dump("module.dump", mod->imagebase());
use lief::runtime::Module;
let module = lief::runtime::module_from_name("libsystem_c.dylib").unwrap();
// Dump the module's memory into a file (the raw bytes are also returned)
let data = module.dump_to_file("module.dump");
let fat = lief::macho::FatBinary::parse_from_dump("module.dump", module.imagebase()).unwrap();
Sometimes, we need to modify the Mach-O RPath commands or the (absolute) path of a linked library in an executable. When recompiling or linking the executable is not possible, LIEF can be used for these modifications.
For example, let’s consider a binary with the following dependencies:
$ otool -L hello.bin
hello:
/Users/romain/dev/libmylib.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1700.255.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1345.100.2)
One can change the directory of libmylib.dylib with the following code:
fat = lief.MachO.parse("hello.bin")
assert isinstance(fat, lief.MachO.FatBinary)
macho = fat.at(0)
assert isinstance(fat, lief.MachO.Binary)
lib = macho.find_library("libmylib.dylib")
assert isinstance(lib, lief.MachO.DylibCommand)
lib.name = "/opt/homebrew/my_package/libmylib.dylib"
macho.write("hello_fixed.bin")
std::unique_ptr<LIEF::MachO::Binary> macho =
LIEF::MachO::Parser::parse("hello.bin")->take(0);
LIEF::MachO::DylibCommand* lib = macho->find_library("libmylib.dylib");
lib->name("/opt/homebrew/my_package/libmylib.dylib");
macho->write("hello_fixed.bin");
let fat = lief::macho::FatBinary::parse("hello.bin").unwrap();
let mut binary = fat.iter().next().unwrap();
let mut lib = binary.find_library("libmylib.dylib").unwrap();
lib.set_name("/opt/homebrew/my_package/libmylib.dylib");
binary.write("hello_fixed.bin");
Note
It is worth mentioning that LIEF doesn’t impose restrictions on the length of modified library paths. LIEF manages all internal modifications to support both longer and shorter library paths.
This type of modification can be used in conjunction with the @rpath feature of Mach-O binaries:
fat = lief.MachO.parse("hello.bin")
assert isinstance(fat, lief.MachO.FatBinary)
macho = fat.at(0)
assert isinstance(fat, lief.MachO.Binary)
rpath = lief.MachO.RPathCommand.create("/opt/homebrew/my_package")
assert isinstance(rpath, lief.MachO.RPathCommand)
macho.add(rpath)
std::unique_ptr<LIEF::MachO::Binary> macho =
LIEF::MachO::Parser::parse("hello.bin")->take(0);
auto rpath = LIEF::MachO::RPathCommand::create("/opt/homebrew/my_package");
macho->add(*rpath);
let fat = lief::macho::FatBinary::parse("hello.bin").unwrap();
let mut binary = fat.iter().next().unwrap();
let rpath = RPath::new("/opt/homebrew/my_package");
binary.add_command(rpath);
Then, we can change the library path of libmylib.dylib to include the RPath prefix:
macho: lief.MachO.Binary
lib = macho.find_library("libmylib.dylib")
assert isinstance(lib, lief.MachO.DylibCommand)
lib.name = "@rpath/libmylib.dylib"
macho.write("hello_fixed.bin")
std::unique_ptr<LIEF::MachO::Binary> macho;
LIEF::MachO::DylibCommand* lib = macho->find_library("libmylib.dylib");
lib->name("@rpath/libmylib.dylib");
macho->write("hello_fixed.bin");
let binary: &mut lief::macho::Binary = some_binary;
let mut lib = binary.find_library("libmylib.dylib").unwrap();
lib.set_name("@rpath/libmylib.dylib");
binary.write("hello_fixed.bin");
For more details, you can check the Obj-C section.