Radare2 Power Ups | Delivering Faster macOS Malware Analysis With r2 Customization

We Keep you Connected

Radare2 Power Ups | Delivering Faster macOS Malware Analysis With r2 Customization

In previous posts, we’ve explored how analysts can use radare2 (aka r2) for macOS malware triage, work around anti-analysis tricks, decrypt encrypted strings, and generate function signatures and YARA rules. Like most reversing tools, radare2 can be customized and extended to increase the analyst’s productivity and make analysis and triage much faster.
In this fifth post in the series, we look at some effective ways to power up r2, providing practical examples to get you started on the path to making radare2 even more productive for macOS malware analysis. We’ll cover automation and customization via aliases, macros and functions. Along the way, we’ll also explore how we can effectively implement binary and function diffing with radare2.
Just as most shells have a “read command” config file (e.g., .bashrc, .zshrc), so r2 has a ~/.radare2rc file in which you can define environment variables, aliases and macros. This file doesn’t exist by default so you need to create it when you make your first customizations.
It’s often said that one of the obstacles to adopting r2 is the steep learning curve, a large part of which is getting muscle-memory familiar with r2’s cryptic commands. One very fast way to flatten that curve is to define macros and aliases for new commands as you learn them – naming any hard-to-remember native commands with your own labels.
Aliases and macros are also useful for chaining oft-used commands together. If you find yourself always running the same commands as your work through your initial triage of a sample, you can save yourself some time and typing by combining those commands into one or more aliases or macros.

An r2 customization to find the entrypoint of x86 dylibs
An r2 customization to find the entrypoint of x86 dylibs

