oscura 0.5.0__py3-none-any.whl → 0.6.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 (513) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/__init__.py +0 -48
  5. oscura/analyzers/digital/edges.py +325 -65
  6. oscura/analyzers/digital/extraction.py +0 -195
  7. oscura/analyzers/digital/quality.py +293 -166
  8. oscura/analyzers/digital/timing.py +260 -115
  9. oscura/analyzers/digital/timing_numba.py +334 -0
  10. oscura/analyzers/entropy.py +605 -0
  11. oscura/analyzers/eye/diagram.py +176 -109
  12. oscura/analyzers/eye/metrics.py +5 -5
  13. oscura/analyzers/jitter/__init__.py +6 -4
  14. oscura/analyzers/jitter/ber.py +52 -52
  15. oscura/analyzers/jitter/classification.py +156 -0
  16. oscura/analyzers/jitter/decomposition.py +163 -113
  17. oscura/analyzers/jitter/spectrum.py +80 -64
  18. oscura/analyzers/ml/__init__.py +39 -0
  19. oscura/analyzers/ml/features.py +600 -0
  20. oscura/analyzers/ml/signal_classifier.py +604 -0
  21. oscura/analyzers/packet/daq.py +246 -158
  22. oscura/analyzers/packet/parser.py +12 -1
  23. oscura/analyzers/packet/payload.py +50 -2110
  24. oscura/analyzers/packet/payload_analysis.py +361 -181
  25. oscura/analyzers/packet/payload_patterns.py +133 -70
  26. oscura/analyzers/packet/stream.py +84 -23
  27. oscura/analyzers/patterns/__init__.py +26 -5
  28. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  29. oscura/analyzers/patterns/clustering.py +169 -108
  30. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  31. oscura/analyzers/patterns/discovery.py +1 -1
  32. oscura/analyzers/patterns/matching.py +581 -197
  33. oscura/analyzers/patterns/pattern_mining.py +778 -0
  34. oscura/analyzers/patterns/periodic.py +121 -38
  35. oscura/analyzers/patterns/sequences.py +175 -78
  36. oscura/analyzers/power/conduction.py +1 -1
  37. oscura/analyzers/power/soa.py +6 -6
  38. oscura/analyzers/power/switching.py +250 -110
  39. oscura/analyzers/protocol/__init__.py +17 -1
  40. oscura/analyzers/protocols/__init__.py +1 -22
  41. oscura/analyzers/protocols/base.py +6 -6
  42. oscura/analyzers/protocols/ble/__init__.py +38 -0
  43. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  44. oscura/analyzers/protocols/ble/uuids.py +288 -0
  45. oscura/analyzers/protocols/can.py +257 -127
  46. oscura/analyzers/protocols/can_fd.py +107 -80
  47. oscura/analyzers/protocols/flexray.py +139 -80
  48. oscura/analyzers/protocols/hdlc.py +93 -58
  49. oscura/analyzers/protocols/i2c.py +247 -106
  50. oscura/analyzers/protocols/i2s.py +138 -86
  51. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  52. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  53. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  54. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  55. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  56. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  57. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  58. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  59. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  60. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  61. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  62. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  63. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  64. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  65. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  66. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  67. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  68. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  69. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  70. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  71. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  72. oscura/analyzers/protocols/jtag.py +180 -98
  73. oscura/analyzers/protocols/lin.py +219 -114
  74. oscura/analyzers/protocols/manchester.py +4 -4
  75. oscura/analyzers/protocols/onewire.py +253 -149
  76. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  77. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  78. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  79. oscura/analyzers/protocols/spi.py +192 -95
  80. oscura/analyzers/protocols/swd.py +321 -167
  81. oscura/analyzers/protocols/uart.py +267 -125
  82. oscura/analyzers/protocols/usb.py +235 -131
  83. oscura/analyzers/side_channel/power.py +17 -12
  84. oscura/analyzers/signal/__init__.py +15 -0
  85. oscura/analyzers/signal/timing_analysis.py +1086 -0
  86. oscura/analyzers/signal_integrity/__init__.py +4 -1
  87. oscura/analyzers/signal_integrity/sparams.py +2 -19
  88. oscura/analyzers/spectral/chunked.py +129 -60
  89. oscura/analyzers/spectral/chunked_fft.py +300 -94
  90. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  91. oscura/analyzers/statistical/checksum.py +376 -217
  92. oscura/analyzers/statistical/classification.py +229 -107
  93. oscura/analyzers/statistical/entropy.py +78 -53
  94. oscura/analyzers/statistics/correlation.py +407 -211
  95. oscura/analyzers/statistics/outliers.py +2 -2
  96. oscura/analyzers/statistics/streaming.py +30 -5
  97. oscura/analyzers/validation.py +216 -101
  98. oscura/analyzers/waveform/measurements.py +9 -0
  99. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  100. oscura/analyzers/waveform/spectral.py +500 -228
  101. oscura/api/__init__.py +31 -5
  102. oscura/api/dsl/__init__.py +582 -0
  103. oscura/{dsl → api/dsl}/commands.py +43 -76
  104. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  105. oscura/{dsl → api/dsl}/parser.py +107 -77
  106. oscura/{dsl → api/dsl}/repl.py +2 -2
  107. oscura/api/dsl.py +1 -1
  108. oscura/{integrations → api/integrations}/__init__.py +1 -1
  109. oscura/{integrations → api/integrations}/llm.py +201 -102
  110. oscura/api/operators.py +3 -3
  111. oscura/api/optimization.py +144 -30
  112. oscura/api/rest_server.py +921 -0
  113. oscura/api/server/__init__.py +17 -0
  114. oscura/api/server/dashboard.py +850 -0
  115. oscura/api/server/static/README.md +34 -0
  116. oscura/api/server/templates/base.html +181 -0
  117. oscura/api/server/templates/export.html +120 -0
  118. oscura/api/server/templates/home.html +284 -0
  119. oscura/api/server/templates/protocols.html +58 -0
  120. oscura/api/server/templates/reports.html +43 -0
  121. oscura/api/server/templates/session_detail.html +89 -0
  122. oscura/api/server/templates/sessions.html +83 -0
  123. oscura/api/server/templates/waveforms.html +73 -0
  124. oscura/automotive/__init__.py +8 -1
  125. oscura/automotive/can/__init__.py +10 -0
  126. oscura/automotive/can/checksum.py +3 -1
  127. oscura/automotive/can/dbc_generator.py +590 -0
  128. oscura/automotive/can/message_wrapper.py +121 -74
  129. oscura/automotive/can/patterns.py +98 -21
  130. oscura/automotive/can/session.py +292 -56
  131. oscura/automotive/can/state_machine.py +6 -3
  132. oscura/automotive/can/stimulus_response.py +97 -75
  133. oscura/automotive/dbc/__init__.py +10 -2
  134. oscura/automotive/dbc/generator.py +84 -56
  135. oscura/automotive/dbc/parser.py +6 -6
  136. oscura/automotive/dtc/data.json +2763 -0
  137. oscura/automotive/dtc/database.py +2 -2
  138. oscura/automotive/flexray/__init__.py +31 -0
  139. oscura/automotive/flexray/analyzer.py +504 -0
  140. oscura/automotive/flexray/crc.py +185 -0
  141. oscura/automotive/flexray/fibex.py +449 -0
  142. oscura/automotive/j1939/__init__.py +45 -8
  143. oscura/automotive/j1939/analyzer.py +605 -0
  144. oscura/automotive/j1939/spns.py +326 -0
  145. oscura/automotive/j1939/transport.py +306 -0
  146. oscura/automotive/lin/__init__.py +47 -0
  147. oscura/automotive/lin/analyzer.py +612 -0
  148. oscura/automotive/loaders/blf.py +13 -2
  149. oscura/automotive/loaders/csv_can.py +143 -72
  150. oscura/automotive/loaders/dispatcher.py +50 -2
  151. oscura/automotive/loaders/mdf.py +86 -45
  152. oscura/automotive/loaders/pcap.py +111 -61
  153. oscura/automotive/uds/__init__.py +4 -0
  154. oscura/automotive/uds/analyzer.py +725 -0
  155. oscura/automotive/uds/decoder.py +140 -58
  156. oscura/automotive/uds/models.py +7 -1
  157. oscura/automotive/visualization.py +1 -1
  158. oscura/cli/analyze.py +348 -0
  159. oscura/cli/batch.py +142 -122
  160. oscura/cli/benchmark.py +275 -0
  161. oscura/cli/characterize.py +137 -82
  162. oscura/cli/compare.py +224 -131
  163. oscura/cli/completion.py +250 -0
  164. oscura/cli/config_cmd.py +361 -0
  165. oscura/cli/decode.py +164 -87
  166. oscura/cli/export.py +286 -0
  167. oscura/cli/main.py +115 -31
  168. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  169. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  170. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  171. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  172. oscura/cli/progress.py +147 -0
  173. oscura/cli/shell.py +157 -135
  174. oscura/cli/validate_cmd.py +204 -0
  175. oscura/cli/visualize.py +158 -0
  176. oscura/convenience.py +125 -79
  177. oscura/core/__init__.py +4 -2
  178. oscura/core/backend_selector.py +3 -3
  179. oscura/core/cache.py +126 -15
  180. oscura/core/cancellation.py +1 -1
  181. oscura/{config → core/config}/__init__.py +20 -11
  182. oscura/{config → core/config}/defaults.py +1 -1
  183. oscura/{config → core/config}/loader.py +7 -5
  184. oscura/{config → core/config}/memory.py +5 -5
  185. oscura/{config → core/config}/migration.py +1 -1
  186. oscura/{config → core/config}/pipeline.py +99 -23
  187. oscura/{config → core/config}/preferences.py +1 -1
  188. oscura/{config → core/config}/protocol.py +3 -3
  189. oscura/{config → core/config}/schema.py +426 -272
  190. oscura/{config → core/config}/settings.py +1 -1
  191. oscura/{config → core/config}/thresholds.py +195 -153
  192. oscura/core/correlation.py +5 -6
  193. oscura/core/cross_domain.py +0 -2
  194. oscura/core/debug.py +9 -5
  195. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  196. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  197. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  198. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  199. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  200. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  201. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  202. oscura/core/gpu_backend.py +11 -7
  203. oscura/core/log_query.py +101 -11
  204. oscura/core/logging.py +126 -54
  205. oscura/core/logging_advanced.py +5 -5
  206. oscura/core/memory_limits.py +108 -70
  207. oscura/core/memory_monitor.py +2 -2
  208. oscura/core/memory_progress.py +7 -7
  209. oscura/core/memory_warnings.py +1 -1
  210. oscura/core/numba_backend.py +13 -13
  211. oscura/{plugins → core/plugins}/__init__.py +9 -9
  212. oscura/{plugins → core/plugins}/base.py +7 -7
  213. oscura/{plugins → core/plugins}/cli.py +3 -3
  214. oscura/{plugins → core/plugins}/discovery.py +186 -106
  215. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  216. oscura/{plugins → core/plugins}/manager.py +7 -7
  217. oscura/{plugins → core/plugins}/registry.py +3 -3
  218. oscura/{plugins → core/plugins}/versioning.py +1 -1
  219. oscura/core/progress.py +16 -1
  220. oscura/core/provenance.py +8 -2
  221. oscura/{schemas → core/schemas}/__init__.py +2 -2
  222. oscura/core/schemas/bus_configuration.json +322 -0
  223. oscura/core/schemas/device_mapping.json +182 -0
  224. oscura/core/schemas/packet_format.json +418 -0
  225. oscura/core/schemas/protocol_definition.json +363 -0
  226. oscura/core/types.py +4 -0
  227. oscura/core/uncertainty.py +3 -3
  228. oscura/correlation/__init__.py +52 -0
  229. oscura/correlation/multi_protocol.py +811 -0
  230. oscura/discovery/auto_decoder.py +117 -35
  231. oscura/discovery/comparison.py +191 -86
  232. oscura/discovery/quality_validator.py +155 -68
  233. oscura/discovery/signal_detector.py +196 -79
  234. oscura/export/__init__.py +18 -20
  235. oscura/export/kaitai_struct.py +513 -0
  236. oscura/export/scapy_layer.py +801 -0
  237. oscura/export/wireshark/README.md +15 -15
  238. oscura/export/wireshark/generator.py +1 -1
  239. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  240. oscura/export/wireshark_dissector.py +746 -0
  241. oscura/guidance/wizard.py +207 -111
  242. oscura/hardware/__init__.py +19 -0
  243. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  244. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  245. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  246. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  247. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  248. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  249. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  250. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  251. oscura/hardware/firmware/__init__.py +29 -0
  252. oscura/hardware/firmware/pattern_recognition.py +874 -0
  253. oscura/hardware/hal_detector.py +736 -0
  254. oscura/hardware/security/__init__.py +37 -0
  255. oscura/hardware/security/side_channel_detector.py +1126 -0
  256. oscura/inference/__init__.py +4 -0
  257. oscura/inference/active_learning/README.md +7 -7
  258. oscura/inference/active_learning/observation_table.py +4 -1
  259. oscura/inference/alignment.py +216 -123
  260. oscura/inference/bayesian.py +113 -33
  261. oscura/inference/crc_reverse.py +101 -55
  262. oscura/inference/logic.py +6 -2
  263. oscura/inference/message_format.py +342 -183
  264. oscura/inference/protocol.py +95 -44
  265. oscura/inference/protocol_dsl.py +180 -82
  266. oscura/inference/signal_intelligence.py +1439 -706
  267. oscura/inference/spectral.py +99 -57
  268. oscura/inference/state_machine.py +810 -158
  269. oscura/inference/stream.py +270 -110
  270. oscura/iot/__init__.py +34 -0
  271. oscura/iot/coap/__init__.py +32 -0
  272. oscura/iot/coap/analyzer.py +668 -0
  273. oscura/iot/coap/options.py +212 -0
  274. oscura/iot/lorawan/__init__.py +21 -0
  275. oscura/iot/lorawan/crypto.py +206 -0
  276. oscura/iot/lorawan/decoder.py +801 -0
  277. oscura/iot/lorawan/mac_commands.py +341 -0
  278. oscura/iot/mqtt/__init__.py +27 -0
  279. oscura/iot/mqtt/analyzer.py +999 -0
  280. oscura/iot/mqtt/properties.py +315 -0
  281. oscura/iot/zigbee/__init__.py +31 -0
  282. oscura/iot/zigbee/analyzer.py +615 -0
  283. oscura/iot/zigbee/security.py +153 -0
  284. oscura/iot/zigbee/zcl.py +349 -0
  285. oscura/jupyter/display.py +125 -45
  286. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  287. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  288. oscura/jupyter/exploratory/fuzzy.py +746 -0
  289. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  290. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  291. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  292. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  293. oscura/jupyter/exploratory/sync.py +612 -0
  294. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  295. oscura/jupyter/magic.py +4 -4
  296. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  297. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  298. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  299. oscura/loaders/__init__.py +171 -63
  300. oscura/loaders/binary.py +88 -1
  301. oscura/loaders/chipwhisperer.py +153 -137
  302. oscura/loaders/configurable.py +208 -86
  303. oscura/loaders/csv_loader.py +458 -215
  304. oscura/loaders/hdf5_loader.py +278 -119
  305. oscura/loaders/lazy.py +87 -54
  306. oscura/loaders/mmap_loader.py +1 -1
  307. oscura/loaders/numpy_loader.py +253 -116
  308. oscura/loaders/pcap.py +226 -151
  309. oscura/loaders/rigol.py +110 -49
  310. oscura/loaders/sigrok.py +201 -78
  311. oscura/loaders/tdms.py +81 -58
  312. oscura/loaders/tektronix.py +291 -174
  313. oscura/loaders/touchstone.py +182 -87
  314. oscura/loaders/vcd.py +215 -117
  315. oscura/loaders/wav.py +155 -68
  316. oscura/reporting/__init__.py +9 -7
  317. oscura/reporting/analyze.py +352 -146
  318. oscura/reporting/argument_preparer.py +69 -14
  319. oscura/reporting/auto_report.py +97 -61
  320. oscura/reporting/batch.py +131 -58
  321. oscura/reporting/chart_selection.py +57 -45
  322. oscura/reporting/comparison.py +63 -17
  323. oscura/reporting/content/executive.py +76 -24
  324. oscura/reporting/core_formats/multi_format.py +11 -8
  325. oscura/reporting/engine.py +312 -158
  326. oscura/reporting/enhanced_reports.py +949 -0
  327. oscura/reporting/export.py +86 -43
  328. oscura/reporting/formatting/numbers.py +69 -42
  329. oscura/reporting/html.py +139 -58
  330. oscura/reporting/index.py +137 -65
  331. oscura/reporting/output.py +158 -67
  332. oscura/reporting/pdf.py +67 -102
  333. oscura/reporting/plots.py +191 -112
  334. oscura/reporting/sections.py +88 -47
  335. oscura/reporting/standards.py +104 -61
  336. oscura/reporting/summary_generator.py +75 -55
  337. oscura/reporting/tables.py +138 -54
  338. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  339. oscura/reporting/templates/index.md +13 -13
  340. oscura/sessions/__init__.py +14 -23
  341. oscura/sessions/base.py +3 -3
  342. oscura/sessions/blackbox.py +106 -10
  343. oscura/sessions/generic.py +2 -2
  344. oscura/sessions/legacy.py +783 -0
  345. oscura/side_channel/__init__.py +63 -0
  346. oscura/side_channel/dpa.py +1025 -0
  347. oscura/utils/__init__.py +15 -1
  348. oscura/utils/autodetect.py +1 -5
  349. oscura/utils/bitwise.py +118 -0
  350. oscura/{builders → utils/builders}/__init__.py +1 -1
  351. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  352. oscura/{comparison → utils/comparison}/compare.py +202 -101
  353. oscura/{comparison → utils/comparison}/golden.py +83 -63
  354. oscura/{comparison → utils/comparison}/limits.py +313 -89
  355. oscura/{comparison → utils/comparison}/mask.py +151 -45
  356. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  357. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  358. oscura/{component → utils/component}/__init__.py +3 -3
  359. oscura/{component → utils/component}/impedance.py +122 -58
  360. oscura/{component → utils/component}/reactive.py +165 -168
  361. oscura/{component → utils/component}/transmission_line.py +3 -3
  362. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  363. oscura/{filtering → utils/filtering}/base.py +1 -1
  364. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  365. oscura/{filtering → utils/filtering}/design.py +169 -93
  366. oscura/{filtering → utils/filtering}/filters.py +2 -2
  367. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  368. oscura/utils/geometry.py +31 -0
  369. oscura/utils/imports.py +184 -0
  370. oscura/utils/lazy.py +1 -1
  371. oscura/{math → utils/math}/__init__.py +2 -2
  372. oscura/{math → utils/math}/arithmetic.py +114 -48
  373. oscura/{math → utils/math}/interpolation.py +139 -106
  374. oscura/utils/memory.py +129 -66
  375. oscura/utils/memory_advanced.py +92 -9
  376. oscura/utils/memory_extensions.py +10 -8
  377. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  378. oscura/{optimization → utils/optimization}/search.py +2 -2
  379. oscura/utils/performance/__init__.py +58 -0
  380. oscura/utils/performance/caching.py +889 -0
  381. oscura/utils/performance/lsh_clustering.py +333 -0
  382. oscura/utils/performance/memory_optimizer.py +699 -0
  383. oscura/utils/performance/optimizations.py +675 -0
  384. oscura/utils/performance/parallel.py +654 -0
  385. oscura/utils/performance/profiling.py +661 -0
  386. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  387. oscura/{pipeline → utils/pipeline}/composition.py +11 -3
  388. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  389. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  390. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  391. oscura/{search → utils/search}/__init__.py +3 -3
  392. oscura/{search → utils/search}/anomaly.py +188 -58
  393. oscura/utils/search/context.py +294 -0
  394. oscura/{search → utils/search}/pattern.py +138 -10
  395. oscura/utils/serial.py +51 -0
  396. oscura/utils/storage/__init__.py +61 -0
  397. oscura/utils/storage/database.py +1166 -0
  398. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  399. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  400. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  401. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  402. oscura/{triggering → utils/triggering}/base.py +6 -6
  403. oscura/{triggering → utils/triggering}/edge.py +2 -2
  404. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  405. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  406. oscura/{triggering → utils/triggering}/window.py +2 -2
  407. oscura/utils/validation.py +32 -0
  408. oscura/validation/__init__.py +121 -0
  409. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  410. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  411. oscura/{compliance → validation/compliance}/masks.py +1 -1
  412. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  413. oscura/{compliance → validation/compliance}/testing.py +114 -52
  414. oscura/validation/compliance_tests.py +915 -0
  415. oscura/validation/fuzzer.py +990 -0
  416. oscura/validation/grammar_tests.py +596 -0
  417. oscura/validation/grammar_validator.py +904 -0
  418. oscura/validation/hil_testing.py +977 -0
  419. oscura/{quality → validation/quality}/__init__.py +4 -4
  420. oscura/{quality → validation/quality}/ensemble.py +251 -171
  421. oscura/{quality → validation/quality}/explainer.py +3 -3
  422. oscura/{quality → validation/quality}/scoring.py +1 -1
  423. oscura/{quality → validation/quality}/warnings.py +4 -4
  424. oscura/validation/regression_suite.py +808 -0
  425. oscura/validation/replay.py +788 -0
  426. oscura/{testing → validation/testing}/__init__.py +2 -2
  427. oscura/{testing → validation/testing}/synthetic.py +5 -5
  428. oscura/visualization/__init__.py +9 -0
  429. oscura/visualization/accessibility.py +1 -1
  430. oscura/visualization/annotations.py +64 -67
  431. oscura/visualization/colors.py +7 -7
  432. oscura/visualization/digital.py +180 -81
  433. oscura/visualization/eye.py +236 -85
  434. oscura/visualization/interactive.py +320 -143
  435. oscura/visualization/jitter.py +587 -247
  436. oscura/visualization/layout.py +169 -134
  437. oscura/visualization/optimization.py +103 -52
  438. oscura/visualization/palettes.py +1 -1
  439. oscura/visualization/power.py +427 -211
  440. oscura/visualization/power_extended.py +626 -297
  441. oscura/visualization/presets.py +2 -0
  442. oscura/visualization/protocols.py +495 -181
  443. oscura/visualization/render.py +79 -63
  444. oscura/visualization/reverse_engineering.py +171 -124
  445. oscura/visualization/signal_integrity.py +460 -279
  446. oscura/visualization/specialized.py +190 -100
  447. oscura/visualization/spectral.py +670 -255
  448. oscura/visualization/thumbnails.py +166 -137
  449. oscura/visualization/waveform.py +150 -63
  450. oscura/workflows/__init__.py +3 -0
  451. oscura/{batch → workflows/batch}/__init__.py +5 -5
  452. oscura/{batch → workflows/batch}/advanced.py +150 -75
  453. oscura/workflows/batch/aggregate.py +531 -0
  454. oscura/workflows/batch/analyze.py +236 -0
  455. oscura/{batch → workflows/batch}/logging.py +2 -2
  456. oscura/{batch → workflows/batch}/metrics.py +1 -1
  457. oscura/workflows/complete_re.py +1144 -0
  458. oscura/workflows/compliance.py +44 -54
  459. oscura/workflows/digital.py +197 -51
  460. oscura/workflows/legacy/__init__.py +12 -0
  461. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  462. oscura/workflows/multi_trace.py +9 -9
  463. oscura/workflows/power.py +42 -62
  464. oscura/workflows/protocol.py +82 -49
  465. oscura/workflows/reverse_engineering.py +351 -150
  466. oscura/workflows/signal_integrity.py +157 -82
  467. oscura-0.6.0.dist-info/METADATA +643 -0
  468. oscura-0.6.0.dist-info/RECORD +590 -0
  469. oscura/analyzers/digital/ic_database.py +0 -498
  470. oscura/analyzers/digital/timing_paths.py +0 -339
  471. oscura/analyzers/digital/vintage.py +0 -377
  472. oscura/analyzers/digital/vintage_result.py +0 -148
  473. oscura/analyzers/protocols/parallel_bus.py +0 -449
  474. oscura/batch/aggregate.py +0 -300
  475. oscura/batch/analyze.py +0 -139
  476. oscura/dsl/__init__.py +0 -73
  477. oscura/exceptions.py +0 -59
  478. oscura/exploratory/fuzzy.py +0 -513
  479. oscura/exploratory/sync.py +0 -384
  480. oscura/export/wavedrom.py +0 -430
  481. oscura/exporters/__init__.py +0 -94
  482. oscura/exporters/csv.py +0 -303
  483. oscura/exporters/exporters.py +0 -44
  484. oscura/exporters/hdf5.py +0 -217
  485. oscura/exporters/html_export.py +0 -701
  486. oscura/exporters/json_export.py +0 -338
  487. oscura/exporters/markdown_export.py +0 -367
  488. oscura/exporters/matlab_export.py +0 -354
  489. oscura/exporters/npz_export.py +0 -219
  490. oscura/exporters/spice_export.py +0 -210
  491. oscura/exporters/vintage_logic_csv.py +0 -247
  492. oscura/reporting/vintage_logic_report.py +0 -523
  493. oscura/search/context.py +0 -149
  494. oscura/session/__init__.py +0 -34
  495. oscura/session/annotations.py +0 -289
  496. oscura/session/history.py +0 -313
  497. oscura/session/session.py +0 -520
  498. oscura/visualization/digital_advanced.py +0 -718
  499. oscura/visualization/figure_manager.py +0 -156
  500. oscura/workflow/__init__.py +0 -13
  501. oscura-0.5.0.dist-info/METADATA +0 -407
  502. oscura-0.5.0.dist-info/RECORD +0 -486
  503. /oscura/core/{config.py → config/legacy.py} +0 -0
  504. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  505. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  506. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  507. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  508. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  509. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  510. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  511. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
  512. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
  513. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,736 @@
