mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-29 07:30:59 +03:00
Add folder anatomy (scripts/agent.py + references/api-reference.md) for 648 cybersecurity skills
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Packed malware analysis agent for UPX and generic packer detection and unpacking."""
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import hashlib
|
||||
import struct
|
||||
import math
|
||||
from collections import Counter
|
||||
|
||||
try:
|
||||
import pefile
|
||||
HAS_PEFILE = True
|
||||
except ImportError:
|
||||
HAS_PEFILE = False
|
||||
|
||||
|
||||
def compute_hashes(filepath):
|
||||
"""Compute file hashes."""
|
||||
md5 = hashlib.md5()
|
||||
sha256 = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(65536), b""):
|
||||
md5.update(chunk)
|
||||
sha256.update(chunk)
|
||||
return {"md5": md5.hexdigest(), "sha256": sha256.hexdigest()}
|
||||
|
||||
|
||||
def calculate_entropy(data):
|
||||
"""Calculate Shannon entropy of binary data."""
|
||||
if not data:
|
||||
return 0.0
|
||||
counter = Counter(data)
|
||||
length = len(data)
|
||||
return round(-sum((c / length) * math.log2(c / length) for c in counter.values()), 4)
|
||||
|
||||
|
||||
def detect_upx(filepath):
|
||||
"""Check for UPX packing signatures in the binary."""
|
||||
indicators = []
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
if b"UPX!" in data:
|
||||
indicators.append("UPX! magic string found in binary")
|
||||
if b"UPX0" in data:
|
||||
indicators.append("UPX0 section name found")
|
||||
if b"UPX1" in data:
|
||||
indicators.append("UPX1 section name found")
|
||||
if b"UPX2" in data:
|
||||
indicators.append("UPX2 section name found")
|
||||
|
||||
# Check for corrupted/modified UPX headers
|
||||
upx_pos = data.find(b"UPX!")
|
||||
if upx_pos != -1:
|
||||
# UPX version info follows the magic
|
||||
if upx_pos + 24 <= len(data):
|
||||
version_byte = data[upx_pos + 4]
|
||||
indicators.append(f"UPX version byte: 0x{version_byte:02X}")
|
||||
return indicators
|
||||
|
||||
|
||||
def detect_generic_packing(filepath):
|
||||
"""Detect generic packing indicators using PE section analysis."""
|
||||
if not HAS_PEFILE:
|
||||
return {"error": "pefile not installed: pip install pefile"}
|
||||
try:
|
||||
pe = pefile.PE(filepath)
|
||||
except pefile.PEFormatError:
|
||||
return {"error": "Not a valid PE file"}
|
||||
|
||||
indicators = []
|
||||
sections = []
|
||||
high_entropy_count = 0
|
||||
|
||||
for section in pe.sections:
|
||||
name = section.Name.rstrip(b"\x00").decode("utf-8", errors="replace")
|
||||
entropy = section.get_entropy()
|
||||
raw_size = section.SizeOfRawData
|
||||
virtual_size = section.Misc_VirtualSize
|
||||
sections.append({
|
||||
"name": name,
|
||||
"entropy": round(entropy, 4),
|
||||
"raw_size": raw_size,
|
||||
"virtual_size": virtual_size,
|
||||
"ratio": round(virtual_size / raw_size, 2) if raw_size > 0 else 0,
|
||||
})
|
||||
if entropy > 7.0:
|
||||
high_entropy_count += 1
|
||||
indicators.append(f"High entropy section: {name} ({entropy:.2f})")
|
||||
if virtual_size > raw_size * 5 and raw_size > 0:
|
||||
indicators.append(f"Suspicious size ratio in {name}: virtual/raw = {virtual_size/raw_size:.1f}x")
|
||||
|
||||
imports = []
|
||||
if hasattr(pe, "DIRECTORY_ENTRY_IMPORT"):
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
dll_name = entry.dll.decode("utf-8", errors="replace")
|
||||
func_count = len(entry.imports)
|
||||
imports.append({"dll": dll_name, "functions": func_count})
|
||||
|
||||
total_imports = sum(i["functions"] for i in imports)
|
||||
if total_imports < 10:
|
||||
indicators.append(f"Very few imports ({total_imports}) - typical of packed binaries")
|
||||
|
||||
# Check for LoadLibrary/GetProcAddress (runtime import resolution)
|
||||
import_names = []
|
||||
if hasattr(pe, "DIRECTORY_ENTRY_IMPORT"):
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
for imp in entry.imports:
|
||||
if imp.name:
|
||||
import_names.append(imp.name.decode("utf-8", errors="replace"))
|
||||
if "LoadLibraryA" in import_names and "GetProcAddress" in import_names:
|
||||
indicators.append("LoadLibraryA + GetProcAddress present (runtime import resolution)")
|
||||
|
||||
pe.close()
|
||||
return {
|
||||
"sections": sections,
|
||||
"imports": imports,
|
||||
"total_imports": total_imports,
|
||||
"high_entropy_sections": high_entropy_count,
|
||||
"indicators": indicators,
|
||||
"likely_packed": high_entropy_count > 0 or total_imports < 10,
|
||||
}
|
||||
|
||||
|
||||
def unpack_upx(filepath, output_path=None):
|
||||
"""Attempt to unpack a UPX-packed binary."""
|
||||
if output_path is None:
|
||||
output_path = filepath + ".unpacked"
|
||||
# First try standard UPX decompression
|
||||
cmd = f"upx -d -o {output_path} {filepath}"
|
||||
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||
if result.returncode == 0:
|
||||
return True, "Standard UPX unpack succeeded", output_path
|
||||
|
||||
# If standard fails, try fixing UPX headers
|
||||
return False, result.stderr.strip(), None
|
||||
|
||||
|
||||
def fix_upx_headers(filepath, output_path):
|
||||
"""Attempt to fix corrupted UPX magic bytes for unpacking."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = bytearray(f.read())
|
||||
|
||||
# Look for known UPX section names that might be renamed
|
||||
modified = False
|
||||
# Common modifications: UPX0/UPX1 renamed to something else
|
||||
for i in range(len(data) - 3):
|
||||
# Look for section header pattern near typical PE section table location
|
||||
if data[i:i+3] in [b"UP0", b"UP1", b"UX0", b"UX1"]:
|
||||
# Might be modified UPX section name
|
||||
pass
|
||||
|
||||
# Fix UPX! magic if corrupted
|
||||
for i in range(len(data) - 4):
|
||||
if data[i:i+2] == b"UX" and data[i+2:i+4] == b"!\x00":
|
||||
data[i:i+3] = b"UPX"
|
||||
modified = True
|
||||
|
||||
if modified:
|
||||
with open(output_path, "wb") as f:
|
||||
f.write(data)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def compare_packed_unpacked(packed_path, unpacked_path):
|
||||
"""Compare packed vs unpacked binary properties."""
|
||||
if not HAS_PEFILE:
|
||||
return {}
|
||||
comparison = {}
|
||||
for label, path in [("packed", packed_path), ("unpacked", unpacked_path)]:
|
||||
try:
|
||||
pe = pefile.PE(path)
|
||||
imports = 0
|
||||
if hasattr(pe, "DIRECTORY_ENTRY_IMPORT"):
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
imports += len(entry.imports)
|
||||
sections = len(pe.sections)
|
||||
pe.close()
|
||||
comparison[label] = {
|
||||
"size": os.path.getsize(path),
|
||||
"sections": sections,
|
||||
"imports": imports,
|
||||
"sha256": compute_hashes(path)["sha256"],
|
||||
}
|
||||
except Exception as e:
|
||||
comparison[label] = {"error": str(e)}
|
||||
return comparison
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Packed Malware Analysis Agent")
|
||||
print("UPX detection, packer identification, automated unpacking")
|
||||
print("=" * 60)
|
||||
|
||||
target = sys.argv[1] if len(sys.argv) > 1 else None
|
||||
|
||||
if target and os.path.exists(target):
|
||||
print(f"\n[*] Analyzing: {target}")
|
||||
hashes = compute_hashes(target)
|
||||
print(f"[*] SHA-256: {hashes['sha256']}")
|
||||
print(f"[*] Size: {os.path.getsize(target)} bytes")
|
||||
|
||||
print("\n--- UPX Signature Check ---")
|
||||
upx_indicators = detect_upx(target)
|
||||
for ind in upx_indicators:
|
||||
print(f" [!] {ind}")
|
||||
|
||||
print("\n--- Generic Packing Analysis ---")
|
||||
packing = detect_generic_packing(target)
|
||||
if "error" not in packing:
|
||||
print(f" Likely packed: {packing['likely_packed']}")
|
||||
print(f" Total imports: {packing['total_imports']}")
|
||||
print(f" High entropy sections: {packing['high_entropy_sections']}")
|
||||
for ind in packing.get("indicators", []):
|
||||
print(f" [!] {ind}")
|
||||
print("\n Sections:")
|
||||
for s in packing.get("sections", []):
|
||||
flag = " [HIGH]" if s["entropy"] > 7.0 else ""
|
||||
print(f" {s['name']:10s} entropy={s['entropy']:.2f} "
|
||||
f"raw={s['raw_size']} virt={s['virtual_size']}{flag}")
|
||||
|
||||
if upx_indicators:
|
||||
print("\n--- UPX Unpacking ---")
|
||||
success, msg, output = unpack_upx(target)
|
||||
if success:
|
||||
print(f" [OK] {msg}")
|
||||
print(f" [*] Unpacked file: {output}")
|
||||
print("\n--- Comparison ---")
|
||||
comp = compare_packed_unpacked(target, output)
|
||||
for label, data in comp.items():
|
||||
if "error" not in data:
|
||||
print(f" {label}: size={data['size']}, "
|
||||
f"sections={data['sections']}, imports={data['imports']}")
|
||||
else:
|
||||
print(f" [FAIL] {msg}")
|
||||
print(" [*] Try fixing UPX headers or use dynamic unpacking with a debugger")
|
||||
else:
|
||||
print(f"\n[DEMO] Usage: python agent.py <packed_binary.exe>")
|
||||
Reference in New Issue
Block a user