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.
Files changed (48) hide show
  1. atlas/__init__.py +3 -0
  2. atlas/agents/__init__.py +8 -0
  3. atlas/agents/command_space.py +227 -0
  4. atlas/analysis/__init__.py +3 -0
  5. atlas/analysis/binary_agent.py +488 -0
  6. atlas/analysis/binary_fuzz.py +389 -0
  7. atlas/analysis/frida_live.py +261 -0
  8. atlas/analysis/graph_annotator.py +147 -0
  9. atlas/analysis/spectrida_bridge.py +84 -0
  10. atlas/analysis/unicorn_harness.py +337 -0
  11. atlas/core/__init__.py +14 -0
  12. atlas/core/decoder.py +65 -0
  13. atlas/core/dynamics.py +217 -0
  14. atlas/core/encoder.py +120 -0
  15. atlas/core/surprise.py +145 -0
  16. atlas/core/world_model.py +334 -0
  17. atlas/environments/__init__.py +5 -0
  18. atlas/environments/base.py +51 -0
  19. atlas/environments/grid_world.py +219 -0
  20. atlas/environments/physics_2d.py +283 -0
  21. atlas/environments/vm_world.py +168 -0
  22. atlas/knowledge/__init__.py +3 -0
  23. atlas/knowledge/instruction_vocab.py +534 -0
  24. atlas/monitor/__init__.py +5 -0
  25. atlas/monitor/execution_monitor.py +518 -0
  26. atlas/optimization/__init__.py +6 -0
  27. atlas/optimization/speed.py +457 -0
  28. atlas/planning/__init__.py +4 -0
  29. atlas/planning/goal.py +100 -0
  30. atlas/planning/mcts.py +228 -0
  31. atlas/training/__init__.py +4 -0
  32. atlas/training/continual.py +392 -0
  33. atlas/training/growth.py +213 -0
  34. atlas/training/loop.py +306 -0
  35. atlas/training/losses.py +101 -0
  36. atlas/training/self_train.py +307 -0
  37. atlas/utils/__init__.py +4 -0
  38. atlas/utils/logging.py +33 -0
  39. atlas/utils/math_helpers.py +30 -0
  40. atlas/utils/viz.py +136 -0
  41. atlas/vm/__init__.py +4 -0
  42. atlas/vm/wsl_vm.py +249 -0
  43. phantomrt-0.1.0.dist-info/METADATA +75 -0
  44. phantomrt-0.1.0.dist-info/RECORD +48 -0
  45. phantomrt-0.1.0.dist-info/WHEEL +5 -0
  46. phantomrt-0.1.0.dist-info/entry_points.txt +3 -0
  47. phantomrt-0.1.0.dist-info/licenses/LICENSE +21 -0
  48. 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"]