Table of Contents

Code Obfuscation Techniques on macOS: Beyond Packers

Water in Hawaii. Used on a post about code obfuscation.

My second article in my holiday series follows my article on packers. Today, I’ll be covering obfuscation techniques beyond packing, and why I think they are a good idea for red teamers! I’ll start by covering the… 

General vibe of obfuscation on Mac!

Dummy or junk code

Dummy code is non-functional instructions inserted into a program solely to obfuscate its purpose, evade detection, or hinder reverse engineering.

For example, OSX.Zuru “contained over 40,000 functions of almost entirely junk code” as a Cobalt Strike payload (Phil Stokes). I have seen junk code used time and time again in random articles I’ve read too. You can just add lots of dummy code; there are plenty of options for this, which you can find here: awesome packing

Types of osascripts like Apple Script

Another technique exploits AppleScript. 

Scripts can be compiled to a “run-only” format. Then, the attacker can embed one run-only script inside another using hex encoding. These double-embedded, hex-encoded AppleScripts are extremely hard to analyze statically. Similarly, some malware directly injects code into legitimate processes or uses hidden sections. 


🌸👋🏻 Join 10,000+ followers! Let’s take this to your inbox. You’ll receive occasional emails about whatever’s on my mind—offensive security, open source, boats, reversing, software freedom, you get the idea.


In contrast, legit software rarely uses such heavy obfuscation. Mac devs rely on compiled languages (Objective-C, Swift, etc.), and OS protections rather than packing; they may strip debug symbols or encrypt specific strings, but rarely pack or massively pad executables. Thus, an unusually high entropy section, obscure section names, or thousands of no-op functions are indicators of malware. 

See: Tearing Apart the Undetected (OSX) Coldroot RAT and Parsing Binaries by Patrick Wardle. 

Obfuscation techniques seen in macOS malware include:

  • Executable packing (UPX/MPRESS): compressing the Mach-O so that it self-unpacks at runtime. I already covered this in my other article. 
  • Large static languages (Go, Nim, Rust): embedding huge runtime libraries and obfuscated sections (string tables without nulls, etc.). I’ll cover this in-depth in my “evading EDRs with lesser-known languages” article.
  • Run-only AppleScript: AppleScript files compiled with run-only produce an encrypted bytecode; malware embeds hex strings and the magic FADE DEAD marker.
  • Polyglot packaging: Using tools like Platypus to wrap scripts in app bundles, often with fake icons. Note: A polyglot is a file crafted to be valid and functional in multiple formats or interpreters, often used to evade detection or confuse analysis tools.
  • Anti-debug/anti-VM: macOS malware sometimes checks for debuggers or virtual environments via syscalls or hidden libraries.

Learn more: NimDoor MacOS Malware by The Hivemind.

I’ll cover some of these techniques in depth next, but I wanted to give a brief overview to make the technical explanations easier. 

Always, in practice, an unknown binary that is packed or contains many dummy functions is likely malicious on macOS. For example, Patrick Wardle notes that Coldroot’s malicious app was flagged for referencing system privacy databases and for being UPX-packed. 

“It’s rare to see a legitimate binary packed on macOS” – Patrick Wardle

Conversely, if an app is signed by a known developer and not packed, it’s more likely benign or stealthy. 

Technical explanation of obfuscation methods 

On macOS, I would try to use code obfuscation techniques beyond packers, including: 

String encoding & encryption

Software and malware often encode sensitive strings (URLs, credentials, license keys) to avoid easy reading. 

Simple cases use Base64 or XOR encoding; for example, seeing strings like VGhpcyBpcyBhbiBCQVNFNjQgZW5jb2RlZCBzdHJpbmcu== (Base64) at runtime is an obfuscation sign. Malware frequently encrypts command-and-control domains or malicious commands to hide them from static scans. Legit apps might encode license checks or API keys. 

The difference is usually what is hidden. 

Malware hides indicators of compromise (IoCs), like IP addresses or sus keywords, whereas legit apps may hide proprietary logic or keys.

Function and control-flow obfuscation

Obfuscators can rename functions, inline or split code, and add opaque predicates (junk logic) to confuse reverse engineers. 

On macOS, a notable example is the open-source Obfuscator-LLVM, which integrates into compilation to scramble control flow and variable names (used to protect commercial software across all platforms).

