Feuille de chauffage du phare
Aperçu général
Lighthouse est un plugin de couverture de code pour désassembleurs qui visualise les données de couverture de code directement dans IDA Pro et Ghidra. Il est particulièrement utile pour corréler les résultats flous avec le démontage, identifier les chemins de code découverts et guider les efforts de recherche sur la sécurité.
C'est-à-dire Caractéristiques principales: visualisation en temps réel de la couverture, support de format de couverture multiple, analyse de couverture différentielle, intégration floue et exploration interactive de la couverture au sein des démonteurs.
Installation et configuration
Installation IDA Pro
# Clone Lighthouse repository
git clone https://github.com/gaasedelen/lighthouse.git
cd lighthouse
# Install Python dependencies
pip install -r requirements.txt
# For IDA Pro 7.x
cp -r lighthouse/ "$IDADIR/plugins/"
cp lighthouse_plugin.py "$IDADIR/plugins/"
# For IDA Pro 8.x
cp -r lighthouse/ "$IDADIR/plugins/"
cp lighthouse_plugin.py "$IDADIR/plugins/"
# Verify installation
# Start IDA Pro and check Edit -> Plugins -> Lighthouse
# Alternative: Install via pip (if available)
pip install lighthouse-ida
# Manual installation verification
python -c "
import sys
sys.path.append('/path/to/ida/python')
import lighthouse
print('Lighthouse installed successfully')
"
Installation de Ghidra
# Clone Lighthouse repository
git clone https://github.com/gaasedelen/lighthouse.git
cd lighthouse
# Build Ghidra extension
cd ghidra_scripts
# Copy scripts to Ghidra script directory
cp *.py "$GHIDRA_INSTALL_DIR/Ghidra/Features/Python/ghidra_scripts/"
# Or use Ghidra's script manager
# 1. Open Ghidra
# 2. Window -> Script Manager
# 3. Script Directories -> Add
# 4. Select lighthouse/ghidra_scripts directory
# Install Python dependencies for Ghidra
pip install coverage
pip install pyqt5 # For GUI components
# Verify installation in Ghidra
# Window -> Script Manager -> Search for "lighthouse"
```_
### Configuration des sources de données de couverture
```bash
# Install coverage tools for different formats
# Intel Pin (for DynamoRIO coverage)
wget https://software.intel.com/sites/landingpage/pintool/downloads/pin-3.21-98484-ge7cd811fd-gcc-linux.tar.gz
tar -xzf pin-3.21-98484-ge7cd811fd-gcc-linux.tar.gz
export PIN_ROOT=/path/to/pin
# DynamoRIO
git clone https://github.com/DynamoRIO/dynamorio.git
cd dynamorio
mkdir build && cd build
cmake ..
make -j$(nproc)
# AFL++ for fuzzing coverage
git clone https://github.com/AFLplusplus/AFLplusplus.git
cd AFLplusplus
make
sudo make install
# Frida for dynamic instrumentation
pip install frida-tools
npm install frida
# GCOV for compile-time coverage
# Already available with GCC
gcc --coverage source.c -o binary
# LLVM coverage
clang -fprofile-instr-generate -fcoverage-mapping source.c -o binary
# Verify coverage tools
which afl-fuzz
which frida
which gcov
which llvm-profdata
```_
### Configuration et configuration
```python
# Lighthouse configuration file (~/.lighthouse/config.json)
{
"coverage_formats": {
"drcov": {
"enabled": true,
"color": "#FF6B6B"
},
"intel_pin": {
"enabled": true,
"color": "#4ECDC4"
},
"frida": {
"enabled": true,
"color": "#45B7D1"
},
"lighthouse": {
"enabled": true,
"color": "#96CEB4"
}
},
"ui_settings": {
"auto_load": true,
"show_coverage_percentage": true,
"highlight_uncovered": true,
"coverage_threshold": 0.8
},
"performance": {
"max_coverage_files": 100,
"cache_coverage_data": true,
"async_loading": true
}
}
Analyse de la couverture de base
Chargement des données de couverture
# IDA Pro - Load coverage data
import lighthouse
# Load single coverage file
lighthouse.load_coverage("coverage.drcov")
# Load multiple coverage files
coverage_files = [
"run1.drcov",
"run2.drcov",
"run3.drcov"
]
for coverage_file in coverage_files:
lighthouse.load_coverage(coverage_file)
# Load coverage directory
lighthouse.load_coverage_directory("/path/to/coverage/files/")
# Load with specific format
lighthouse.load_coverage("coverage.log", format="intel_pin")
# Load Frida coverage
lighthouse.load_coverage("frida_trace.json", format="frida")
# Load GCOV coverage
lighthouse.load_coverage("coverage.gcov", format="gcov")
Visualisation de la couverture
# Enable coverage visualization
lighthouse.show_coverage()
# Hide coverage visualization
lighthouse.hide_coverage()
# Toggle coverage display
lighthouse.toggle_coverage()
# Set coverage colors
lighthouse.set_coverage_color(0xFF6B6B) # Red for covered
lighthouse.set_uncovered_color(0x808080) # Gray for uncovered
# Highlight specific coverage
lighthouse.highlight_coverage("run1.drcov")
# Show coverage statistics
stats = lighthouse.get_coverage_stats()
print(f"Total functions: {stats['total_functions']}")
print(f"Covered functions: {stats['covered_functions']}")
print(f"Coverage percentage: {stats['coverage_percentage']:.2f}%")
# Navigate to uncovered code
lighthouse.goto_next_uncovered()
lighthouse.goto_previous_uncovered()
# Find uncovered functions
uncovered_functions = lighthouse.get_uncovered_functions()
for func in uncovered_functions:
print(f"Uncovered function: {func['name']} at 0x{func['address']:x}")
Comparaison de la couverture
# Compare two coverage runs
lighthouse.compare_coverage("baseline.drcov", "new_run.drcov")
# Differential coverage analysis
diff_coverage = lighthouse.differential_coverage([
"run1.drcov",
"run2.drcov",
"run3.drcov"
])
# Show only new coverage
lighthouse.show_new_coverage_only()
# Show coverage intersection
lighthouse.show_coverage_intersection()
# Show coverage union
lighthouse.show_coverage_union()
# Export coverage comparison
lighthouse.export_coverage_diff("coverage_diff.json")
# Generate coverage report
report = lighthouse.generate_coverage_report()
with open("coverage_report.html", "w") as f:
f.write(report)
Analyse de couverture avancée
Intégration floue
# AFL++ integration
import subprocess
import time
import os
class AFLLighthouseIntegration:
def __init__(self, target_binary, afl_output_dir):
self.target_binary = target_binary
self.afl_output_dir = afl_output_dir
self.coverage_files = []
def start_afl_fuzzing(self, input_dir, timeout=3600):
"""Start AFL fuzzing with coverage collection"""
# Prepare AFL command
afl_cmd = [
"afl-fuzz",
"-i", input_dir,
"-o", self.afl_output_dir,
"-t", "1000", # Timeout in ms
"-m", "none", # No memory limit
"--", self.target_binary, "@@"
]
print(f"Starting AFL fuzzing: {' '.join(afl_cmd)}")
# Start AFL in background
afl_process = subprocess.Popen(afl_cmd)
# Monitor for new test cases and collect coverage
start_time = time.time()
while time.time() - start_time < timeout:
self.collect_afl_coverage()
time.sleep(60) # Check every minute
# Stop AFL
afl_process.terminate()
return self.coverage_files
def collect_afl_coverage(self):
"""Collect coverage from AFL test cases"""
queue_dir = os.path.join(self.afl_output_dir, "default", "queue")
if not os.path.exists(queue_dir):
return
# Get new test cases
test_cases = [f for f in os.listdir(queue_dir) if f.startswith("id:")]
for test_case in test_cases:
test_path = os.path.join(queue_dir, test_case)
coverage_file = f"coverage_{test_case}.drcov"
if coverage_file not in self.coverage_files:
# Run test case with coverage collection
self.run_with_coverage(test_path, coverage_file)
self.coverage_files.append(coverage_file)
def run_with_coverage(self, input_file, coverage_file):
"""Run target with coverage collection using DynamoRIO"""
drrun_cmd = [
"drrun",
"-t", "drcov",
"-dump_text",
"-logdir", ".",
"-logprefix", coverage_file.replace(".drcov", ""),
"--", self.target_binary, input_file
]
try:
subprocess.run(drrun_cmd, timeout=10, capture_output=True)
except subprocess.TimeoutExpired:
pass # Timeout is expected for some test cases
# Usage example
afl_integration = AFLLighthouseIntegration("./target_binary", "./afl_output")
coverage_files = afl_integration.start_afl_fuzzing("./input_seeds", timeout=1800)
# Load coverage in Lighthouse
for coverage_file in coverage_files:
lighthouse.load_coverage(coverage_file)
Collecte de la couverture personnalisée
# Custom coverage collector using Frida
import frida
import json
class FridaCoverageCollector:
def __init__(self, target_process):
self.target_process = target_process
self.session = None
self.script = None
self.coverage_data = []
def attach_to_process(self):
"""Attach Frida to target process"""
try:
self.session = frida.attach(self.target_process)
print(f"Attached to process: {self.target_process}")
except frida.ProcessNotFoundError:
print(f"Process {self.target_process} not found")
return False
return True
def start_coverage_collection(self, module_name=None):
"""Start collecting coverage data"""
# Frida script for coverage collection
frida_script = """
var coverage_data = [];
var module_base = null;
var module_size = 0;
// Get module information
if ("%s") {
var module = Process.getModuleByName("%s");
module_base = module.base;
module_size = module.size;
console.log("Monitoring module: " + module.name + " at " + module_base);
} else {
// Monitor main module
var modules = Process.enumerateModules();
if (modules.length > 0) {
module_base = modules[0].base;
module_size = modules[0].size;
console.log("Monitoring main module: " + modules[0].name);
}
}
// Hook instruction execution
Interceptor.attach(module_base, {
onEnter: function(args) {
var address = this.context.pc;
if (address >= module_base && address < module_base.add(module_size)) {
coverage_data.push({
address: address.toString(),
timestamp: Date.now()
});
}
}
});
// Export coverage data
rpc.exports.getCoverageData = function() {
return coverage_data;
};
rpc.exports.clearCoverageData = function() {
coverage_data = [];
};
""" % (module_name or "", module_name or "")
self.script = self.session.create_script(frida_script)
self.script.load()
print("Coverage collection started")
def get_coverage_data(self):
"""Get collected coverage data"""
if self.script:
return self.script.exports.get_coverage_data()
return []
def save_coverage(self, output_file):
"""Save coverage data to file"""
coverage_data = self.get_coverage_data()
# Convert to Lighthouse format
lighthouse_format = {
"version": "1.0",
"type": "frida",
"coverage": []
}
for entry in coverage_data:
lighthouse_format["coverage"].append({
"address": entry["address"],
"hit_count": 1,
"timestamp": entry["timestamp"]
})
with open(output_file, "w") as f:
json.dump(lighthouse_format, f, indent=2)
print(f"Coverage saved to: {output_file}")
def detach(self):
"""Detach from process"""
if self.session:
self.session.detach()
print("Detached from process")
# Usage example
collector = FridaCoverageCollector("target_process")
if collector.attach_to_process():
collector.start_coverage_collection("main_module.exe")
# Let it run for some time
time.sleep(30)
# Save coverage
collector.save_coverage("frida_coverage.json")
collector.detach()
# Load in Lighthouse
lighthouse.load_coverage("frida_coverage.json", format="frida")
Analyse de couverture
# Coverage-guided vulnerability research
class CoverageGuidedAnalysis:
def __init__(self, binary_path):
self.binary_path = binary_path
self.coverage_data = {}
self.interesting_functions = []
self.uncovered_paths = []
def analyze_coverage_gaps(self):
"""Analyze coverage gaps to find interesting code paths"""
# Get all functions in binary
all_functions = lighthouse.get_all_functions()
covered_functions = lighthouse.get_covered_functions()
# Find uncovered functions
uncovered_functions = []
for func in all_functions:
if func not in covered_functions:
uncovered_functions.append(func)
# Analyze uncovered functions for interesting patterns
for func in uncovered_functions:
func_analysis = self.analyze_function(func)
if func_analysis["interesting"]:
self.interesting_functions.append({
"function": func,
"reason": func_analysis["reason"],
"priority": func_analysis["priority"]
})
# Sort by priority
self.interesting_functions.sort(key=lambda x: x["priority"], reverse=True)
return self.interesting_functions
def analyze_function(self, function):
"""Analyze function for interesting characteristics"""
analysis = {
"interesting": False,
"reason": [],
"priority": 0
}
# Get function disassembly
disasm = lighthouse.get_function_disassembly(function)
# Check for interesting patterns
if "strcpy" in disasm or "sprintf" in disasm:
analysis["interesting"] = True
analysis["reason"].append("Potentially unsafe string operations")
analysis["priority"] += 30
if "malloc" in disasm or "free" in disasm:
analysis["interesting"] = True
analysis["reason"].append("Memory management operations")
analysis["priority"] += 20
if "system" in disasm or "exec" in disasm:
analysis["interesting"] = True
analysis["reason"].append("System command execution")
analysis["priority"] += 40
if "crypto" in disasm.lower() or "encrypt" in disasm.lower():
analysis["interesting"] = True
analysis["reason"].append("Cryptographic operations")
analysis["priority"] += 25
# Check for error handling paths
if "error" in disasm.lower() or "exception" in disasm.lower():
analysis["interesting"] = True
analysis["reason"].append("Error handling code")
analysis["priority"] += 15
# Check for network operations
if "socket" in disasm or "connect" in disasm or "send" in disasm:
analysis["interesting"] = True
analysis["reason"].append("Network operations")
analysis["priority"] += 35
return analysis
def generate_test_cases(self, target_function):
"""Generate test cases to reach uncovered function"""
# Analyze function parameters and calling conventions
func_info = lighthouse.get_function_info(target_function)
# Generate inputs based on function signature
test_cases = []
if func_info["parameters"]:
for param_type in func_info["parameters"]:
if param_type == "string":
test_cases.extend([
"A" * 10,
"A" * 100,
"A" * 1000,
"../../../etc/passwd",
"%s%s%s%s",
"\x00\x01\x02\x03"
])
elif param_type == "integer":
test_cases.extend([
0, 1, -1, 0x7FFFFFFF, 0x80000000,
0xFFFFFFFF, 0x100000000
])
return test_cases
def guided_fuzzing_campaign(self):
"""Run coverage-guided fuzzing campaign"""
# Analyze current coverage gaps
interesting_functions = self.analyze_coverage_gaps()
print(f"Found {len(interesting_functions)} interesting uncovered functions")
for func_info in interesting_functions[:10]: # Top 10 priority
func = func_info["function"]
print(f"Targeting function: {func['name']} (Priority: {func_info['priority']})")
# Generate test cases for this function
test_cases = self.generate_test_cases(func)
# Run fuzzing campaign targeting this function
self.run_targeted_fuzzing(func, test_cases)
# Check if we achieved coverage
if lighthouse.is_function_covered(func):
print(f"Successfully covered function: {func['name']}")
else:
print(f"Failed to cover function: {func['name']}")
def run_targeted_fuzzing(self, target_function, test_cases):
"""Run fuzzing campaign targeting specific function"""
# Create input files for test cases
input_dir = f"inputs_{target_function['name']}"
os.makedirs(input_dir, exist_ok=True)
for i, test_case in enumerate(test_cases):
input_file = os.path.join(input_dir, f"input_{i:04d}")
with open(input_file, "wb") as f:
if isinstance(test_case, str):
f.write(test_case.encode())
else:
f.write(bytes([test_case]))
# Run AFL with coverage feedback
afl_cmd = [
"afl-fuzz",
"-i", input_dir,
"-o", f"output_{target_function['name']}",
"-t", "1000",
"-m", "none",
"--", self.binary_path, "@@"
]
# Run for limited time
process = subprocess.Popen(afl_cmd)
time.sleep(300) # 5 minutes
process.terminate()
# Collect and load new coverage
self.collect_campaign_coverage(f"output_{target_function['name']}")
# Usage example
analysis = CoverageGuidedAnalysis("./target_binary")
analysis.guided_fuzzing_campaign()
Rapports de couverture et mesures
Rapports complets de couverture
# Generate detailed coverage reports
class LighthouseCoverageReporter:
def __init__(self):
self.coverage_data = {}
self.metrics = {}
def generate_comprehensive_report(self, output_file="coverage_report.html"):
"""Generate comprehensive HTML coverage report"""
# Collect coverage metrics
self.collect_coverage_metrics()
# Generate HTML report
html_content = self.create_html_report()
with open(output_file, "w") as f:
f.write(html_content)
print(f"Coverage report generated: {output_file}")
def collect_coverage_metrics(self):
"""Collect comprehensive coverage metrics"""
# Basic coverage statistics
self.metrics["basic"] = lighthouse.get_coverage_stats()
# Function-level coverage
all_functions = lighthouse.get_all_functions()
covered_functions = lighthouse.get_covered_functions()
self.metrics["functions"] = {
"total": len(all_functions),
"covered": len(covered_functions),
"uncovered": len(all_functions) - len(covered_functions),
"coverage_percentage": (len(covered_functions) / len(all_functions)) * 100
}
# Basic block coverage
all_blocks = lighthouse.get_all_basic_blocks()
covered_blocks = lighthouse.get_covered_basic_blocks()
self.metrics["basic_blocks"] = {
"total": len(all_blocks),
"covered": len(covered_blocks),
"uncovered": len(all_blocks) - len(covered_blocks),
"coverage_percentage": (len(covered_blocks) / len(all_blocks)) * 100
}
# Instruction coverage
all_instructions = lighthouse.get_all_instructions()
covered_instructions = lighthouse.get_covered_instructions()
self.metrics["instructions"] = {
"total": len(all_instructions),
"covered": len(covered_instructions),
"uncovered": len(all_instructions) - len(covered_instructions),
"coverage_percentage": (len(covered_instructions) / len(all_instructions)) * 100
}
# Coverage by module
self.metrics["modules"] = {}
modules = lighthouse.get_modules()
for module in modules:
module_coverage = lighthouse.get_module_coverage(module)
self.metrics["modules"][module["name"]] = module_coverage
# Coverage hotspots (most frequently hit)
self.metrics["hotspots"] = lighthouse.get_coverage_hotspots(limit=20)
# Coverage timeline
self.metrics["timeline"] = lighthouse.get_coverage_timeline()
def create_html_report(self):
"""Create HTML coverage report"""
html_template = """
<!DOCTYPE html>
<html>
<head>
<title>Lighthouse Coverage Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background-color: #f0f0f0; padding: 20px; border-radius: 5px; }
.metric-box { display: inline-block; margin: 10px; padding: 15px;
border: 1px solid #ddd; border-radius: 5px; min-width: 150px; }
.covered { background-color: #d4edda; }
.uncovered { background-color: #f8d7da; }
.partial { background-color: #fff3cd; }
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.progress-bar { width: 100%; height: 20px; background-color: #f0f0f0; border-radius: 10px; }
.progress-fill { height: 100%; background-color: #28a745; border-radius: 10px; }
.chart { margin: 20px 0; }
</style>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="header">
<h1>Lighthouse Coverage Report</h1>
<p>Generated on: {timestamp}</p>
<p>Binary: {binary_name}</p>
</div>
<h2>Coverage Summary</h2>
<div class="metric-box covered">
<h3>Function Coverage</h3>
<div class="progress-bar">
<div class="progress-fill" style="width: {function_coverage}%"></div>
</div>
<p>{covered_functions}/{total_functions} functions ({function_coverage:.1f}%)</p>
</div>
<div class="metric-box covered">
<h3>Basic Block Coverage</h3>
<div class="progress-bar">
<div class="progress-fill" style="width: {block_coverage}%"></div>
</div>
<p>{covered_blocks}/{total_blocks} blocks ({block_coverage:.1f}%)</p>
</div>
<div class="metric-box covered">
<h3>Instruction Coverage</h3>
<div class="progress-bar">
<div class="progress-fill" style="width: {instruction_coverage}%"></div>
</div>
<p>{covered_instructions}/{total_instructions} instructions ({instruction_coverage:.1f}%)</p>
</div>
<h2>Module Coverage</h2>
<table>
<tr>
<th>Module</th>
<th>Functions</th>
<th>Coverage</th>
<th>Progress</th>
</tr>
{module_rows}
</table>
<h2>Coverage Hotspots</h2>
<table>
<tr>
<th>Address</th>
<th>Function</th>
<th>Hit Count</th>
<th>Percentage</th>
</tr>
{hotspot_rows}
</table>
<h2>Uncovered Functions</h2>
<table>
<tr>
<th>Address</th>
<th>Function Name</th>
<th>Size</th>
<th>Complexity</th>
</tr>
{uncovered_rows}
</table>
<h2>Coverage Timeline</h2>
<div class="chart">
<canvas id="timelineChart" width="800" height="400"></canvas>
</div>
<script>
// Coverage timeline chart
var ctx = document.getElementById('timelineChart').getContext('2d');
var chart = new Chart(ctx, {{
type: 'line',
data: {{
labels: {timeline_labels},
datasets: [{{
label: 'Coverage Percentage',
data: {timeline_data},
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}}]
}},
options: {{
responsive: true,
scales: {{
y: {{
beginAtZero: true,
max: 100
}}
}}
}}
}});
</script>
</body>
</html>
"""
# Format template with metrics
return html_template.format(
timestamp=time.strftime("%Y-%m-%d %H:%M:%S"),
binary_name=lighthouse.get_binary_name(),
function_coverage=self.metrics["functions"]["coverage_percentage"],
covered_functions=self.metrics["functions"]["covered"],
total_functions=self.metrics["functions"]["total"],
block_coverage=self.metrics["basic_blocks"]["coverage_percentage"],
covered_blocks=self.metrics["basic_blocks"]["covered"],
total_blocks=self.metrics["basic_blocks"]["total"],
instruction_coverage=self.metrics["instructions"]["coverage_percentage"],
covered_instructions=self.metrics["instructions"]["covered"],
total_instructions=self.metrics["instructions"]["total"],
module_rows=self.generate_module_rows(),
hotspot_rows=self.generate_hotspot_rows(),
uncovered_rows=self.generate_uncovered_rows(),
timeline_labels=json.dumps([t["timestamp"] for t in self.metrics["timeline"]]),
timeline_data=json.dumps([t["coverage"] for t in self.metrics["timeline"]])
)
def generate_module_rows(self):
"""Generate HTML rows for module coverage table"""
rows = []
for module_name, coverage in self.metrics["modules"].items():
progress_width = coverage["coverage_percentage"]
row = f"""
<tr>
<td>{module_name}</td>
<td>{coverage["covered"]}/{coverage["total"]}</td>
<td>{coverage["coverage_percentage"]:.1f}%</td>
<td>
<div class="progress-bar">
<div class="progress-fill" style="width: {progress_width}%"></div>
</div>
</td>
</tr>
"""
rows.append(row)
return "".join(rows)
def generate_hotspot_rows(self):
"""Generate HTML rows for coverage hotspots"""
rows = []
for hotspot in self.metrics["hotspots"]:
row = f"""
<tr>
<td>0x{hotspot["address"]:x}</td>
<td>{hotspot["function_name"]}</td>
<td>{hotspot["hit_count"]}</td>
<td>{hotspot["percentage"]:.2f}%</td>
</tr>
"""
rows.append(row)
return "".join(rows)
def generate_uncovered_rows(self):
"""Generate HTML rows for uncovered functions"""
uncovered_functions = lighthouse.get_uncovered_functions()
rows = []
for func in uncovered_functions[:50]: # Limit to top 50
row = f"""
<tr>
<td>0x{func["address"]:x}</td>
<td>{func["name"]}</td>
<td>{func["size"]} bytes</td>
<td>{func["complexity"]}</td>
</tr>
"""
rows.append(row)
return "".join(rows)
def export_coverage_data(self, format="json"):
"""Export coverage data in various formats"""
if format == "json":
output_file = "coverage_data.json"
with open(output_file, "w") as f:
json.dump(self.metrics, f, indent=2)
elif format == "csv":
import csv
# Export function coverage
with open("function_coverage.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["Address", "Function", "Covered", "Hit Count"])
for func in lighthouse.get_all_functions():
coverage_info = lighthouse.get_function_coverage(func)
writer.writerow([
f"0x{func['address']:x}",
func["name"],
coverage_info["covered"],
coverage_info["hit_count"]
])
elif format == "xml":
import xml.etree.ElementTree as ET
root = ET.Element("coverage_report")
# Add summary
summary = ET.SubElement(root, "summary")
for key, value in self.metrics["functions"].items():
elem = ET.SubElement(summary, key)
elem.text = str(value)
# Add functions
functions = ET.SubElement(root, "functions")
for func in lighthouse.get_all_functions():
func_elem = ET.SubElement(functions, "function")
func_elem.set("address", f"0x{func['address']:x}")
func_elem.set("name", func["name"])
coverage_info = lighthouse.get_function_coverage(func)
func_elem.set("covered", str(coverage_info["covered"]))
func_elem.set("hit_count", str(coverage_info["hit_count"]))
tree = ET.ElementTree(root)
tree.write("coverage_data.xml")
print(f"Coverage data exported to {format} format")
# Usage example
reporter = LighthouseCoverageReporter()
reporter.generate_comprehensive_report("detailed_coverage_report.html")
reporter.export_coverage_data("json")
reporter.export_coverage_data("csv")
Intégration avec CI/CD
Analyse automatisée de la couverture
#!/bin/bash
# CI/CD integration script for Lighthouse coverage analysis
set -e
BINARY_PATH="$1"
COVERAGE_DIR="$2"
OUTPUT_DIR="$3"
THRESHOLD="$4"
if [ -z "$BINARY_PATH" ] || [ -z "$COVERAGE_DIR" ] || [ -z "$OUTPUT_DIR" ]; then
echo "Usage: $0 <binary_path> <coverage_dir> <output_dir> [threshold]"
exit 1
fi
THRESHOLD=${THRESHOLD:-80} # Default 80% coverage threshold
echo "Starting automated coverage analysis..."
echo "Binary: $BINARY_PATH"
echo "Coverage directory: $COVERAGE_DIR"
echo "Output directory: $OUTPUT_DIR"
echo "Coverage threshold: $THRESHOLD%"
mkdir -p "$OUTPUT_DIR"
# Generate coverage report
python3 << EOF
import sys
sys.path.append('/path/to/lighthouse')
import lighthouse
# Load binary in headless mode
lighthouse.load_binary("$BINARY_PATH")
# Load all coverage files
import os
coverage_files = []
for root, dirs, files in os.walk("$COVERAGE_DIR"):
for file in files:
if file.endswith(('.drcov', '.cov', '.gcov')):
coverage_files.append(os.path.join(root, file))
print(f"Found {len(coverage_files)} coverage files")
for coverage_file in coverage_files:
try:
lighthouse.load_coverage(coverage_file)
print(f"Loaded: {coverage_file}")
except Exception as e:
print(f"Failed to load {coverage_file}: {e}")
# Generate metrics
stats = lighthouse.get_coverage_stats()
function_coverage = stats['function_coverage_percentage']
block_coverage = stats['block_coverage_percentage']
print(f"Function coverage: {function_coverage:.2f}%")
print(f"Block coverage: {block_coverage:.2f}%")
# Generate reports
lighthouse.export_coverage_report("$OUTPUT_DIR/coverage_report.html")
lighthouse.export_coverage_data("$OUTPUT_DIR/coverage_data.json")
# Check threshold
if function_coverage < $THRESHOLD:
print(f"ERROR: Coverage {function_coverage:.2f}% below threshold {$THRESHOLD}%")
sys.exit(1)
else:
print(f"SUCCESS: Coverage {function_coverage:.2f}% meets threshold {$THRESHOLD}%")
# Generate uncovered functions list
uncovered = lighthouse.get_uncovered_functions()
with open("$OUTPUT_DIR/uncovered_functions.txt", "w") as f:
for func in uncovered:
f.write(f"0x{func['address']:x} {func['name']}\n")
print(f"Found {len(uncovered)} uncovered functions")
EOF
# Generate summary for CI
cat > "$OUTPUT_DIR/coverage_summary.txt" << EOF
Coverage Analysis Summary
========================
Date: $(date)
Binary: $BINARY_PATH
Coverage Files: $(find "$COVERAGE_DIR" -name "*.drcov" -o -name "*.cov" -o -name "*.gcov" | wc -l)
Threshold: $THRESHOLD%
Results:
- Function Coverage: $(grep "Function coverage:" "$OUTPUT_DIR/coverage_report.html" | sed 's/.*: //' | sed 's/%.*//')%
- Block Coverage: $(grep "Block coverage:" "$OUTPUT_DIR/coverage_report.html" | sed 's/.*: //' | sed 's/%.*//')%
- Uncovered Functions: $(wc -l < "$OUTPUT_DIR/uncovered_functions.txt")
Status: $(if [ $? -eq 0 ]; then echo "PASS"; else echo "FAIL"; fi)
EOF
echo "Coverage analysis complete!"
echo "Results saved to: $OUTPUT_DIR"
Intégration des actions GitHub
# .github/workflows/coverage-analysis.yml
name: Coverage Analysis with Lighthouse
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
coverage-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake
# Install WABT for WASM analysis
sudo apt-get install wabt
# Install DynamoRIO for coverage collection
wget https://github.com/DynamoRIO/dynamorio/releases/download/release_9.0.1/DynamoRIO-Linux-9.0.1.tar.gz
tar -xzf DynamoRIO-Linux-9.0.1.tar.gz
export DYNAMORIO_HOME=$PWD/DynamoRIO-Linux-9.0.1
# Install Lighthouse
pip install lighthouse-ida
# Install AFL++ for fuzzing
git clone https://github.com/AFLplusplus/AFLplusplus.git
cd AFLplusplus
make
sudo make install
cd ..
- name: Build target binary
run: |
gcc -g -O0 --coverage src/main.c -o target_binary
- name: Run fuzzing campaign
run: |
mkdir -p input_seeds
echo "test input" > input_seeds/seed1
echo "another test" > input_seeds/seed2
# Run AFL for limited time
timeout 300 afl-fuzz -i input_seeds -o afl_output -t 1000 -m none -- ./target_binary @@ || true
- name: Collect coverage data
run: |
mkdir -p coverage_data
# Collect AFL coverage
find afl_output -name "id:*" | head -20 | while read testcase; do
coverage_file="coverage_data/$(basename "$testcase").drcov"
$DYNAMORIO_HOME/bin64/drrun -t drcov -dump_text -logdir coverage_data -- ./target_binary "$testcase"
done
# Collect GCOV coverage
gcov src/main.c
mv *.gcov coverage_data/
- name: Analyze coverage with Lighthouse
run: |
python3 scripts/lighthouse_analysis.py ./target_binary coverage_data coverage_results 75
- name: Upload coverage results
uses: actions/upload-artifact@v3
with:
name: coverage-results
path: coverage_results/
- name: Comment PR with coverage
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const summary = fs.readFileSync('coverage_results/coverage_summary.txt', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## Coverage Analysis Results\n\n\`\`\`\n${summary}\n\`\`\``
});
- name: Fail if coverage below threshold
run: |
if grep -q "FAIL" coverage_results/coverage_summary.txt; then
echo "Coverage analysis failed!"
exit 1
fi
Ressources et documentation
Ressources officielles
- Lighthouse GitHub Repository - Dépôt principal et documentation
- [Lighthouse Wiki] (LINK_15) - Guide d'utilisation complet
- IDA Pro Plugin Development - Documentation de l'API Python de l'IDA
- Ghidra Scripting - Guide de script Ghidra
Intégration des outils de couverture
- Dynamorio - Plateforme d'instrumentation binaire dynamique
- [Intel Pin] (LINK_15) - Cadre d'instrumentation binaire
- AFL++ - Cadre de flou avancé
- Frida - Trousse d'instruments dynamiques
Recherche et communications
- Code Coverage in Reverse Engineering - Recherche universitaire sur la couverture-guided RE
- Fuzzing with Code Coverage - Méthode de flou AFL
- [Analyse binaire avec couverture] (LINK_15) - Document du foreur sur l'analyse guidée de la couverture
Ressources communautaires
- [Groupe d'utilisateurs du phare] (LINK_15) - Discussions communautaires
- [Renverser l'échange d'emplacements techniques] (LINK_15) - Questions et réponses pour les sujets de RE
- r/ReversEngineering - Communauté Reddit
- [Binary Analysis Discord] (LINK_15) - Discussion communautaire en temps réel