This module allows to inspect Objective-C metadata from a Mach-O binary.
macho: lief.MachO.Binary = ...
metadata: lief.objc.Metadata = macho.objc_metadata
if metadata is not None:
print("Objective-C metadata found")
std::unique_ptr<LIEF::MachO::Binary> macho;
std::unique_ptr<LIEF::objc::Metadata> metadata = macho->objc_metadata();
if (metadata != nullptr) {
std::cout << "Objective metadata found\n";
}
let macho: lief::macho::Binary;
if let Some(metadata) = macho.objc_metadata() {
println!("Objective-C metadata found");
}
macho: lief.MachO.Binary = lief.parse("some_macho")
metadata: lief.objc.Metadata = macho.objc_metadata
for clazz in metadata.classes:
print(f"name={clazz.name}")
for meth in clazz.methods:
print(f" method.name={meth.name}")
print(metadata.to_decl())
std::unique_ptr<LIEF::MachO::FatBinary> fat = LIEF::MachO::Parser::parse(argv[1]);
LIEF::MachO::Binary* bin = fat->at(0);
std::unique_ptr<LIEF::objc::Metadata> metadata = bin->objc_metadata();
for (const std::unique_ptr<LIEF::objc::Class>& clazz : metadata->classes()) {
log(LOG_LVL, "name={}", clazz->name());
for (const std::unique_ptr<LIEF::objc::Method>& meth : clazz->methods()) {
log(LOG_LVL, " method.name={}", meth->name());
}
}
log(LOG_LVL, metadata->to_decl());
let Some(lief::Binary::MachO(fat)) = lief::Binary::parse(&path) else { process::exit(1); };
let Some(bin) = fat.iter().next() else { process::exit(1); };
let Some(metadata) = bin.objc_metadata() else { process::exit(1); };
for class in metadata.classes() {
println!("name={}", class.name());
for method in class.methods() {
println!(" method.name={}", method.name());
}
}
println!("{}", metadata.to_decl());
When doing binary analysis it can useful to generate header-like information to get a global overview of the different structures present in the objective-c metadata.
LIEF provides a way to generate this header like information at different levels:
From a technical standpoint, this output is generated by generating a Clang AST and by applying the LLVM’s printer visitor on this AST.
fn classdump(macho: &lief::macho::Binary) {
let metadata = macho.objc_metadata().expect("Missing Objective-C info");
for class in metadata.classes() {
println!("{}", class.to_decl());
}
}
@interface APMEventFilter<APMAudienceFilter> {
bool _sessionScoped;
bool _dynamic;
bool _sequence;
int _audienceID;
int _filterID;
NSString * _eventName;
NSData * _data;
}
// Address: 0x0101859ee0
- (NSObject *)initWithAudienceID:(APMEventFilter *)self filterID:(SEL)id eventName:(int)arg2 data:(int)arg3 sessionScoped:(NSObject *)arg4 dynamic:(NSObject *)arg5 sequence:(bool)arg6 :(bool)arg7 :(bool)arg8;
// Address: 0x0101682590
- (int)audienceID:(APMEventFilter *)self :(SEL)id;
// Address: 0x01017b6d98
- (int)filterID:(APMEventFilter *)self :(SEL)id;
// Address: 0x01018a4a6c
- (bool)isSessionScoped:(APMEventFilter *)self :(SEL)id;
// Address: 0x01017630d4
- (bool)isDynamic:(APMEventFilter *)self :(SEL)id;
// Address: 0x01016adbb4
- (bool)isSequence:(APMEventFilter *)self :(SEL)id;
// Address: 0x010187a5f8
- (NSObject *)eventName:(APMEventFilter *)self :(SEL)id;
// Address: 0x0101581bfc
- (NSObject *)data:(APMEventFilter *)self :(SEL)id;
// Address: 0x01018e1f3c
- (void).cxx_destruct:(APMEventFilter *)self :(SEL)id;
@property void eventName;
@property void data;
@property void audienceID;
@property void filterID;
@property void sessionScoped;
@property void dynamic;
@property void sequence;
@property void hash;
@property void superclass;
@property void description;
@property void debugDescription;
@end
def print_without_address(macho: lief.MachO.Binary):
metadata = macho.objc_metadata
config = lief.objc.DeclOpt()
config.show_annotations = False
for cls in metadata.classes:
print(cls.to_decl(config))
You can find the documentation of the API for the different languages here:
Rust API: lief::objc