콘텐츠로 이동

Mandiant FLOSS Cheat Sheet

Overview

FLOSS (FLARE Obfuscated String Solver) is an open-source tool developed by Mandiant’s FLARE team that automatically extracts obfuscated strings from malware samples. Malware authors routinely encode, encrypt, or dynamically construct strings to evade static analysis and signature-based detection. FLOSS goes beyond simple strings extraction by using static analysis heuristics and CPU emulation to identify and decode strings that are deobfuscated at runtime. It can recover strings hidden by XOR encoding, stack strings (constructed character-by-character on the stack), and tight strings (built through compact loops).

FLOSS combines three extraction methods: static string extraction (like the strings utility), stack string extraction (identifying sequences of character-by-character assignments to stack variables), and decoded string extraction (emulating functions identified as likely string decoders). The tool supports PE, ELF, and .NET executables and outputs results in plain text, JSON, or as IDA Pro/Ghidra scripts for annotation. FLOSS is an essential triage tool in malware analysis workflows — it can reveal C2 server addresses, registry keys, filenames, API function names, and other indicators that would otherwise require manual reverse engineering to uncover.

Installation

Via pip

# Install FLOSS
pip install flare-floss

# Verify installation
floss --version

Pre-built Binary

# Linux
wget https://github.com/mandiant/flare-floss/releases/latest/download/floss-v3.1.0-linux.zip
unzip floss-v3.1.0-linux.zip
chmod +x floss
sudo mv floss /usr/local/bin/

# Windows
# Download floss-v3.1.0-windows.zip from GitHub releases

# macOS
wget https://github.com/mandiant/flare-floss/releases/latest/download/floss-v3.1.0-macos.zip
unzip floss-v3.1.0-macos.zip
chmod +x floss
sudo mv floss /usr/local/bin/

From Source

git clone https://github.com/mandiant/flare-floss.git
cd flare-floss
pip install -e ".[dev]"

Core Commands

CommandDescription
floss <file>Run all extraction methods on a file
floss --only static <file>Extract only static strings
floss --only stack <file>Extract only stack strings
floss --only decoded <file>Extract only decoded strings
floss --only tight <file>Extract only tight strings
floss -j <file>Output as JSON
floss -n <min_length> <file>Set minimum string length
floss --functions <addr> <file>Analyze specific function(s)
# Full analysis (all extraction methods)
floss malware.exe

# JSON output for automation
floss -j malware.exe > strings.json

# Only decoded (deobfuscated) strings
floss --only decoded malware.exe

# Only stack strings
floss --only stack malware.exe

# Set minimum string length
floss -n 6 malware.exe

# Analyze specific functions
floss --functions 0x401000 0x402500 malware.exe

# Verbose output showing function details
floss -v malware.exe

# Very verbose with debug information
floss -vv malware.exe

# Analyze .NET assembly
floss dotnet_malware.exe

# Analyze ELF binary
floss linux_malware.elf

# Analyze shellcode (specify base address)
floss --format sc64 shellcode.bin

Understanding Output

String Types

TypeDescriptionExample
StaticPlain ASCII/Unicode strings in the binary"kernel32.dll"
StackCharacters pushed one at a time to the stack"cmd.exe /c" (from mov [esp], 'c'; mov [esp+1], 'm'...)
TightStrings decoded via compact loops"http://evil.com" (from XOR loop)
DecodedStrings decoded by identified decoder functions"HKLM\Software\Microsoft" (from decryption routine)
# Example output structure
# FLOSS static strings
# ────────────────────
# kernel32.dll
# VirtualAlloc
# GetProcAddress
#
# FLOSS decoded strings
# ─────────────────────
# http://c2server.example.com/beacon
# HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
# cmd.exe /c del %s
#
# FLOSS stack strings
# ───────────────────
# powershell.exe
# -ExecutionPolicy Bypass

JSON Output Structure

# Parse JSON output with jq
# Get all decoded strings
floss -j malware.exe | jq '.strings.decoded_strings[].string'

# Get strings with addresses
floss -j malware.exe | jq '.strings.decoded_strings[] | {string, decoded_at, decoding_routine}'

# Get stack strings
floss -j malware.exe | jq '.strings.stack_strings[].string'

# Count strings by type
floss -j malware.exe | jq '{
  static: (.strings.static_strings | length),
  decoded: (.strings.decoded_strings | length),
  stack: (.strings.stack_strings | length),
  tight: (.strings.tight_strings | length)
}'

