oscura 0.5.1__py3-none-any.whl → 0.7.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 (497) 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/edges.py +325 -65
  5. oscura/analyzers/digital/quality.py +293 -166
  6. oscura/analyzers/digital/timing.py +260 -115
  7. oscura/analyzers/digital/timing_numba.py +334 -0
  8. oscura/analyzers/entropy.py +605 -0
  9. oscura/analyzers/eye/diagram.py +176 -109
  10. oscura/analyzers/eye/metrics.py +5 -5
  11. oscura/analyzers/jitter/__init__.py +6 -4
  12. oscura/analyzers/jitter/ber.py +52 -52
  13. oscura/analyzers/jitter/classification.py +156 -0
  14. oscura/analyzers/jitter/decomposition.py +163 -113
  15. oscura/analyzers/jitter/spectrum.py +80 -64
  16. oscura/analyzers/ml/__init__.py +39 -0
  17. oscura/analyzers/ml/features.py +600 -0
  18. oscura/analyzers/ml/signal_classifier.py +604 -0
  19. oscura/analyzers/packet/daq.py +246 -158
  20. oscura/analyzers/packet/parser.py +12 -1
  21. oscura/analyzers/packet/payload.py +50 -2110
  22. oscura/analyzers/packet/payload_analysis.py +361 -181
  23. oscura/analyzers/packet/payload_patterns.py +133 -70
  24. oscura/analyzers/packet/stream.py +84 -23
  25. oscura/analyzers/patterns/__init__.py +26 -5
  26. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  27. oscura/analyzers/patterns/clustering.py +169 -108
  28. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  29. oscura/analyzers/patterns/discovery.py +1 -1
  30. oscura/analyzers/patterns/matching.py +581 -197
  31. oscura/analyzers/patterns/pattern_mining.py +778 -0
  32. oscura/analyzers/patterns/periodic.py +121 -38
  33. oscura/analyzers/patterns/sequences.py +175 -78
  34. oscura/analyzers/power/conduction.py +1 -1
  35. oscura/analyzers/power/soa.py +6 -6
  36. oscura/analyzers/power/switching.py +250 -110
  37. oscura/analyzers/protocol/__init__.py +17 -1
  38. oscura/analyzers/protocols/base.py +6 -6
  39. oscura/analyzers/protocols/ble/__init__.py +38 -0
  40. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  41. oscura/analyzers/protocols/ble/uuids.py +288 -0
  42. oscura/analyzers/protocols/can.py +257 -127
  43. oscura/analyzers/protocols/can_fd.py +107 -80
  44. oscura/analyzers/protocols/flexray.py +139 -80
  45. oscura/analyzers/protocols/hdlc.py +93 -58
  46. oscura/analyzers/protocols/i2c.py +247 -106
  47. oscura/analyzers/protocols/i2s.py +138 -86
  48. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  49. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  50. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  51. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  52. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  53. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  54. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  55. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  56. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  57. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  58. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  59. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  60. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  61. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  62. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  63. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  64. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  65. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  66. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  67. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  68. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  69. oscura/analyzers/protocols/jtag.py +180 -98
  70. oscura/analyzers/protocols/lin.py +219 -114
  71. oscura/analyzers/protocols/manchester.py +4 -4
  72. oscura/analyzers/protocols/onewire.py +253 -149
  73. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  74. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  75. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  76. oscura/analyzers/protocols/spi.py +192 -95
  77. oscura/analyzers/protocols/swd.py +321 -167
  78. oscura/analyzers/protocols/uart.py +267 -125
  79. oscura/analyzers/protocols/usb.py +235 -131
  80. oscura/analyzers/side_channel/power.py +17 -12
  81. oscura/analyzers/signal/__init__.py +15 -0
  82. oscura/analyzers/signal/timing_analysis.py +1086 -0
  83. oscura/analyzers/signal_integrity/__init__.py +4 -1
  84. oscura/analyzers/signal_integrity/sparams.py +2 -19
  85. oscura/analyzers/spectral/chunked.py +129 -60
  86. oscura/analyzers/spectral/chunked_fft.py +300 -94
  87. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  88. oscura/analyzers/statistical/checksum.py +376 -217
  89. oscura/analyzers/statistical/classification.py +229 -107
  90. oscura/analyzers/statistical/entropy.py +78 -53
  91. oscura/analyzers/statistics/correlation.py +407 -211
  92. oscura/analyzers/statistics/outliers.py +2 -2
  93. oscura/analyzers/statistics/streaming.py +30 -5
  94. oscura/analyzers/validation.py +216 -101
  95. oscura/analyzers/waveform/measurements.py +9 -0
  96. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  97. oscura/analyzers/waveform/spectral.py +500 -228
  98. oscura/api/__init__.py +31 -5
  99. oscura/api/dsl/__init__.py +582 -0
  100. oscura/{dsl → api/dsl}/commands.py +43 -76
  101. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  102. oscura/{dsl → api/dsl}/parser.py +107 -77
  103. oscura/{dsl → api/dsl}/repl.py +2 -2
  104. oscura/api/dsl.py +1 -1
  105. oscura/{integrations → api/integrations}/__init__.py +1 -1
  106. oscura/{integrations → api/integrations}/llm.py +201 -102
  107. oscura/api/operators.py +3 -3
  108. oscura/api/optimization.py +144 -30
  109. oscura/api/rest_server.py +921 -0
  110. oscura/api/server/__init__.py +17 -0
  111. oscura/api/server/dashboard.py +850 -0
  112. oscura/api/server/static/README.md +34 -0
  113. oscura/api/server/templates/base.html +181 -0
  114. oscura/api/server/templates/export.html +120 -0
  115. oscura/api/server/templates/home.html +284 -0
  116. oscura/api/server/templates/protocols.html +58 -0
  117. oscura/api/server/templates/reports.html +43 -0
  118. oscura/api/server/templates/session_detail.html +89 -0
  119. oscura/api/server/templates/sessions.html +83 -0
  120. oscura/api/server/templates/waveforms.html +73 -0
  121. oscura/automotive/__init__.py +8 -1
  122. oscura/automotive/can/__init__.py +10 -0
  123. oscura/automotive/can/checksum.py +3 -1
  124. oscura/automotive/can/dbc_generator.py +590 -0
  125. oscura/automotive/can/message_wrapper.py +121 -74
  126. oscura/automotive/can/patterns.py +98 -21
  127. oscura/automotive/can/session.py +292 -56
  128. oscura/automotive/can/state_machine.py +6 -3
  129. oscura/automotive/can/stimulus_response.py +97 -75
  130. oscura/automotive/dbc/__init__.py +10 -2
  131. oscura/automotive/dbc/generator.py +84 -56
  132. oscura/automotive/dbc/parser.py +6 -6
  133. oscura/automotive/dtc/data.json +17 -102
  134. oscura/automotive/dtc/database.py +2 -2
  135. oscura/automotive/flexray/__init__.py +31 -0
  136. oscura/automotive/flexray/analyzer.py +504 -0
  137. oscura/automotive/flexray/crc.py +185 -0
  138. oscura/automotive/flexray/fibex.py +449 -0
  139. oscura/automotive/j1939/__init__.py +45 -8
  140. oscura/automotive/j1939/analyzer.py +605 -0
  141. oscura/automotive/j1939/spns.py +326 -0
  142. oscura/automotive/j1939/transport.py +306 -0
  143. oscura/automotive/lin/__init__.py +47 -0
  144. oscura/automotive/lin/analyzer.py +612 -0
  145. oscura/automotive/loaders/blf.py +13 -2
  146. oscura/automotive/loaders/csv_can.py +143 -72
  147. oscura/automotive/loaders/dispatcher.py +50 -2
  148. oscura/automotive/loaders/mdf.py +86 -45
  149. oscura/automotive/loaders/pcap.py +111 -61
  150. oscura/automotive/uds/__init__.py +4 -0
  151. oscura/automotive/uds/analyzer.py +725 -0
  152. oscura/automotive/uds/decoder.py +140 -58
  153. oscura/automotive/uds/models.py +7 -1
  154. oscura/automotive/visualization.py +1 -1
  155. oscura/cli/analyze.py +348 -0
  156. oscura/cli/batch.py +142 -122
  157. oscura/cli/benchmark.py +275 -0
  158. oscura/cli/characterize.py +137 -82
  159. oscura/cli/compare.py +224 -131
  160. oscura/cli/completion.py +250 -0
  161. oscura/cli/config_cmd.py +361 -0
  162. oscura/cli/decode.py +164 -87
  163. oscura/cli/export.py +286 -0
  164. oscura/cli/main.py +115 -31
  165. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  166. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  167. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  168. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  169. oscura/cli/progress.py +147 -0
  170. oscura/cli/shell.py +157 -135
  171. oscura/cli/validate_cmd.py +204 -0
  172. oscura/cli/visualize.py +158 -0
  173. oscura/convenience.py +125 -79
  174. oscura/core/__init__.py +4 -2
  175. oscura/core/backend_selector.py +3 -3
  176. oscura/core/cache.py +126 -15
  177. oscura/core/cancellation.py +1 -1
  178. oscura/{config → core/config}/__init__.py +20 -11
  179. oscura/{config → core/config}/defaults.py +1 -1
  180. oscura/{config → core/config}/loader.py +7 -5
  181. oscura/{config → core/config}/memory.py +5 -5
  182. oscura/{config → core/config}/migration.py +1 -1
  183. oscura/{config → core/config}/pipeline.py +99 -23
  184. oscura/{config → core/config}/preferences.py +1 -1
  185. oscura/{config → core/config}/protocol.py +3 -3
  186. oscura/{config → core/config}/schema.py +426 -272
  187. oscura/{config → core/config}/settings.py +1 -1
  188. oscura/{config → core/config}/thresholds.py +195 -153
  189. oscura/core/correlation.py +5 -6
  190. oscura/core/cross_domain.py +0 -2
  191. oscura/core/debug.py +9 -5
  192. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  193. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  194. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  195. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  196. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  197. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  198. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  199. oscura/core/gpu_backend.py +11 -7
  200. oscura/core/log_query.py +101 -11
  201. oscura/core/logging.py +126 -54
  202. oscura/core/logging_advanced.py +5 -5
  203. oscura/core/memory_limits.py +108 -70
  204. oscura/core/memory_monitor.py +2 -2
  205. oscura/core/memory_progress.py +7 -7
  206. oscura/core/memory_warnings.py +1 -1
  207. oscura/core/numba_backend.py +13 -13
  208. oscura/{plugins → core/plugins}/__init__.py +9 -9
  209. oscura/{plugins → core/plugins}/base.py +7 -7
  210. oscura/{plugins → core/plugins}/cli.py +3 -3
  211. oscura/{plugins → core/plugins}/discovery.py +186 -106
  212. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  213. oscura/{plugins → core/plugins}/manager.py +7 -7
  214. oscura/{plugins → core/plugins}/registry.py +3 -3
  215. oscura/{plugins → core/plugins}/versioning.py +1 -1
  216. oscura/core/progress.py +16 -1
  217. oscura/core/provenance.py +8 -2
  218. oscura/{schemas → core/schemas}/__init__.py +2 -2
  219. oscura/{schemas → core/schemas}/device_mapping.json +2 -8
  220. oscura/{schemas → core/schemas}/packet_format.json +4 -24
  221. oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
  222. oscura/core/types.py +4 -0
  223. oscura/core/uncertainty.py +3 -3
  224. oscura/correlation/__init__.py +52 -0
  225. oscura/correlation/multi_protocol.py +811 -0
  226. oscura/discovery/auto_decoder.py +117 -35
  227. oscura/discovery/comparison.py +191 -86
  228. oscura/discovery/quality_validator.py +155 -68
  229. oscura/discovery/signal_detector.py +196 -79
  230. oscura/export/__init__.py +18 -8
  231. oscura/export/kaitai_struct.py +513 -0
  232. oscura/export/scapy_layer.py +801 -0
  233. oscura/export/wireshark/generator.py +1 -1
  234. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  235. oscura/export/wireshark_dissector.py +746 -0
  236. oscura/guidance/wizard.py +207 -111
  237. oscura/hardware/__init__.py +19 -0
  238. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  239. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  240. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  241. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  242. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  243. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  244. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  245. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  246. oscura/hardware/firmware/__init__.py +29 -0
  247. oscura/hardware/firmware/pattern_recognition.py +874 -0
  248. oscura/hardware/hal_detector.py +736 -0
  249. oscura/hardware/security/__init__.py +37 -0
  250. oscura/hardware/security/side_channel_detector.py +1126 -0
  251. oscura/inference/__init__.py +4 -0
  252. oscura/inference/active_learning/observation_table.py +4 -1
  253. oscura/inference/alignment.py +216 -123
  254. oscura/inference/bayesian.py +113 -33
  255. oscura/inference/crc_reverse.py +101 -55
  256. oscura/inference/logic.py +6 -2
  257. oscura/inference/message_format.py +342 -183
  258. oscura/inference/protocol.py +95 -44
  259. oscura/inference/protocol_dsl.py +180 -82
  260. oscura/inference/signal_intelligence.py +1439 -706
  261. oscura/inference/spectral.py +99 -57
  262. oscura/inference/state_machine.py +810 -158
  263. oscura/inference/stream.py +270 -110
  264. oscura/iot/__init__.py +34 -0
  265. oscura/iot/coap/__init__.py +32 -0
  266. oscura/iot/coap/analyzer.py +668 -0
  267. oscura/iot/coap/options.py +212 -0
  268. oscura/iot/lorawan/__init__.py +21 -0
  269. oscura/iot/lorawan/crypto.py +206 -0
  270. oscura/iot/lorawan/decoder.py +801 -0
  271. oscura/iot/lorawan/mac_commands.py +341 -0
  272. oscura/iot/mqtt/__init__.py +27 -0
  273. oscura/iot/mqtt/analyzer.py +999 -0
  274. oscura/iot/mqtt/properties.py +315 -0
  275. oscura/iot/zigbee/__init__.py +31 -0
  276. oscura/iot/zigbee/analyzer.py +615 -0
  277. oscura/iot/zigbee/security.py +153 -0
  278. oscura/iot/zigbee/zcl.py +349 -0
  279. oscura/jupyter/display.py +125 -45
  280. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  281. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  282. oscura/jupyter/exploratory/fuzzy.py +746 -0
  283. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  284. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  285. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  286. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  287. oscura/jupyter/exploratory/sync.py +612 -0
  288. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  289. oscura/jupyter/magic.py +4 -4
  290. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  291. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  292. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  293. oscura/loaders/__init__.py +183 -67
  294. oscura/loaders/binary.py +88 -1
  295. oscura/loaders/chipwhisperer.py +153 -137
  296. oscura/loaders/configurable.py +208 -86
  297. oscura/loaders/csv_loader.py +458 -215
  298. oscura/loaders/hdf5_loader.py +278 -119
  299. oscura/loaders/lazy.py +87 -54
  300. oscura/loaders/mmap_loader.py +1 -1
  301. oscura/loaders/numpy_loader.py +253 -116
  302. oscura/loaders/pcap.py +226 -151
  303. oscura/loaders/rigol.py +110 -49
  304. oscura/loaders/sigrok.py +201 -78
  305. oscura/loaders/tdms.py +81 -58
  306. oscura/loaders/tektronix.py +291 -174
  307. oscura/loaders/touchstone.py +182 -87
  308. oscura/loaders/tss.py +456 -0
  309. oscura/loaders/vcd.py +215 -117
  310. oscura/loaders/wav.py +155 -68
  311. oscura/reporting/__init__.py +9 -0
  312. oscura/reporting/analyze.py +352 -146
  313. oscura/reporting/argument_preparer.py +69 -14
  314. oscura/reporting/auto_report.py +97 -61
  315. oscura/reporting/batch.py +131 -58
  316. oscura/reporting/chart_selection.py +57 -45
  317. oscura/reporting/comparison.py +63 -17
  318. oscura/reporting/content/executive.py +76 -24
  319. oscura/reporting/core_formats/multi_format.py +11 -8
  320. oscura/reporting/engine.py +312 -158
  321. oscura/reporting/enhanced_reports.py +949 -0
  322. oscura/reporting/export.py +86 -43
  323. oscura/reporting/formatting/numbers.py +69 -42
  324. oscura/reporting/html.py +139 -58
  325. oscura/reporting/index.py +137 -65
  326. oscura/reporting/output.py +158 -67
  327. oscura/reporting/pdf.py +67 -102
  328. oscura/reporting/plots.py +191 -112
  329. oscura/reporting/sections.py +88 -47
  330. oscura/reporting/standards.py +104 -61
  331. oscura/reporting/summary_generator.py +75 -55
  332. oscura/reporting/tables.py +138 -54
  333. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  334. oscura/sessions/__init__.py +14 -23
  335. oscura/sessions/base.py +3 -3
  336. oscura/sessions/blackbox.py +106 -10
  337. oscura/sessions/generic.py +2 -2
  338. oscura/sessions/legacy.py +783 -0
  339. oscura/side_channel/__init__.py +63 -0
  340. oscura/side_channel/dpa.py +1025 -0
  341. oscura/utils/__init__.py +15 -1
  342. oscura/utils/bitwise.py +118 -0
  343. oscura/{builders → utils/builders}/__init__.py +1 -1
  344. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  345. oscura/{comparison → utils/comparison}/compare.py +202 -101
  346. oscura/{comparison → utils/comparison}/golden.py +83 -63
  347. oscura/{comparison → utils/comparison}/limits.py +313 -89
  348. oscura/{comparison → utils/comparison}/mask.py +151 -45
  349. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  350. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  351. oscura/{component → utils/component}/__init__.py +3 -3
  352. oscura/{component → utils/component}/impedance.py +122 -58
  353. oscura/{component → utils/component}/reactive.py +165 -168
  354. oscura/{component → utils/component}/transmission_line.py +3 -3
  355. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  356. oscura/{filtering → utils/filtering}/base.py +1 -1
  357. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  358. oscura/{filtering → utils/filtering}/design.py +169 -93
  359. oscura/{filtering → utils/filtering}/filters.py +2 -2
  360. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  361. oscura/utils/geometry.py +31 -0
  362. oscura/utils/imports.py +184 -0
  363. oscura/utils/lazy.py +1 -1
  364. oscura/{math → utils/math}/__init__.py +2 -2
  365. oscura/{math → utils/math}/arithmetic.py +114 -48
  366. oscura/{math → utils/math}/interpolation.py +139 -106
  367. oscura/utils/memory.py +129 -66
  368. oscura/utils/memory_advanced.py +92 -9
  369. oscura/utils/memory_extensions.py +10 -8
  370. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  371. oscura/{optimization → utils/optimization}/search.py +2 -2
  372. oscura/utils/performance/__init__.py +58 -0
  373. oscura/utils/performance/caching.py +889 -0
  374. oscura/utils/performance/lsh_clustering.py +333 -0
  375. oscura/utils/performance/memory_optimizer.py +699 -0
  376. oscura/utils/performance/optimizations.py +675 -0
  377. oscura/utils/performance/parallel.py +654 -0
  378. oscura/utils/performance/profiling.py +661 -0
  379. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  380. oscura/{pipeline → utils/pipeline}/composition.py +1 -1
  381. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  382. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  383. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  384. oscura/{search → utils/search}/__init__.py +3 -3
  385. oscura/{search → utils/search}/anomaly.py +188 -58
  386. oscura/utils/search/context.py +294 -0
  387. oscura/{search → utils/search}/pattern.py +138 -10
  388. oscura/utils/serial.py +51 -0
  389. oscura/utils/storage/__init__.py +61 -0
  390. oscura/utils/storage/database.py +1166 -0
  391. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  392. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  393. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  394. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  395. oscura/{triggering → utils/triggering}/base.py +6 -6
  396. oscura/{triggering → utils/triggering}/edge.py +2 -2
  397. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  398. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  399. oscura/{triggering → utils/triggering}/window.py +2 -2
  400. oscura/utils/validation.py +32 -0
  401. oscura/validation/__init__.py +121 -0
  402. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  403. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  404. oscura/{compliance → validation/compliance}/masks.py +1 -1
  405. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  406. oscura/{compliance → validation/compliance}/testing.py +114 -52
  407. oscura/validation/compliance_tests.py +915 -0
  408. oscura/validation/fuzzer.py +990 -0
  409. oscura/validation/grammar_tests.py +596 -0
  410. oscura/validation/grammar_validator.py +904 -0
  411. oscura/validation/hil_testing.py +977 -0
  412. oscura/{quality → validation/quality}/__init__.py +4 -4
  413. oscura/{quality → validation/quality}/ensemble.py +251 -171
  414. oscura/{quality → validation/quality}/explainer.py +3 -3
  415. oscura/{quality → validation/quality}/scoring.py +1 -1
  416. oscura/{quality → validation/quality}/warnings.py +4 -4
  417. oscura/validation/regression_suite.py +808 -0
  418. oscura/validation/replay.py +788 -0
  419. oscura/{testing → validation/testing}/__init__.py +2 -2
  420. oscura/{testing → validation/testing}/synthetic.py +5 -5
  421. oscura/visualization/__init__.py +9 -0
  422. oscura/visualization/accessibility.py +1 -1
  423. oscura/visualization/annotations.py +64 -67
  424. oscura/visualization/colors.py +7 -7
  425. oscura/visualization/digital.py +180 -81
  426. oscura/visualization/eye.py +236 -85
  427. oscura/visualization/interactive.py +320 -143
  428. oscura/visualization/jitter.py +587 -247
  429. oscura/visualization/layout.py +169 -134
  430. oscura/visualization/optimization.py +103 -52
  431. oscura/visualization/palettes.py +1 -1
  432. oscura/visualization/power.py +427 -211
  433. oscura/visualization/power_extended.py +626 -297
  434. oscura/visualization/presets.py +2 -0
  435. oscura/visualization/protocols.py +495 -181
  436. oscura/visualization/render.py +79 -63
  437. oscura/visualization/reverse_engineering.py +171 -124
  438. oscura/visualization/signal_integrity.py +460 -279
  439. oscura/visualization/specialized.py +190 -100
  440. oscura/visualization/spectral.py +670 -255
  441. oscura/visualization/thumbnails.py +166 -137
  442. oscura/visualization/waveform.py +150 -63
  443. oscura/workflows/__init__.py +3 -0
  444. oscura/{batch → workflows/batch}/__init__.py +5 -5
  445. oscura/{batch → workflows/batch}/advanced.py +150 -75
  446. oscura/workflows/batch/aggregate.py +531 -0
  447. oscura/workflows/batch/analyze.py +236 -0
  448. oscura/{batch → workflows/batch}/logging.py +2 -2
  449. oscura/{batch → workflows/batch}/metrics.py +1 -1
  450. oscura/workflows/complete_re.py +1144 -0
  451. oscura/workflows/compliance.py +44 -54
  452. oscura/workflows/digital.py +197 -51
  453. oscura/workflows/legacy/__init__.py +12 -0
  454. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  455. oscura/workflows/multi_trace.py +9 -9
  456. oscura/workflows/power.py +42 -62
  457. oscura/workflows/protocol.py +82 -49
  458. oscura/workflows/reverse_engineering.py +351 -150
  459. oscura/workflows/signal_integrity.py +157 -82
  460. oscura-0.7.0.dist-info/METADATA +661 -0
  461. oscura-0.7.0.dist-info/RECORD +591 -0
  462. oscura/batch/aggregate.py +0 -300
  463. oscura/batch/analyze.py +0 -139
  464. oscura/dsl/__init__.py +0 -73
  465. oscura/exceptions.py +0 -59
  466. oscura/exploratory/fuzzy.py +0 -513
  467. oscura/exploratory/sync.py +0 -384
  468. oscura/exporters/__init__.py +0 -94
  469. oscura/exporters/csv.py +0 -303
  470. oscura/exporters/exporters.py +0 -44
  471. oscura/exporters/hdf5.py +0 -217
  472. oscura/exporters/html_export.py +0 -701
  473. oscura/exporters/json_export.py +0 -291
  474. oscura/exporters/markdown_export.py +0 -367
  475. oscura/exporters/matlab_export.py +0 -354
  476. oscura/exporters/npz_export.py +0 -219
  477. oscura/exporters/spice_export.py +0 -210
  478. oscura/search/context.py +0 -149
  479. oscura/session/__init__.py +0 -34
  480. oscura/session/annotations.py +0 -289
  481. oscura/session/history.py +0 -313
  482. oscura/session/session.py +0 -520
  483. oscura/workflow/__init__.py +0 -13
  484. oscura-0.5.1.dist-info/METADATA +0 -583
  485. oscura-0.5.1.dist-info/RECORD +0 -481
  486. /oscura/core/{config.py → config/legacy.py} +0 -0
  487. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  488. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  489. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  490. /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
  491. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  492. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  493. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  494. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  495. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
  496. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
  497. {oscura-0.5.1.dist-info → oscura-0.7.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
+ ]