Reverse Engineering Rust Malware
Overview
Rust has become increasingly popular for malware development due to its cross-compilation, memory safety guarantees, and the complexity it introduces for reverse engineers. Rust binaries contain the entire standard library statically linked, producing large binaries with extensive boilerplate code. Key challenges include non-null-terminated strings (Rust uses fat pointers with pointer+length), monomorphization generating duplicated generic code, complex error handling (Result/Option unwrap chains), and unfamiliar calling conventions. Decompiling Rust to C produces unhelpful output compared to C/C++ binaries. Tools like Ghidra scripts for crate extraction, and training focused on Rust-specific patterns (2024-2025) help address these challenges. Notable Rust malware includes BlackCat/ALPHV ransomware, Hive ransomware variants, and Buer Loader.
Prerequisites
- IDA Pro 8.0+ or Ghidra 11.0+
- Rust toolchain for reference compilation
- Python 3.9+ for helper scripts
- Understanding of Rust memory model (ownership, borrowing)
- Familiarity with Rust string types (String, &str, CString)
Practical Steps
Step 1: Identify and Parse Rust Binary Metadata
#!/usr/bin/env python3
"""Analyze Rust malware binary metadata and extract crate dependencies."""
import re
import sys
import json
def identify_rust_binary(data):
"""Check if binary is Rust-compiled and extract version info."""
indicators = {
"rust_panic_strings": bool(re.search(rb'panicked at', data)),
"rust_unwrap": bool(re.search(rb'called.*unwrap.*on.*None', data)),
"core_panic": bool(re.search(rb'core::panicking', data)),
"std_rt": bool(re.search(rb'std::rt::lang_start', data)),
"cargo_path": bool(re.search(rb'\.cargo[/\\]registry', data)),
"rustc_version": None,
}
version = re.search(rb'rustc\s+(\d+\.\d+\.\d+)', data)
if version:
indicators["rustc_version"] = version.group(1).decode()
is_rust = sum(1 for v in indicators.values() if v) >= 2
return is_rust, indicators
def extract_crates(data):
"""Extract Rust crate (dependency) names from binary strings."""
crate_pattern = re.compile(
rb'(?:crates\.io-[a-f0-9]+/|\.cargo/registry/src/[^/]+/)'
rb'([\w-]+)-(\d+\.\d+\.\d+)'
)
crates = {}
for match in crate_pattern.finditer(data):
name = match.group(1).decode()
version = match.group(2).decode()
crates[name] = version
# Also check for common malware-relevant crates
suspicious_crates = {
"reqwest": "HTTP client",
"hyper": "HTTP library",
"tokio": "Async runtime",
"aes": "AES encryption",
"chacha20": "ChaCha20 encryption",
"rsa": "RSA encryption",
"ring": "Crypto library",
"base64": "Base64 encoding",
"winapi": "Windows API bindings",
"winreg": "Registry access",
"sysinfo": "System information",
"screenshots": "Screen capture",
"clipboard": "Clipboard access",
"keylogger": "Key logging",
}
capabilities = []
for crate_name, description in suspicious_crates.items():
if crate_name in crates:
capabilities.append({
"crate": crate_name,
"version": crates[crate_name],
"capability": description,
})
return crates, capabilities
def extract_rust_strings(data):
"""Extract strings handling Rust's non-null-terminated format."""
# Rust strings are stored as pointer+length, but string literals
# are often in .rodata as contiguous sequences
strings = []
ascii_pattern = re.compile(rb'[\x20-\x7e]{8,500}')
for match in ascii_pattern.finditer(data):
s = match.group().decode('ascii')
# Filter for malware-relevant strings
keywords = ['http', 'socket', 'encrypt', 'decrypt', 'shell',
'exec', 'cmd', 'upload', 'download', 'persist',
'registry', 'mutex', 'pipe', 'inject']
if any(kw in s.lower() for kw in keywords):
strings.append(s)
return strings
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <rust_binary>")
sys.exit(1)
with open(sys.argv[1], 'rb') as f:
data = f.read()
is_rust, indicators = identify_rust_binary(data)
print(f"[{'+'if is_rust else '-'}] Rust binary: {is_rust}")
print(json.dumps(indicators, indent=2, default=str))
crates, capabilities = extract_crates(data)
print(f"\n[+] Crates ({len(crates)}):")
for name, ver in sorted(crates.items()):
print(f" {name} v{ver}")
if capabilities:
print(f"\n[!] Suspicious capabilities:")
for cap in capabilities:
print(f" {cap['crate']} -> {cap['capability']}")
strings = extract_rust_strings(data)
if strings:
print(f"\n[+] Suspicious strings ({len(strings)}):")
for s in strings[:20]:
print(f" {s}")
Validation Criteria
- Binary correctly identified as Rust-compiled with version info
- Crate dependencies extracted revealing malware capabilities
- Rust-specific string extraction handles fat pointer format
- Main entry point and core logic functions identified
- Encryption, networking, and persistence code located
References
1---2name: reverse-engineering-rust-malware3description: Reverse engineer Rust-compiled malware using IDA Pro and Ghidra with techniques for handling non-null-terminated strings, crate dependency extraction, and Rust-specific control flow analysis.4domain: cybersecurity5subdomain: malware-analysis6tags: [rust, reverse-engineering, malware-analysis, ghidra, ida-pro, binary-analysis, rust-malware]7version: "1.0"8author: mahipal9license: Apache-2.010---1112# Reverse Engineering Rust Malware1314## Overview1516Rust has become increasingly popular for malware development due to its cross-compilation, memory safety guarantees, and the complexity it introduces for reverse engineers. Rust binaries contain the entire standard library statically linked, producing large binaries with extensive boilerplate code. Key challenges include non-null-terminated strings (Rust uses fat pointers with pointer+length), monomorphization generating duplicated generic code, complex error handling (Result/Option unwrap chains), and unfamiliar calling conventions. Decompiling Rust to C produces unhelpful output compared to C/C++ binaries. Tools like Ghidra scripts for crate extraction, and training focused on Rust-specific patterns (2024-2025) help address these challenges. Notable Rust malware includes BlackCat/ALPHV ransomware, Hive ransomware variants, and Buer Loader.1718## Prerequisites1920- IDA Pro 8.0+ or Ghidra 11.0+21- Rust toolchain for reference compilation22- Python 3.9+ for helper scripts23- Understanding of Rust memory model (ownership, borrowing)24- Familiarity with Rust string types (String, &str, CString)2526## Practical Steps2728### Step 1: Identify and Parse Rust Binary Metadata2930```python31#!/usr/bin/env python332"""Analyze Rust malware binary metadata and extract crate dependencies."""33import re34import sys35import json363738def identify_rust_binary(data):39 """Check if binary is Rust-compiled and extract version info."""40 indicators = {41 "rust_panic_strings": bool(re.search(rb'panicked at', data)),42 "rust_unwrap": bool(re.search(rb'called.*unwrap.*on.*None', data)),43 "core_panic": bool(re.search(rb'core::panicking', data)),44 "std_rt": bool(re.search(rb'std::rt::lang_start', data)),45 "cargo_path": bool(re.search(rb'\.cargo[/\\]registry', data)),46 "rustc_version": None,47 }4849 version = re.search(rb'rustc\s+(\d+\.\d+\.\d+)', data)50 if version:51 indicators["rustc_version"] = version.group(1).decode()5253 is_rust = sum(1 for v in indicators.values() if v) >= 254 return is_rust, indicators555657def extract_crates(data):58 """Extract Rust crate (dependency) names from binary strings."""59 crate_pattern = re.compile(60 rb'(?:crates\.io-[a-f0-9]+/|\.cargo/registry/src/[^/]+/)'61 rb'([\w-]+)-(\d+\.\d+\.\d+)'62 )63 crates = {}64 for match in crate_pattern.finditer(data):65 name = match.group(1).decode()66 version = match.group(2).decode()67 crates[name] = version6869 # Also check for common malware-relevant crates70 suspicious_crates = {71 "reqwest": "HTTP client",72 "hyper": "HTTP library",73 "tokio": "Async runtime",74 "aes": "AES encryption",75 "chacha20": "ChaCha20 encryption",76 "rsa": "RSA encryption",77 "ring": "Crypto library",78 "base64": "Base64 encoding",79 "winapi": "Windows API bindings",80 "winreg": "Registry access",81 "sysinfo": "System information",82 "screenshots": "Screen capture",83 "clipboard": "Clipboard access",84 "keylogger": "Key logging",85 }8687 capabilities = []88 for crate_name, description in suspicious_crates.items():89 if crate_name in crates:90 capabilities.append({91 "crate": crate_name,92 "version": crates[crate_name],93 "capability": description,94 })9596 return crates, capabilities979899def extract_rust_strings(data):100 """Extract strings handling Rust's non-null-terminated format."""101 # Rust strings are stored as pointer+length, but string literals102 # are often in .rodata as contiguous sequences103 strings = []104 ascii_pattern = re.compile(rb'[\x20-\x7e]{8,500}')105 for match in ascii_pattern.finditer(data):106 s = match.group().decode('ascii')107 # Filter for malware-relevant strings108 keywords = ['http', 'socket', 'encrypt', 'decrypt', 'shell',109 'exec', 'cmd', 'upload', 'download', 'persist',110 'registry', 'mutex', 'pipe', 'inject']111 if any(kw in s.lower() for kw in keywords):112 strings.append(s)113114 return strings115116117if __name__ == "__main__":118 if len(sys.argv) < 2:119 print(f"Usage: {sys.argv[0]} <rust_binary>")120 sys.exit(1)121122 with open(sys.argv[1], 'rb') as f:123 data = f.read()124125 is_rust, indicators = identify_rust_binary(data)126 print(f"[{'+'if is_rust else '-'}] Rust binary: {is_rust}")127 print(json.dumps(indicators, indent=2, default=str))128129 crates, capabilities = extract_crates(data)130 print(f"\n[+] Crates ({len(crates)}):")131 for name, ver in sorted(crates.items()):132 print(f" {name} v{ver}")133134 if capabilities:135 print(f"\n[!] Suspicious capabilities:")136 for cap in capabilities:137 print(f" {cap['crate']} -> {cap['capability']}")138139 strings = extract_rust_strings(data)140 if strings:141 print(f"\n[+] Suspicious strings ({len(strings)}):")142 for s in strings[:20]:143 print(f" {s}")144```145146## Validation Criteria147148- Binary correctly identified as Rust-compiled with version info149- Crate dependencies extracted revealing malware capabilities150- Rust-specific string extraction handles fat pointer format151- Main entry point and core logic functions identified152- Encryption, networking, and persistence code located153154## References155156- [Binary Defense - Extracting Secrets from Rust Malware](https://binarydefense.com/resources/blog/digging-through-rust-to-find-gold-extracting-secrets-from-rust-malware)157- [Ghidra Extension for Rust Analysis](https://cir.nii.ac.jp/crid/1050302237609671296)158- [Fuzzing Labs - Reversing Modern Binaries](https://fuzzinglabs.com/reversing-modern-binaries/)159- [Bishop Fox - Rust for Malware Development](https://bishopfox.com/blog/rust-for-malware-development)