XingLo SkillSearch

恶意程序加密字符串解密

用于恢复恶意程序中经过XOR、RC4、Base64、栈字符串或自定义算法处理的隐藏字符串,并结合FLOSS、静态代码和脚本识别解密逻辑。适合提取被隐藏的C2地址、命令、文件路径、注册表项、密钥和配置参数,是从混淆样本中快速获得有效IOC和进一步逆向入口的重要步骤。

在 AI 中使用此 Skill将本页链接复制给 AI,即可让 AI 获取完整 Skill 内容并按此执行
安全提示: 本站 Skill 均经 ChatGPT 最新模型扫描,未发现恶意脚本及危险指令、未检出已知恶意行为特征,但不保证绝对安全,使用即表示接受此风险

Skill 文件

版本 20260301 · 9e009d7c6a95b1a454070bf842ef797b

scripts/
SKILL.md
---
name: string-decryption
description: >
  Extract and decrypt obfuscated strings from malware samples including XOR-encoded,
  RC4-encrypted, AES-CBC, base64-encoded, and stack-constructed strings. Use when
  static analysis reveals encrypted or obfuscated strings hiding C2 addresses, API names,
  or configuration data. Covers automated extraction with FLOSS, IDAPython/Ghidra scripting,
  .NET string decryption, dynamic extraction via debugger breakpoints, and building custom
  decryptors from reverse-engineered algorithms. Supports both Linux and Windows analysis
  environments.
---

# String Decryption

Identify, extract, and decrypt obfuscated strings embedded in malware samples to reveal
hidden indicators of compromise, C2 infrastructure, API calls, and configuration data.

## Prerequisites

