Files
Anthropic-Cybersecurity-Skills/skills/detecting-mobile-malware-behavior/scripts/process.py
T

236 lines
8.8 KiB
Python

#!/usr/bin/env python3
"""
Mobile Malware Behavior Analyzer
Performs static indicator analysis on Android APK files to detect malware behaviors.
Checks permissions, code patterns, and VirusTotal reputation.
Usage:
python process.py --apk suspicious.apk [--vt-key API_KEY] [--output report.json]
"""
import argparse
import hashlib
import json
import subprocess
import sys
import zipfile
import re
from datetime import datetime
from pathlib import Path
try:
import requests
except ImportError:
requests = None
DANGEROUS_PERMISSIONS = {
"android.permission.READ_SMS": "SMS access - banking trojan indicator",
"android.permission.RECEIVE_SMS": "SMS interception - banking trojan indicator",
"android.permission.SEND_SMS": "SMS sending - premium SMS fraud indicator",
"android.permission.CAMERA": "Camera access - spyware indicator",
"android.permission.RECORD_AUDIO": "Microphone access - spyware indicator",
"android.permission.READ_CONTACTS": "Contact harvesting - worm/spyware indicator",
"android.permission.READ_CALL_LOG": "Call log access - spyware indicator",
"android.permission.ACCESS_FINE_LOCATION": "Location tracking - stalkerware indicator",
"android.permission.SYSTEM_ALERT_WINDOW": "Overlay capability - credential stealer indicator",
"android.permission.BIND_DEVICE_ADMIN": "Device admin - ransomware indicator",
"android.permission.BIND_ACCESSIBILITY_SERVICE": "Accessibility abuse - overlay attacks",
"android.permission.REQUEST_INSTALL_PACKAGES": "Silent app installation capability",
"android.permission.WRITE_EXTERNAL_STORAGE": "External storage write - data staging",
}
MALWARE_CODE_PATTERNS = {
"dynamic_dex_loading": r"DexClassLoader|InMemoryDexClassLoader|PathClassLoader",
"reflection": r"java\.lang\.reflect|Method\.invoke|Class\.forName",
"native_code_loading": r"System\.loadLibrary|System\.load\(",
"command_execution": r"Runtime\.getRuntime\(\)\.exec|ProcessBuilder",
"crypto_operations": r"javax\.crypto\.Cipher|javax\.crypto\.spec",
"base64_encoding": r"android\.util\.Base64|java\.util\.Base64",
"root_detection": r"\/system\/xbin\/su|\/system\/app\/Superuser|isRooted|RootBeer",
"emulator_detection": r"Build\.FINGERPRINT.*generic|goldfish|ranchu|sdk_gphone",
"keylogger": r"AccessibilityService|onAccessibilityEvent|TYPE_VIEW_TEXT_CHANGED",
"screen_capture": r"MediaProjection|createVirtualDisplay|CAPTURE_SECURE",
}
def compute_hashes(file_path: str) -> dict:
"""Compute file hashes."""
with open(file_path, "rb") as f:
data = f.read()
return {
"md5": hashlib.md5(data).hexdigest(),
"sha1": hashlib.sha1(data).hexdigest(),
"sha256": hashlib.sha256(data).hexdigest(),
"size": len(data),
}
def extract_permissions(apk_path: str) -> list:
"""Extract permissions from APK using aapt."""
try:
result = subprocess.run(
["aapt", "dump", "permissions", apk_path],
capture_output=True, text=True, timeout=30
)
perms = []
for line in result.stdout.split("\n"):
if "uses-permission:" in line:
perm = line.split("name='")[1].split("'")[0] if "name='" in line else line.strip()
perms.append(perm)
return perms
except (subprocess.TimeoutExpired, FileNotFoundError, IndexError):
return []
def scan_code_patterns(apk_path: str) -> dict:
"""Scan DEX code for malware patterns."""
findings = {}
try:
with zipfile.ZipFile(apk_path, "r") as z:
for name in z.namelist():
if name.endswith(".dex"):
dex_data = z.read(name).decode("utf-8", errors="replace")
for pattern_name, pattern in MALWARE_CODE_PATTERNS.items():
matches = re.findall(pattern, dex_data)
if matches:
findings[pattern_name] = {
"count": len(matches),
"samples": list(set(matches))[:3],
}
except zipfile.BadZipFile:
findings["error"] = "Invalid ZIP/APK file"
return findings
def check_virustotal(sha256: str, api_key: str) -> dict:
"""Query VirusTotal for file reputation."""
if not requests or not api_key:
return {"skipped": True}
try:
resp = requests.get(
f"https://www.virustotal.com/api/v3/files/{sha256}",
headers={"x-apikey": api_key},
timeout=15
)
if resp.status_code == 200:
data = resp.json().get("data", {}).get("attributes", {})
stats = data.get("last_analysis_stats", {})
return {
"malicious": stats.get("malicious", 0),
"suspicious": stats.get("suspicious", 0),
"undetected": stats.get("undetected", 0),
"total_engines": sum(stats.values()),
"detection_names": [
f"{engine}: {result.get('result')}"
for engine, result in data.get("last_analysis_results", {}).items()
if result.get("category") == "malicious"
][:10],
}
return {"status_code": resp.status_code}
except Exception as e:
return {"error": str(e)}
def assess_risk(permissions: list, code_patterns: dict, vt_result: dict) -> dict:
"""Calculate overall malware risk assessment."""
risk_score = 0
indicators = []
# Permission-based risk
dangerous_found = [p for p in permissions if p in DANGEROUS_PERMISSIONS]
risk_score += len(dangerous_found) * 10
# High-risk combinations
perm_set = set(permissions)
if {"android.permission.READ_SMS", "android.permission.INTERNET"} <= perm_set:
indicators.append("SMS stealer pattern (READ_SMS + INTERNET)")
risk_score += 30
if {"android.permission.CAMERA", "android.permission.RECORD_AUDIO", "android.permission.INTERNET"} <= perm_set:
indicators.append("Spyware pattern (CAMERA + AUDIO + INTERNET)")
risk_score += 40
if "android.permission.BIND_DEVICE_ADMIN" in perm_set:
indicators.append("Device admin capability (ransomware indicator)")
risk_score += 25
# Code pattern risk
if "dynamic_dex_loading" in code_patterns:
indicators.append("Dynamic DEX loading detected")
risk_score += 20
if "command_execution" in code_patterns:
indicators.append("Command execution capability")
risk_score += 15
if "emulator_detection" in code_patterns:
indicators.append("Anti-emulator checks (sandbox evasion)")
risk_score += 15
if "keylogger" in code_patterns:
indicators.append("Accessibility service abuse (keylogger)")
risk_score += 30
# VirusTotal
if vt_result.get("malicious", 0) > 0:
risk_score += min(vt_result["malicious"] * 5, 50)
indicators.append(f"VirusTotal: {vt_result['malicious']} engines detected as malicious")
risk_level = "CRITICAL" if risk_score >= 80 else "HIGH" if risk_score >= 50 else "MEDIUM" if risk_score >= 25 else "LOW"
return {
"risk_score": min(risk_score, 100),
"risk_level": risk_level,
"indicators": indicators,
}
def main():
parser = argparse.ArgumentParser(description="Mobile Malware Behavior Analyzer")
parser.add_argument("--apk", required=True, help="Path to APK file")
parser.add_argument("--vt-key", help="VirusTotal API key")
parser.add_argument("--output", default="malware_report.json", help="Output report")
args = parser.parse_args()
if not Path(args.apk).exists():
print(f"[-] File not found: {args.apk}")
sys.exit(1)
print("[*] Computing hashes...")
hashes = compute_hashes(args.apk)
print("[*] Extracting permissions...")
permissions = extract_permissions(args.apk)
print("[*] Scanning code patterns...")
code_patterns = scan_code_patterns(args.apk)
print("[*] Checking VirusTotal...")
vt_result = check_virustotal(hashes["sha256"], args.vt_key)
print("[*] Assessing risk...")
risk = assess_risk(permissions, code_patterns, vt_result)
report = {
"analysis": {
"file": args.apk,
"date": datetime.now().isoformat(),
"hashes": hashes,
},
"permissions": {
"total": len(permissions),
"dangerous": {p: DANGEROUS_PERMISSIONS[p] for p in permissions if p in DANGEROUS_PERMISSIONS},
},
"code_patterns": code_patterns,
"virustotal": vt_result,
"risk_assessment": risk,
}
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(f"\n[+] Report saved: {args.output}")
print(f"[!] Risk Level: {risk['risk_level']} (Score: {risk['risk_score']}/100)")
for ind in risk["indicators"]:
print(f" - {ind}")
if __name__ == "__main__":
main()