Files
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

197 lines
7.6 KiB
Python

#!/usr/bin/env python3
"""Android malware reverse engineering agent using jadx and androguard subprocess wrappers."""
import subprocess
import os
import sys
import re
import hashlib
from xml.etree import ElementTree
def compute_apk_hashes(apk_path):
"""Compute hashes for APK identification."""
with open(apk_path, "rb") as f:
data = f.read()
return {
"md5": hashlib.md5(data).hexdigest(),
"sha256": hashlib.sha256(data).hexdigest(),
"size": len(data),
}
def extract_manifest(apk_path, output_dir):
"""Extract and parse AndroidManifest.xml using apktool."""
subprocess.run(
["apktool", "d", apk_path, "-o", output_dir, "-f"],
capture_output=True, text=True, timeout=120
)
manifest_path = os.path.join(output_dir, "AndroidManifest.xml")
if not os.path.exists(manifest_path):
return {"error": "Manifest extraction failed"}
tree = ElementTree.parse(manifest_path)
root = tree.getroot()
ns = {"android": "http://schemas.android.com/apk/res/android"}
permissions = []
for perm in root.findall(".//uses-permission"):
name = perm.get(f"{{{ns['android']}}}name", "")
permissions.append(name)
activities = []
for act in root.findall(".//activity"):
name = act.get(f"{{{ns['android']}}}name", "")
exported = act.get(f"{{{ns['android']}}}exported", "false")
activities.append({"name": name, "exported": exported})
services = []
for svc in root.findall(".//service"):
name = svc.get(f"{{{ns['android']}}}name", "")
services.append(name)
receivers = []
for rcv in root.findall(".//receiver"):
name = rcv.get(f"{{{ns['android']}}}name", "")
intents = []
for intent in rcv.findall(".//intent-filter/action"):
intents.append(intent.get(f"{{{ns['android']}}}name", ""))
receivers.append({"name": name, "intents": intents})
package = root.get("package", "")
return {
"package": package,
"permissions": permissions,
"activities": activities,
"services": services,
"receivers": receivers,
}
DANGEROUS_PERMISSIONS = [
"android.permission.READ_SMS", "android.permission.SEND_SMS",
"android.permission.RECEIVE_SMS", "android.permission.READ_CONTACTS",
"android.permission.CAMERA", "android.permission.RECORD_AUDIO",
"android.permission.ACCESS_FINE_LOCATION", "android.permission.READ_PHONE_STATE",
"android.permission.CALL_PHONE", "android.permission.READ_CALL_LOG",
"android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE",
"android.permission.SYSTEM_ALERT_WINDOW", "android.permission.BIND_ACCESSIBILITY_SERVICE",
"android.permission.REQUEST_INSTALL_PACKAGES", "android.permission.BIND_DEVICE_ADMIN",
]
def analyze_permissions(permissions):
"""Classify permissions by risk level."""
dangerous = [p for p in permissions if p in DANGEROUS_PERMISSIONS]
sms_related = [p for p in permissions if "SMS" in p]
accessibility = [p for p in permissions if "ACCESSIBILITY" in p]
admin = [p for p in permissions if "DEVICE_ADMIN" in p or "BIND_ADMIN" in p]
risk = "LOW"
if len(dangerous) > 5:
risk = "HIGH"
if sms_related or accessibility or admin:
risk = "CRITICAL"
return {
"total": len(permissions),
"dangerous": dangerous,
"sms_related": sms_related,
"accessibility": accessibility,
"device_admin": admin,
"risk": risk,
}
def decompile_with_jadx(apk_path, output_dir):
"""Decompile APK to Java source using JADX."""
result = subprocess.run(
["jadx", "-d", output_dir, "--deobf", apk_path],
capture_output=True, text=True, timeout=300
)
return {
"output_dir": output_dir,
"returncode": result.returncode,
"stdout": result.stdout[-500:] if result.stdout else "",
}
def search_source_code(source_dir, patterns=None):
"""Search decompiled source for suspicious patterns."""
if patterns is None:
patterns = {
"urls": r'https?://[^\s"\'<>]+',
"ips": r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b',
"crypto_keys": r'(?:AES|DES|RSA|key|secret|encrypt).*?["\']([^"\']{8,})["\']',
"base64_strings": r'[A-Za-z0-9+/]{40,}={0,2}',
"exec_commands": r'Runtime\.getRuntime\(\)\.exec|ProcessBuilder',
"reflection": r'Class\.forName|getMethod|getDeclaredMethod',
"dex_loading": r'DexClassLoader|PathClassLoader|InMemoryDexClassLoader',
"overlay_attack": r'TYPE_APPLICATION_OVERLAY|SYSTEM_ALERT_WINDOW',
"accessibility_abuse": r'AccessibilityService|onAccessibilityEvent',
"sms_intercept": r'SmsReceiver|SMS_RECEIVED|sendTextMessage',
}
findings = {p: [] for p in patterns}
for root, dirs, files in os.walk(source_dir):
for filename in files:
if not filename.endswith(".java"):
continue
filepath = os.path.join(root, filename)
try:
with open(filepath, "r", errors="ignore") as f:
content = f.read()
for pattern_name, regex in patterns.items():
matches = re.findall(regex, content)
if matches:
findings[pattern_name].extend([
{"file": filepath, "match": m[:100]} for m in matches[:5]
])
except (OSError, UnicodeDecodeError):
pass
for key in findings:
findings[key] = findings[key][:20]
return findings
def analyze_apk(apk_path, output_base="/tmp/apk_analysis"):
"""Full APK analysis pipeline."""
os.makedirs(output_base, exist_ok=True)
report = {"apk": apk_path}
report["hashes"] = compute_apk_hashes(apk_path)
apktool_dir = os.path.join(output_base, "apktool")
report["manifest"] = extract_manifest(apk_path, apktool_dir)
if "permissions" in report["manifest"]:
report["permission_analysis"] = analyze_permissions(report["manifest"]["permissions"])
jadx_dir = os.path.join(output_base, "jadx_output")
report["decompilation"] = decompile_with_jadx(apk_path, jadx_dir)
if os.path.exists(jadx_dir):
source_dir = os.path.join(jadx_dir, "sources")
if os.path.exists(source_dir):
report["code_analysis"] = search_source_code(source_dir)
return report
def print_report(report):
print("Android Malware Analysis Report")
print("=" * 50)
print(f"APK: {report['apk']}")
print(f"SHA-256: {report['hashes']['sha256']}")
print(f"Size: {report['hashes']['size']} bytes")
manifest = report.get("manifest", {})
print(f"\nPackage: {manifest.get('package', 'N/A')}")
perm = report.get("permission_analysis", {})
print(f"Permissions: {perm.get('total', 0)} (Risk: {perm.get('risk', 'N/A')})")
if perm.get("dangerous"):
print(f" Dangerous: {', '.join(p.split('.')[-1] for p in perm['dangerous'][:8])}")
print(f"Activities: {len(manifest.get('activities', []))}")
print(f"Services: {len(manifest.get('services', []))}")
print(f"Receivers: {len(manifest.get('receivers', []))}")
code = report.get("code_analysis", {})
if code:
print("\nCode Analysis Findings:")
for pattern, matches in code.items():
if matches:
print(f" {pattern}: {len(matches)} match(es)")
for m in matches[:3]:
print(f" -> {m['match'][:80]}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python agent.py <apk_file>")
sys.exit(1)
result = analyze_apk(sys.argv[1])
print_report(result)