We will look at some useful examples below, but first let’s understand the syntax for aliases and macros.
An alias is defined with a name prefixed by a $ sign, an = operator, and a value in single quotes. Values can be one or more commands, separated by a semi-colon. For example, if you struggle to remember r2’s rather cryptic command names, you could replace them with more memorable command names of your own. Create a file at ~/.radare2rc, add the following line and then save the file.
Start a new r2 session. Now, typing $libs at the r2 prompt will run the il command. You can still use il directly as well – as the name suggests, aliases are just alternative names, not replacements, for existing commands.
From the Official Radare2 book, we learn that macros are written inside parentheses with each command separated by a semi-colon. The first item in the list is the macro name. By way of example, rather than having a $libs alias, why not print out sections and linked libraries at the same time? This example would do just that:
Macros are called with the syntax .(macro) like so:
It’s easy to see how you can build on this idea. I use a macro called .(meta) to give me all the basic info about a file’s structure as soon as I’ve loaded it into radare2.
This macro provides the file hashes in various algos, the compiled language, file size, sections, section entropy and the load commands. If the file under analysis is UPX packed, it will also indicate that, and if the source code is Go it displays the Go Build ID string. The macro is defined as follows, feel free to adopt or adapt it for your needs:
Within the .(meta) macro, notice the command sequence ih~cmd~!cmdsize. This warrants a little explanation. Readers of our previous posts on r2 and macOS malware may recall that the tilde is r2’s internal grep function. The tilde followed by an exclamation mark ~!<expression> filters out the given expression, equivalent to grep -v. You can see the difference in the following image.
Moreover, note that the .(meta) macro calls out to the system grep utility as well. The ability to utilize any command line utility on the system from within r2 is one of its major advantages over other reversing platforms.
Many of the things you can do with macros you could also do with Aliases, and vice versa; it’s largely a matter of personal preference. However, note that macros have one neat superpower – you can pass arguments to them.
Here’s a good example: r2 has a command for diffing or comparing code within a sample, either as hex or disassembly (cc and ccd). For some reason (I’m sure there’s a perfectly good one), this function counterintuitively displays the output from the first address given to the right of the output from the second address given. We can ‘correct’ this with a macro that takes the addresses as arguments but swaps their order when it passes them to cc.
Incidentally, the cc command (or our reimplementation of it in a macro) can be very useful for finding common code within samples when writing YARA or other hunting rules, a topic we’ll discuss a bit further below.
To find IP address patterns and other useful artifacts in a binary, you can create macros with search regexes.
Here’s a few examples to get your started.
Search for places where an executable gathers user and local environment information.
You can automate different searches for XOR instructions with the following r2 macro:
For the following two macros, you will need YARA installed locally on the host. This can be done with MacPorts, Homebrew or by installing from Github and following the instructions here.
With YARA installed, it is easy to call it from within r2 to see if a rule you’ve created for a sample will fire. This is a great way to develop and test rules on the fly as you triage new samples.
On my analysis machines, I have my rules stored in a subdirectory of /usr/local/bin, so my macro looks like this:
As yara is an external command, it is prefixed by an exclamation point !. This is how to tell the r2 shell that we want to call an external command line utility, a very useful feature that allows you to bring in all the power of the command line utilities at your disposal directly into r2. The -s option allows us to see which strings hit (and how many times). See man yara for more options. The `o.` command at the end of the macro is an r2 command that returns the file name of the currently loaded binary.
Since Apple’s own built-in malware blocking tool XProtect also uses YARA rules, you can create a macro to see whether Apple has a rule for your sample. To create an .(xp) macro to check files against Apple’s XProtect database signatures file (remember: YARA must be installed first), use the following macro:
Don’t be surprised, however, if you don’t get many matches: XProtect’s YARA signature database is thin at best.
By now, you might be starting to collect quite a list of macros and aliases. How to remember them all? There’s a couple of built-in ways, and we’ll also look at one last .radare2rc customization to help us out with this, too.
From within, r2 you can see all defined aliases and macros by typing $* and (*, respectively.
We can also have r2 print our entire config file when it starts up by adding a further customization. At the end of the .radare2rc file, try something like this:
The sed command after the pipe prevents the last line of the file from being printed – an optional customization you can ignore if you wish. You could also just add the $* and (* commands above to the config file instead, but I like to see the whole file as a reminder of the entire environment.
These examples should be enough to get you started creating useful aliases and macros to help speed along your own analysis.
Aliases and macros are useful shortcuts – the command line equivalent to GUI apps’ hotkeys and key chords – but there are other, more powerful ways we can customize radare2 and drive it with custom functions and scripts.
As an example, let’s add the following function to our shell config file (e.g., ~/.zshrc or ~/.bashrc):
This leverages a radare2 tool called radiff2. This tool (among a bunch of others) is installed as part of the radare2 suite. With the function added to our shell config, we’ll start a new Terminal session and call the function directly from the command line rather than from within r2.
The rfunc() function tells us which functions match, which do not, and which are new between any two given binaries. Here’s part of the output from two very different variants of Atomic Stealer:
To get a graphical output of how two functions differ, let’s begin by using radiff2 directly. This utility has many options and we’ll only explore a few here, but it is well worth digging into deeper.
You can compare two functions or offset addresses in two binaries with the following syntax:
Or, in case both binaries use the same function name, e.g., sym._main.sendlog in our example above, you can simply provide the function name instead of the addresses:
In this example, I’ll compare the main function of two samples of Genieo adware.
As shown in the image above, the files are quite different sizes.
However, the output shows us that the main functions are structured identically and differ only in terms of offset addresses and certain hard coded values. This kind of information is extremely helpful for creating effective signatures for a malware family.
As radiff2 outputs to the Terminal, display can sometimes be tricky. It’s possible to leverage Graphviz and the dot and xdot utilities to produce more readable graphs. Though a deep dive into Graphviz takes us beyond the scope of this post, try installing xdot from brew install xdot and playing around with options such as these:
As xdot is Python based, I’ve found it can sometimes be temperamental when it comes to escaping strings passed from radiff2 and occassionally spits out “unknown op code” errors. When this happens, one of a few ways you can sidestep xdot and Python is as follows:
These can produce graphical diffs such as the following:

Of course, once you hit on one or more graph workflows that work for you, you can then add them as functions to your shell config file for maximum convenience. Here’s an example:
This function allows you to specify either three args (a function name, and two filepaths) or four (two offsets, two filepaths) – beware there’s minimal error checking. Two other things of note: via the -A option, radiff2 passes the files to r2 for analysis. This can improve radiff2‘s diffing output. However, recall that our earlier customization has r2 print out our config file when it runs. We don’t want this output passed to xdot (or dot) or it will cause errors. In my case, my .radare2rc file is 27 lines long, so I use tail -n +28 to start printing from the 28th line. That number will need to be adjusted for the length of your own .radare2rc config file, and you’ll need to remember to adjust the function if you later edit the config file such that it changes length either way. Secondly, note the series of sed commands. These are a quick and dirty way to alter the default colors of the output, so adjust or remove to your liking.
In this post we’ve seen how we can power up radare2 by means of aliases, macros and functions. We’ve learned how these shortcuts and automations can allow us to make r2 easier and more productive to use.
That’s not all there is to powering up radare2, however, as we have yet to explore driving radare2 with scripts via r2pipe to do deeper analysis, decrypt strings and other advanced functions. We’ll cover that in a future post on radare2 and macOS malware, so be sure to follow us on social media to stay notified when that goes live. In the meantime, don’t forget to check out our earlier posts on radare2 if you didn’t already!
Get notified when we post new content.
Thanks! Keep an eye out for new content!
In the era of interconnectivity, when markets, geographies, and jurisdictions merge in the melting pot of the digital domain, the perils of the threat ecosystem become unparalleled. Crimeware families achieve an unparalleled level of technical sophistication, APT groups are competing in fully-fledged cyber warfare, while once decentralized and scattered threat actors are forming adamant alliances of operating as elite corporate espionage teams.
Get notified when we post new content.
Thanks! Keep an eye out for new content!