1
+ """Hardware Abstraction Layer (HAL) detection and analysis.
2
+
3
+ - RE-HAL-001: Register Access Pattern Detection
4
+ - RE-HAL-002: Peripheral Driver Identification
5
+ - RE-HAL-003: HAL Framework Recognition
6
+
7
+ This module provides tools for identifying hardware abstraction layer patterns
8
+ in firmware binaries and protocol traffic, detecting register access patterns,
9
+ peripheral drivers, and HAL framework signatures.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ from collections import Counter, defaultdict
16
+ from dataclasses import dataclass, field
17
+ from typing import Any, Literal
18
+
19
+ import numpy as np
20
+
21
+
22
+ @dataclass
23
+ class RegisterAccess:
24
+ """Register access pattern.
25
+
26
+ Implements RE-HAL-001: Register access pattern representation.
27
+
28
+ Attributes:
29
+ address: Register address (hex).
30
+ access_type: Type of access (read/write/rmw).
31
+ bit_mask: Bit mask for read-modify-write operations.
32
+ frequency: Number of times this access occurred.
33
+ offset_from_base: Offset from peripheral base address.
34
+
35
+ Example:
36
+ >>> reg = RegisterAccess(
37
+ ... address=0x40021000,
38
+ ... access_type="rmw",
39
+ ... bit_mask=0x00000001,
40
+ ... frequency=5
41
+ ... )
42
+ >>> reg.access_type
43
+ 'rmw'
44
+ """
45
+
46
+ address: int
47
+ access_type: Literal["read", "write", "rmw"]
48
+ bit_mask: int | None = None
49
+ frequency: int = 1
50
+ offset_from_base: int | None = None
51
+
52
+
53
+ @dataclass
54
+ class Peripheral:
55
+ """Peripheral driver information.
56
+
57
+ Implements RE-HAL-002: Peripheral driver representation.
58
+
59
+ Attributes:
60
+ peripheral_type: Type of peripheral (GPIO, UART, SPI, etc.).
61
+ base_address: Base address of peripheral registers.
62
+ registers: Dictionary of register offsets to access patterns.
63
+ access_patterns: List of detected access patterns.
64
+ initialization_sequence: Ordered list of register accesses during init.
65
+
66
+ Example:
67
+ >>> periph = Peripheral(
68
+ ... peripheral_type="UART",
69
+ ... base_address=0x40011000,
70
+ ... registers={0x00: "Control Register"}
71
+ ... )
72
+ >>> periph.peripheral_type
73
+ 'UART'
74
+ """
75
+
76
+ peripheral_type: str
77
+ base_address: int
78
+ registers: dict[int, str] = field(default_factory=dict)
79
+ access_patterns: list[RegisterAccess] = field(default_factory=list)
80
+ initialization_sequence: list[int] = field(default_factory=list)
81
+
82
+
83
+ @dataclass
84
+ class HALAnalysisResult:
85
+ """Result of HAL detection analysis.
86
+
87
+ Implements RE-HAL-003: HAL analysis result.
88
+
89
+ Attributes:
90
+ detected_hal: Name of detected HAL framework (or "Unknown").
91
+ peripherals: List of identified peripherals.
92
+ register_map: Complete register address map.
93
+ initialization_sequence: Ordered initialization operations.
94
+ confidence: Detection confidence (0.0-1.0).
95
+ framework_signatures: List of detected framework signatures.
96
+
97
+ Example:
98
+ >>> result = HALAnalysisResult(
99
+ ... detected_hal="STM32 HAL",
100
+ ... peripherals=[],
101
+ ... register_map={},
102
+ ... initialization_sequence=[],
103
+ ... confidence=0.95
104
+ ... )
105
+ >>> result.detected_hal
106
+ 'STM32 HAL'
107
+ """
108
+
109
+ detected_hal: str
110
+ peripherals: list[Peripheral]
111
+ register_map: dict[int, str]
112
+ initialization_sequence: list[dict[str, Any]]
113
+ confidence: float = 0.0
114
+ framework_signatures: list[str] = field(default_factory=list)
115
+
116
+
117
+ # Known HAL framework signatures
118
+ HAL_SIGNATURES: dict[str, dict[str, Any]] = {
119
+ "STM32 HAL": {
120
+ "patterns": [
121
+ b"HAL_Init",
122
+ b"HAL_Delay",
123
+ b"HAL_GPIO_",
124
+ b"HAL_UART_",
125
+ b"HAL_SPI_",
126
+ b"HAL_I2C_",
127
+ ],
128
+ "base_addresses": {
129
+ 0x40000000: "APB1 Peripheral",
130
+ 0x40010000: "APB2 Peripheral",
131
+ 0x40020000: "AHB1 Peripheral",
132
+ 0x50000000: "AHB2 Peripheral",
133
+ },
134
+ },
135
+ "Nordic SDK": {
136
+ "patterns": [
137
+ b"nrf_",
138
+ b"NRF_",
139
+ b"nrfx_",
140
+ b"NRFX_",
141
+ b"app_uart_",
142
+ b"ble_",
143
+ ],
144
+ "base_addresses": {
145
+ 0x40000000: "APB Peripheral",
146
+ 0x50000000: "AHB Peripheral",
147
+ },
148
+ },
149
+ "ESP-IDF": {
150
+ "patterns": [
151
+ b"esp_",
152
+ b"ESP_",
153
+ b"gpio_",
154
+ b"uart_",
155
+ b"spi_",
156
+ b"i2c_",
157
+ ],
158
+ "base_addresses": {
159
+ 0x3FF00000: "DPORT Peripheral",
160
+ 0x3FF40000: "UART Peripheral",
161
+ 0x3FF44000: "SPI Peripheral",
162
+ },
163
+ },
164
+ "Arduino": {
165
+ "patterns": [
166
+ b"digitalWrite",
167
+ b"digitalRead",
168
+ b"pinMode",
169
+ b"analogRead",
170
+ b"analogWrite",
171
+ b"Serial.begin",
172
+ ],
173
+ "base_addresses": {},
174
+ },
175
+ "CMSIS": {
176
+ "patterns": [
177
+ b"CMSIS",
178
+ b"__NVIC_",
179
+ b"SysTick_",
180
+ b"SCB->",
181
+ b"NVIC->",
182
+ ],
183
+ "base_addresses": {
184
+ 0xE000E000: "System Control Space",
185
+ 0xE000E010: "SysTick",
186
+ 0xE000E100: "NVIC",
187
+ },
188
+ },
189
+ }
190
+
191
+ # Common MCU peripheral base addresses (ARM Cortex-M)
192
+ ARM_PERIPHERAL_BASES: dict[int, str] = {
193
+ # STM32F4 family
194
+ 0x40000000: "TIM2",
195
+ 0x40000400: "TIM3",
196
+ 0x40000800: "TIM4",
197
+ 0x40000C00: "TIM5",
198
+ 0x40001000: "TIM6",
199
+ 0x40001400: "TIM7",
200
+ 0x40002800: "RTC",
201
+ 0x40002C00: "WWDG",
202
+ 0x40003000: "IWDG",
203
+ 0x40003800: "SPI2",
204
+ 0x40003C00: "SPI3",
205
+ 0x40004400: "USART2",
206
+ 0x40004800: "USART3",
207
+ 0x40004C00: "UART4",
208
+ 0x40005000: "UART5",
209
+ 0x40005400: "I2C1",
210
+ 0x40005800: "I2C2",
211
+ 0x40005C00: "I2C3",
212
+ 0x40007000: "PWR",
213
+ 0x40007400: "DAC",
214
+ 0x40010000: "TIM1",
215
+ 0x40011000: "USART1",
216
+ 0x40011400: "USART6",
217
+ 0x40012C00: "ADC1",
218
+ 0x40013000: "SPI1",
219
+ 0x40014000: "SYSCFG",
220
+ 0x40020000: "GPIOA",
221
+ 0x40020400: "GPIOB",
222
+ 0x40020800: "GPIOC",
223
+ 0x40020C00: "GPIOD",
224
+ 0x40021000: "GPIOE",
225
+ 0x40021400: "GPIOF",
226
+ 0x40021800: "GPIOG",
227
+ 0x40021C00: "GPIOH",
228
+ 0x40023000: "CRC",
229
+ 0x40023800: "RCC",
230
+ 0x40023C00: "FLASH",
231
+ 0x40026000: "DMA1",
232
+ 0x40026400: "DMA2",
233
+ }
234
+
235
+ # AVR peripheral base addresses
236
+ AVR_PERIPHERAL_BASES: dict[int, str] = {
237
+ 0x0020: "PORTA",
238
+ 0x0023: "PORTB",
239
+ 0x0026: "PORTC",
240
+ 0x0029: "PORTD",
241
+ 0x00B8: "USART0",
242
+ 0x00C0: "USART1",
243
+ 0x004C: "SPI",
244
+ 0x00B0: "TWI (I2C)",
245
+ 0x0080: "ADC",
246
+ }
247
+
248
+ # PIC peripheral base addresses
249
+ PIC_PERIPHERAL_BASES: dict[int, str] = {
250
+ 0x0005: "PORTA",
251
+ 0x0006: "PORTB",
252
+ 0x0007: "PORTC",
253
+ 0x0011: "USART",
254
+ 0x0015: "SPI",
255
+ 0x001E: "ADC",
256
+ }
257
+
258
+
259
+ class HALDetector:
260
+ """Hardware Abstraction Layer detector.
261
+
262
+ Identifies HAL patterns, peripheral drivers, and register access
263
+ patterns in firmware binaries and protocol traffic.
264
+
265
+ Attributes:
266
+ register_accesses: Tracked register access patterns.
267
+ peripherals: Detected peripheral drivers.
268
+ hal_framework: Detected HAL framework name.
269
+
270
+ Example:
271
+ >>> detector = HALDetector()
272
+ >>> # Analyze firmware binary
273
+ >>> binary = b"\\x00\\x10\\x02\\x40HAL_Init..."
274
+ >>> result = detector.analyze_firmware(binary)
275
+ >>> result.detected_hal
276
+ 'STM32 HAL'
277
+ >>> len(result.peripherals) > 0
278
+ True
279
+ """
280
+
281
+ def __init__(self) -> None:
282
+ """Initialize HAL detector."""
283
+ self.register_accesses: list[RegisterAccess] = []
284
+ self.peripherals: list[Peripheral] = []
285
+ self.hal_framework: str = "Unknown"
286
+ self._address_frequency: Counter[int] = Counter()
287
+
288
+ def analyze_firmware(
289
+ self,
290
+ binary_data: bytes,
291
+ *,
292
+ detect_peripherals: bool = True,
293
+ detect_framework: bool = True,
294
+ ) -> HALAnalysisResult:
295
+ """Analyze firmware binary for HAL patterns.
296
+
297
+ Args:
298
+ binary_data: Firmware binary data.
299
+ detect_peripherals: Enable peripheral detection.
300
+ detect_framework: Enable HAL framework detection.
301
+
302
+ Returns:
303
+ HAL analysis result with detected patterns.
304
+
305
+ Raises:
306
+ ValueError: If binary_data is empty.
307
+
308
+ Example:
309
+ >>> detector = HALDetector()
310
+ >>> binary = b"HAL_Init\\x00\\x00\\x10\\x02\\x40"
311
+ >>> result = detector.analyze_firmware(binary)
312
+ >>> result.confidence > 0.0
313
+ True
314
+ """
315
+ if not binary_data:
316
+ raise ValueError("Binary data cannot be empty")
317
+
318
+ # Reset state
319
+ self.register_accesses = []
320
+ self.peripherals = []
321
+ self.hal_framework = "Unknown"
322
+ self._address_frequency = Counter()
323
+
324
+ # Detect HAL framework
325
+ framework_confidence = 0.0
326
+ if detect_framework:
327
+ self.hal_framework, framework_confidence = self._detect_hal_framework(binary_data)
328
+
329
+ # Extract register access patterns
330
+ self._extract_register_accesses(binary_data)
331
+
332
+ # Detect peripherals
333
+ if detect_peripherals:
334
+ self._detect_peripherals()
335
+
336
+ # Build register map
337
+ register_map = self._build_register_map()
338
+
339
+ # Extract initialization sequence
340
+ init_sequence = self._extract_initialization_sequence()
341
+
342
+ # Calculate overall confidence
343
+ confidence = self._calculate_confidence(framework_confidence)
344
+
345
+ return HALAnalysisResult(
346
+ detected_hal=self.hal_framework,
347
+ peripherals=self.peripherals,
348
+ register_map=register_map,
349
+ initialization_sequence=init_sequence,
350
+ confidence=confidence,
351
+ framework_signatures=self._get_detected_signatures(binary_data),
352
+ )
353
+
354
+ def _detect_hal_framework(self, binary_data: bytes) -> tuple[str, float]:
355
+ """Detect HAL framework from binary signatures.
356
+
357
+ Args:
358
+ binary_data: Firmware binary data.
359
+
360
+ Returns:
361
+ Tuple of (framework_name, confidence).
362
+ """
363
+ scores: dict[str, float] = {}
364
+
365
+ for framework, info in HAL_SIGNATURES.items():
366
+ matches = 0
367
+ patterns = info["patterns"]
368
+
369
+ for pattern in patterns:
370
+ if pattern in binary_data:
371
+ matches += 1
372
+
373
+ if len(patterns) > 0:
374
+ score = matches / len(patterns)
375
+ scores[framework] = score
376
+
377
+ if not scores:
378
+ return "Unknown", 0.0
379
+
380
+ best_framework = max(scores, key=scores.get) # type: ignore[arg-type]
381
+ confidence = scores[best_framework]
382
+
383
+ return best_framework, confidence
384
+
385
+ def _extract_register_accesses(self, binary_data: bytes) -> None:
386
+ """Extract register access patterns from binary.
387
+
388
+ Args:
389
+ binary_data: Firmware binary data.
390
+ """
391
+ # Convert to numpy array for easier processing
392
+ data_array = np.frombuffer(binary_data, dtype=np.uint8)
393
+
394
+ # Look for 32-bit addresses (little-endian)
395
+ if len(data_array) < 4:
396
+ return
397
+
398
+ # Scan for potential memory-mapped register addresses
399
+ for i in range(len(data_array) - 3):
400
+ # Extract potential 32-bit address
401
+ addr = int.from_bytes(data_array[i : i + 4].tobytes(), byteorder="little", signed=False)
402
+
403
+ # Check if address looks like a peripheral register
404
+ if self._is_peripheral_address(addr):
405
+ self._address_frequency[addr] += 1
406
+
407
+ # Try to determine access type from surrounding bytes
408
+ access_type = self._infer_access_type(data_array, i)
409
+
410
+ self.register_accesses.append(
411
+ RegisterAccess(address=addr, access_type=access_type, frequency=1)
412
+ )
413
+
414
+ def _is_peripheral_address(self, addr: int) -> bool:
415
+ """Check if address is likely a peripheral register.
416
+
417
+ Args:
418
+ addr: Memory address.
419
+
420
+ Returns:
421
+ True if address is in peripheral memory range.
422
+ """
423
+ # ARM Cortex-M peripheral ranges
424
+ if 0x40000000 <= addr < 0x60000000: # Peripheral range
425
+ return True
426
+ if 0xE0000000 <= addr < 0xE0100000: # System control
427
+ return True
428
+
429
+ # AVR peripheral range
430
+ if 0x0020 <= addr < 0x0100:
431
+ return True
432
+
433
+ # PIC peripheral range
434
+ return 0x0000 <= addr < 0x0030
435
+
436
+ def _infer_access_type(
437
+ self, data_array: np.ndarray[tuple[int], np.dtype[np.uint8]], index: int
438
+ ) -> Literal["read", "write", "rmw"]:
439
+ """Infer register access type from context.
440
+
441
+ Args:
442
+ data_array: Binary data as numpy array.
443
+ index: Index of address in array.
444
+
445
+ Returns:
446
+ Access type (read, write, or rmw).
447
+ """
448
+ # Simple heuristic: look at nearby opcodes (ARM Thumb)
449
+ if index > 0:
450
+ prev_byte = int(data_array[index - 1])
451
+
452
+ # Common ARM Thumb load/store opcodes
453
+ if prev_byte in (0x68, 0x78, 0x88): # LDR variants
454
+ return "read"
455
+ if prev_byte in (0x60, 0x70, 0x80): # STR variants
456
+ return "write"
457
+
458
+ # Check for bit manipulation patterns (read-modify-write)
459
+ if index + 8 < len(data_array):
460
+ # Look for ORR, AND, BIC patterns
461
+ window = data_array[index : index + 8]
462
+ if any(b in (0x43, 0x40, 0x44) for b in window): # ORR, AND, BIC opcodes
463
+ return "rmw"
464
+
465
+ return "write"
466
+
467
+ def _detect_peripherals(self) -> None:
468
+ """Detect peripheral drivers from register access patterns."""
469
+ # Group accesses by base address
470
+ base_groups: dict[int, list[RegisterAccess]] = defaultdict(list)
471
+
472
+ for access in self.register_accesses:
473
+ # Try to find matching peripheral base
474
+ base = self._find_peripheral_base(access.address)
475
+ if base is not None:
476
+ access.offset_from_base = access.address - base
477
+ base_groups[base].append(access)
478
+
479
+ # Create peripheral objects
480
+ for base, accesses in base_groups.items():
481
+ periph_type = self._identify_peripheral_type(base, accesses)
482
+
483
+ peripheral = Peripheral(
484
+ peripheral_type=periph_type,
485
+ base_address=base,
486
+ access_patterns=accesses,
487
+ )
488
+
489
+ # Extract register names
490
+ peripheral.registers = self._extract_register_names(base)
491
+
492
+ self.peripherals.append(peripheral)
493
+
494
+ def _find_peripheral_base(self, address: int) -> int | None:
495
+ """Find peripheral base address for given register address.
496
+
497
+ Args:
498
+ address: Register address.
499
+
500
+ Returns:
501
+ Base address or None.
502
+ """
503
+ # Check ARM peripherals
504
+ for base in ARM_PERIPHERAL_BASES:
505
+ if base <= address < base + 0x400: # Typical peripheral size
506
+ return base
507
+
508
+ # Check AVR peripherals
509
+ for base in AVR_PERIPHERAL_BASES:
510
+ if base <= address < base + 0x10:
511
+ return base
512
+
513
+ # Check PIC peripherals
514
+ for base in PIC_PERIPHERAL_BASES:
515
+ if base <= address < base + 0x10:
516
+ return base
517
+
518
+ return None
519
+
520
+ def _identify_peripheral_type(self, base_address: int, accesses: list[RegisterAccess]) -> str:
521
+ """Identify peripheral type from base address and access patterns.
522
+
523
+ Args:
524
+ base_address: Peripheral base address.
525
+ accesses: List of register accesses.
526
+
527
+ Returns:
528
+ Peripheral type name.
529
+ """
530
+ # Check known base addresses
531
+ if base_address in ARM_PERIPHERAL_BASES:
532
+ return ARM_PERIPHERAL_BASES[base_address]
533
+
534
+ if base_address in AVR_PERIPHERAL_BASES:
535
+ return AVR_PERIPHERAL_BASES[base_address]
536
+
537
+ if base_address in PIC_PERIPHERAL_BASES:
538
+ return PIC_PERIPHERAL_BASES[base_address]
539
+
540
+ # Heuristic: identify by access patterns
541
+ if len(accesses) > 10 and any(a.access_type == "rmw" for a in accesses):
542
+ return "GPIO"
543
+
544
+ return "Unknown Peripheral"
545
+
546
+ def _extract_register_names(self, base_address: int) -> dict[int, str]:
547
+ """Extract register names for peripheral.
548
+
549
+ Args:
550
+ base_address: Peripheral base address.
551
+
552
+ Returns:
553
+ Dictionary of offset to register name.
554
+ """
555
+ registers: dict[int, str] = {}
556
+
557
+ # GPIO registers (common pattern)
558
+ if "GPIO" in self._identify_peripheral_type(base_address, []):
559
+ registers = {
560
+ 0x00: "MODER (Mode Register)",
561
+ 0x04: "OTYPER (Output Type)",
562
+ 0x08: "OSPEEDR (Output Speed)",
563
+ 0x0C: "PUPDR (Pull-up/Pull-down)",
564
+ 0x10: "IDR (Input Data)",
565
+ 0x14: "ODR (Output Data)",
566
+ 0x18: "BSRR (Bit Set/Reset)",
567
+ }
568
+ # UART registers
569
+ elif "UART" in self._identify_peripheral_type(base_address, []):
570
+ registers = {
571
+ 0x00: "CR1 (Control 1)",
572
+ 0x04: "CR2 (Control 2)",
573
+ 0x08: "CR3 (Control 3)",
574
+ 0x0C: "BRR (Baud Rate)",
575
+ 0x1C: "RDR (Receive Data)",
576
+ 0x28: "TDR (Transmit Data)",
577
+ }
578
+
579
+ return registers
580
+
581
+ def _build_register_map(self) -> dict[int, str]:
582
+ """Build complete register address map.
583
+
584
+ Returns:
585
+ Dictionary of address to description.
586
+ """
587
+ register_map: dict[int, str] = {}
588
+
589
+ for peripheral in self.peripherals:
590
+ for offset, name in peripheral.registers.items():
591
+ addr = peripheral.base_address + offset
592
+ register_map[addr] = f"{peripheral.peripheral_type}.{name}"
593
+
594
+ return register_map
595
+
596
+ def _extract_initialization_sequence(self) -> list[dict[str, Any]]:
597
+ """Extract initialization sequence from register accesses.
598
+
599
+ Returns:
600
+ List of initialization operations.
601
+ """
602
+ init_ops: list[dict[str, Any]] = []
603
+
604
+ # Group accesses by peripheral
605
+ for peripheral in self.peripherals:
606
+ # Clock configuration often comes first
607
+ if "RCC" in peripheral.peripheral_type or "Clock" in peripheral.peripheral_type:
608
+ init_ops.append(
609
+ {
610
+ "step": "clock_config",
611
+ "peripheral": peripheral.peripheral_type,
612
+ "base_address": hex(peripheral.base_address),
613
+ }
614
+ )
615
+
616
+ # GPIO configuration
617
+ elif "GPIO" in peripheral.peripheral_type:
618
+ init_ops.append(
619
+ {
620
+ "step": "gpio_init",
621
+ "peripheral": peripheral.peripheral_type,
622
+ "base_address": hex(peripheral.base_address),
623
+ "registers": list(peripheral.registers.keys()),
624
+ }
625
+ )
626
+
627
+ # Peripheral initialization
628
+ elif peripheral.peripheral_type in ("UART", "SPI", "I2C", "ADC"):
629
+ init_ops.append(
630
+ {
631
+ "step": "peripheral_init",
632
+ "peripheral": peripheral.peripheral_type,
633
+ "base_address": hex(peripheral.base_address),
634
+ }
635
+ )
636
+
637
+ return init_ops
638
+
639
+ def _calculate_confidence(self, framework_confidence: float) -> float:
640
+ """Calculate overall detection confidence.
641
+
642
+ Args:
643
+ framework_confidence: HAL framework detection confidence.
644
+
645
+ Returns:
646
+ Overall confidence score (0.0-1.0).
647
+ """
648
+ # Weight different factors
649
+ weights = {
650
+ "framework": 0.4,
651
+ "peripherals": 0.3,
652
+ "registers": 0.3,
653
+ }
654
+
655
+ # Framework confidence
656
+ conf = framework_confidence * weights["framework"]
657
+
658
+ # Peripheral detection confidence
659
+ if len(self.peripherals) > 0:
660
+ conf += min(len(self.peripherals) / 5.0, 1.0) * weights["peripherals"]
661
+
662
+ # Register access confidence
663
+ if len(self.register_accesses) > 0:
664
+ conf += min(len(self.register_accesses) / 20.0, 1.0) * weights["registers"]
665
+
666
+ return min(conf, 1.0)
667
+
668
+ def _get_detected_signatures(self, binary_data: bytes) -> list[str]:
669
+ """Get list of detected HAL framework signatures.
670
+
671
+ Args:
672
+ binary_data: Firmware binary data.
673
+
674
+ Returns:
675
+ List of detected signature strings.
676
+ """
677
+ signatures: list[str] = []
678
+
679
+ if self.hal_framework in HAL_SIGNATURES:
680
+ patterns = HAL_SIGNATURES[self.hal_framework]["patterns"]
681
+
682
+ for pattern in patterns:
683
+ if pattern in binary_data:
684
+ signatures.append(pattern.decode("utf-8", errors="ignore"))
685
+
686
+ return signatures
687
+
688
+ def export_to_json(self, result: HALAnalysisResult, *, indent: int = 2) -> str:
689
+ """Export HAL analysis result to JSON.
690
+
691
+ Args:
692
+ result: HAL analysis result.
693
+ indent: JSON indentation level.
694
+
695
+ Returns:
696
+ JSON string representation.
697
+
698
+ Example:
699
+ >>> detector = HALDetector()
700
+ >>> result = HALAnalysisResult(
701
+ ... detected_hal="STM32 HAL",
702
+ ... peripherals=[],
703
+ ... register_map={},
704
+ ... initialization_sequence=[],
705
+ ... confidence=0.8
706
+ ... )
707
+ >>> json_str = detector.export_to_json(result)
708
+ >>> "STM32 HAL" in json_str
709
+ True
710
+ """
711
+ data = {
712
+ "hal_framework": result.detected_hal,
713
+ "confidence": result.confidence,
714
+ "framework_signatures": result.framework_signatures,
715
+ "peripherals": [
716
+ {
717
+ "type": p.peripheral_type,
718
+ "base_address": hex(p.base_address),
719
+ "registers": {hex(offset): name for offset, name in p.registers.items()},
720
+ "access_count": len(p.access_patterns),
721
+ }
722
+ for p in result.peripherals
723
+ ],
724
+ "register_map": {hex(addr): desc for addr, desc in result.register_map.items()},
725
+ "initialization_sequence": result.initialization_sequence,
726
+ }
727
+
728
+ return json.dumps(data, indent=indent)
729
+
730
+
731
+ __all__ = [
732
+ "HALAnalysisResult",
733
+ "HALDetector",
734
+ "Peripheral",
735
+ "RegisterAccess",
736
+ ]