Skip to content

Pattern-Based Hooking

Pattern-based hooking is one of friTap's most powerful features, allowing you to analyze applications with stripped SSL libraries or statically linked implementations where traditional symbol-based hooking fails.

Overview

When SSL/TLS libraries are stripped of symbols or statically linked into applications, friTap cannot use traditional function name resolution. Pattern-based hooking solves this by matching byte patterns in memory to identify and hook the required functions. By default friTap has already some patterns included for well-known libaries but due to compilation and library updates it might be the case that these patterns needs to be updated.

When to Use Pattern-Based Hooking

Pattern-based hooking works by searching for unique byte sequences (patterns) that identify specific functions in memory. This technique is essential for:

  • Stripped libraries: No symbol information available
  • Statically linked SSL: BoringSSL embedded in Chrome, libflutter.so
  • Obfuscated binaries: Anti-analysis protections
  • Custom SSL implementations: Modified or proprietary libraries
  • Mobile applications: Flutter, React Native, Unity apps

Pattern File Format

Pattern files use JSON format to define byte patterns for different hooking categories:

{
  "version": "1.0",
  "architecture": "arm64",
  "platform": "android",
  "library": "libflutter.so",
  "patterns": {
    "Dump-Keys": {
      "primary": "1F 20 03 D5 ?? ?? ?? ?? F4 4F 01 A9",
      "fallback": "1F 20 03 D5 ?? ?? ?? ?? ?? ?? ?? ?? F4 4F 01 A9",
      "offset": 0,
      "description": "SSL_read function pattern"
    },
    "Install-Key-Log-Callback": {
      "primary": "FF 83 00 D1 ?? ?? ?? ?? F4 4F 02 A9",
      "fallback": "FF 83 00 D1 ?? ?? ?? ?? ?? ?? ?? ?? F4 4F 02 A9",
      "offset": 0,
      "description": "SSL_write function pattern"
    }
  }
}

Pattern Categories

friTap supports five main hooking categories:

  1. Dump-Keys: Extract encryption keys right now we support only this feature
  2. Install-Key-Log-Callback: Install key logging callbacks
  3. KeyLogCallback-Function: Key callback function hooks
  4. SSL_Read: Hook SSL read operations
  5. SSL_Write: Hook SSL write operations

Creating Pattern Files

Manual Pattern Creation

Step 1: Identify Target Functions

Use tools like Ghidra, IDA Pro, or Radare2 to analyze the binary code of target functions:

# Use radare2 to analyze library
r2 -A libflutter.so
[0x00000000]> afl | grep -i ssl
[0x00000000]> pdf @ sym.ssl_log_secret

Step 2: Extract Byte Patterns

# Extract bytes around function prologue
# example pattern 1
[0x00000000]> px 32 @ sym.ssl_log_secret
0x12345678  1f2003d5 12345678 f44f01a9 87654321  .....O......

In other tools, retrieving the function’s bytes often requires an even more manual process:

; Example ssl_log_secret function prologue
; example pattern 2
push rbp           ; 55
mov rbp, rsp       ; 48 89 E5
sub rsp, 0x20      ; 48 83 EC 20
mov [rbp-8], rdi   ; 48 89 7D F8

This translates to the pattern: 55 48 89 E5 48 83 EC 20 48 89 7D F8

Step 3: Create Pattern with Wildcards

Replace variable bytes with ? or ??:

; example pattern 1
1F 20 03 D5 ?? ?? ?? ?? F4 4F 01 A9
; example pattern 2
55 48 89 E? ?? 83 EC 20 ?8 89 ?? F8
;  
Wildcards (? or ??) are used for: - Register variations - Immediate value variations - Padding bytes - Compiler-specific differences

Automated Pattern Generation

Using BoringSecretHunter to Generate Patterns:

For applications with stripped libraries where we only interested in a pattern to dump the keys (category Dump-Keys), we recommend using our tool BoringSecretHunter to automatically generate these patterns. These generated byte patterns can then be supplied to friTap.

Use BoringSecretHunter with its Docker container:

# Create directories for BoringSecretHunter
mkdir -p binary results

# Copy target libraries to analyze
cp libcronet.113.0.5672.61.so binary/
cp libssl.so binary/

# Run BoringSecretHunter with Docker (recommended)
docker run --rm \
  -v "$(pwd)/binary":/usr/local/src/binaries \
  -v "$(pwd)/results":/host_output \
  boringsecrethunter

Analyzing libcronet.113.0.5672.61.so...
        BoringSecretHunter
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⠾⠛⢉⣉⣉⣉⡉⠛⠷⣦⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⠋⣠⣴⣿⣿⣿⣿⣿⡿⣿⣶⣌⠹⣷⡀⠀⠀⠀⠀⠀⠀⠀
 ⠀⠀⠀⠀⠀⠀⠀⠀⣼⠁⣴⣿⣿⣿⣿⣿⣿⣿⣿⣆⠉⠻⣧⠘⣷⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢰⡇⢰⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⢸⣿⠛⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠈⣷⠀⢿⡆⠈⠛⠻⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣧⡀⠻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢼⠿⣦⣄⠀⠀⠀⠀⠀⠀⠀⣀⣴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⣠⣾⣿⣦⠀⠀⠈⠉⠛⠓⠲⠶⠖⠚⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⣠⣾⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⣠⣾⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⣾⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⣄⠈⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

