December 18, 2024 by Jacob Latonis4 minutes
Detecting things in Mach-O binaries used to be quite an effort in the original YARA; it would involve magic byte validation, guessing offsets, counting occurrences, and a lot more.
With the advent of YARA-X, the new and improved Mach-O module can be leveraged in various ways. This blog post will detail particular use-cases and show examples for those and more.
If you’re interested in seeing a more in-depth look at some of the features mentioned below, you can watch the talk linked below, given by Greg Lesnewich and myself, which breaks down the motivations behind the Mach-O module in YARA-X and features examples of how it can be utilized. If you prefer a written summary, keep reading.
macho
moduleTo begin using the macho
module inside a YARA rule, add the following at the
top of your rule like so:
A Mach-O binary can feature a lot of different information and structures. To
see all of what YARA-X and the macho
module can potentially parse, you can
see everything documented in the macho
documentation
for YARA-X.
This section will cover commonly used structures for detections in YARA rules.
If you want to detect around something in the symbol table, we can leverage
the macho.symtab
structure, where each string is located in entries
.
The macho
module has multiple ways to explore imports in a Mach-O binary.
To iterate over the imports defined via load commands in the Mach-O binary, one
can use the array of imports located at macho.imports
:
Use has_import
to detect whether an import is present in a given binary.
Exported symbols from the Mach-O binary are parsed in YARA-X and can be used queried against and enumerated.
To iterate over the exports found in a Mach-O binary, the macho
module
contains the list of exports as a string found at macho.exports
.
To check if a Mach-O binary contains a specific export, one can leverage the
has_export()
function like so:
Remote paths are used to tell the Mach-O binary where it can search for the libraries it depends on. These load commands are parsed via YARA-X and can be leveraged in YARA rules.
To iterate through the rpaths
used in load commands in the Mach-O binary, one
can leverage the rpaths
array from the macho
module:
To detect if a specific rpath
is present in the load commands, one can use
the has_rpath()
function:
Dylibs are shared libraries leveraged by the Mach-O binary. To iterate through the dylibs loaded in the Mach-O binary, one can iterate through the dylib structures parsed by YARA-X:
To detect if a certain dylib is loaded via a load command in the binary, the
has_dylib()
function is available for use.
Mach-O binaries can feature entitlements, which are XML properties for
requesting
certain permissions from the user/device that it is being executed on. These are
parsed out into strings which are able to be queried via the macho
module.
These entitlements can be leveraged in multiple ways.
One can iterate over the entitlements like so:
To detect a specific entitlement, one can also use the has_entitlement()
function.
If you wish to detect if a Mach-O binary is using a certain set of dylibs,
imports, exports, entitlements, or more, you can leverage the respective
_hash()
function for each structure.
The hash algorithm is an MD5 hash of the deduplicated, sorted, and lowercased
entries joined via a comma (md5("dylib_1,dylib_2,dylib_n")
) found in the
binary for each category.
There are many features and structures parsed with the macho
module in YARA-X,
and only a subset of them were covered in this blog post. To fully explore what
is possible with the macho
module, please consult the documentation.