- **Linux**: `python3`, `strings`, `file`, `xxd`, `radare2` (optional)
- **Windows**: Python 3, x64dbg or WinDbg, IDA Pro or Ghidra
- **Python packages**: `pefile`, `yara-python`, `capstone`, `unicorn`, `dnfile` (for .NET)
- **Tools**: [FLOSS](https://github.com/mandiant/flare-floss) (FireEye Labs Obfuscated String Solver)
- **Optional**: IDA Pro with IDAPython, Ghidra with Ghidrathon, dnSpy / ilspy for .NET

Install Python dependencies:
```bash
pip install pefile yara-python capstone unicorn dnfile cryptography
```

Install FLOSS:
```bash
# Download latest release from GitHub
wget https://github.com/mandiant/flare-floss/releases/latest/download/floss-linux.zip
unzip floss-linux.zip -d /opt/floss
chmod +x /opt/floss/floss
export PATH=$PATH:/opt/floss
```

## Step-by-Step Instructions

### Step 1: Identify Encrypted and Obfuscated Strings

Determine whether the sample contains encrypted or obfuscated strings and characterize the obfuscation method.

**Entropy analysis to find high-entropy regions (likely encrypted data):**

```bash
python3 scripts/string_decryptor.py --mode detect --binary sample.exe --output detection_report.json
```

This script performs:
- Section-by-section entropy analysis (entropy > 7.0 in data sections suggests encryption)
- Scanning for XOR patterns (loops with XOR instructions operating on memory)
- Detection of base64-encoded blobs (regex matching for base64 character sets)
- Identification of stack string construction patterns
- Locating high-entropy byte sequences within code sections

**Manual entropy scanning per section:**

```bash
python3 -c "
import pefile, math
pe = pefile.PE('sample.exe')
for section in pe.sections:
    data = section.get_data()
    if not data:
        continue
    freq = [0]*256
    for b in data:
        freq[b] += 1
    ent = -sum((f/len(data))*math.log2(f/len(data)) for f in freq if f > 0)
    print(f'{section.Name.decode().strip(chr(0)):10s} entropy={ent:.4f} size={len(data):>8d} '
          f'{"[HIGH - likely encrypted]" if ent > 7.0 else ""}'
          f'{"[MODERATE - possible encoding]" if 6.0 < ent <= 7.0 else ""}')
"
```

**Check for common obfuscation indicators:**

```bash
# Very few readable strings compared to binary size suggests obfuscation
strings -n 6 sample.exe | wc -l
file sample.exe
ls -la sample.exe

# Search for base64-encoded blobs
strings -n 20 sample.exe | grep -E '^[A-Za-z0-9+/]{20,}={0,2}$'

# Search for hex-encoded strings
strings -n 20 sample.exe | grep -E '^[0-9a-fA-F]{20,}$'

# Look for XOR key candidates in the binary
strings sample.exe | grep -iE "(decrypt|encode|decode|xor|cipher|crypt|key|rc4|aes)"
```

### Step 2: Automated Extraction with FLOSS

FLOSS automatically extracts obfuscated strings using static analysis, stack string recovery, and emulation-based decoding.

**Run FLOSS on the sample:**

```bash
# Full analysis (static + stack strings + decoded strings)
floss sample.exe -o floss_output.json --json

# Extract only decoded (emulated) strings
floss sample.exe --only decoded

# Extract only stack strings
floss sample.exe --only stack

# Increase analysis timeout for complex samples
floss sample.exe --timeout 600

# Verbose output for troubleshooting
floss sample.exe -v
```

**Parse FLOSS output for actionable strings:**

```bash
# Extract URLs and domains from FLOSS output
cat floss_output.json | python3 -c "
import json, sys, re
data = json.load(sys.stdin)
for category in ['decoded_strings', 'stack_strings', 'static_strings']:
    strings = data.get(category, [])
    for s in strings:
        text = s if isinstance(s, str) else s.get('string', '')
        if re.search(r'https?://|[a-zA-Z0-9.-]+\.(com|net|org|ru|cn|tk|xyz|top|cc)', text):
            print(f'[{category}] {text}')
"
```

### Step 3: Decrypt Common Encryption Methods

Apply targeted decryption based on the identified encryption scheme.

**Single-byte XOR decryption:**

```bash
# If the XOR key is known (e.g., 0x5A found through analysis)
python3 scripts/string_decryptor.py --mode decrypt --method xor-single --key 0x5A \
    --input encrypted_blob.bin --output decrypted.bin --extract-strings

# Brute-force single-byte XOR (tries all 256 keys, ranks by printability)
python3 scripts/string_decryptor.py --mode decrypt --method xor-brute \
    --input encrypted_blob.bin --output brute_results.json
```

**Multi-byte / rolling XOR decryption:**

```bash
# Multi-byte repeating key XOR
python3 scripts/string_decryptor.py --mode decrypt --method xor-multi \
    --key "secretkey" --input encrypted.bin

# Rolling XOR (key evolves with each byte)
python3 scripts/string_decryptor.py --mode decrypt --method xor-rolling \
    --key 0x41 --input encrypted.bin
```

**RC4 decryption (common in RATs like Gh0st, PlugX, Cobalt Strike):**

```bash
python3 scripts/string_decryptor.py --mode decrypt --method rc4 \
    --key "malwarekey123" --input encrypted.bin --extract-strings
```

**AES-CBC with hardcoded keys (common in modern malware):**

```bash
python3 scripts/string_decryptor.py --mode decrypt --method aes-cbc \
    --key 0102030405060708090a0b0c0d0e0f10 \
    --iv 00000000000000000000000000000000 \
    --input encrypted.bin --output decrypted.bin
```

**Stack string reconstruction:**

Stack strings are built character-by-character on the stack to avoid appearing in the strings table.

```bash
# FLOSS is the best tool for stack strings
floss sample.exe --only stack

# Manual identification in disassembly: look for patterns like:
#   mov [rbp-0x20], 0x68   ; 'h'
#   mov [rbp-0x1f], 0x74   ; 't'
#   mov [rbp-0x1e], 0x74   ; 't'
#   mov [rbp-0x1d], 0x70   ; 'p'
```

### Step 4: IDAPython and Ghidra Scripts for Batch Decryption

When you identify a decryption routine in the binary, write scripts to decrypt all strings at once.

**IDAPython script template for batch XOR decryption:**

```python
# Run in IDA Pro: File -> Script File
import idautils
import idc
import ida_bytes

def decrypt_xor_strings(xor_key, encrypted_refs):
    """Decrypt all XOR-encrypted strings referenced in the binary."""
    for ea in encrypted_refs:
        encrypted = ida_bytes.get_bytes(ea, 256)
        if not encrypted:
            continue
        # Decrypt until null terminator
        decrypted = []
        for b in encrypted:
            dec = b ^ xor_key
            if dec == 0:
                break
            decrypted.append(dec)
        result = bytes(decrypted).decode('utf-8', errors='replace')
        # Add comment at the reference address
        idc.set_cmt(ea, f'Decrypted: "{result}"', 0)
        print(f"0x{ea:08X}: {result}")

# Find all cross-references to the decryption function
decrypt_func = idc.get_name_ea_simple("decrypt_string")
if decrypt_func != idc.BADADDR:
    xrefs = [ref.frm for ref in idautils.XrefsTo(decrypt_func)]
    decrypt_xor_strings(0x5A, xrefs)
```

**Ghidra script template (Python via Ghidrathon):**

```python
# Run in Ghidra: Script Manager -> Run
from ghidra.program.model.symbol import RefType

def find_decrypt_calls(func_name):
    """Find all calls to the decryption function."""
    fm = currentProgram.getFunctionManager()
    funcs = fm.getFunctions(True)
    for func in funcs:
        if func.getName() == func_name:
            refs = getReferencesTo(func.getEntryPoint())
            return [ref.getFromAddress() for ref in refs
                    if ref.getReferenceType() == RefType.UNCONDITIONAL_CALL]
    return []

def read_bytes_at(addr, length):
    """Read bytes from the program at a given address."""
    mem = currentProgram.getMemory()
    buf = bytearray(length)
    mem.getBytes(addr, buf)
    return bytes(buf)

# Example: decrypt all strings with XOR key 0x5A
call_sites = find_decrypt_calls("FUN_00401234")
for site in call_sites:
    print(f"Decryption call at: {site}")
```

### Step 5: .NET String Decryption

.NET malware frequently uses obfuscators like ConfuserEx, SmartAssembly, or custom string encryption.

**ConfuserEx string decryption:**

```bash
# Use de4dot for automated .NET deobfuscation
de4dot sample.exe -o deobfuscated.exe

# Verify deobfuscation
strings deobfuscated.exe | wc -l
# Compare with original
strings sample.exe | wc -l
```

**Using dnSpy for interactive .NET string decryption:**

1. Load the .NET assembly in dnSpy
2. Find the string decryption method (often called from a static constructor)
3. Set a breakpoint on the decryption method's return statement
4. Run with debugging - each break reveals a decrypted string
5. Use the Analyzer pane to find all callers of the decryption method

**Custom .NET string decryptor with dnfile:**

```python
import dnfile
import base64

def extract_dotnet_user_strings(filepath):
    """Extract all user strings from .NET metadata."""
    pe = dnfile.dnPE(filepath)
    if hasattr(pe, 'net') and hasattr(pe.net, 'user_strings'):
        strings = []
        for entry in pe.net.user_strings:
            if entry.value and len(entry.value) > 3:
                strings.append(entry.value)
        return strings
    return []

# SmartAssembly-style decryption (common pattern)
def decrypt_smartassembly(encrypted_bytes, key):
    """Decrypt SmartAssembly-style string encryption."""
    result = bytearray()
    for i, b in enumerate(encrypted_bytes):
        result.append(b ^ key[i % len(key)])
    return result.decode('utf-16-le', errors='replace')
```

### Step 6: Dynamic String Extraction via Debugger Breakpoints

Extract strings at runtime by setting breakpoints on decryption routine outputs.

**x64dbg approach:**

1. Identify the decryption function address through static analysis
2. Set a breakpoint at the function's return instruction (`ret`)
3. Log the return value (typically a pointer to the decrypted string in EAX/RAX)
4. Use conditional logging: `log "Decrypted: {s:[$result]}"` at the breakpoint
5. Run the sample and collect all logged strings

**GDB/Linux approach:**

```bash
# Break on the decryption function return and dump the result
gdb -batch -ex "file sample.elf" \
    -ex "break *0x08048567" \
    -ex "commands" \
    -ex "  x/s \$eax" \
    -ex "  continue" \
    -ex "end" \
    -ex "run" 2>&1 | grep "0x"
```

**Frida-based dynamic string extraction:**

```python
import frida
import sys

js_code = """
Interceptor.attach(ptr("0x00401234"), {
    onLeave: function(retval) {
        try {
            var str = retval.readUtf8String();
            if (str && str.length > 2) {
                send({type: "decrypted_string", value: str, address: retval.toString()});
            }
        } catch(e) {}
    }
});
"""

def on_message(message, data):
    if message['type'] == 'send':
        payload = message['payload']
        print(f"[+] {payload['address']}: {payload['value']}")

pid = frida.spawn(["./sample.exe"])
session = frida.attach(pid)
script = session.create_script(js_code)
script.on('message', on_message)
script.load()
frida.resume(pid)
sys.stdin.read()
```

### Step 7: Build Custom Decryptors from Identified Algorithms

When you reverse engineer a custom encryption algorithm, implement it for batch decryption.

**Workflow for building a custom decryptor:**

1. **Identify the algorithm** in the disassembly/decompilation
2. **Extract key material** (hardcoded keys, IVs, S-boxes)
3. **Reimplement the algorithm** in Python
4. **Locate all encrypted blobs** in the binary (cross-references to the encrypt function)
5. **Batch decrypt** all strings and annotate the disassembly

```bash
# Use the string decryptor framework with a custom algorithm
python3 scripts/string_decryptor.py --mode batch --binary sample.exe \
    --decrypt-func-addr 0x00401234 --key 0x5A \
    --output all_decrypted_strings.json
```

**Identifying the algorithm from disassembly patterns:**

| Pattern | Likely Algorithm |
|---------|-----------------|
| Single XOR in a loop with constant byte | Single-byte XOR |
| XOR in a loop with array index modulo key length | Multi-byte XOR |
| 256-byte array initialization + swap loop | RC4 |
| S-box lookup `0x63, 0x7c, 0x77, 0x7b...` | AES |
| Feistel network structure (split, round, swap) | DES/3DES |
| Character-by-character `mov [rbp+offset], imm8` | Stack strings |
| Base64 alphabet string in data section | Base64 encoding |
| Bit rotation (`ROL`/`ROR`) in loop | Custom cipher |

## Output Format

The string decryptor produces JSON output:

```json
{
  "metadata": {
    "tool": "string_decryptor.py",
    "timestamp": "2025-01-15T10:30:00Z",
    "binary": "sample.exe",
    "sha256": "a1b2c3d4e5f6...",
    "mode": "detect"
  },
  "detection": {
    "overall_entropy": 7.42,
    "high_entropy_sections": [
      {"name": ".data", "entropy": 7.85, "size": 45056, "offset": "0x00005000"}
    ],
    "xor_patterns_found": 3,
    "base64_blobs_found": 2,
    "stack_string_candidates": 15,
    "suspected_encryption": "single-byte XOR with key rotation"
  },
  "decrypted_strings": [
    {
      "offset": "0x00405120",
      "method": "xor-single",
      "key": "0x5A",
      "encrypted_hex": "32393a2f2f...",
      "decrypted": "http://malware-c2.example.com/gate.php",
      "category": "c2_url",
      "confidence": "high"
    },
    {
      "offset": "0x00405180",
      "method": "xor-single",
      "key": "0x5A",
      "encrypted_hex": "2b3924332e...",
      "decrypted": "cmd.exe /c whoami",
      "category": "command",
      "confidence": "high"
    },
    {
      "offset": "0x004051C0",
      "method": "stack-string",
      "key": null,
      "encrypted_hex": null,
      "decrypted": "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
      "category": "registry_key",
      "confidence": "medium"
    }
  ],
  "statistics": {
    "total_encrypted_regions": 45,
    "successfully_decrypted": 42,
    "failed": 3,
    "unique_strings": 38,
    "categories": {
      "c2_url": 5,
      "command": 8,
      "registry_key": 3,
      "api_name": 15,
      "file_path": 7
    }
  }
}
```

## Tips

- Start with FLOSS for quick wins before investing in manual decryption
- XOR with 0x00 is a no-op; skip key 0x00 when brute-forcing single-byte XOR
- Many malware families reuse the same encryption across versions - check public reports first
- Stack strings are commonly used for API names to evade import table analysis
- For RC4, the key scheduling algorithm's 256-iteration swap loop is a reliable signature
- When brute-forcing XOR, look for known plaintext (MZ header, http://, common DLL names) to identify the key
- .NET obfuscators often store the decryption key in the assembly metadata or a resource stream
- Use YARA rules to identify encrypted string patterns across multiple samples in a campaign
- Document every decrypted string with its offset - this helps correlate with behavioral analysis
- If dynamic extraction is needed, use a snapshot-capable VM to revert after each run