scientific-method-engine 0.9.0__tar.gz → 0.9.1__tar.gz

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 (58) hide show
  1. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/PKG-INFO +1 -1
  2. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/pyproject.toml +1 -1
  3. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/trace.py +5 -2
  4. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/tests/test_table_continuations.py +49 -0
  5. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/.gitignore +0 -0
  6. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/LICENSE +0 -0
  7. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/README.md +0 -0
  8. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/__init__.py +0 -0
  9. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/__main__.py +0 -0
  10. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/cli.py +0 -0
  11. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ClearNoReturnFunctions.java +0 -0
  12. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/CreateFunctions.java +0 -0
  13. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ExportBoundedFlow.java +0 -0
  14. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ExportCallEdges.java +0 -0
  15. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ExportFunctionFingerprints.java +0 -0
  16. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ExportFunctionInventory.java +0 -0
  17. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/MergeFallThroughFragment.java +0 -0
  18. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/RecoverCitedFunctions.java +0 -0
  19. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/RepairReturningCallers.java +0 -0
  20. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportCallArguments.java +0 -0
  21. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportCallPaths.java +0 -0
  22. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportCallSitesWithScalars.java +0 -0
  23. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportCallsToRange.java +0 -0
  24. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportConstantFirstArgumentCalls.java +0 -0
  25. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportDataBytes.java +0 -0
  26. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportDecompileMatches.java +0 -0
  27. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportDecompileWindow.java +0 -0
  28. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportFilePatternInMemory.java +0 -0
  29. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportFirstArgumentCallSummary.java +0 -0
  30. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportFunctionScalarConstants.java +0 -0
  31. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportFunctionSummary.java +0 -0
  32. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportInstructionContext.java +0 -0
  33. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportInstructionWindow.java +0 -0
  34. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportMemoryBlockForFileOffset.java +0 -0
  35. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportMemoryBlocks.java +0 -0
  36. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportRandomnessCandidates.java +0 -0
  37. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportReferences.java +0 -0
  38. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportScalarConstants.java +0 -0
  39. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportStringReferences.java +0 -0
  40. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/ghidra/ReportSymbolReferences.java +0 -0
  41. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/__init__.py +0 -0
  42. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/dispatch.py +0 -0
  43. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/effect_order.py +0 -0
  44. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/image.py +0 -0
  45. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/machine.py +0 -0
  46. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/pcode.py +0 -0
  47. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/pcode_backend.py +0 -0
  48. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/pe.py +0 -0
  49. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/reports.py +0 -0
  50. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/result_flow.py +0 -0
  51. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/src/scientific_method_engine/x86/values.py +0 -0
  52. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/tests/oracle.py +0 -0
  53. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/tests/test_dispatch.py +0 -0
  54. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/tests/test_effect_order.py +0 -0
  55. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/tests/test_nested_frame_request.py +0 -0
  56. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/tests/test_oracle.py +0 -0
  57. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/tests/test_pe.py +0 -0
  58. {scientific_method_engine-0.9.0 → scientific_method_engine-0.9.1}/tests/test_x86.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: scientific-method-engine
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Bounded instruction-derived x86 evidence reports for segmented MZ/FBOV and PE32/i386 code.
5
5
  Project-URL: Source, https://github.com/kibertoad/refurbished-dinosaurs-toolkit/tree/main/packages/scientific-method-engine
6
6
  Author: kibertoad
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
5
5
  [project]
6
6
  name = "scientific-method-engine"
7
7
  # The release workflow writes the published version from the package's release tag.
8
- version = "0.9.0"
8
+ version = "0.9.1"
9
9
  description = "Bounded instruction-derived x86 evidence reports for segmented MZ/FBOV and PE32/i386 code."
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.12"
@@ -305,8 +305,11 @@ def trace(image, config, continue_declared_jumps=True):
305
305
  if boundary_budget < 1:
306
306
  global_gaps.append({"site": s.at, "reason": "conditional table boundary instruction limit"})
307
307
  return
308
- seen, walk_gaps, _, undecoded, contested = walk(image, [root_entry], boundary_budget)
309
- boundary_budget -= max(1, len(seen) + len(undecoded) + len(contested))
308
+ seen, walk_gaps, _, _, contested = walk(image, [root_entry], boundary_budget)
309
+ # walk drops rejected overlapping starts and contested instructions from seen,
310
+ # but it decoded them, so they are charged with the established ones.
311
+ overlapping = sum(g["reason"] == OVERLAP_REASON for g in walk_gaps)
312
+ boundary_budget -= max(1, len(seen) + len(contested) + overlapping)
310
313
  # A truncated walk never saw the instructions that could contest a target start.