Again, this is still rare on Mac.

Legit companies (especially games) may use such tools to prevent piracy or cheating. Malware authors similarly obfuscate their compiled code to make analysis a nightmare for reversers. 

An OceanLotus APT malware even placed its code in an unconventional Mach-O section (__cfstring section marked as strings) to trick disassemblers into treating code as data. A regular Mac app wouldn’t need to do that. It’s a red flag for something trying to fool analysis tools.

Dynamic API resolution

Read: Ghosts in the Endpoint: How Attackers Evade Modern EDR Solutions by Mat Fuchs.

Rather than calling macOS API functions directly (which can leave incriminating import strings), malware may resolve functions at runtime using dlsym or Objective-C runtime APIs. This is a form of dynamic API hiding. 

Windows malware does this heavily. On macOS, we see parallels (e.g., malware retrieving function pointers for mach_vm_write or system() at runtime to avoid static detection of those symbols). 

Legit software occasionally uses dynamic loading for optional features or plugin systems, but malware uses it to hide malicious API usage (such as injecting code or launching shells). Similarly, position-independent shellcode or JIT compiled payloads are used to avoid fixed API call patterns. 

Runtime packing and encryption

Some macOS malware will decrypt or decompress parts of itself only in memory at runtime… a behavior distinct from whole-file packers! For example, a Trojan might store an encrypted second-stage payload in its binary, then decrypt it in memory when ready to execute. 

This is what MetaStealer did. It hid its main code until after installation, trying to evade Apple’s scans. Legit software might encrypt some resources for anti-tamper, but generally won’t decrypt code at runtime unless it’s implementing a licensed plugin or similar. 

When you see code that “only decrypts at the last moment in memory” and stays encrypted during idle periods, it’s likely malicious (Cobalt Strike beacons do this to thwart memory scans)(Mat Fuchs).

Anti-debugging and VM checks

Malware can includes anti-analysis. 

On macOS, calling ptrace(PT_DENY_ATTACH) to prevent debuggers, checking for processes like lldb or probing for virtualization (VMware, Parallels) are common in malicious binaries. 

OceanLotus did all of these, spawning a watchdog thread that exits if a debugger is detected and querying hardware info to see if it’s running in a VM. Legit apps rarely include such checks… except perhaps high-end DRM or anti-cheat systems. If you see an app on Mac performing these anti-debug/anti-VM calls, you can be fairly suspicious that it’s malware.

Legit obfuscation? 

In general, legit macOS software might use mild obfuscation to protect proprietary code, but it typically avoids extreme measures that hinder the system. 

Apple’s guidelines discourage anti-debugging in App Store apps, for example. (Some) Malware, on the other hand, pulls out all the stops: if you see heavy string encryption, code hiding in strange segments, self-modifying code, and anti-debug traps combined, the sample is almost certainly malicious. 


🌸👋🏻 Join 10,000+ followers! Let’s take this to your inbox. You’ll receive occasional emails about whatever’s on my mind—offensive security, open source, boats, reversing, software freedom, you get the idea.


One way to tell is by context.

Is the binary signed by a known developer and expected to be on the system? If it’s unsigned or signed with an untrusted certificate and obfuscationed, it leans malicious. 

Also, legitimate obfuscation usually doesn’t impede the app’s basic transparency (e.g., a normal app will still have human-readable Info.plist, identifiable UI resources, etc., whereas malware might strip all metadata). 

I would use tools to measure binary entropy. A packed or heavily encrypted file shows high entropy (randomness) in large sections. A legit app rarely has uniformly high entropy across its code sections except when using known compression for size. 

High entropy plus unknown signature == likely malicious.

Conclusion

I hope you enjoyed this post on code obfuscation on macOS. 

If you’d like to learn more about why packing with a third-party packer is sub-optimal on Mac (at least in my opinion of malware devs in December 2025), consider reading Why Packers are Rare and Sus on macOS. My next article will be on evading EDRs using lesser-known languages! 

Portrait of Olivia Gallucci in garden, used in LNP article.

Written by Olivia Gallucci

Olivia is senior security engineer, certified personal trainer, and freedom software advocate. She writes about offensive security, open source software, and professional development.

Discover more from [ret]2read

An OS Internals Newsletter by Olivia Gallucci. Subscribe now to keep reading and get access to the full archive.

Continue reading