Configuration

Analysis Options

# Limit analysis time per function (seconds)
floss --max-function-time 30 malware.exe

# Limit total analysis time
floss --max-total-time 300 malware.exe

# Set number of address space limits for emulation
floss --max-address-revisits 800 malware.exe

# Disable specific analysis methods
floss --only static,decoded malware.exe  # Skip stack and tight

Output Filtering

# Filter by minimum string length
floss -n 8 malware.exe

# Extract only strings matching a pattern
floss malware.exe | grep -E "(http|ftp|https|\.exe|\.dll|\.bat)"

# Extract URLs
floss -j malware.exe | jq -r '.. | .string? // empty' | grep -oP 'https?://[^\s"]+' | sort -u

# Extract IP addresses
floss -j malware.exe | jq -r '.. | .string? // empty' | grep -oP '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' | sort -u

# Extract registry keys
floss -j malware.exe | jq -r '.. | .string? // empty' | grep -iP '^(HKLM|HKCU|HKCR)' | sort -u

Advanced Usage

Batch Processing

# Process all files in a directory
for f in /malware/samples/*; do
  echo "=== $(basename "$f") ===" >> all_strings.txt
  floss --only decoded,stack "$f" >> all_strings.txt 2>/dev/null
  echo "" >> all_strings.txt
done

# JSON batch processing
find /malware/samples -type f | while read f; do
  floss -j "$f" 2>/dev/null | jq -c "{
    file: \"$(basename "$f")\",
    decoded: [.strings.decoded_strings[].string],
    stack: [.strings.stack_strings[].string]
  }"
done > batch_strings.jsonl

# Extract IOCs from batch results
cat batch_strings.jsonl | jq -r '.decoded[], .stack[]' | \
  grep -oP '(https?://[^\s"]+|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' | \
  sort -u > iocs.txt

IDA Pro Integration

# Generate IDA Python script with string annotations
floss --ida malware.exe > annotate_strings.py

# In IDA Pro: File > Script file > annotate_strings.py
# This adds comments at the locations where decoded strings are used

Ghidra Integration

# Generate Ghidra script
floss --ghidra malware.exe > annotate_strings_ghidra.py

# In Ghidra: Script Manager > Run Script > annotate_strings_ghidra.py

Comparison with Other Tools

# Traditional strings vs FLOSS
strings -n 6 malware.exe > static_strings.txt
floss --only decoded,stack malware.exe > floss_strings.txt

# Find strings only FLOSS found
comm -13 <(sort static_strings.txt) <(sort floss_strings.txt)

# Compare with YARA string extraction
floss -j malware.exe | jq -r '.strings.decoded_strings[].string' | \
  while read s; do
    echo "    \$s_$(echo "$s" | md5sum | cut -c1-8) = \"$s\""
  done

Integration with Malware Analysis Pipeline

import subprocess
import json

def extract_floss_strings(filepath):
    """Extract obfuscated strings using FLOSS."""
    result = subprocess.run(
        ["floss", "-j", filepath],
        capture_output=True, text=True, timeout=300
    )
    if result.returncode == 0:
        data = json.loads(result.stdout)
        return {
            "decoded": [s["string"] for s in data["strings"]["decoded_strings"]],
            "stack": [s["string"] for s in data["strings"]["stack_strings"]],
            "tight": [s["string"] for s in data["strings"]["tight_strings"]],
        }
    return None

# Usage
strings = extract_floss_strings("/malware/sample.exe")
if strings:
    print(f"Decoded strings: {len(strings['decoded'])}")
    for s in strings["decoded"]:
        print(f"  {s}")

Troubleshooting

IssueSolution
No decoded strings foundSample may use custom encoding; try manual analysis in IDA/Ghidra
Analysis timeoutIncrease --max-function-time and --max-total-time values
Memory error on large filesLimit scope with --functions to specific addresses
vivisect import errorReinstall: pip install flare-floss --force-reinstall
Packed samples yield few resultsUnpack the sample first (UPX, custom packer), then run FLOSS
False positive stringsFilter by minimum length (-n 8) and validate against known IOCs
.NET analysis failsEnsure .NET runtime libraries are accessible; try --format dotnet
Slow emulationReduce --max-address-revisits or target specific functions