Autarch/web/routes/reverse_eng.py

201 lines
6.4 KiB
Python
Raw Permalink Normal View History

"""Reverse Engineering routes."""
from flask import Blueprint, request, jsonify, render_template
from web.auth import login_required
reverse_eng_bp = Blueprint('reverse_eng', __name__, url_prefix='/reverse-eng')
def _get_re():
from modules.reverse_eng import get_reverse_eng
return get_reverse_eng()
# ==================== PAGE ====================
@reverse_eng_bp.route('/')
@login_required
def index():
return render_template('reverse_eng.html')
# ==================== ANALYSIS ====================
@reverse_eng_bp.route('/analyze', methods=['POST'])
@login_required
def analyze():
"""Comprehensive binary analysis."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
if not file_path:
return jsonify({'error': 'No file path provided'}), 400
result = _get_re().analyze_binary(file_path)
return jsonify(result)
@reverse_eng_bp.route('/strings', methods=['POST'])
@login_required
def strings():
"""Extract strings from binary."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
if not file_path:
return jsonify({'error': 'No file path provided'}), 400
min_length = int(data.get('min_length', 4))
encoding = data.get('encoding', 'both')
result = _get_re().extract_strings(file_path, min_length=min_length, encoding=encoding)
return jsonify({'strings': result, 'total': len(result)})
# ==================== DISASSEMBLY ====================
@reverse_eng_bp.route('/disassemble', methods=['POST'])
@login_required
def disassemble():
"""Disassemble binary data or file."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
hex_data = data.get('hex', '').strip()
arch = data.get('arch', 'x64')
count = int(data.get('count', 100))
section = data.get('section', '.text')
if hex_data:
try:
raw = bytes.fromhex(hex_data.replace(' ', '').replace('\n', ''))
except ValueError:
return jsonify({'error': 'Invalid hex data'}), 400
instructions = _get_re().disassemble(raw, arch=arch, count=count)
elif file_path:
offset = int(data.get('offset', 0))
instructions = _get_re().disassemble_file(
file_path, section=section, offset=offset, count=count)
else:
return jsonify({'error': 'Provide file path or hex data'}), 400
return jsonify({'instructions': instructions, 'total': len(instructions)})
# ==================== HEX ====================
@reverse_eng_bp.route('/hex', methods=['POST'])
@login_required
def hex_dump():
"""Hex dump of file region."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
if not file_path:
return jsonify({'error': 'No file path provided'}), 400
offset = int(data.get('offset', 0))
length = int(data.get('length', 256))
length = min(length, 65536) # Cap at 64KB
result = _get_re().hex_dump(file_path, offset=offset, length=length)
return jsonify(result)
@reverse_eng_bp.route('/hex/search', methods=['POST'])
@login_required
def hex_search():
"""Search for hex pattern in binary."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
pattern = data.get('pattern', '').strip()
if not file_path or not pattern:
return jsonify({'error': 'File path and pattern required'}), 400
result = _get_re().hex_search(file_path, pattern)
return jsonify(result)
# ==================== YARA ====================
@reverse_eng_bp.route('/yara/scan', methods=['POST'])
@login_required
def yara_scan():
"""Scan file with YARA rules."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
if not file_path:
return jsonify({'error': 'No file path provided'}), 400
rules_path = data.get('rules_path') or None
rules_string = data.get('rules_string') or None
result = _get_re().yara_scan(file_path, rules_path=rules_path, rules_string=rules_string)
return jsonify(result)
@reverse_eng_bp.route('/yara/rules')
@login_required
def yara_rules():
"""List available YARA rule files."""
rules = _get_re().list_yara_rules()
return jsonify({'rules': rules, 'total': len(rules)})
# ==================== PACKER ====================
@reverse_eng_bp.route('/packer', methods=['POST'])
@login_required
def packer():
"""Detect packer in binary."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
if not file_path:
return jsonify({'error': 'No file path provided'}), 400
result = _get_re().detect_packer(file_path)
return jsonify(result)
# ==================== COMPARE ====================
@reverse_eng_bp.route('/compare', methods=['POST'])
@login_required
def compare():
"""Compare two binaries."""
data = request.get_json(silent=True) or {}
file1 = data.get('file1', '').strip()
file2 = data.get('file2', '').strip()
if not file1 or not file2:
return jsonify({'error': 'Two file paths required'}), 400
result = _get_re().compare_binaries(file1, file2)
return jsonify(result)
# ==================== DECOMPILE ====================
@reverse_eng_bp.route('/decompile', methods=['POST'])
@login_required
def decompile():
"""Decompile binary with Ghidra headless."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
if not file_path:
return jsonify({'error': 'No file path provided'}), 400
function = data.get('function') or None
result = _get_re().ghidra_decompile(file_path, function=function)
return jsonify(result)
# ==================== PE / ELF PARSING ====================
@reverse_eng_bp.route('/pe', methods=['POST'])
@login_required
def parse_pe():
"""Parse PE headers."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
if not file_path:
return jsonify({'error': 'No file path provided'}), 400
result = _get_re().parse_pe(file_path)
return jsonify(result)
@reverse_eng_bp.route('/elf', methods=['POST'])
@login_required
def parse_elf():
"""Parse ELF headers."""
data = request.get_json(silent=True) or {}
file_path = data.get('file', '').strip()
if not file_path:
return jsonify({'error': 'No file path provided'}), 400
result = _get_re().parse_elf(file_path)
return jsonify(result)