Mach-O file format for red team professionals - Part 2
Learn about standard and universal Mach-O binaries and how they differ from each other.
In the last post, I covered the Mach-O file format at a high level. Before I descent into further details, lets understand what are standard and universal Mach-O binaries.
In macOS, binaries refer to compiled executable files that the system can run. The structure of these binaries depends on the type of processor architecture they support. Over the years, Apple has transitioned between multiple processor architectures, leading to the need for different types of binaries: standard binaries and universal binaries.
A standard binary is a compiled executable file that is designed to run on a single CPU architecture. For example, Intel-only binaries are compiled to run only on Intel x86_64 processors whereas ARM-only binaries are compiled to run only on Apple Silicon (ARM64) processors.
Standard binaries start with mach_header
(32-bit) or mach_header_64
(64-bit). I will discuss these in more detail in upcoming posts.
You can view the mach_header
of a standard binary by using otool
with -h flag:
otool -h ls_x64
A universal binary (also called a fat binary) is an executable that contains code capable of running on multiple architectures within the same file, allowing it to run on different types of processors seamlessly. macOS automatically selects the appropriate architecture when executing the binary. Universal binaries allow a single app to run on both the old and new architectures without requiring users to manually choose the correct version.
You can check whether an executable is universal or standard, by using the lipo
or file
tool:
lipo -info /bin/ls
lipo -info ls_x64
file /bin/ls
file ls_x64
Universal binaries start with fat_header
followed by fat_arch
structures. The number of fat_arch
structures depend on the number of architectures supported by the binary. The fat_arch
structure contains information about supported architectures. fat_header
and fat_arch
structures are not present in standard binaries.
You can view the fat_header
of a universal binary by using otool
with -f flag:
otool -f /bin/ls
You can use the lipo
tool to extract the architecture specific Mach-O binary from a universal binary:
lipo /bin/ls -thin x86_64 -output ls_x64
file ls_x64
Key differences between a standard and a universal binary are as follows:
Red Team Notes
- A standard binary is a compiled executable file that is designed to run on a single CPU architecture.
- A universal binary (also called a fat binary) is an executable that contains code capable of running on multiple architectures within the same file.
Follow my journey of 100 Days of Red Team on WhatsApp or Discord.