Note
import lief
# Using filepath
macho: lief.MachO.FatBinary = lief.MachO.parse("/bin/ls")
# Using a Path from pathlib
macho: lief.MachO.FatBinary = 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 = 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;
std::unique_ptr<LIEF::MachO::FatBinary> macho = LIEF::MachO::Parser::parse(my_raw_macho);
let macho: lief::macho::FatBinary = lief::macho::FatBinary::parse("/bin/ls");
fat: lief.MachO.FatBinary
# Iterate
for macho in fat:
print(macho.entrypoint)
print(len(macho.commands))
# Pick one at the specified index
macho: lief.MachO.Binary = fat.at(0)
# Pick one based on the architecture
macho: lief.MachO.Binary = 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
const LIEF::MachO::Binary* macho = fat->take(0);
// Pick one with the given arch and taking ownership
const LIEF::MachO::Binary* macho = fat->take(LIEF::MachO::Header::CPU_TYPE::ARM64);
let fat: lief::macho::FatBinary;
// Iterate
for macho in fat {
println!("{}", macho.entrypoint());
}
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->at(LIEF::MachO::Header::CPU_TYPE::ARM64)->write("fit.macho");
macho->write("fat.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();
Warning
parser_config = lief.MachO.ParserConfig()
parser_config.parse_dyld_bindings = False
macho: lief.MachO.FatBinary = lief.MachO.parse("my.macho", parser_config)
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;
auto macho = LIEF::MachO::Parser::parse("my.macho", parser_config);
LIEF::MachO::Builder::config_t builder_config;
builder_config.linkedit = false;
macho->write("new.macho", builder_config);
See also
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:
macho = lief.MachO.parse("hello.bin")
lib: lief.MachO.DylibCommand = macho.find_library("libmylib.dylib")
lib.name = "/opt/hombrew/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/hombrew/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/hombrew/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:
macho = lief.MachO.parse("hello.bin")
rpath = lief.MachO.RPathCommand.create("/opt/hombrew/my_package")
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/hombrew/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/hombrew/my_package");
binary.add_command(rpath);
Then, we can change the library path of libmylib.dylib to include the RPath prefix:
lib: lief.MachO.DylibCommand = macho.find_library("libmylib.dylib")
lib.name = "@rpath/libmylib.dylib"
macho.write("hello_fixed.bin")
LIEF::MachO::DylibCommand* lib = macho->find_library("libmylib.dylib");
lib->name("@rpath/libmylib.dylib");
macho->write("hello_fixed.bin");
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.