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 take the ownership)
const LIEF::MachO::Binary* macho = fat->at(0);
// Pick one at the specified index and take the ownership
const LIEF::MachO::Binary* macho = fat->take(0);
// Pick one with the given arch and take the 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 instance, 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");
Note
It is worth mentioning that LIEF does not have restrictions on the length of the modified library path. LIEF manages all the internal modifications to support both longer and shorter library paths.
This kind of modification can be used in pair 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);
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");
For more details, you can check the Obj-C section.