Identifying the ssl_log_secret() function for extracting key material using Frida.
Version: 1.0.2 by Daniel Baier

[*] Start analyzing binary libcronet.113.0.5672.61.so (CPU Architecture: AARCH64). This might take a while ...


[*] Target function identified (ssl_log_secret):

Function label: FUN_00493BB0
Function offset: 00493BB0 (0X493BB0)
Byte pattern for frida (friTap): 3F 23 03 D5 FF C3 01 D1 FD 7B 04 A9 F6 57 05 A9 F4 4F 06 A9 FD 03 01 91 08 34 40 F9 08 11 41 F9 C8 07 00 B4

# Use generated patterns with friTap
fritap --patterns results/libcronet.so_patterns.json -k keys.log com.example.flutter_app

Why Docker?

The Docker approach provides a pre-configured environment with Ghidra, eliminating setup complexity and ensuring consistent results across platforms.

Using Pattern Files

Basic Pattern Usage

# Use pattern file for analysis
fritap --patterns patterns.json -k keys.log target_app

# Combine with other options
fritap --patterns patterns.json --pcap traffic.pcap -k keys.log target_app

# Mobile application with patterns
fritap -m --patterns android_patterns.json -k keys.log com.example.app

Debug Pattern Matching

# Enable debug output to see pattern matching
fritap -do --patterns patterns.json -v target_app

# Expected output:
# [*] Pattern matching enabled
# [*] Loading patterns from patterns.json
# [*] Searching for SSL_Read pattern in libssl.so
# [*] Pattern match found at offset 0x12345678
# [*] Hooking SSL_read at 0x12345678

Platform-Specific Patterns

Android Patterns:

# ARM64 Android patterns
fritap -m --patterns android_arm64_patterns.json -k keys.log com.example.app

# x86_64 Android patterns (emulator)
fritap -m --patterns android_x64_patterns.json -k keys.log com.example.app

iOS Patterns:

# ARM64 iOS patterns
fritap -m --patterns ios_arm64_patterns.json -k keys.log com.example.app

Advanced Pattern Techniques

Multi-Architecture Support

Create patterns for multiple architectures:

{
  "version": "1.0",
  "patterns": {
    "arm64": {
      "SSL_Read": {
        "primary": "1F 20 03 D5 ?? ?? ?? ?? F4 4F 01 A9"
      }
    },
    "x86_64": {
      "SSL_Read": {
        "primary": "55 48 89 E5 ?? ?? ?? ?? 48 83 EC ??"
      }
    },
    "armv7": {
      "SSL_Read": {
        "primary": "?? ?? 2D E9 ?? ?? ?? ?? ?? ?? ?? ??"
      }
    }
  }
}

Real-World Examples

Flutter Applications

Flutter apps often have statically linked BoringSSL:

Flutter Pattern File (flutter_patterns.json):

{
  "version": "1.0",
  "architecture": "arm64",
  "platform": "android",
  "library": "libflutter.so",
  "patterns": {
    "Dump-Keys": {
      "primary": "FF 83 00 D1 FD 7B 01 A9 ?? ?? ?? ?? F4 4F 03 A9",
      "fallback": "FF 83 00 D1 ?? ?? ?? ?? ?? ?? ?? ?? F4 4F 03 A9"
    }
  }
}

Usage:

fritap -m --patterns flutter_patterns.json -k flutter_keys.log com.example.flutter_app

Cronet Applications

Chrome's Cronet library with embedded BoringSSL:

Cronet Pattern File (cronet_patterns.json):

{
  "version": "1.0",
  "architecture": "arm64",
  "platform": "android",
  "library": "libcronet.so",
  "patterns": {
    "SSL_Read": {
      "primary": "FF 83 00 D1 FD 7B 01 A9 F4 4F 02 A9 F6 57 03 A9",
      "fallback": "FF 83 00 D1 ?? ?? ?? ?? F4 4F 02 A9",
      "offset": 0,
      "description": "Cronet BoringSSL SSL_read"
    },
    "SSL_Write": {
      "primary": "FF 83 00 D1 FD 7B 01 A9 F4 4F 02 A9 F6 57 03 A9",
      "fallback": "FF 83 00 D1 ?? ?? ?? ?? F4 4F 02 A9",
      "offset": 0,
      "description": "Cronet BoringSSL SSL_write"
    }
  }
}

Usage:

fritap -m --patterns cronet_patterns.json -k cronet_keys.log com.google.android.gms

Pattern Development Workflow

Step-by-Step Pattern Creation

1. Analyze Target Application:

# Extract APK and analyze libraries
apktool d target_app.apk
cd target_app/lib/arm64-v8a/
file *.so | grep -v stripped

