rea-agents 5.0.0 → 6.0.0

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 (275) hide show
  1. package/README.md +157 -650
  2. package/bridge/mitmproxy/capture.py +210 -0
  3. package/bridge/native/rea_lldb_tracer.py +303 -0
  4. package/bridge/process/ProcessRunTokenReader.swift +263 -0
  5. package/bridge/process/ProcessRunTokenReaderMain.swift +35 -0
  6. package/bridge/pwndbg/core_context.py +101 -0
  7. package/bridge/pwndbg/launch.py +39 -0
  8. package/bridge/pwndbg/launch_impl.py +34 -0
  9. package/bridge/pwntools/decoder_runtime.py +59 -0
  10. package/bridge/pwntools/layout.py +39 -0
  11. package/bridge/pwntools/layout_impl.py +309 -0
  12. package/bridge/pwntools/recorded_crash.py +39 -0
  13. package/bridge/pwntools/recorded_crash_impl.py +143 -0
  14. package/dist/android/JadxProvider.js +1 -0
  15. package/dist/application/CapabilityInventory.js +21 -0
  16. package/dist/application/DirectAnalysis.js +97 -23
  17. package/dist/application/EvidenceBundleFiles.js +1 -1
  18. package/dist/application/ReconstructionObligationCandidates.js +5 -1
  19. package/dist/application/WebNetworkCapturePort.js +1 -0
  20. package/dist/application/WebNetworkCaptureService.js +132 -0
  21. package/dist/application/WindowsCapabilities.js +1 -1
  22. package/dist/application/binary/AnalysisSnapshotCache.js +49 -6
  23. package/dist/application/binary/BinarySession.js +38 -10
  24. package/dist/application/binary/BinarySessionOpen.js +16 -3
  25. package/dist/application/binary/BinarySessionRecords.js +20 -0
  26. package/dist/application/binaryDiagnostics/BinaryLayoutPort.js +1 -0
  27. package/dist/application/binaryDiagnostics/BinaryLayoutService.js +57 -0
  28. package/dist/application/binaryDiagnostics/RecordedCrashPort.js +1 -0
  29. package/dist/application/binaryDiagnostics/RecordedCrashService.js +60 -0
  30. package/dist/application/evm/EvmInterfacePort.js +1 -0
  31. package/dist/application/evm/EvmInterfaceService.js +62 -0
  32. package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -2
  33. package/dist/application/javascript/JavaScriptApplicationService.js +15 -2
  34. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +42 -23
  35. package/dist/application/javascript/JavaScriptArtifactGraphDocuments.js +26 -2
  36. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +74 -24
  37. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +23 -10
  38. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +7 -7
  39. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +145 -70
  40. package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +14 -15
  41. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +41 -1
  42. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +7 -7
  43. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +5 -9
  44. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +3 -3
  45. package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +118 -12
  46. package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +3 -3
  47. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +5 -5
  48. package/dist/application/{ProcessCli.js → process/ProcessCli.js} +10 -10
  49. package/dist/application/{ProcessEvidence.js → process/ProcessEvidence.js} +4 -4
  50. package/dist/artifacts/ArtifactProvider.js +30 -3
  51. package/dist/artifacts/ArtifactProviderMetadata.js +2 -1
  52. package/dist/artifacts/ZipArtifactReader.js +3 -0
  53. package/dist/artifacts/apple/DylibResolutionReader.js +259 -0
  54. package/dist/artifacts/apple/MachoLoadCommandReader.js +294 -0
  55. package/dist/{application → artifacts/extraction}/ArtifactExtraction.js +10 -10
  56. package/dist/{application → artifacts/inventory}/ArtifactGraphConstruction.js +3 -3
  57. package/dist/{application → artifacts/inventory}/ArtifactInventory.js +4 -4
  58. package/dist/{application/ArtifactInventory → artifacts/inventory}/classify.js +2 -2
  59. package/dist/{application/ArtifactInventory → artifacts/inventory}/reader.js +5 -5
  60. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanCanonical.js +3 -3
  61. package/dist/{application/ArtifactInventory → artifacts/inventory}/scanReader.js +5 -5
  62. package/dist/artifacts/readStableArtifact.js +20 -4
  63. package/dist/browser/CdpCaptureEventHandlers.js +90 -2
  64. package/dist/browser/CdpCaptureEvents.js +9 -0
  65. package/dist/browser/PlaywrightElectronActiveProvider.js +12 -3
  66. package/dist/browser/history/CaptureFailures.js +101 -0
  67. package/dist/browser/history/CaptureFormatError.js +10 -0
  68. package/dist/browser/history/CaptureRedaction.js +65 -0
  69. package/dist/browser/history/CaptureRelease.js +12 -0
  70. package/dist/browser/history/HarCapture.js +165 -0
  71. package/dist/browser/history/HarCaptureAdapter.js +20 -0
  72. package/dist/browser/history/HarCaptureProcess.js +57 -0
  73. package/dist/browser/history/HarJson.js +91 -0
  74. package/dist/browser/history/HarSchema.js +72 -0
  75. package/dist/browser/history/HistoricalCaptureDecoder.js +186 -0
  76. package/dist/browser/history/HistoricalCaptureFormatAdapter.js +1 -0
  77. package/dist/browser/history/MitmproxyCaptureAdapter.js +42 -0
  78. package/dist/cli/artifactCommands.js +26 -1
  79. package/dist/cli/binaryDiagnosticsCommands.js +39 -0
  80. package/dist/cli/coreAnalysisCommands.js +2 -2
  81. package/dist/cli/coreBinaryCommands.js +8 -8
  82. package/dist/cli/electronCommands.js +16 -9
  83. package/dist/cli/evmCommands.js +24 -0
  84. package/dist/cli/firmwareCommands.js +6 -3
  85. package/dist/cli/javascriptApplicationAnalysis.js +16 -3
  86. package/dist/cli/jsonOutput.js +133 -0
  87. package/dist/cli/nativeCallCommands.js +35 -0
  88. package/dist/cli/processCommands.js +1 -1
  89. package/dist/cli/streamedJsonOutput.js +56 -0
  90. package/dist/cli/utilityCommands.js +2 -0
  91. package/dist/cli/webNetworkCaptureCommands.js +40 -0
  92. package/dist/cli.js +9 -3
  93. package/dist/cliBrowserScenarioCommands.js +2 -2
  94. package/dist/cliCommandNames.js +6 -0
  95. package/dist/cliJsonInput.js +30 -1
  96. package/dist/cliOutput.js +21 -6
  97. package/dist/composition/binaryDiagnostics.js +8 -0
  98. package/dist/composition/evm.js +4 -0
  99. package/dist/composition/webNetworkCaptures.js +6 -0
  100. package/dist/contracts/artifactToolContracts.js +6 -0
  101. package/dist/contracts/browserCaptureToolInputSchema.js +43 -0
  102. package/dist/contracts/browserToolContracts.js +4 -3
  103. package/dist/contracts/errorSchemas.js +8 -1
  104. package/dist/contracts/evm/evmToolContracts.js +20 -0
  105. package/dist/contracts/investigationExamples.js +2 -2
  106. package/dist/contracts/managed/managedToolContracts.js +7 -3
  107. package/dist/contracts/native/binaryDiagnosticsToolContracts.js +42 -0
  108. package/dist/contracts/native/nativeToolContracts.js +17 -1
  109. package/dist/contracts/officialToolContracts.js +12 -9
  110. package/dist/contracts/process/processCaptureExample.js +2 -0
  111. package/dist/contracts/sessionLifecycleInputs.js +22 -5
  112. package/dist/contracts/sessionStatusContract.js +10 -2
  113. package/dist/contracts/sessionToolContracts.js +8 -1
  114. package/dist/contracts/sessionToolSchemas.js +10 -3
  115. package/dist/contracts/toolContractExamples.js +1 -1
  116. package/dist/contracts/toolContracts.js +6 -0
  117. package/dist/contracts/toolEffects.js +30 -0
  118. package/dist/contracts/toolOutputSchemaGroups.js +5 -1
  119. package/dist/contracts/toolOutputSchemaPrimitives.js +1 -1
  120. package/dist/contracts/webNetworkCaptureToolContracts.js +24 -0
  121. package/dist/domain/analysisErrorBase.js +15 -0
  122. package/dist/domain/analysisErrorCore.js +59 -5
  123. package/dist/domain/analysisErrorPresentation.js +29 -1
  124. package/dist/domain/analysisErrorProjection.js +52 -2
  125. package/dist/domain/analysisSnapshot.js +166 -2
  126. package/dist/domain/apple/dyldPaths.js +81 -0
  127. package/dist/domain/apple/dylibResolution.js +367 -0
  128. package/dist/domain/apple/dylibResolutionFindings.js +75 -0
  129. package/dist/domain/artifactPathSyntax.js +1 -1
  130. package/dist/domain/browserObservation.js +1 -1
  131. package/dist/domain/browserObservationSchemas.js +13 -0
  132. package/dist/domain/browserScenarioValues.js +9 -2
  133. package/dist/domain/callPathSchemas.js +1 -1
  134. package/dist/domain/canonicalDigest.js +13 -1
  135. package/dist/domain/changedBehavior.js +3 -3
  136. package/dist/domain/comparisonStatus.js +2 -30
  137. package/dist/domain/conformanceTrustGate.js +0 -16
  138. package/dist/domain/evidence.js +17 -5
  139. package/dist/domain/evm/evmInterface.js +63 -0
  140. package/dist/domain/explicitSensitiveFailure.js +92 -0
  141. package/dist/domain/explicitSensitiveValues.js +10 -0
  142. package/dist/domain/firmware/firmwareAnalysis.js +8 -1
  143. package/dist/domain/javascript/electronActiveObservation.js +9 -4
  144. package/dist/domain/javascript/htmlArtifactReferences.js +40 -0
  145. package/dist/domain/javascript/javascriptExportShapeComparison.js +21 -5
  146. package/dist/domain/javascript/javascriptExportShapeVariants.js +0 -10
  147. package/dist/domain/javascript/javascriptSemanticGraph.js +21 -5
  148. package/dist/domain/jsonValue.js +4 -0
  149. package/dist/domain/localPath.js +2 -0
  150. package/dist/domain/native/binaryLayout.js +357 -0
  151. package/dist/domain/native/nativeCallObservation.js +164 -0
  152. package/dist/domain/native/objcSwiftMetadata.js +0 -39
  153. package/dist/domain/native/recordedCrash.js +266 -0
  154. package/dist/domain/native/recordedCrashNoteCoverage.js +73 -0
  155. package/dist/domain/{processCapture.js → process/processCapture.js} +139 -2
  156. package/dist/domain/{processCaptureExample.js → process/processCaptureExample.js} +5 -0
  157. package/dist/domain/process/processCaptureParsing.js +54 -0
  158. package/dist/domain/{processCaptureValidation.js → process/processCaptureValidation.js} +18 -2
  159. package/dist/domain/{processComparison.js → process/processComparison.js} +1 -1
  160. package/dist/domain/{processObservation.js → process/processObservation.js} +1 -1
  161. package/dist/domain/{processTraceSpecification.js → process/processTraceSpecification.js} +1 -1
  162. package/dist/domain/providerSelectionError.js +4 -2
  163. package/dist/domain/reconstructionVerification.js +3 -3
  164. package/dist/domain/reconstructionVerificationSchemas.js +1 -1
  165. package/dist/domain/residualUnknown.js +3 -2
  166. package/dist/domain/staticRuntimeCorrelation.js +3 -3
  167. package/dist/domain/webCaptureDiff.js +1 -0
  168. package/dist/domain/webNetworkCapture.js +118 -0
  169. package/dist/domain/webNetworkCaptureCoordinates.js +58 -0
  170. package/dist/dotnet/ManagedExceptionRegionValidation.js +41 -0
  171. package/dist/dotnet/ManagedMemberInspector.js +4 -2
  172. package/dist/dotnet/ManagedMemberInspectorCore.js +38 -32
  173. package/dist/dotnet/ManagedMemberInstructionDecoder.js +23 -7
  174. package/dist/dotnet/ManagedMemberRows.js +14 -3
  175. package/dist/dotnet/ManagedMetadataHeaps.js +20 -2
  176. package/dist/dotnet/ManagedMetadataInventory.js +38 -28
  177. package/dist/dotnet/ManagedMetadataInventoryRows.js +24 -10
  178. package/dist/dotnet/ManagedMethodBodyReader.js +7 -1
  179. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +15 -3
  180. package/dist/dotnet/ManagedNativeBoundaryInspector.js +3 -1
  181. package/dist/dotnet/ManagedPeReader.js +11 -0
  182. package/dist/evm/EvmBytecodeCarrier.js +21 -0
  183. package/dist/evm/EvmWorkerLimits.js +40 -0
  184. package/dist/evm/EvmoleFailures.js +126 -0
  185. package/dist/evm/EvmoleInterfaceAdapter.js +53 -0
  186. package/dist/evm/EvmoleInterfaceProvider.js +273 -0
  187. package/dist/evm/EvmoleInterfaceWorker.js +87 -0
  188. package/dist/evm/EvmoleRelease.js +16 -0
  189. package/dist/firmware/FirmwareCommand.js +1 -0
  190. package/dist/generatedPackageMetadata.js +2 -2
  191. package/dist/ghidra/GhidraLauncher.js +1 -0
  192. package/dist/ghidra/GhidraProvider.js +2 -2
  193. package/dist/ghidra/GhidraProviderCapabilities.js +1 -1
  194. package/dist/hopper/BridgeLauncher.js +2 -0
  195. package/dist/hopper/LinuxPrivateDisplayProbe.js +4 -2
  196. package/dist/javascript/recovery/WakaruCommand.js +3 -0
  197. package/dist/javascript/sourceMaps/SourceMapCommand.js +1 -0
  198. package/dist/native/CommandRunner.js +2 -1
  199. package/dist/native/LldbCallTracer.js +278 -0
  200. package/dist/native/NativeCallObservation.js +135 -0
  201. package/dist/native/NativeMacOSProvider.js +19 -3
  202. package/dist/native/NativeMacOSProviderMetadata.js +8 -3
  203. package/dist/native/pwndbg/PwndbgCoreContext.js +160 -0
  204. package/dist/native/pwntools/PwntoolsDecoder.js +203 -0
  205. package/dist/native/pwntools/PwntoolsFailures.js +180 -0
  206. package/dist/native/pwntools/PwntoolsLayoutProvider.js +36 -0
  207. package/dist/native/pwntools/PwntoolsRecordedCrashProvider.js +75 -0
  208. package/dist/native/pwntools/PwntoolsRelease.js +19 -0
  209. package/dist/native/pwntools/PwntoolsResourceLimits.js +76 -0
  210. package/dist/native/pwntools/RecordedCrashSourceBindings.js +46 -0
  211. package/dist/native/pwntools/RecordedCrashStructureBindings.js +101 -0
  212. package/dist/process/DarwinProcessRunTokenReader.js +294 -0
  213. package/dist/process/OwnedCommand.js +113 -0
  214. package/dist/process/ProcessOwnership.js +263 -33
  215. package/dist/process/ProcessOwnershipObservation.js +161 -24
  216. package/dist/process/ProviderProcess.js +30 -3
  217. package/dist/{application → process/capture}/ProcessCaptureCapability.js +15 -0
  218. package/dist/{application → process/capture}/ProcessCaptureError.js +17 -1
  219. package/dist/process/capture/ProcessCaptureLifecycle.js +615 -0
  220. package/dist/{application → process/capture}/ProcessHarness.js +195 -29
  221. package/dist/{application → process/capture}/ProcessSampling.js +2 -2
  222. package/dist/server/createServer.js +22 -0
  223. package/dist/server/promptCompletion.js +10 -3
  224. package/dist/server/registerBinaryDiagnosticsTools.js +17 -0
  225. package/dist/server/registerEvidenceTools.js +1 -1
  226. package/dist/server/registerEvmTools.js +17 -0
  227. package/dist/server/registerProcessComparisonTool.js +1 -1
  228. package/dist/server/registerRecordedCrashTools.js +17 -0
  229. package/dist/server/registerSessionTools.js +2 -2
  230. package/dist/server/registerWebNetworkCaptureTool.js +28 -0
  231. package/dist/server/sessionAvailabilityPolicy.js +3 -0
  232. package/dist/server/sessionToolPolicies.js +1 -1
  233. package/dist/server/toolResult.js +1 -1
  234. package/native/windows/build/manifest.json +2 -2
  235. package/native/windows/build/rea-windows-x64.node +0 -0
  236. package/package.json +37 -9
  237. package/scripts/rea.mjs +11 -3
  238. package/skills/reverse-engineer-anything/SKILL.md +23 -3
  239. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +29 -0
  240. package/third_party/README.md +60 -0
  241. package/third_party/evmole/LICENSE +21 -0
  242. package/third_party/evmole/README.md +22 -0
  243. package/third_party/pwndbg/LICENSE.md +21 -0
  244. package/third_party/pwndbg/README.md +23 -0
  245. package/third_party/pwntools/README.md +40 -0
  246. package/dist/application/AuthorizedArtifactInventory.js +0 -7
  247. package/dist/application/ProcessCaptureLifecycle.js +0 -299
  248. package/dist/application/StoreFileLock.js +0 -124
  249. package/dist/contracts/processCaptureExample.js +0 -2
  250. package/dist/domain/bytecodeProvider.js +0 -178
  251. package/dist/domain/customProtocolCapture.js +0 -137
  252. package/dist/domain/eventProcessTree.js +0 -225
  253. package/dist/domain/mobileApplicationInvestigation.js +0 -159
  254. package/dist/domain/packageAnalysis.js +0 -123
  255. package/dist/domain/peInspection.js +0 -197
  256. package/dist/domain/processCaptureParsing.js +0 -21
  257. package/dist/domain/protocolCapture.js +0 -217
  258. package/dist/domain/runtimeIdentification.js +0 -212
  259. package/dist/generatedMcpToolCatalog.js +0 -37
  260. /package/dist/application/{ArtifactExtractionDestination.js → artifacts/ArtifactExtractionDestination.js} +0 -0
  261. /package/dist/{application/ArtifactInventory → artifacts/inventory}/policy.js +0 -0
  262. /package/dist/{application/ArtifactInventory → artifacts/inventory}/types.js +0 -0
  263. /package/dist/domain/{processEvidenceProvider.js → process/processEvidenceProvider.js} +0 -0
  264. /package/dist/domain/{processScenario.js → process/processScenario.js} +0 -0
  265. /package/dist/domain/{processTraceComparison.js → process/processTraceComparison.js} +0 -0
  266. /package/dist/domain/{processTraceDimensionProjection.js → process/processTraceDimensionProjection.js} +0 -0
  267. /package/dist/domain/{processTraceEvaluation.js → process/processTraceEvaluation.js} +0 -0
  268. /package/dist/{application → process/capture}/FilesystemSnapshot.js +0 -0
  269. /package/dist/{application → process/capture}/ProcessCaptureEnvironment.js +0 -0
  270. /package/dist/{application → process/capture}/ProcessCaptureJournal.js +0 -0
  271. /package/dist/{application → process/capture}/ProcessFilesystemEffects.js +0 -0
  272. /package/dist/{application → process/capture}/ProcessNormalization.js +0 -0
  273. /package/dist/{application → process/capture}/ProcessScenarioRuntimeValidation.js +0 -0
  274. /package/dist/{application → process/capture}/ProcessTimer.js +0 -0
  275. /package/dist/{application → process/capture}/TerminalRenderer.js +0 -0
