phantomrt 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- atlas/__init__.py +3 -0
- atlas/agents/__init__.py +8 -0
- atlas/agents/command_space.py +227 -0
- atlas/analysis/__init__.py +3 -0
- atlas/analysis/binary_agent.py +488 -0
- atlas/analysis/binary_fuzz.py +389 -0
- atlas/analysis/frida_live.py +261 -0
- atlas/analysis/graph_annotator.py +147 -0
- atlas/analysis/spectrida_bridge.py +84 -0
- atlas/analysis/unicorn_harness.py +337 -0
- atlas/core/__init__.py +14 -0
- atlas/core/decoder.py +65 -0
- atlas/core/dynamics.py +217 -0
- atlas/core/encoder.py +120 -0
- atlas/core/surprise.py +145 -0
- atlas/core/world_model.py +334 -0
- atlas/environments/__init__.py +5 -0
- atlas/environments/base.py +51 -0
- atlas/environments/grid_world.py +219 -0
- atlas/environments/physics_2d.py +283 -0
- atlas/environments/vm_world.py +168 -0
- atlas/knowledge/__init__.py +3 -0
- atlas/knowledge/instruction_vocab.py +534 -0
- atlas/monitor/__init__.py +5 -0
- atlas/monitor/execution_monitor.py +518 -0
- atlas/optimization/__init__.py +6 -0
- atlas/optimization/speed.py +457 -0
- atlas/planning/__init__.py +4 -0
- atlas/planning/goal.py +100 -0
- atlas/planning/mcts.py +228 -0
- atlas/training/__init__.py +4 -0
- atlas/training/continual.py +392 -0
- atlas/training/growth.py +213 -0
- atlas/training/loop.py +306 -0
- atlas/training/losses.py +101 -0
- atlas/training/self_train.py +307 -0
- atlas/utils/__init__.py +4 -0
- atlas/utils/logging.py +33 -0
- atlas/utils/math_helpers.py +30 -0
- atlas/utils/viz.py +136 -0
- atlas/vm/__init__.py +4 -0
- atlas/vm/wsl_vm.py +249 -0
- phantomrt-0.1.0.dist-info/METADATA +75 -0
- phantomrt-0.1.0.dist-info/RECORD +48 -0
- phantomrt-0.1.0.dist-info/WHEEL +5 -0
- phantomrt-0.1.0.dist-info/entry_points.txt +3 -0
- phantomrt-0.1.0.dist-info/licenses/LICENSE +21 -0
- phantomrt-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Binary Analysis Agent — The Vulnerability Hunter
|
|
3
|
+
|
|
4
|
+
Ties together:
|
|
5
|
+
1. World Model (understands execution patterns)
|
|
6
|
+
2. Execution Monitor (sees everything the binary does)
|
|
7
|
+
3. Knowledge Base (knows what instructions mean)
|
|
8
|
+
4. Planner (figures out what inputs to try next)
|
|
9
|
+
|
|
10
|
+
This is the complete AI that:
|
|
11
|
+
- Reads binary execution
|
|
12
|
+
- Builds understanding of how it works
|
|
13
|
+
- Predicts where vulnerabilities are
|
|
14
|
+
- Plans inputs to test its predictions
|
|
15
|
+
- Learns from every binary it analyzes
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import torch
|
|
19
|
+
import torch.nn as nn
|
|
20
|
+
import numpy as np
|
|
21
|
+
import random
|
|
22
|
+
import json
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Optional
|
|
25
|
+
from collections import defaultdict
|
|
26
|
+
|
|
27
|
+
from ..core.world_model import WorldModel
|
|
28
|
+
from ..monitor.execution_monitor import ExecutionMonitor, ExecutionTrace, analyze_trace
|
|
29
|
+
from ..knowledge.instruction_vocab import BinaryKnowledgeBase, InstructionEncoder
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BinaryAnalysisAgent:
|
|
33
|
+
"""
|
|
34
|
+
Complete binary analysis agent.
|
|
35
|
+
|
|
36
|
+
Workflow:
|
|
37
|
+
1. Load binary into sandbox
|
|
38
|
+
2. Feed it inputs
|
|
39
|
+
3. Watch execution via monitor
|
|
40
|
+
4. Encode execution into world model
|
|
41
|
+
5. Build understanding of binary behavior
|
|
42
|
+
6. Predict vulnerabilities
|
|
43
|
+
7. Plan inputs to test predictions
|
|
44
|
+
8. Learn from results
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
world_model: Optional[WorldModel] = None,
|
|
50
|
+
latent_dim: int = 256,
|
|
51
|
+
device: str = "cpu",
|
|
52
|
+
):
|
|
53
|
+
self.device = device
|
|
54
|
+
self.latent_dim = latent_dim
|
|
55
|
+
|
|
56
|
+
# Components
|
|
57
|
+
self.monitor = ExecutionMonitor()
|
|
58
|
+
self.knowledge = BinaryKnowledgeBase()
|
|
59
|
+
self.instruction_encoder = InstructionEncoder(output_dim=128)
|
|
60
|
+
|
|
61
|
+
# World model for understanding execution
|
|
62
|
+
if world_model is None:
|
|
63
|
+
# Execution trace features → latent state
|
|
64
|
+
# Input: encoded instruction sequence + execution stats
|
|
65
|
+
trace_feature_dim = 128 + 20 # instruction embedding + stats
|
|
66
|
+
self.world_model = WorldModel(
|
|
67
|
+
obs_dim=trace_feature_dim,
|
|
68
|
+
action_dim=8, # input modification actions
|
|
69
|
+
latent_dim=latent_dim,
|
|
70
|
+
hidden_dim=256,
|
|
71
|
+
).to(device)
|
|
72
|
+
else:
|
|
73
|
+
self.world_model = world_model.to(device)
|
|
74
|
+
|
|
75
|
+
# Learning state
|
|
76
|
+
self.experience_count = 0
|
|
77
|
+
self.known_patterns = defaultdict(int)
|
|
78
|
+
self.confirmed_vulnerabilities = []
|
|
79
|
+
self.false_positives = []
|
|
80
|
+
self.trace_history = []
|
|
81
|
+
|
|
82
|
+
# Confidence tracking
|
|
83
|
+
self.pattern_confidence = defaultdict(float)
|
|
84
|
+
|
|
85
|
+
# Binary state tracking
|
|
86
|
+
self.current_binary = None
|
|
87
|
+
self.binary_understanding = {}
|
|
88
|
+
|
|
89
|
+
def load_binary(self, binary_path: str) -> dict:
|
|
90
|
+
"""
|
|
91
|
+
Load and analyze a binary file.
|
|
92
|
+
|
|
93
|
+
Returns initial understanding of the binary.
|
|
94
|
+
"""
|
|
95
|
+
self.current_binary = binary_path
|
|
96
|
+
path = Path(binary_path)
|
|
97
|
+
|
|
98
|
+
if not path.exists():
|
|
99
|
+
return {"error": f"Binary not found: {binary_path}"}
|
|
100
|
+
|
|
101
|
+
# Read binary
|
|
102
|
+
data = path.read_bytes()
|
|
103
|
+
|
|
104
|
+
# Basic analysis
|
|
105
|
+
info = {
|
|
106
|
+
"path": str(path),
|
|
107
|
+
"size": len(data),
|
|
108
|
+
"format": self._detect_format(data),
|
|
109
|
+
"architecture": self._detect_architecture(data),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Disassemble entry point for initial understanding
|
|
113
|
+
info["entry_analysis"] = self._analyze_entry_point(data)
|
|
114
|
+
|
|
115
|
+
# Store understanding
|
|
116
|
+
self.binary_understanding = {
|
|
117
|
+
"info": info,
|
|
118
|
+
"traces": [],
|
|
119
|
+
"vulnerabilities": [],
|
|
120
|
+
"risk_assessment": 0.0,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return info
|
|
124
|
+
|
|
125
|
+
def analyze_with_input(self, input_data: bytes, num_variations: int = 5) -> dict:
|
|
126
|
+
"""
|
|
127
|
+
Analyze binary with given input and variations.
|
|
128
|
+
|
|
129
|
+
This is the core learning loop:
|
|
130
|
+
1. Execute with input
|
|
131
|
+
2. Monitor execution
|
|
132
|
+
3. Feed to world model
|
|
133
|
+
4. Detect anomalies
|
|
134
|
+
5. Learn patterns
|
|
135
|
+
"""
|
|
136
|
+
results = {
|
|
137
|
+
"input": input_data.hex(),
|
|
138
|
+
"traces": [],
|
|
139
|
+
"anomalies": [],
|
|
140
|
+
"new_patterns": [],
|
|
141
|
+
"confidence_updates": [],
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Execute with original input
|
|
145
|
+
trace = self.monitor.trace_execution(
|
|
146
|
+
self.current_binary, input_data
|
|
147
|
+
)
|
|
148
|
+
results["traces"].append(trace.summary())
|
|
149
|
+
|
|
150
|
+
# Analyze the trace
|
|
151
|
+
analysis = analyze_trace(trace)
|
|
152
|
+
results["analysis"] = analysis
|
|
153
|
+
|
|
154
|
+
# Feed to world model
|
|
155
|
+
world_model_output = self._process_trace(trace)
|
|
156
|
+
results["world_model"] = {
|
|
157
|
+
"surprise_score": world_model_output["surprise_score"],
|
|
158
|
+
"is_surprising": world_model_output["is_surprising"],
|
|
159
|
+
"latent_state_norm": world_model_output["latent_state_norm"],
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Track patterns
|
|
163
|
+
self._update_patterns(trace, analysis)
|
|
164
|
+
|
|
165
|
+
# Generate variations and test
|
|
166
|
+
variations = self._generate_variations(input_data, num_variations)
|
|
167
|
+
for var_input in variations:
|
|
168
|
+
var_trace = self.monitor.trace_execution(
|
|
169
|
+
self.current_binary, var_input
|
|
170
|
+
)
|
|
171
|
+
var_analysis = analyze_trace(var_trace)
|
|
172
|
+
|
|
173
|
+
if var_analysis["vulnerability_indicators"]:
|
|
174
|
+
results["anomalies"].extend(var_analysis["vulnerability_indicators"])
|
|
175
|
+
|
|
176
|
+
# Check if this variation confirms or denies a pattern
|
|
177
|
+
self._check_pattern_consistency(trace, var_trace, var_analysis)
|
|
178
|
+
|
|
179
|
+
# Update confidence scores
|
|
180
|
+
results["confidence_updates"] = self._update_confidence()
|
|
181
|
+
|
|
182
|
+
# Store experience
|
|
183
|
+
self.trace_history.append({
|
|
184
|
+
"input": input_data,
|
|
185
|
+
"trace": trace,
|
|
186
|
+
"analysis": analysis,
|
|
187
|
+
})
|
|
188
|
+
self.experience_count += 1
|
|
189
|
+
|
|
190
|
+
return results
|
|
191
|
+
|
|
192
|
+
def predict_vulnerabilities(self) -> list:
|
|
193
|
+
"""
|
|
194
|
+
Based on accumulated understanding, predict where
|
|
195
|
+
vulnerabilities might exist in the current binary.
|
|
196
|
+
|
|
197
|
+
Uses world model's latent state to reason about patterns.
|
|
198
|
+
"""
|
|
199
|
+
if not self.trace_history:
|
|
200
|
+
return [{"error": "No analysis history. Run analyze_with_input first."}]
|
|
201
|
+
|
|
202
|
+
predictions = []
|
|
203
|
+
|
|
204
|
+
# Get recent traces for pattern analysis
|
|
205
|
+
recent_traces = self.trace_history[-100:]
|
|
206
|
+
|
|
207
|
+
# Analyze patterns across traces
|
|
208
|
+
pattern_freq = defaultdict(int)
|
|
209
|
+
risk_areas = defaultdict(float)
|
|
210
|
+
|
|
211
|
+
for entry in recent_traces:
|
|
212
|
+
trace = entry["trace"]
|
|
213
|
+
analysis = entry["analysis"]
|
|
214
|
+
|
|
215
|
+
# Track instruction patterns
|
|
216
|
+
for inst in trace.instructions:
|
|
217
|
+
pattern = f"{inst.mnemonic}_{inst.operands[:20]}"
|
|
218
|
+
pattern_freq[pattern] += 1
|
|
219
|
+
|
|
220
|
+
# Risk assessment
|
|
221
|
+
risk = self.knowledge.assess_risk(inst.mnemonic, inst.operands)
|
|
222
|
+
risk_areas[inst.address] += risk
|
|
223
|
+
|
|
224
|
+
# Find high-risk areas
|
|
225
|
+
for addr, risk in sorted(risk_areas.items(), key=lambda x: -x[1]):
|
|
226
|
+
if risk > 1.0: # threshold
|
|
227
|
+
# Check confidence
|
|
228
|
+
pattern_key = f"addr_{addr}"
|
|
229
|
+
confidence = self.pattern_confidence.get(pattern_key, 0.0)
|
|
230
|
+
|
|
231
|
+
predictions.append({
|
|
232
|
+
"address": hex(addr),
|
|
233
|
+
"risk_score": min(risk / 10, 1.0),
|
|
234
|
+
"confidence": confidence,
|
|
235
|
+
"pattern": "high_risk_instruction_cluster",
|
|
236
|
+
"reasoning": f"Address {hex(addr)} has accumulated risk score {risk:.2f} "
|
|
237
|
+
f"across {len(recent_traces)} traces",
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
# Check knowledge base patterns
|
|
241
|
+
for entry in recent_traces[-10:]: # last 10 traces
|
|
242
|
+
pattern_matches = self.knowledge.get_pattern_matches(entry["trace"].instructions)
|
|
243
|
+
for match in pattern_matches:
|
|
244
|
+
predictions.append({
|
|
245
|
+
"pattern": match["pattern"],
|
|
246
|
+
"severity": match["severity"],
|
|
247
|
+
"signature": match["signature"],
|
|
248
|
+
"confidence": self.pattern_confidence.get(match["pattern"], 0.0),
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
return predictions
|
|
252
|
+
|
|
253
|
+
def generate_smart_input(self) -> bytes:
|
|
254
|
+
"""
|
|
255
|
+
Generate an input designed to test predictions
|
|
256
|
+
or explore unknown areas of the binary.
|
|
257
|
+
|
|
258
|
+
Uses the world model to imagine which inputs
|
|
259
|
+
would be most informative.
|
|
260
|
+
"""
|
|
261
|
+
if not self.trace_history:
|
|
262
|
+
# Cold start: generate random inputs
|
|
263
|
+
return self._random_input()
|
|
264
|
+
|
|
265
|
+
# Get predictions
|
|
266
|
+
predictions = self.predict_vulnerabilities()
|
|
267
|
+
|
|
268
|
+
# Focus on high-risk, low-confidence areas
|
|
269
|
+
uncertain = [p for p in predictions if p.get("confidence", 0) < 0.7]
|
|
270
|
+
|
|
271
|
+
if uncertain:
|
|
272
|
+
# Generate input targeting uncertain areas
|
|
273
|
+
return self._target_input(uncertain)
|
|
274
|
+
|
|
275
|
+
# Explore new areas
|
|
276
|
+
return self._exploration_input()
|
|
277
|
+
|
|
278
|
+
def _process_trace(self, trace: ExecutionTrace) -> dict:
|
|
279
|
+
"""Process an execution trace through the world model."""
|
|
280
|
+
# Encode instructions
|
|
281
|
+
if trace.instructions:
|
|
282
|
+
encoded = self.instruction_encoder.encode_trace([
|
|
283
|
+
{"mnemonic": inst.mnemonic, "operands": inst.operands}
|
|
284
|
+
for inst in trace.instructions[:100]
|
|
285
|
+
])
|
|
286
|
+
else:
|
|
287
|
+
encoded = torch.zeros(1, 128)
|
|
288
|
+
|
|
289
|
+
# Add summary features
|
|
290
|
+
summary = trace.summary()
|
|
291
|
+
stats = torch.tensor([
|
|
292
|
+
summary["total_instructions"] / 10000.0,
|
|
293
|
+
summary["total_memory_reads"] / 1000.0,
|
|
294
|
+
summary["total_memory_writes"] / 1000.0,
|
|
295
|
+
summary["stack_depth_max"] / 10000.0,
|
|
296
|
+
float(summary["crash"]),
|
|
297
|
+
summary["branch_count"] / 1000.0,
|
|
298
|
+
summary["syscall_count"] / 100.0,
|
|
299
|
+
float(summary.get("unique_mnemonics", {}).get("call", 0)) / 10.0,
|
|
300
|
+
float(summary.get("unique_mnemonics", {}).get("ret", 0)) / 10.0,
|
|
301
|
+
float(summary.get("unique_mnemonics", {}).get("jmp", 0)) / 10.0,
|
|
302
|
+
])
|
|
303
|
+
|
|
304
|
+
# Combine: take mean of instruction embeddings + stats
|
|
305
|
+
inst_embedding = encoded.mean(dim=0) # [128]
|
|
306
|
+
features = torch.cat([inst_embedding, stats]) # [138]
|
|
307
|
+
|
|
308
|
+
# Pad to match world model obs_dim
|
|
309
|
+
target_dim = self.world_model.obs_dim
|
|
310
|
+
if features.shape[0] < target_dim:
|
|
311
|
+
features = torch.cat([features, torch.zeros(target_dim - features.shape[0])])
|
|
312
|
+
elif features.shape[0] > target_dim:
|
|
313
|
+
features = features[:target_dim]
|
|
314
|
+
|
|
315
|
+
features = features.unsqueeze(0).to(self.device)
|
|
316
|
+
|
|
317
|
+
# Forward through world model
|
|
318
|
+
with torch.no_grad():
|
|
319
|
+
output = self.world_model(features)
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
"surprise_score": output.surprise_score,
|
|
323
|
+
"is_surprising": output.is_surprising,
|
|
324
|
+
"latent_state_norm": output.latent_state.norm().item(),
|
|
325
|
+
"reconstruction_error": output.reconstruction_loss.item(),
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
def _update_patterns(self, trace: ExecutionTrace, analysis: dict):
|
|
329
|
+
"""Track and update pattern knowledge."""
|
|
330
|
+
# Track instruction sequences
|
|
331
|
+
for i in range(len(trace.instructions) - 2):
|
|
332
|
+
pattern = (
|
|
333
|
+
trace.instructions[i].mnemonic,
|
|
334
|
+
trace.instructions[i+1].mnemonic,
|
|
335
|
+
trace.instructions[i+2].mnemonic,
|
|
336
|
+
)
|
|
337
|
+
self.known_patterns[pattern] += 1
|
|
338
|
+
|
|
339
|
+
# Track vulnerability indicators
|
|
340
|
+
for indicator in analysis.get("vulnerability_indicators", []):
|
|
341
|
+
key = f"{indicator['type']}_{indicator.get('address', 0)}"
|
|
342
|
+
self.known_patterns[key] += 1
|
|
343
|
+
|
|
344
|
+
def _check_pattern_consistency(self, original_trace, variation_trace, variation_analysis):
|
|
345
|
+
"""Check if patterns are consistent across variations."""
|
|
346
|
+
# If original had a pattern and variation has it too, it's likely real
|
|
347
|
+
# If only one had it, might be a fluke
|
|
348
|
+
|
|
349
|
+
for indicator in variation_analysis.get("vulnerability_indicators", []):
|
|
350
|
+
key = indicator["type"]
|
|
351
|
+
|
|
352
|
+
# Check if this pattern appeared in original trace
|
|
353
|
+
original_analysis = analyze_trace(original_trace)
|
|
354
|
+
original_has = any(i["type"] == key for i in original_analysis.get("vulnerability_indicators", []))
|
|
355
|
+
|
|
356
|
+
if original_has:
|
|
357
|
+
# Confirmed pattern! Increase confidence
|
|
358
|
+
self.pattern_confidence[key] = min(
|
|
359
|
+
self.pattern_confidence.get(key, 0.0) + 0.1,
|
|
360
|
+
1.0
|
|
361
|
+
)
|
|
362
|
+
else:
|
|
363
|
+
# Might be variation-specific, don't increase confidence
|
|
364
|
+
pass
|
|
365
|
+
|
|
366
|
+
def _update_confidence(self) -> list:
|
|
367
|
+
"""Update confidence scores based on accumulated evidence."""
|
|
368
|
+
updates = []
|
|
369
|
+
|
|
370
|
+
for pattern, count in self.known_patterns.items():
|
|
371
|
+
old_conf = self.pattern_confidence.get(pattern, 0.0)
|
|
372
|
+
|
|
373
|
+
# Confidence increases with repeated observations
|
|
374
|
+
new_conf = min(1.0 - (1.0 - old_conf) * 0.9, 1.0)
|
|
375
|
+
|
|
376
|
+
self.pattern_confidence[pattern] = new_conf
|
|
377
|
+
|
|
378
|
+
if abs(new_conf - old_conf) > 0.01:
|
|
379
|
+
updates.append({
|
|
380
|
+
"pattern": str(pattern),
|
|
381
|
+
"old_confidence": old_conf,
|
|
382
|
+
"new_confidence": new_conf,
|
|
383
|
+
"observation_count": count,
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
return updates
|
|
387
|
+
|
|
388
|
+
def _generate_variations(self, input_data: bytes, num_variations: int) -> list:
|
|
389
|
+
"""Generate input variations for testing."""
|
|
390
|
+
variations = []
|
|
391
|
+
|
|
392
|
+
for _ in range(num_variations):
|
|
393
|
+
var = bytearray(input_data)
|
|
394
|
+
|
|
395
|
+
# Random mutation
|
|
396
|
+
if len(var) > 0:
|
|
397
|
+
idx = random.randint(0, len(var) - 1)
|
|
398
|
+
var[idx] = random.randint(0, 255)
|
|
399
|
+
|
|
400
|
+
variations.append(bytes(var))
|
|
401
|
+
|
|
402
|
+
return variations
|
|
403
|
+
|
|
404
|
+
def _random_input(self) -> bytes:
|
|
405
|
+
"""Generate a random input."""
|
|
406
|
+
size = random.randint(1, 256)
|
|
407
|
+
return bytes(random.randint(0, 255) for _ in range(size))
|
|
408
|
+
|
|
409
|
+
def _target_input(self, predictions: list) -> bytes:
|
|
410
|
+
"""Generate input targeting predicted vulnerability areas."""
|
|
411
|
+
# Start with common vulnerability-triggering patterns
|
|
412
|
+
patterns = [
|
|
413
|
+
b"A" * 64, # buffer overflow attempt
|
|
414
|
+
b"%s%s%s%s%s", # format string attempt
|
|
415
|
+
b"\x00" * 32, # null bytes
|
|
416
|
+
b"\xff" * 64, # max values
|
|
417
|
+
b"A" * 32 + b"B" * 32, # boundary test
|
|
418
|
+
b"A" * 256, # large buffer
|
|
419
|
+
]
|
|
420
|
+
|
|
421
|
+
return random.choice(patterns)
|
|
422
|
+
|
|
423
|
+
def _exploration_input(self) -> bytes:
|
|
424
|
+
"""Generate input to explore unknown behavior."""
|
|
425
|
+
size = random.randint(1, 128)
|
|
426
|
+
data = bytes(random.randint(0, 255) for _ in range(size))
|
|
427
|
+
return data
|
|
428
|
+
|
|
429
|
+
def _detect_format(self, data: bytes) -> str:
|
|
430
|
+
"""Detect binary format."""
|
|
431
|
+
if data[:2] == b"MZ":
|
|
432
|
+
return "PE (Windows)"
|
|
433
|
+
elif data[:4] == b"\x7fELF":
|
|
434
|
+
return "ELF (Linux)"
|
|
435
|
+
elif data[:4] == b"\xfe\xed\xfa\xce" or data[:4] == b"\xfe\xed\xfa\xcf":
|
|
436
|
+
return "Mach-O (macOS)"
|
|
437
|
+
elif data[:2] == b"#!":
|
|
438
|
+
return "Script"
|
|
439
|
+
else:
|
|
440
|
+
return "Unknown"
|
|
441
|
+
|
|
442
|
+
def _detect_architecture(self, data: bytes) -> str:
|
|
443
|
+
"""Detect architecture."""
|
|
444
|
+
if data[:4] == b"\x7fELF":
|
|
445
|
+
if len(data) > 4 and data[4] == 1:
|
|
446
|
+
return "x86 (32-bit)"
|
|
447
|
+
elif len(data) > 4 and data[4] == 2:
|
|
448
|
+
return "x86-64 (64-bit)"
|
|
449
|
+
return "Unknown"
|
|
450
|
+
|
|
451
|
+
def _analyze_entry_point(self, data: bytes) -> dict:
|
|
452
|
+
"""Basic entry point analysis."""
|
|
453
|
+
return {
|
|
454
|
+
"size": len(data),
|
|
455
|
+
"first_bytes": data[:16].hex(),
|
|
456
|
+
"printable_ratio": sum(32 <= b <= 126 for b in data[:1000]) / min(len(data), 1000),
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
def get_stats(self) -> dict:
|
|
460
|
+
"""Get analysis statistics."""
|
|
461
|
+
return {
|
|
462
|
+
"total_analyses": self.experience_count,
|
|
463
|
+
"known_patterns": len(self.known_patterns),
|
|
464
|
+
"confirmed_vulnerabilities": len(self.confirmed_vulnerabilities),
|
|
465
|
+
"false_positives": len(self.false_positives),
|
|
466
|
+
"avg_confidence": (
|
|
467
|
+
sum(self.pattern_confidence.values()) / len(self.pattern_confidence)
|
|
468
|
+
if self.pattern_confidence else 0.0
|
|
469
|
+
),
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
def save_state(self, path: str):
|
|
473
|
+
"""Save agent state for continuation."""
|
|
474
|
+
state = {
|
|
475
|
+
"known_patterns": dict(self.known_patterns),
|
|
476
|
+
"pattern_confidence": dict(self.pattern_confidence),
|
|
477
|
+
"confirmed_vulnerabilities": self.confirmed_vulnerabilities,
|
|
478
|
+
"experience_count": self.experience_count,
|
|
479
|
+
}
|
|
480
|
+
Path(path).write_text(json.dumps(state, indent=2, default=str))
|
|
481
|
+
|
|
482
|
+
def load_state(self, path: str):
|
|
483
|
+
"""Load agent state."""
|
|
484
|
+
state = json.loads(Path(path).read_text())
|
|
485
|
+
self.known_patterns = defaultdict(int, state["known_patterns"])
|
|
486
|
+
self.pattern_confidence = defaultdict(float, state["pattern_confidence"])
|
|
487
|
+
self.confirmed_vulnerabilities = state["confirmed_vulnerabilities"]
|
|
488
|
+
self.experience_count = state["experience_count"]
|