恶意程序加密字符串解密
用于恢复恶意程序中经过XOR、RC4、Base64、栈字符串或自定义算法处理的隐藏字符串,并结合FLOSS、静态代码和脚本识别解密逻辑。适合提取被隐藏的C2地址、命令、文件路径、注册表项、密钥和配置参数,是从混淆样本中快速获得有效IOC和进一步逆向入口的重要步骤。
在 AI 中使用此 Skill将本页链接复制给 AI,即可让 AI 获取完整 Skill 内容并按此执行
安全提示: 本站 Skill 均经 ChatGPT 最新模型扫描,未发现恶意脚本及危险指令、未检出已知恶意行为特征,但不保证绝对安全,使用即表示接受此风险
Skill 文件
版本 20260301 · 9e009d7c6a95b1a454070bf842ef797b
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
此文件不提供内嵌文本预览
请使用左侧文件行末尾的外链图标打开原始文件。
