mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-01 08:47:41 +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,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Anthropic Agent Skills Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,75 @@
|
||||
# API Reference: Malware Reverse Engineering with Ghidra Agent
|
||||
|
||||
## Overview
|
||||
|
||||
Combines Ghidra headless analysis with r2pipe (radare2) for automated malware binary analysis: function enumeration, import classification, section entropy, cryptographic constant detection, and network indicator extraction.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| r2pipe | >= 1.8 | Radare2 scripting interface for binary analysis |
|
||||
| hashlib | stdlib | File hash computation |
|
||||
|
||||
## External Tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| Ghidra (analyzeHeadless) | Automated disassembly and decompilation |
|
||||
| radare2 | Binary analysis, function detection, string extraction |
|
||||
|
||||
## Core Functions
|
||||
|
||||
### `run_ghidra_headless(ghidra_path, project_dir, project_name, binary_path, script)`
|
||||
Executes Ghidra in headless mode with optional post-analysis script.
|
||||
- **Timeout**: 600 seconds
|
||||
- **Returns**: `dict` with command, returncode, stdout/stderr
|
||||
|
||||
### `export_functions_ghidra(...)`
|
||||
Generates and runs a Ghidra script to export function list as JSON.
|
||||
- **Exports**: name, address, size, calling convention, is_thunk
|
||||
|
||||
### `analyze_with_radare2(filepath)`
|
||||
Full r2pipe analysis: binary info, functions, imports, strings, sections, entry points.
|
||||
- **Classifies imports**: injection, network, evasion, crypto, persistence
|
||||
- **Extracts**: network indicators (URLs, IPs) from strings
|
||||
- **Returns**: `dict` with info, function_count, suspicious_imports, sections, etc.
|
||||
|
||||
### `extract_crypto_constants(filepath)`
|
||||
Searches binary for known cryptographic constants: AES S-box, RC4 init table, SHA-256 init vector, RSA magic bytes.
|
||||
- **Returns**: `list[dict]` with constant name and file offset
|
||||
|
||||
### `analyze_malware(filepath, ghidra_path, output_dir)`
|
||||
Full pipeline: hashes -> crypto constants -> radare2 analysis -> Ghidra headless.
|
||||
|
||||
## Suspicious Import Categories
|
||||
|
||||
| Category | Example Functions |
|
||||
|----------|-------------------|
|
||||
| injection | VirtualAllocEx, WriteProcessMemory, CreateRemoteThread |
|
||||
| network | InternetOpenA, WSAStartup, URLDownloadToFileA |
|
||||
| evasion | IsDebuggerPresent, NtQueryInformationProcess |
|
||||
| crypto | CryptEncrypt, CryptDecrypt |
|
||||
| persistence | RegSetValueExA, CreateServiceA |
|
||||
|
||||
## Radare2 Commands Used
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `aaa` | Full auto-analysis |
|
||||
| `ij` | Binary info as JSON |
|
||||
| `aflj` | Function list as JSON |
|
||||
| `iij` | Import list as JSON |
|
||||
| `izj` | String list as JSON |
|
||||
| `iSj` | Section list as JSON |
|
||||
| `iej` | Entry points as JSON |
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# With radare2 only
|
||||
python agent.py malware.exe
|
||||
|
||||
# With Ghidra headless analysis
|
||||
python agent.py malware.exe /opt/ghidra
|
||||
```
|
||||
@@ -0,0 +1,233 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Malware reverse engineering agent using Ghidra headless analyzer and r2pipe."""
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import r2pipe
|
||||
except ImportError:
|
||||
r2pipe = None
|
||||
|
||||
|
||||
def compute_hashes(filepath):
|
||||
"""Compute file hashes for identification."""
|
||||
with open(filepath, "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 run_ghidra_headless(ghidra_path, project_dir, project_name, binary_path,
|
||||
script=None, script_args=None):
|
||||
"""Run Ghidra in headless mode for automated analysis."""
|
||||
os.makedirs(project_dir, exist_ok=True)
|
||||
cmd = [
|
||||
os.path.join(ghidra_path, "support", "analyzeHeadless"),
|
||||
project_dir, project_name,
|
||||
"-import", binary_path,
|
||||
"-overwrite",
|
||||
]
|
||||
if script:
|
||||
cmd.extend(["-postScript", script])
|
||||
if script_args:
|
||||
cmd.extend(script_args)
|
||||
result = subprocess.run(
|
||||
cmd, capture_output=True, text=True, timeout=600
|
||||
)
|
||||
return {
|
||||
"command": " ".join(cmd),
|
||||
"returncode": result.returncode,
|
||||
"stdout": result.stdout[-2000:] if result.stdout else "",
|
||||
"stderr": result.stderr[-1000:] if result.stderr else "",
|
||||
}
|
||||
|
||||
|
||||
def export_functions_ghidra(ghidra_path, project_dir, project_name, binary_path,
|
||||
output_file):
|
||||
"""Export function list using Ghidra headless with a script."""
|
||||
script_content = """
|
||||
import ghidra.program.model.listing.FunctionIterator
|
||||
import json
|
||||
|
||||
output = []
|
||||
fm = currentProgram.getFunctionManager()
|
||||
funcs = fm.getFunctions(True)
|
||||
for func in funcs:
|
||||
entry = {
|
||||
"name": func.getName(),
|
||||
"address": str(func.getEntryPoint()),
|
||||
"size": func.getBody().getNumAddresses(),
|
||||
"calling_convention": func.getCallingConventionName(),
|
||||
"is_thunk": func.isThunk(),
|
||||
}
|
||||
output.append(entry)
|
||||
|
||||
with open("{output}", "w") as f:
|
||||
json.dump(output, f, indent=2)
|
||||
""".replace("{output}", output_file.replace("\\", "\\\\"))
|
||||
script_path = os.path.join(project_dir, "export_functions.py")
|
||||
with open(script_path, "w") as f:
|
||||
f.write(script_content)
|
||||
return run_ghidra_headless(
|
||||
ghidra_path, project_dir, project_name, binary_path,
|
||||
script="export_functions.py"
|
||||
)
|
||||
|
||||
|
||||
def analyze_with_radare2(filepath):
|
||||
"""Analyze binary with radare2 via r2pipe for quick triage."""
|
||||
if r2pipe is None:
|
||||
return {"error": "r2pipe not installed (pip install r2pipe)"}
|
||||
r2 = r2pipe.open(filepath, flags=["-2"])
|
||||
r2.cmd("aaa")
|
||||
info = r2.cmdj("ij")
|
||||
functions = r2.cmdj("aflj") or []
|
||||
imports = r2.cmdj("iij") or []
|
||||
strings = r2.cmdj("izj") or []
|
||||
sections = r2.cmdj("iSj") or []
|
||||
entry_points = r2.cmdj("iej") or []
|
||||
suspicious_imports = {
|
||||
"injection": ["VirtualAllocEx", "WriteProcessMemory", "CreateRemoteThread",
|
||||
"NtCreateThreadEx"],
|
||||
"network": ["InternetOpenA", "HttpSendRequestA", "WSAStartup",
|
||||
"URLDownloadToFileA"],
|
||||
"evasion": ["IsDebuggerPresent", "CheckRemoteDebuggerPresent",
|
||||
"NtQueryInformationProcess"],
|
||||
"crypto": ["CryptEncrypt", "CryptDecrypt", "CryptAcquireContextA"],
|
||||
"persistence": ["RegSetValueExA", "CreateServiceA"],
|
||||
}
|
||||
import_findings = []
|
||||
for imp in imports:
|
||||
name = imp.get("name", "")
|
||||
for category, funcs in suspicious_imports.items():
|
||||
if name in funcs:
|
||||
import_findings.append({
|
||||
"category": category,
|
||||
"function": name,
|
||||
"library": imp.get("lib", ""),
|
||||
})
|
||||
network_strings = []
|
||||
for s in strings:
|
||||
val = s.get("string", "")
|
||||
if re.search(r"https?://", val) or re.search(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", val):
|
||||
network_strings.append(val[:200])
|
||||
section_analysis = []
|
||||
for sec in sections:
|
||||
entropy = sec.get("entropy", 0)
|
||||
flags = []
|
||||
if entropy and entropy > 7.0:
|
||||
flags.append("HIGH_ENTROPY")
|
||||
section_analysis.append({
|
||||
"name": sec.get("name", ""),
|
||||
"size": sec.get("size", 0),
|
||||
"vsize": sec.get("vsize", 0),
|
||||
"entropy": entropy,
|
||||
"flags": flags,
|
||||
})
|
||||
r2.quit()
|
||||
return {
|
||||
"info": {
|
||||
"arch": info.get("bin", {}).get("arch", ""),
|
||||
"bits": info.get("bin", {}).get("bits", 0),
|
||||
"os": info.get("bin", {}).get("os", ""),
|
||||
"type": info.get("bin", {}).get("bintype", ""),
|
||||
"compiler": info.get("bin", {}).get("compiler", ""),
|
||||
},
|
||||
"function_count": len(functions),
|
||||
"import_count": len(imports),
|
||||
"string_count": len(strings),
|
||||
"suspicious_imports": import_findings,
|
||||
"network_indicators": network_strings[:20],
|
||||
"sections": section_analysis,
|
||||
"entry_points": [{"vaddr": e.get("vaddr"), "type": e.get("type")} for e in entry_points],
|
||||
}
|
||||
|
||||
|
||||
def extract_crypto_constants(filepath):
|
||||
"""Search binary for known cryptographic constants."""
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
constants = {
|
||||
"AES_SBOX": bytes([0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5]),
|
||||
"RC4_INIT": bytes(range(256)),
|
||||
"SHA256_INIT": bytes.fromhex("6a09e667bb67ae853c6ef372a54ff53a"),
|
||||
"RSA_MAGIC": b"RSA1",
|
||||
}
|
||||
found = []
|
||||
for name, pattern in constants.items():
|
||||
offset = data.find(pattern)
|
||||
if offset >= 0:
|
||||
found.append({"constant": name, "offset": hex(offset)})
|
||||
return found
|
||||
|
||||
|
||||
def analyze_malware(filepath, ghidra_path=None, output_dir="/tmp/ghidra_analysis"):
|
||||
"""Full malware analysis pipeline."""
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
report = {"file": os.path.basename(filepath)}
|
||||
report["hashes"] = compute_hashes(filepath)
|
||||
report["crypto_constants"] = extract_crypto_constants(filepath)
|
||||
if r2pipe:
|
||||
report["radare2"] = analyze_with_radare2(filepath)
|
||||
if ghidra_path and os.path.exists(ghidra_path):
|
||||
ghidra_result = run_ghidra_headless(
|
||||
ghidra_path, output_dir, "malware_project", filepath
|
||||
)
|
||||
report["ghidra"] = {
|
||||
"analysis_complete": ghidra_result["returncode"] == 0,
|
||||
"output": ghidra_result["stdout"][-500:],
|
||||
}
|
||||
return report
|
||||
|
||||
|
||||
def print_report(report):
|
||||
print("Malware Reverse Engineering Report")
|
||||
print("=" * 50)
|
||||
print(f"File: {report['file']}")
|
||||
print(f"SHA-256: {report['hashes']['sha256']}")
|
||||
print(f"Size: {report['hashes']['size']} bytes")
|
||||
if report.get("crypto_constants"):
|
||||
print(f"\nCrypto Constants Found:")
|
||||
for c in report["crypto_constants"]:
|
||||
print(f" {c['constant']} at {c['offset']}")
|
||||
r2 = report.get("radare2", {})
|
||||
if r2 and "error" not in r2:
|
||||
info = r2.get("info", {})
|
||||
print(f"\nBinary Info: {info.get('arch', '?')}/{info.get('bits', '?')}bit "
|
||||
f"({info.get('os', '?')}) [{info.get('type', '?')}]")
|
||||
print(f"Functions: {r2.get('function_count', 0)}")
|
||||
print(f"Imports: {r2.get('import_count', 0)}")
|
||||
if r2.get("suspicious_imports"):
|
||||
print(f"\nSuspicious Imports:")
|
||||
for imp in r2["suspicious_imports"]:
|
||||
print(f" [{imp['category']}] {imp['library']} -> {imp['function']}")
|
||||
if r2.get("network_indicators"):
|
||||
print(f"\nNetwork Indicators:")
|
||||
for ni in r2["network_indicators"][:10]:
|
||||
print(f" {ni}")
|
||||
print(f"\nSections:")
|
||||
for sec in r2.get("sections", []):
|
||||
flags = f" [{', '.join(sec['flags'])}]" if sec.get("flags") else ""
|
||||
print(f" {sec['name']:10s} size={sec['size']:>8} entropy={sec.get('entropy', 0):.2f}{flags}")
|
||||
if report.get("ghidra", {}).get("analysis_complete"):
|
||||
print(f"\nGhidra: Analysis complete")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python agent.py <binary> [ghidra_install_path]")
|
||||
sys.exit(1)
|
||||
binary = sys.argv[1]
|
||||
ghidra = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
result = analyze_malware(binary, ghidra_path=ghidra)
|
||||
print_report(result)
|
||||
Reference in New Issue
Block a user