Skip to main content
Successful reverse engineering of obfuscated code begins with accurately identifying which obfuscation techniques have been applied. This guide covers systematic detection strategies.

Initial Triage

Before diving deep into analysis, perform a quick assessment to understand what you’re dealing with.
1

Basic Binary Analysis

# File type and architecture
file binary_name

# Check if stripped
nm binary_name | wc -l

# Identify encryption
otool -l binary_name | grep -A 5 LC_ENCRYPTION_INFO

# List dependencies
otool -L binary_name
2

String Analysis

# Extract strings
strings binary_name > strings.txt

# Count meaningful strings
wc -l strings.txt

# Look for patterns
grep -E '^[A-Za-z0-9+/]{20,}={0,2}$' strings.txt  # Base64
grep -E '^[0-9a-fA-F]{32,}$' strings.txt          # Hex
Few readable strings suggest string encryption or minimization.
3

Symbol Table Inspection

# List symbols
nm -a binary_name | head -50

# Count Objective-C classes
otool -oV binary_name | grep "class_name" | wc -l

# Check for symbol obfuscation
nm binary_name | grep -E '^[a-z]$'  # Single char names
4

Code Size Analysis

# Segment sizes
otool -l binary_name | grep -A 3 "segname __TEXT"

# Section details
size binary_name
Unusually large code sections may indicate dead code injection.

Identifying Obfuscation Types

Control Flow Flattening Indicators

Disassembly Patterns:
; Dispatcher loop pattern
.loop_start:
    ldr     w8, [sp, #state_var]
    cmp     w8, #0
    b.eq    .case_0
    cmp     w8, #1
    b.eq    .case_1
    cmp     w8, #2
    b.eq    .case_2
    ; ... many cases

.case_0:
    ; block code
    mov     w8, #next_state
    str     w8, [sp, #state_var]
    b       .loop_start
Key Indicators:
  • Central dispatch loop with switch/jump table
  • State variable controlling flow
  • All basic blocks at same nesting level
  • Unconditional jumps back to dispatcher
Use tools to visualize control flow graphs:
# Generate CFG with radare2
r2 -A binary_name
[0x100000000]> s main
[0x100001234]> ag  # ASCII graph
[0x100001234]> agf > cfg.dot  # Graphviz format

# Visualize
dot -Tpng cfg.dot -o cfg.png
Look for:
  • Star topology (all blocks connect to center)
  • High fan-in to dispatcher block
  • Flat structure (no natural hierarchy)
  • Many back-edges to single block
Calculate complexity metrics:
import r2pipe

r2 = r2pipe.open("binary_name")
r2.cmd("aaa")  # Analyze all

# Get function info
funcs = r2.cmdj("aflj")  # JSON format

for func in funcs:
    name = func.get("name")
    cyclomatic = func.get("cc", 0)  # Cyclomatic complexity
    nbbs = func.get("nbbs", 0)      # Number of basic blocks
    
    # High complexity with many blocks suggests flattening
    if cyclomatic > 50 and nbbs > 20:
        print(f"[!] Suspicious: {name}")
        print(f"    Complexity: {cyclomatic}")
        print(f"    Basic blocks: {nbbs}")

Automated Detection Tools

DIE (Detect It Easy)

Cross-platform tool for detecting packers, obfuscators, and compilers.
# Install
brew install detect-it-easy

# Analyze binary
diec binary_name

r2pipe Scripts

Custom detection scripts using radare2 Python bindings.
import r2pipe
r2 = r2pipe.open("binary")
r2.cmd("aaa")
info = r2.cmdj("ij")

MobSF

Mobile Security Framework with obfuscation detection.
# Run analysis
python3 manage.py runserver
# Upload IPA via web interface

Ghidra Scripts

Custom Ghidra analyzers for pattern detection.
// FlatteningDetector.java
// Custom Ghidra script

Pattern Recognition

Building a Detection Matrix

Create a systematic checklist to identify multiple obfuscation techniques simultaneously.
class ObfuscationDetector:
    def __init__(self, binary_path):
        self.binary = binary_path
        self.results = {}
    
    def detect_all(self):
        print("[*] Analyzing {}\n".format(self.binary))
        
        self.results['control_flow'] = self.check_control_flow()
        self.results['strings'] = self.check_string_encryption()
        self.results['symbols'] = self.check_symbol_obfuscation()
        self.results['anti_debug'] = self.check_anti_debug()
        self.results['anti_jailbreak'] = self.check_anti_jailbreak()
        
        self.print_report()
    
    def check_control_flow(self):
        # Analyze CFG complexity
        return {
            'detected': True,
            'confidence': 'high',
            'evidence': ['High cyclomatic complexity', 'Dispatcher pattern']
        }
    
    def check_string_encryption(self):
        # Entropy analysis
        return {
            'detected': True,
            'confidence': 'medium',
            'evidence': ['High entropy sections', 'Few readable strings']
        }
    
    def check_symbol_obfuscation(self):
        # Symbol analysis
        return {
            'detected': True,
            'confidence': 'high',
            'evidence': ['40% single-char symbols']
        }
    
    def check_anti_debug(self):
        # API detection
        return {
            'detected': True,
            'confidence': 'high',
            'evidence': ['ptrace calls', 'sysctl checks']
        }
    
    def check_anti_jailbreak(self):
        # File/path checks
        return {
            'detected': True,
            'confidence': 'high',
            'evidence': ['Cydia path checks', 'fork attempts']
        }
    
    def print_report(self):
        print("=" * 60)
        print("OBFUSCATION DETECTION REPORT")
        print("=" * 60)
        
        for technique, result in self.results.items():
            status = "[DETECTED]" if result['detected'] else "[NOT FOUND]"
            print(f"\n{status} {technique.replace('_', ' ').title()}")
            print(f"  Confidence: {result['confidence']}")
            print(f"  Evidence:")
            for evidence in result['evidence']:
                print(f"    - {evidence}")

# Usage
detector = ObfuscationDetector('binary_name')
detector.detect_all()

Workflow Diagram

Best Practices

Begin with automated tools and high-level indicators before diving into detailed analysis. This saves time and helps prioritize efforts.
Static analysis alone can miss runtime obfuscation. Dynamic analysis alone can miss dormant protections. Use both approaches.
Keep detailed notes on detected techniques, evidence, and confidence levels. This helps when returning to the analysis later.
Maintain a collection of known obfuscation patterns, tool outputs, and bypass techniques for future reference.
Initial detection may be incomplete. As you analyze deeper, you may discover additional obfuscation layers. Update your assessment.

Common Pitfalls

False Positives: Compiler optimizations can resemble obfuscation. Verify with multiple indicators before concluding obfuscation is present.
Layered Obfuscation: Detecting one technique doesn’t mean there aren’t others. Always check for multiple simultaneous techniques.
Evolving Techniques: Obfuscators constantly evolve. Patterns that worked yesterday may not apply to new versions. Stay current.

Further Reading

Obfuscation Overview

Return to the overview for context on why obfuscation is used.

Control Flow Flattening

Deep dive into analyzing detected control flow obfuscation.

String Encryption

Learn to extract and decrypt obfuscated strings.

Anti-Tampering

Bypass detected runtime protections.

Build docs developers (and LLMs) love