2. Use Static Analysis:

# Analyze with radare2
r2 -A libssl.so
[0x00000000]> afl | grep -i ssl_read
[0x00000000]> pdf @ sym.SSL_read

3. Extract and Test Patterns:

# Test pattern matching
fritap --patterns test_patterns.json -do -v target_app

# Check debug output for pattern matches
grep -i "pattern" debug_output.log

4. Refine Patterns:

# Adjust patterns based on results
# Test with different app versions
# Add fallback patterns

Pattern Validation

Test Pattern Reliability:

#!/bin/bash
# Test pattern across multiple app versions

PATTERN_FILE="$1"
APP_PACKAGE="$2"

for version in v1.0 v1.1 v1.2; do
    echo "Testing $APP_PACKAGE $version"
    fritap -m --patterns "$PATTERN_FILE" -k "test_${version}.log" "$APP_PACKAGE"

    if [ -s "test_${version}.log" ]; then
        echo "✓ $version: Pattern worked"
    else
        echo "✗ $version: Pattern failed"
    fi
done

Troubleshooting Patterns

Common Issues

Pattern Not Found:

# Enable debug output
fritap -do --patterns patterns.json -v target_app

# Check library loading
fritap --list-libraries target_app

# Verify pattern syntax
python -m json.tool patterns.json

False Positives:

# Make patterns more specific
# Add additional context bytes
# Use multiple validation patterns

Performance Issues:

# Optimize pattern length
# Use specific library targeting
# Implement pattern caching

Debug Techniques

Pattern Matching Debug:

# Enable maximum verbosity
fritap -do -v --patterns patterns.json target_app 2>&1 | tee pattern_debug.log

# Analyze pattern matching process
grep -E "(Pattern|Match|Hook)" pattern_debug.log

Memory Analysis:

# Dump memory regions for analysis
fritap -c memory_dump.js --patterns patterns.json target_app

# Where memory_dump.js contains:
# Memory.scan(ptr("0x7000000000"), 0x10000000, "1F 20 03 D5", {
#     onMatch: function(address, size) {
#         console.log("Match at: " + address);
#     }
# });

Best Practices

1. Pattern Design

  • Use sufficient context: Include enough bytes to avoid false positives
  • Implement fallbacks: Provide alternative patterns for robustness
  • Document patterns: Include descriptions and version information
  • Test thoroughly: Validate across different versions and devices

2. Maintenance

  • Version control: Track pattern changes over time
  • Automated testing: Validate patterns against known samples
  • Community sharing: Contribute patterns to friTap community
  • Regular updates: Update patterns for new library versions

3. Performance

  • Optimize pattern length: Balance specificity with performance
  • Target specific libraries: Avoid scanning unnecessary memory regions
  • Use caching: Cache successful pattern matches
  • Parallel scanning: Use multiple patterns simultaneously

How Pattern-Based Hooking Works

1. Pattern Generation

Patterns are generated by analyzing the binary code of target functions:

; Example SSL_read function prologue
push rbp           ; 55
mov rbp, rsp       ; 48 89 E5
sub rsp, 0x20      ; 48 83 EC 20
mov [rbp-8], rdi   ; 48 89 7D F8

This translates to the pattern: 55 48 89 E5 48 83 EC 20 48 89 7D F8

2. Pattern Matching

friTap searches for these patterns in the target process memory:

function find_pattern_in_module(module_name: string, pattern: string): NativePointer[] {
    const module = Process.getModuleByName(module_name);
    const pattern_bytes = pattern_to_bytes(pattern);

    return Memory.scan(module.base, module.size, pattern_bytes, {
        onMatch: function(address, size) {
            return address;
        },
        onError: function(reason) {
            devlog_error(`Pattern scan failed: ${reason}`);
        }
    });
}

3. Hook Installation

Once patterns are found, hooks are installed at the matching addresses:

function hook_by_pattern(
    module_name: string,
    pattern: string,
    function_name: string,
    hook_callback: Function
): boolean {
    const addresses = find_pattern_in_module(module_name, pattern);

    if (addresses.length === 0) {
        devlog_error(`Pattern not found: ${pattern}`);
        return false;
    }

    if (addresses.length > 1) {
        devlog_error(`Multiple matches for pattern: ${pattern}`);
        return false;
    }

    Interceptor.attach(addresses[0], hook_callback);
    return true;
}

Summary

Pattern-based hooking is a powerful technique that extends friTap's capabilities to handle stripped binaries and complex scenarios. By understanding the principles, implementing proper validation, and following best practices, you can create robust pattern-based hooks that work reliably across different environments and library versions.

The key to successful pattern-based hooking is careful pattern selection, thorough testing, and robust error handling. Combined with friTap's other hooking methods, it provides comprehensive coverage for SSL/TLS traffic analysis in any scenario.

Next Steps

  • Learn about custom Frida scripts using -c parameter for advanced hooking
  • Explore anti-detection techniques in specialized security analysis scenarios
  • Check platform-specific guides for pattern examples