311
314
  if any(g["reason"] == "instruction limit" for g in walk_gaps):
312
315
  seen = {}
@@ -141,6 +141,55 @@ class TableContinuationTests(unittest.TestCase):
141
141
  self.assertFalse(r["declaredContinuationPaths"])
142
142
  self.assertTrue(any("boundary" in g["reason"] for g in r["gaps"]))
143
143
 
144
+ def test_boundary_budget_counts_only_decoded_instructions(self):
145
+ # Two functions each end in a declared table jump and branch once outside every region.
146
+ # Their boundary walks decode four instructions apiece and leave most region bytes uncovered.
147
+ data = bytearray([0x90] * 56)
148
+ data[0:6] = bytes.fromhex("72 06 e8 0b 00 c3")
149
+ data[8:12] = bytes.fromhex("e8 15 00 c3")
150
+ data[16:20] = bytes.fromhex("74 7f ff e3")
151
+ data[22:24] = bytes.fromhex("c3 c3")
152
+ data[32:36] = bytes.fromhex("74 7f ff e3")
153
+ data[38:40] = bytes.fromhex("c3 c3")
154
+ data[48:56] = bytes.fromhex("16 00 17 00 26 00 27 00")
155
+ jumps = [{"site": site, "exhaustive": True, "evidence": "constructed BX consumer",
156
+ "table": {"start": table, "count": 2, "stride": 2, "evidence": "constructed words"}}
157
+ for site, table in ((18, 48), (34, 52))]
158
+ config = {"entry": 0, "indirectJumps": jumps,
159
+ "regions": [{"name": "code", "start": 0, "end": 48, "segment": 4096, "ip": 0,
160
+ "entries": [0], "evidence": "constructed mappings"}]}
161
+ r = run_report(bytes(data), {**config, "instructionLimit": 8}, "trace")
162
+ self.assertEqual(len(r["declaredContinuationPaths"]), 4)
163
+ self.assertFalse(any("boundary" in g["reason"] for g in r["gaps"]))
164
+ r = run_report(bytes(data), {**config, "instructionLimit": 7}, "trace")
165
+ self.assertEqual(len(r["declaredContinuationPaths"]), 2)
166
+ self.assertTrue(any("boundary" in g["reason"] for g in r["gaps"]))
167
+
168
+ def test_boundary_budget_charges_rejected_overlapping_instructions(self):
169
+ # The function at 32 is walked first. Its side branch calls into its own call
170
+ # (40 e8 ff ff -> 42 inc bx) and returns to 43, so 40, 42 and 43 overlap and 44
171
+ # is reached only through them. The walk decodes eight instructions and establishes four.
172
+ data = bytearray([0x90] * 56)
173
+ data[0:6] = bytes.fromhex("72 06 e8 0b 00 c3")
174
+ data[8:12] = bytes.fromhex("e8 15 00 c3")
175
+ data[16:20] = bytes.fromhex("74 7f ff e3")
176
+ data[22:24] = bytes.fromhex("c3 c3")
177
+ data[32:36] = bytes.fromhex("74 06 ff e3")
178
+ data[38:40] = bytes.fromhex("c3 c3")
179
+ data[40:45] = bytes.fromhex("e8 ff ff c3 c3")
180
+ data[48:56] = bytes.fromhex("16 00 17 00 26 00 27 00")
181
+ jumps = [{"site": site, "exhaustive": True, "evidence": "constructed BX consumer",
182
+ "table": {"start": table, "count": 2, "stride": 2, "evidence": "constructed words"}}
183
+ for site, table in ((18, 48), (34, 52))]
184
+ config = {"entry": 0, "indirectJumps": jumps,
185
+ "regions": [{"name": "code", "start": 0, "end": 48, "segment": 4096, "ip": 0,
186
+ "entries": [0], "evidence": "constructed mappings"}]}
187
+ r = run_report(bytes(data), {**config, "instructionLimit": 12}, "trace")
188
+ self.assertEqual(len(r["declaredContinuationPaths"]), 4)
189
+ r = run_report(bytes(data), {**config, "instructionLimit": 11}, "trace")
190
+ self.assertEqual(len(r["declaredContinuationPaths"]), 2)
191
+ self.assertTrue(any("boundary" in g["reason"] for g in r["gaps"]))
192
+
144
193
  def test_field_address_uses_the_table_region_mapping(self):
145
194
  data, config = fixture()
146
195
  data = bytearray(data)