@@ -0,0 +1,263 @@
1
+ import Darwin
2
+ import Foundation
3
+
4
+ struct RunTokenObservation: Encodable {
5
+ let pid: Int32
6
+ let state: String
7
+ let run_id: String?
8
+ let reason: String?
9
+ }
10
+
11
+ struct ProcessIdentityObservation: Encodable {
12
+ let pid: Int32
13
+ let state: String
14
+ let identity: String?
15
+ let reason: String?
16
+ }
17
+
18
+ private struct ProcessMetadata {
19
+ let identity: String
20
+ let pointerSize: Int
21
+ }
22
+
23
+ struct ProcessSnapshot {
24
+ fileprivate let metadata: [Int32: ProcessMetadata]
25
+ let failure: String?
26
+ }
27
+
28
+ typealias ProcessTableSysctlCall = (
29
+ _ mib: inout [Int32],
30
+ _ output: UnsafeMutableRawPointer?,
31
+ _ size: inout Int
32
+ ) -> Int32
33
+
34
+ private func readProcessSnapshot() -> ProcessSnapshot {
35
+ readProcessSnapshot { mib, output, size in
36
+ sysctl(&mib, u_int(mib.count), output, &size, nil, 0)
37
+ }
38
+ }
39
+
40
+ func readProcessSnapshot(
41
+ sysctlCall: ProcessTableSysctlCall
42
+ ) -> ProcessSnapshot {
43
+ for _ in 0..<3 {
44
+ var mib = [CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0]
45
+ var size = 0
46
+ guard sysctlCall(&mib, nil, &size) == 0 else {
47
+ let failure = errno
48
+ return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(failure)")
49
+ }
50
+ guard size >= MemoryLayout<kinfo_proc>.size,
51
+ size % MemoryLayout<kinfo_proc>.stride == 0 else {
52
+ return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(errno)")
53
+ }
54
+ var processes = [kinfo_proc](
55
+ repeating: kinfo_proc(),
56
+ count: size / MemoryLayout<kinfo_proc>.stride
57
+ )
58
+ let result = processes.withUnsafeMutableBufferPointer { buffer in
59
+ let output = buffer.baseAddress.map { UnsafeMutableRawPointer($0) }
60
+ return sysctlCall(&mib, output, &size)
61
+ }
62
+ guard result == 0 else {
63
+ let failure = errno
64
+ if failure == ENOMEM { continue }
65
+ return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(failure)")
66
+ }
67
+ guard size % MemoryLayout<kinfo_proc>.stride == 0 else {
68
+ return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(errno)")
69
+ }
70
+ let processCount = size / MemoryLayout<kinfo_proc>.stride
71
+ let metadata = Dictionary(
72
+ processes.prefix(processCount).map { process in
73
+ let start = process.kp_proc.p_starttime
74
+ return (
75
+ process.kp_proc.p_pid,
76
+ ProcessMetadata(
77
+ identity: "\(start.tv_sec):\(start.tv_usec)",
78
+ pointerSize: process.kp_proc.p_flag & P_LP64 != 0 ? 8 : 4
79
+ )
80
+ )
81
+ },
82
+ uniquingKeysWith: { first, _ in first }
83
+ )
84
+ return ProcessSnapshot(metadata: metadata, failure: nil)
85
+ }
86
+ return ProcessSnapshot(metadata: [:], failure: "process_table_failed_\(ENOMEM)")
87
+ }
88
+
89
+ func readProcessIdentities(pids: [Int32]) -> [ProcessIdentityObservation] {
90
+ let snapshot = readProcessSnapshot()
91
+ return pids.map { pid in
92
+ guard let metadata = snapshot.metadata[pid] else {
93
+ return unavailableIdentity(
94
+ pid: pid,
95
+ reason: snapshot.failure ?? "process_not_in_snapshot"
96
+ )
97
+ }
98
+ return ProcessIdentityObservation(pid: pid, state: "readable", identity: metadata.identity, reason: nil)
99
+ }
100
+ }
101
+
102
+ private func unavailableIdentity(pid: Int32, reason: String) -> ProcessIdentityObservation {
103
+ ProcessIdentityObservation(pid: pid, state: "unavailable", identity: nil, reason: reason)
104
+ }
105
+
106
+ enum RunTokenReadError: Error {
107
+ case invalidBuffer
108
+ case duplicateToken
109
+ case invalidTokenEncoding
110
+ case environmentUnavailable
111
+ case appleVectorUnavailable
112
+ case ambiguousEnvironmentBoundary
113
+ }
114
+
115
+ private func readRunToken(pid: Int32, pointerSize: Int) -> RunTokenObservation {
116
+ var mib = [CTL_KERN, KERN_PROCARGS2, Int32(pid)]
117
+ var size = 0
118
+ guard sysctl(&mib, u_int(mib.count), nil, &size, nil, 0) == 0, size >= MemoryLayout<Int32>.size else {
119
+ return unavailable(pid: pid, reason: "sysctl_failed_\(errno)")
120
+ }
121
+ var bytes = [UInt8](repeating: 0, count: size)
122
+ let result = bytes.withUnsafeMutableBytes { buffer in
123
+ sysctl(&mib, u_int(mib.count), buffer.baseAddress, &size, nil, 0)
124
+ }
125
+ guard result == 0 else {
126
+ return unavailable(pid: pid, reason: "sysctl_failed_\(errno)")
127
+ }
128
+ bytes = Array(bytes.prefix(size))
129
+ do {
130
+ return RunTokenObservation(pid: pid, state: "readable", run_id: try token(in: bytes, pointerSize: pointerSize), reason: nil)
131
+ } catch RunTokenReadError.duplicateToken {
132
+ return unavailable(pid: pid, reason: "duplicate_run_token")
133
+ } catch RunTokenReadError.invalidTokenEncoding {
134
+ return unavailable(pid: pid, reason: "invalid_run_token_encoding")
135
+ } catch RunTokenReadError.environmentUnavailable {
136
+ return unavailable(pid: pid, reason: "environment_unavailable")
137
+ } catch RunTokenReadError.appleVectorUnavailable {
138
+ return unavailable(pid: pid, reason: "apple_vector_unavailable")
139
+ } catch RunTokenReadError.ambiguousEnvironmentBoundary {
140
+ return unavailable(pid: pid, reason: "ambiguous_environment_boundary")
141
+ } catch {
142
+ return unavailable(pid: pid, reason: "malformed_procargs")
143
+ }
144
+ }
145
+
146
+ func readRunTokens(pids: [Int32]) -> [RunTokenObservation] {
147
+ let before = readProcessSnapshot()
148
+ let observations = pids.map { pid -> RunTokenObservation in
149
+ guard let metadata = before.metadata[pid] else {
150
+ return unavailable(pid: pid, reason: before.failure ?? "process_not_in_snapshot")
151
+ }
152
+ return readRunToken(pid: pid, pointerSize: metadata.pointerSize)
153
+ }
154
+ let after = readProcessSnapshot()
155
+ return zip(pids, observations).map { pid, observation in
156
+ guard let initial = before.metadata[pid], let current = after.metadata[pid] else {
157
+ return unavailable(pid: pid, reason: after.failure ?? "process_not_in_snapshot")
158
+ }
159
+ guard initial.identity == current.identity,
160
+ initial.pointerSize == current.pointerSize else {
161
+ return unavailable(pid: pid, reason: "process_identity_changed_during_token_read")
162
+ }
163
+ return observation
164
+ }
165
+ }
166
+
167
+ func token(in bytes: [UInt8], pointerSize: Int = MemoryLayout<UnsafeRawPointer>.size) throws -> String? {
168
+ guard bytes.count >= MemoryLayout<Int32>.size else { throw RunTokenReadError.invalidBuffer }
169
+ let argc = bytes.withUnsafeBytes { $0.loadUnaligned(as: Int32.self).littleEndian }
170
+ guard argc >= 0, Int(argc) <= bytes.count else { throw RunTokenReadError.invalidBuffer }
171
+ var offset = MemoryLayout<Int32>.size
172
+ guard let executablePath = readString(in: bytes, offset: &offset) else {
173
+ throw RunTokenReadError.invalidBuffer
174
+ }
175
+ guard pointerSize == 4 || pointerSize == 8 else { throw RunTokenReadError.invalidBuffer }
176
+ let pathRecordSize = Array("executable_path=".utf8).count + executablePath.count + 1
177
+ let padding = (pointerSize - pathRecordSize % pointerSize) % pointerSize
178
+ guard offset + padding <= bytes.count,
179
+ bytes[offset..<(offset + padding)].allSatisfy({ $0 == 0 }) else {
180
+ throw RunTokenReadError.invalidBuffer
181
+ }
182
+ offset += padding
183
+ for _ in 0..<argc {
184
+ guard readString(in: bytes, offset: &offset, allowEmpty: true) != nil else {
185
+ throw RunTokenReadError.invalidBuffer
186
+ }
187
+ }
188
+ guard bytes.last == 0 else { throw RunTokenReadError.invalidBuffer }
189
+ if offset == bytes.count {
190
+ throw RunTokenReadError.environmentUnavailable
191
+ }
192
+ var trailingEntries: [ArraySlice<UInt8>] = []
193
+ var zeroedAppleVectorStart: Int?
194
+ while offset < bytes.count {
195
+ if bytes[offset] == 0 {
196
+ let start = offset
197
+ while offset < bytes.count, bytes[offset] == 0 {
198
+ offset += 1
199
+ }
200
+ if offset - start > 1 {
201
+ zeroedAppleVectorStart = zeroedAppleVectorStart ?? trailingEntries.count
202
+ }
203
+ continue
204
+ }
205
+ guard let entry = readString(in: bytes, offset: &offset) else {
206
+ throw RunTokenReadError.invalidBuffer
207
+ }
208
+ trailingEntries.append(entry)
209
+ }
210
+ // XNU appends its Apple vector after the environment. libc clears the
211
+ // initial `pfz=` record after startup, leaving a multi-NUL run in its place.
212
+ // A caller can also set `pfz`, so prefer the final intact sentinel when it
213
+ // remains available and otherwise stop at the cleared record.
214
+ let intactAppleVectorStart = trailingEntries.lastIndex(where: {
215
+ $0.starts(with: Array("pfz=".utf8))
216
+ })
217
+ let preferZeroedBoundary = zeroedAppleVectorStart.flatMap { zeroedStart in
218
+ intactAppleVectorStart.map { intactStart in intactStart < zeroedStart } ?? true
219
+ } ?? false
220
+ let appleVectorStart = preferZeroedBoundary
221
+ ? zeroedAppleVectorStart
222
+ : intactAppleVectorStart
223
+ guard let appleVectorStart else {
224
+ throw RunTokenReadError.appleVectorUnavailable
225
+ }
226
+ if trailingEntries[appleVectorStart...].contains(where: {
227
+ $0.starts(with: Array("REA_PROCESS_RUN_ID=".utf8))
228
+ }) {
229
+ throw RunTokenReadError.ambiguousEnvironmentBoundary
230
+ }
231
+ let environmentEntries = trailingEntries[..<appleVectorStart]
232
+ guard !environmentEntries.isEmpty else {
233
+ throw RunTokenReadError.environmentUnavailable
234
+ }
235
+ var found: String?
236
+ for entry in environmentEntries {
237
+ if entry.starts(with: Array("REA_PROCESS_RUN_ID=".utf8)) {
238
+ guard found == nil else { throw RunTokenReadError.duplicateToken }
239
+ let value = entry.dropFirst("REA_PROCESS_RUN_ID=".utf8.count)
240
+ guard let decoded = String(bytes: value, encoding: .utf8) else {
241
+ throw RunTokenReadError.invalidTokenEncoding
242
+ }
243
+ found = decoded
244
+ }
245
+ }
246
+ return found
247
+ }
248
+
249
+ private func readString(
250
+ in bytes: [UInt8],
251
+ offset: inout Int,
252
+ allowEmpty: Bool = false
253
+ ) -> ArraySlice<UInt8>? {
254
+ guard offset < bytes.count, let end = bytes[offset...].firstIndex(of: 0),
255
+ allowEmpty || end > offset else { return nil }
256
+ let string = bytes[offset..<end]
257
+ offset = end + 1
258
+ return string
259
+ }
260
+
261
+ private func unavailable(pid: Int32, reason: String) -> RunTokenObservation {
262
+ RunTokenObservation(pid: pid, state: "unavailable", run_id: nil, reason: reason)
263
+ }
@@ -0,0 +1,35 @@
1
+ import Foundation
2
+
3
+ struct RunTokenResponse: Encodable {
4
+ let results: [RunTokenObservation]
5
+ }
6
+
7
+ struct ProcessIdentityResponse: Encodable {
8
+ let results: [ProcessIdentityObservation]
9
+ }
10
+
11
+ @main
12
+ struct ProcessRunTokenReaderMain {
13
+ static func main() {
14
+ let arguments = Array(CommandLine.arguments.dropFirst())
15
+ let identityMode = arguments.first == "--identities"
16
+ let pidArguments = identityMode ? Array(arguments.dropFirst()) : arguments
17
+ let pids = pidArguments.compactMap(Int32.init)
18
+ guard pids.count == pidArguments.count else {
19
+ fputs("invalid process ID input\n", stderr)
20
+ exit(64)
21
+ }
22
+ do {
23
+ let output: Data
24
+ if identityMode {
25
+ output = try JSONEncoder().encode(ProcessIdentityResponse(results: readProcessIdentities(pids: pids)))
26
+ } else {
27
+ output = try JSONEncoder().encode(RunTokenResponse(results: readRunTokens(pids: pids)))
28
+ }
29
+ FileHandle.standardOutput.write(output)
30
+ } catch {
31
+ fputs("could not encode process token observations\n", stderr)
32
+ exit(70)
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,101 @@
1
+ """Fixed core-only GDB/pwndbg operation; no caller commands or historical PID lookup."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import resource
6
+ import gdb
7
+
8
+ OUTPUT_BYTES = 64 * 1024 * 1024
9
+
10
+
11
+ class ContextFailure(Exception):
12
+ def __init__(self, reason, message):
13
+ super().__init__(message)
14
+ self.reason = reason
15
+
16
+
17
+ def inspect(request):
18
+ if gdb.selected_inferior().connection is not None:
19
+ raise ContextFailure("decoder", "Owned GDB unexpectedly has an inferior connection before core loading.")
20
+ root = Path(os.environ["REA_CORE_CONTEXT_REQUEST"]).parent
21
+ empty = root / "no-libraries"
22
+ empty.mkdir(mode=0o700)
23
+ for command in ["set auto-load off", "set debuginfod enabled off", "set auto-solib-add off", "set history save off", "set pagination off",
24
+ "set sysroot ./no-libraries", "set solib-search-path ./no-libraries"]:
25
+ gdb.execute(command, to_string=True)
26
+ entry = request["gdbinit"]
27
+ try:
28
+ exec(compile(Path(entry).read_bytes(), entry, "exec"), {"__name__": "__main__", "__file__": entry})
29
+ import pwndbg.aglib.vmmap
30
+ import pwndbg.lib.version
31
+ except (ImportError, OSError) as error:
32
+ raise ContextFailure("unavailable", "Selected pwndbg could not initialize: " + str(error)) from error
33
+ version = pwndbg.lib.version.__version__
34
+ if version != "2026.09.15" and not version.startswith("2026.09.15 build: "):
35
+ raise ContextFailure("unsupported", "Expected unchanged pwndbg2026.09.15; selected plugin reports " + version + ".")
36
+ gdb.execute('set context-sections ""', to_string=True)
37
+ # core-file takes a raw filename and retains quote characters. Use a fixed
38
+ # relative filename in the owned cwd so no selected path becomes GDB syntax.
39
+ if Path(request["snapshot_path"]) != root.parent / "object.snapshot":
40
+ raise ContextFailure("decoder", "Core context request did not select the shared owned snapshot.")
41
+ gdb.execute("core-file ../object.snapshot", to_string=True)
42
+ inferior = gdb.selected_inferior()
43
+ if inferior.connection is None or inferior.connection.type != "core" or gdb.current_progspace().filename is not None:
44
+ raise ContextFailure("decoder", "Requested core-only boundary was not established; no mapping query performed.")
45
+ maps = []
46
+ for page in pwndbg.aglib.vmmap.get():
47
+ flags = page.flags
48
+ maps.append({"start": hex(page.start), "end": hex(page.end), "offset": hex(page.offset), "reported_flags": flags,
49
+ "permissions": {"read": bool(flags & 4), "write": bool(flags & 2), "execute": bool(flags & 1)} if 1 <= flags <= 7 else None,
50
+ "pathname_display": page.objfile, "current_file_identity": "unknown"})
51
+ return {"status": "available", "gdb_version": gdb.VERSION, "pwndbg_version": version, "connection": "core", "executable": None,
52
+ "historical_pid": inferior.pid, "reported_limits": {"address_space_bytes": resource.getrlimit(resource.RLIMIT_AS)[0], "cpu_seconds": resource.getrlimit(resource.RLIMIT_CPU)[0], "file_size_bytes": resource.getrlimit(resource.RLIMIT_FSIZE)[0]}, "maps": maps, "confidence": "derived", "completeness": "unknown",
53
+ "diagnostics": {"stdout": "", "stderr": "", "truncated": False}}
54
+
55
+
56
+ def failure_exit(request_path, status, marker):
57
+ try:
58
+ descriptor = os.open(Path(request_path).parent / "resource.failure", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
59
+ os.write(descriptor, marker)
60
+ os.close(descriptor)
61
+ except (OSError, MemoryError):
62
+ pass
63
+ os._exit(status)
64
+
65
+
66
+ def main():
67
+ request_path = os.environ["REA_CORE_CONTEXT_REQUEST"]
68
+ request = json.loads(Path(request_path).read_text(encoding="utf-8"))
69
+ reserve = bytearray(1024 * 1024)
70
+ try:
71
+ reply = {"ok": True, "value": inspect(request)}
72
+ except ContextFailure as error:
73
+ reply = {"ok": False, "reason": error.reason, "message": str(error)}
74
+ except MemoryError:
75
+ reserve = None
76
+ failure_exit(request_path, 75, b"M")
77
+ except Exception as error:
78
+ if isinstance(error, OSError) and error.errno == 12:
79
+ reserve = None
80
+ failure_exit(request_path, 75, b"M")
81
+ reply = {"ok": False, "reason": "decoder", "message": type(error).__name__ + ": " + str(error)}
82
+ try:
83
+ encoded = json.dumps(reply, ensure_ascii=True, allow_nan=False).encode("utf-8")
84
+ if len(encoded) > OUTPUT_BYTES:
85
+ encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete debugger context exceeds64 MiB reply budget."}).encode("utf-8")
86
+ descriptor = os.open(request["reply_path"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
87
+ with os.fdopen(descriptor, "wb") as handle:
88
+ handle.write(encoded)
89
+ except MemoryError:
90
+ reserve = None
91
+ failure_exit(request_path, 75, b"M")
92
+ except OSError as error:
93
+ if error.errno == 27:
94
+ failure_exit(request_path, 76, b"F")
95
+ if error.errno == 12:
96
+ reserve = None
97
+ failure_exit(request_path, 75, b"M")
98
+ raise
99
+
100
+
101
+ main()
@@ -0,0 +1,39 @@
1
+ """Catch typed resource failures while loading or running the owned debugger launcher.
2
+
3
+ Only this package's fixed sibling is executed; caller input stays in request.json.
4
+ Interpreter startup failures before this boundary remain unclassified.
5
+ """
6
+ import os
7
+ import sys
8
+
9
+
10
+ def resource_exit(status, marker):
11
+ try:
12
+ if len(sys.argv) > 2:
13
+ descriptor = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
14
+ os.write(descriptor, marker)
15
+ os.close(descriptor)
16
+ except (OSError, MemoryError):
17
+ # Without a complete marker the parent keeps this failure unclassified.
18
+ pass
19
+ os._exit(status)
20
+
21
+ try:
22
+ implementation = os.path.join(os.path.dirname(__file__), "launch_impl.py")
23
+ with open(implementation, "rb") as handle:
24
+ source = handle.read()
25
+ exec(compile(source, implementation, "exec"), {
26
+ "__file__": implementation,
27
+ "__name__": "__main__",
28
+ })
29
+ except MemoryError:
30
+ # Reserved status: an observed allocation failure, including imports/compile.
31
+ resource_exit(75, b"M")
32
+ except OSError as error:
33
+ # Linux ENOMEM is an observed allocation failure, independent of error text.
34
+ if error.errno == 12:
35
+ resource_exit(75, b"M")
36
+ # Linux EFBIG is observed directly, including a partial limits/reply write.
37
+ if error.errno == 27:
38
+ resource_exit(76, b"F")
39
+ raise
@@ -0,0 +1,34 @@
1
+ """Lower inherited limits before replacing this owned process with GDB."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import sys
6
+
7
+ runtime_path = Path(__file__).parent.parent / "pwntools" / "decoder_runtime.py"
8
+ runtime = {"__file__": str(runtime_path), "__name__": "rea_decoder_runtime"}
9
+ exec(compile(runtime_path.read_bytes(), str(runtime_path), "exec"), runtime)
10
+
11
+
12
+ class LaunchFailure(Exception):
13
+ def __init__(self, reason, message):
14
+ super().__init__(message)
15
+ self.reason = reason
16
+
17
+
18
+ def launch(snapshot, cache):
19
+ request_path = sys.argv[-1]
20
+ request = json.loads(Path(request_path).read_text(encoding="utf-8"))
21
+ bridge = str(Path(__file__).with_name("core_context.py"))
22
+ environment = dict(os.environ, REA_CORE_CONTEXT_REQUEST=request_path)
23
+ # Only fixed package code appears in the GDB Python command. Selected paths stay JSON data.
24
+ command = "python exec(compile(open(" + repr(bridge) + ", 'rb').read(), " + repr(bridge) + ", 'exec'), {'__name__':'__main__','__file__':" + repr(bridge) + "})"
25
+ try:
26
+ os.execve(request["gdb"], [request["gdb"], "-nx", "-nh", "--batch", "--quiet",
27
+ "-iex", "set auto-load off", "-iex", "set debuginfod enabled off",
28
+ "-iex", "set auto-solib-add off", "-ex", command], environment)
29
+ except OSError as error:
30
+ raise LaunchFailure("unavailable", "Selected GDB could not start (" + str(error.errno) + "): " + request["gdb"] + "; " + str(error)) from error
31
+
32
+
33
+ if __name__ == "__main__":
34
+ runtime["main"](sys.argv[-1], launch, LaunchFailure, "pwndbg-core-launcher")
@@ -0,0 +1,59 @@
1
+ """Shared bounded reply/limit protocol, independent of the supplied artifact format."""
2
+ import json
3
+ import os
4
+ from pathlib import Path
5
+ import resource
6
+ import sys
7
+
8
+ OUTPUT_BYTES = 64 * 1024 * 1024
9
+
10
+ def lower_resource_limits():
11
+ limits = {}
12
+ for name, kind, maximum in (
13
+ ("address_space_bytes", resource.RLIMIT_AS, 3 * 1024**3),
14
+ ("cpu_seconds", resource.RLIMIT_CPU, 30),
15
+ ("file_size_bytes", resource.RLIMIT_FSIZE, OUTPUT_BYTES),
16
+ ):
17
+ soft, hard = resource.getrlimit(kind)
18
+ finite = [maximum] + [value for value in (soft, hard) if value != resource.RLIM_INFINITY]
19
+ effective = min(finite)
20
+ # Preserve the inherited hard boundary and never raise a caller's soft limit.
21
+ resource.setrlimit(kind, (effective, hard))
22
+ limits[name] = effective
23
+ return limits
24
+
25
+
26
+ def main(request_path, inspect, failure_type, profile):
27
+ request = json.loads(Path(request_path).read_text(encoding="utf-8"))
28
+ os.environ["PWNLIB_NOTERM"] = "1"
29
+ os.environ["PWNLIB_CACHE_DIR"] = str(Path(request_path).parent / "cache")
30
+ limits = None
31
+ # Release bounded emergency headroom before constructing a MemoryError reply.
32
+ # The outer exit-status contract also covers allocation failure during serialization.
33
+ memory_reserve = bytearray(1024 * 1024)
34
+ try:
35
+ limits = lower_resource_limits()
36
+ descriptor = os.open(Path(request_path).parent / "limits.json", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
37
+ with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
38
+ json.dump(limits, handle)
39
+ value = inspect(Path(request["snapshot_path"]), Path(request_path).parent / "cache")
40
+ value["limitations"].append("Effective owned Python resource soft limits: " + json.dumps(limits, sort_keys=True) + ". Inherited tighter limits are retained.")
41
+ reply = {"ok": True, "profile": profile, "value": value}
42
+ except failure_type as error:
43
+ reply = {"ok": False, "reason": error.reason, "message": str(error)}
44
+ except MemoryError:
45
+ memory_reserve = None
46
+ reply = {"ok": False, "reason": "resource-limit", "message": "pwntools memory allocation failed under the effective resource limits; the exact allocation cause is unknown.", "reported_limits": limits}
47
+ except Exception as error:
48
+ if isinstance(error, OSError) and error.errno == 12:
49
+ memory_reserve = None
50
+ reply = {"ok": False, "reason": "resource-limit", "message": "pwntools reported OSError(ENOMEM); the exact allocation cause is unknown.", "reported_limits": limits}
51
+ else:
52
+ reply = {"ok": False, "reason": "decoder", "message": type(error).__name__ + ": " + str(error)}
53
+ encoded = json.dumps(reply, ensure_ascii=True, allow_nan=False).encode("utf-8")
54
+ if len(encoded) > OUTPUT_BYTES:
55
+ encoded = json.dumps({"ok": False, "reason": "output-limit", "message": "Complete artifact result exceeds the 64 MiB reply budget."}).encode("utf-8")
56
+ descriptor = os.open(request["reply_path"], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
57
+ with os.fdopen(descriptor, "wb") as handle:
58
+ handle.write(encoded)
59
+
@@ -0,0 +1,39 @@
1
+ """Catch typed resource failures while loading or running the owned ELF adapter.
2
+
3
+ Only this package's fixed sibling is executed; caller input stays in request.json.
4
+ Interpreter startup failures before this boundary remain unclassified.
5
+ """
6
+ import os
7
+ import sys
8
+
9
+
10
+ def resource_exit(status, marker):
11
+ try:
12
+ if len(sys.argv) > 2:
13
+ descriptor = os.open(sys.argv[1], os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
14
+ os.write(descriptor, marker)
15
+ os.close(descriptor)
16
+ except (OSError, MemoryError):
17
+ # Without a complete marker the parent keeps this failure unclassified.
18
+ pass
19
+ os._exit(status)
20
+
21
+ try:
22
+ implementation = os.path.join(os.path.dirname(__file__), "layout_impl.py")
23
+ with open(implementation, "rb") as handle:
24
+ source = handle.read()
25
+ exec(compile(source, implementation, "exec"), {
26
+ "__file__": implementation,
27
+ "__name__": "__main__",
28
+ })
29
+ except MemoryError:
30
+ # Reserved status: an observed allocation failure, including imports/compile.
31
+ resource_exit(75, b"M")
32
+ except OSError as error:
33
+ # Linux ENOMEM is an observed allocation failure, independent of error text.
34
+ if error.errno == 12:
35
+ resource_exit(75, b"M")
36
+ # Linux EFBIG is observed directly, including a partial limits/reply write.
37
+ if error.errno == 27:
38
+ resource_exit(76, b"F")
39
+ raise