scientific-method-engine 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. scientific_method_engine/__init__.py +5 -0
  2. scientific_method_engine/__main__.py +3 -0
  3. scientific_method_engine/cli.py +75 -0
  4. scientific_method_engine/ghidra/ClearNoReturnFunctions.java +22 -0
  5. scientific_method_engine/ghidra/CreateFunctions.java +27 -0
  6. scientific_method_engine/ghidra/ExportBoundedFlow.java +59 -0
  7. scientific_method_engine/ghidra/ExportFunctionFingerprints.java +135 -0
  8. scientific_method_engine/ghidra/ExportFunctionInventory.java +50 -0
  9. scientific_method_engine/ghidra/MergeFallThroughFragment.java +63 -0
  10. scientific_method_engine/ghidra/RecoverCitedFunctions.java +93 -0
  11. scientific_method_engine/ghidra/RepairReturningCallers.java +76 -0
  12. scientific_method_engine/ghidra/ReportCallArguments.java +65 -0
  13. scientific_method_engine/ghidra/ReportCallPaths.java +106 -0
  14. scientific_method_engine/ghidra/ReportCallSitesWithScalars.java +95 -0
  15. scientific_method_engine/ghidra/ReportCallsToRange.java +67 -0
  16. scientific_method_engine/ghidra/ReportConstantFirstArgumentCalls.java +55 -0
  17. scientific_method_engine/ghidra/ReportDataBytes.java +36 -0
  18. scientific_method_engine/ghidra/ReportDecompileMatches.java +71 -0
  19. scientific_method_engine/ghidra/ReportDecompileWindow.java +61 -0
  20. scientific_method_engine/ghidra/ReportFilePatternInMemory.java +102 -0
  21. scientific_method_engine/ghidra/ReportFirstArgumentCallSummary.java +63 -0
  22. scientific_method_engine/ghidra/ReportFunctionScalarConstants.java +56 -0
  23. scientific_method_engine/ghidra/ReportFunctionSummary.java +64 -0
  24. scientific_method_engine/ghidra/ReportInstructionContext.java +64 -0
  25. scientific_method_engine/ghidra/ReportInstructionWindow.java +36 -0
  26. scientific_method_engine/ghidra/ReportMemoryBlockForFileOffset.java +105 -0
  27. scientific_method_engine/ghidra/ReportMemoryBlocks.java +52 -0
  28. scientific_method_engine/ghidra/ReportRandomnessCandidates.java +74 -0
  29. scientific_method_engine/ghidra/ReportReferences.java +42 -0
  30. scientific_method_engine/ghidra/ReportScalarConstants.java +55 -0
  31. scientific_method_engine/ghidra/ReportStringReferences.java +102 -0
  32. scientific_method_engine/ghidra/ReportSymbolReferences.java +72 -0
  33. scientific_method_engine/x86/__init__.py +0 -0
  34. scientific_method_engine/x86/dispatch.py +51 -0
  35. scientific_method_engine/x86/image.py +172 -0
  36. scientific_method_engine/x86/machine.py +659 -0
  37. scientific_method_engine/x86/pe.py +96 -0
  38. scientific_method_engine/x86/reports.py +1259 -0
  39. scientific_method_engine/x86/trace.py +547 -0
  40. scientific_method_engine/x86/values.py +123 -0
  41. scientific_method_engine-0.1.0.dist-info/METADATA +110 -0
  42. scientific_method_engine-0.1.0.dist-info/RECORD +45 -0
  43. scientific_method_engine-0.1.0.dist-info/WHEEL +4 -0
  44. scientific_method_engine-0.1.0.dist-info/entry_points.txt +2 -0
  45. scientific_method_engine-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,63 @@
1
+ // Summarizes immediate x86 cdecl first arguments at every direct call to one function.
2
+ // Calls without an immediately preceding literal PUSH are listed for follow-up.
3
+ // @category Restoration
4
+
5
+ import java.util.Map;
6
+ import java.util.TreeMap;
7
+
8
+ import ghidra.app.script.GhidraScript;
9
+ import ghidra.program.model.address.Address;
10
+ import ghidra.program.model.listing.Function;
11
+ import ghidra.program.model.listing.Instruction;
12
+ import ghidra.program.model.scalar.Scalar;
13
+ import ghidra.program.model.symbol.Reference;
14
+ import ghidra.program.model.symbol.ReferenceIterator;
15
+
16
+ public class ReportFirstArgumentCallSummary extends GhidraScript {
17
+ @Override
18
+ protected void run() throws Exception {
19
+ String[] arguments = getScriptArgs();
20
+ if (arguments.length != 1) {
21
+ printerr("Supply one callee virtual address.");
22
+ return;
23
+ }
24
+
25
+ Address callee = toAddr(arguments[0]);
26
+ ReferenceIterator references = currentProgram.getReferenceManager().getReferencesTo(callee);
27
+ Map<Long, Integer> literals = new TreeMap<>();
28
+ int calls = 0;
29
+ int nonLiteral = 0;
30
+ while (references.hasNext() && !monitor.isCancelled()) {
31
+ Reference reference = references.next();
32
+ Instruction call = currentProgram.getListing().getInstructionAt(reference.getFromAddress());
33
+ if (call == null || !call.getFlowType().isCall()) continue;
34
+ calls++;
35
+
36
+ Instruction push = call.getPrevious();
37
+ Long literal = pushedLiteral(push);
38
+ if (literal != null) {
39
+ literals.merge(literal, 1, Integer::sum);
40
+ continue;
41
+ }
42
+
43
+ nonLiteral++;
44
+ Function function = currentProgram.getFunctionManager().getFunctionContaining(call.getAddress());
45
+ println("Non-literal at " + call.getAddress()
46
+ + (function == null ? "" : " in " + function.getEntryPoint() + " " + function.getName())
47
+ + " first=" + (push == null ? "<none>" : push));
48
+ }
49
+
50
+ println("Direct calls: " + calls);
51
+ for (Map.Entry<Long, Integer> entry : literals.entrySet())
52
+ println("Literal " + entry.getKey() + ": " + entry.getValue());
53
+ println("Non-literal: " + nonLiteral);
54
+ }
55
+
56
+ private static Long pushedLiteral(Instruction instruction) {
57
+ if (instruction == null || !"PUSH".equals(instruction.getMnemonicString())) return null;
58
+ for (Object object : instruction.getOpObjects(0)) {
59
+ if (object instanceof Scalar scalar) return scalar.getUnsignedValue();
60
+ }
61
+ return null;
62
+ }
63
+ }
@@ -0,0 +1,56 @@
1
+ // Reports instruction references to selected scalar values within one function.
2
+ // @category Restoration
3
+
4
+ import ghidra.app.script.GhidraScript;
5
+ import ghidra.program.model.address.Address;
6
+ import ghidra.program.model.listing.Function;
7
+ import ghidra.program.model.listing.Instruction;
8
+ import ghidra.program.model.scalar.Scalar;
9
+
10
+ import java.util.HashSet;
11
+ import java.util.Set;
12
+
13
+ public class ReportFunctionScalarConstants extends GhidraScript {
14
+ private static final int MAX_MATCHES = 200;
15
+
16
+ @Override
17
+ protected void run() throws Exception {
18
+ String[] arguments = getScriptArgs();
19
+ if (arguments.length < 2) {
20
+ printerr("Supply a function address followed by one or more scalar values.");
21
+ return;
22
+ }
23
+
24
+ Address address = toAddr(arguments[0]);
25
+ Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
26
+ if (function == null) {
27
+ printerr(arguments[0] + ": no containing function");
28
+ return;
29
+ }
30
+
31
+ Set<Long> requested = new HashSet<>();
32
+ for (int index = 1; index < arguments.length; index++)
33
+ requested.add(Long.decode(arguments[index]));
34
+
35
+ int matches = 0;
36
+ Instruction instruction = currentProgram.getListing()
37
+ .getInstructionAt(function.getEntryPoint());
38
+ while (instruction != null && function.getBody().contains(instruction.getAddress())
39
+ && !monitor.isCancelled()) {
40
+ for (int operand = 0; operand < instruction.getNumOperands(); operand++) {
41
+ for (Object object : instruction.getOpObjects(operand)) {
42
+ if (!(object instanceof Scalar scalar)
43
+ || !requested.contains(scalar.getUnsignedValue())) continue;
44
+ println(scalar.getUnsignedValue() + " at " + instruction.getAddress()
45
+ + ": " + instruction);
46
+ if (++matches >= MAX_MATCHES) {
47
+ println("... output capped at " + MAX_MATCHES + " matches");
48
+ return;
49
+ }
50
+ }
51
+ }
52
+ instruction = instruction.getNext();
53
+ }
54
+ if (matches == 0) println("No requested scalar constants matched.");
55
+ }
56
+ }
@@ -0,0 +1,64 @@
1
+ // Prints focused decompiler output for explicitly supplied virtual addresses.
2
+ // @category Restoration
3
+
4
+ import ghidra.app.decompiler.DecompInterface;
5
+ import ghidra.app.decompiler.DecompileResults;
6
+ import ghidra.app.script.GhidraScript;
7
+ import ghidra.program.model.address.Address;
8
+ import ghidra.program.model.listing.Function;
9
+ import ghidra.program.model.symbol.Reference;
10
+ import ghidra.program.model.symbol.ReferenceIterator;
11
+
12
+ import java.util.Set;
13
+ import java.util.TreeSet;
14
+
15
+ public class ReportFunctionSummary extends GhidraScript {
16
+ @Override
17
+ protected void run() throws Exception {
18
+ String[] arguments = getScriptArgs();
19
+ if (arguments.length == 0) {
20
+ printerr("Supply one or more virtual addresses, for example 0x00478cd0.");
21
+ return;
22
+ }
23
+
24
+ DecompInterface decompiler = new DecompInterface();
25
+ decompiler.openProgram(currentProgram);
26
+ try {
27
+ for (String argument : arguments) {
28
+ Address address = toAddr(argument);
29
+ Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
30
+ if (function == null) {
31
+ println(argument + ": no containing function");
32
+ continue;
33
+ }
34
+
35
+ println("===== " + function.getEntryPoint() + " " + function.getName() + " =====");
36
+ Set<String> callers = new TreeSet<>();
37
+ ReferenceIterator references = currentProgram.getReferenceManager()
38
+ .getReferencesTo(function.getEntryPoint());
39
+ while (references.hasNext()) {
40
+ Reference reference = references.next();
41
+ Function caller = currentProgram.getFunctionManager()
42
+ .getFunctionContaining(reference.getFromAddress());
43
+ callers.add(caller == null
44
+ ? reference.getFromAddress().toString()
45
+ : caller.getEntryPoint() + " " + caller.getName()
46
+ + " at " + reference.getFromAddress());
47
+ }
48
+ println("Incoming references:");
49
+ for (String caller : callers) {
50
+ println(" " + caller);
51
+ }
52
+ DecompileResults result = decompiler.decompileFunction(function, 30, monitor);
53
+ if (!result.decompileCompleted()) {
54
+ println("Decompiler failed: " + result.getErrorMessage());
55
+ continue;
56
+ }
57
+ println(result.getDecompiledFunction().getC());
58
+ }
59
+ }
60
+ finally {
61
+ decompiler.dispose();
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,64 @@
1
+ // Prints bounded instruction context around explicitly supplied addresses.
2
+ // @category Restoration
3
+
4
+ import ghidra.app.script.GhidraScript;
5
+ import ghidra.program.model.address.Address;
6
+ import ghidra.program.model.listing.Function;
7
+ import ghidra.program.model.listing.Instruction;
8
+ import ghidra.program.model.listing.Listing;
9
+
10
+ import java.util.ArrayList;
11
+ import java.util.List;
12
+
13
+ public class ReportInstructionContext extends GhidraScript {
14
+ private static final int CONTEXT_INSTRUCTIONS = 8;
15
+
16
+ @Override
17
+ protected void run() throws Exception {
18
+ String[] arguments = getScriptArgs();
19
+ if (arguments.length == 0) {
20
+ printerr("Supply one or more instruction addresses.");
21
+ return;
22
+ }
23
+
24
+ Listing listing = currentProgram.getListing();
25
+ for (String argument : arguments) {
26
+ Address address = toAddr(argument);
27
+ Instruction focus = listing.getInstructionContaining(address);
28
+ if (focus == null) {
29
+ printerr(argument + ": no containing instruction");
30
+ continue;
31
+ }
32
+
33
+ Function function = currentProgram.getFunctionManager()
34
+ .getFunctionContaining(focus.getAddress());
35
+ println("===== " + focus.getAddress()
36
+ + (function == null ? "" : " in " + function.getEntryPoint()
37
+ + " " + function.getName()) + " =====");
38
+
39
+ List<Instruction> before = new ArrayList<>();
40
+ Instruction cursor = focus;
41
+ for (int count = 0; count < CONTEXT_INSTRUCTIONS; count++) {
42
+ cursor = cursor.getPrevious();
43
+ if (cursor == null || (function != null
44
+ && !function.getBody().contains(cursor.getAddress()))) break;
45
+ before.add(0, cursor);
46
+ }
47
+ for (Instruction instruction : before) printInstruction(instruction, false);
48
+ printInstruction(focus, true);
49
+
50
+ cursor = focus;
51
+ for (int count = 0; count < CONTEXT_INSTRUCTIONS; count++) {
52
+ cursor = cursor.getNext();
53
+ if (cursor == null || (function != null
54
+ && !function.getBody().contains(cursor.getAddress()))) break;
55
+ printInstruction(cursor, false);
56
+ }
57
+ }
58
+ }
59
+
60
+ private void printInstruction(Instruction instruction, boolean focus) {
61
+ println((focus ? "> " : " ") + instruction.getAddress() + " "
62
+ + instruction.toString());
63
+ }
64
+ }
@@ -0,0 +1,36 @@
1
+ // Prints a bounded instruction window beginning at a virtual address.
2
+ // @category Restoration
3
+
4
+ import ghidra.app.script.GhidraScript;
5
+ import ghidra.program.model.address.Address;
6
+ import ghidra.program.model.listing.Instruction;
7
+
8
+ public class ReportInstructionWindow extends GhidraScript {
9
+ private static final int MAX_INSTRUCTIONS = 200;
10
+
11
+ @Override
12
+ protected void run() throws Exception {
13
+ String[] arguments = getScriptArgs();
14
+ if (arguments.length != 2) {
15
+ printerr("Supply a virtual address and instruction count.");
16
+ return;
17
+ }
18
+
19
+ Address address = toAddr(arguments[0]);
20
+ int count = Integer.parseInt(arguments[1]);
21
+ if (count < 1 || count > MAX_INSTRUCTIONS) {
22
+ printerr("Instruction count must be between 1 and " + MAX_INSTRUCTIONS + ".");
23
+ return;
24
+ }
25
+
26
+ Instruction instruction = currentProgram.getListing().getInstructionAt(address);
27
+ if (instruction == null) {
28
+ instruction = currentProgram.getListing().getInstructionAfter(address);
29
+ }
30
+
31
+ for (int index = 0; instruction != null && index < count; index++) {
32
+ println(instruction.getAddress() + ": " + instruction);
33
+ instruction = instruction.getNext();
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,105 @@
1
+ // Maps file offsets to loaded addresses and their memory blocks, and prints nearby instructions.
2
+ // @category Restoration
3
+
4
+ import ghidra.app.script.GhidraScript;
5
+ import ghidra.program.model.address.Address;
6
+ import ghidra.program.model.listing.Function;
7
+ import ghidra.program.model.listing.Instruction;
8
+ import ghidra.program.model.listing.Listing;
9
+ import ghidra.program.model.mem.Memory;
10
+ import ghidra.program.model.mem.MemoryBlock;
11
+
12
+ import java.io.File;
13
+ import java.nio.file.Files;
14
+ import java.util.List;
15
+
16
+ public class ReportMemoryBlockForFileOffset extends GhidraScript {
17
+ private static final int PATTERN_BYTES = 12;
18
+ private static final int FOLLOW_INSTRUCTIONS = 8;
19
+
20
+ @Override
21
+ protected void run() throws Exception {
22
+ String[] arguments = getScriptArgs();
23
+ if (arguments.length == 0) {
24
+ printerr("Supply one or more file offsets in hex, for example 76640.");
25
+ return;
26
+ }
27
+
28
+ File executable = new File(currentProgram.getExecutablePath());
29
+ if (!executable.isFile()) {
30
+ printerr("Executable path unavailable: " + currentProgram.getExecutablePath());
31
+ return;
32
+ }
33
+
34
+ byte[] fileBytes = Files.readAllBytes(executable.toPath());
35
+ Memory memory = currentProgram.getMemory();
36
+ Listing listing = currentProgram.getListing();
37
+
38
+ for (String argument : arguments) {
39
+ long fileOffset = parseHex(argument);
40
+ if (fileOffset < 0 || fileOffset + PATTERN_BYTES > fileBytes.length) {
41
+ printerr(argument + ": file offset out of range.");
42
+ continue;
43
+ }
44
+
45
+ byte[] pattern = new byte[PATTERN_BYTES];
46
+ System.arraycopy(fileBytes, (int) fileOffset, pattern, 0, PATTERN_BYTES);
47
+ println("===== file offset " + argument + " =====");
48
+ println("Pattern: " + toHex(pattern));
49
+
50
+ // The loader's own file-byte mapping is exact. Without one (some loaders keep no
51
+ // FileBytes), fall back to the first place the bytes occur, and say when that is ambiguous.
52
+ List<Address> mapped = memory.locateAddressesForFileOffset(fileOffset);
53
+ if (mapped.isEmpty()) {
54
+ Address found = memory.findBytes(memory.getMinAddress(), pattern, null, true, monitor);
55
+ if (found == null) {
56
+ println("Pattern not found in loaded memory.");
57
+ continue;
58
+ }
59
+ Address after = found.next();
60
+ if (after != null && memory.findBytes(after, pattern, null, true, monitor) != null) {
61
+ println("Pattern occurs more than once; the first match may not be this offset.");
62
+ }
63
+ mapped = List.of(found);
64
+ }
65
+
66
+ for (Address address : mapped) {
67
+ MemoryBlock block = memory.getBlock(address);
68
+ println("Mapped address: " + address
69
+ + (block == null ? "" : " in block " + block.getName()));
70
+ Instruction instruction = listing.getInstructionContaining(address);
71
+ if (instruction == null) {
72
+ println("No instruction at mapped address.");
73
+ continue;
74
+ }
75
+
76
+ Function function = listing.getFunctionContaining(instruction.getAddress());
77
+ println("===== " + instruction.getAddress()
78
+ + (function == null ? "" : " in " + function.getEntryPoint() + " " + function.getName())
79
+ + " =====");
80
+ Instruction cursor = instruction;
81
+ for (int count = 0; count < FOLLOW_INSTRUCTIONS; count++) {
82
+ if (cursor == null) break;
83
+ println(cursor.getAddress() + " " + cursor);
84
+ cursor = cursor.getNext();
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ private static long parseHex(String argument) {
91
+ String normalized = argument.trim();
92
+ if (normalized.startsWith("0x") || normalized.startsWith("0X")) {
93
+ return Long.decode(normalized);
94
+ }
95
+ return Long.parseLong(normalized, 16);
96
+ }
97
+
98
+ private static String toHex(byte[] bytes) {
99
+ StringBuilder builder = new StringBuilder();
100
+ for (byte value : bytes) {
101
+ builder.append(String.format("%02X ", value));
102
+ }
103
+ return builder.toString().trim();
104
+ }
105
+ }
@@ -0,0 +1,52 @@
1
+ // Reports a bounded selection of memory blocks; never bytes or instructions.
2
+ // @category Restoration
3
+ import ghidra.app.script.GhidraScript;
4
+ import ghidra.program.model.mem.MemoryBlock;
5
+
6
+ public class ReportMemoryBlocks extends GhidraScript {
7
+ private static final int MAX_BLOCKS = 512;
8
+ private static final int MAX_LISTED_MATCHES = 16;
9
+ @Override
10
+ protected void run() throws Exception {
11
+ String[] args = getScriptArgs();
12
+ MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
13
+ int start = 0, count = blocks.length;
14
+ String scope = "whole map", requested = "";
15
+ if (args.length == 3 && args[0].equals("page")) {
16
+ try { start = Integer.parseInt(args[1]); count = Integer.parseInt(args[2]); }
17
+ catch (NumberFormatException e) { printerr("Page start/count must be integers."); return; }
18
+ if (start < 0 || start > blocks.length || count < 1 || count > MAX_BLOCKS) {
19
+ printerr("Page start must be within 0.." + blocks.length + "; count within 1.." + MAX_BLOCKS + "."); return;
20
+ }
21
+ requested = " requested=" + count;
22
+ count = Math.min(count, blocks.length - start);
23
+ scope = "page; indices follow the current program's block order";
24
+ } else if (args.length == 2 && args[0].equals("name")) {
25
+ start = -1;
26
+ int matches = 0;
27
+ StringBuilder indices = new StringBuilder();
28
+ for (int i = 0; i < blocks.length; i++) {
29
+ if (!blocks[i].getName().equals(args[1])) continue;
30
+ if (matches++ == 0) start = i;
31
+ if (matches <= MAX_LISTED_MATCHES) indices.append(matches == 1 ? "" : ",").append(i);
32
+ }
33
+ if (matches == 0) { printerr("No block with that exact name."); return; }
34
+ if (matches > 1) {
35
+ printerr("Ambiguous block name: " + matches + " blocks at indices " + indices +
36
+ (matches > MAX_LISTED_MATCHES ? ",..." : "") + "; select a page instead."); return;
37
+ }
38
+ count = 1; scope = "exact block name";
39
+ } else if (args.length != 0) {
40
+ printerr("Usage: ReportMemoryBlocks.java [page <start-index> <count> | name <exact-name>]"); return;
41
+ }
42
+ if (count > MAX_BLOCKS) {
43
+ printerr("Program has " + blocks.length + " blocks; maximum is " + MAX_BLOCKS + ". Select page or name."); return;
44
+ }
45
+ println("total=" + blocks.length + " start=" + start + " emitted=" + count +
46
+ " partial=" + (count != blocks.length) + requested + " scope=" + scope);
47
+ for (int i = start; i < start + count; i++) {
48
+ MemoryBlock b = blocks[i];
49
+ println(i + " " + b.getName() + " " + b.getStart() + "-" + b.getEnd() + " size=" + b.getSize());
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,74 @@
1
+ // Lists references to symbols named like C runtime or Windows random and timing functions (rand,
2
+ // srand, time, GetTickCount, QueryPerformanceCounter and similar), with their calling functions.
3
+ // @category Restoration
4
+
5
+ import ghidra.app.script.GhidraScript;
6
+ import ghidra.program.model.listing.Function;
7
+ import ghidra.program.model.symbol.Reference;
8
+ import ghidra.program.model.symbol.ReferenceIterator;
9
+ import ghidra.program.model.symbol.Symbol;
10
+ import ghidra.program.model.symbol.SymbolIterator;
11
+
12
+ import java.util.Locale;
13
+ import java.util.Set;
14
+ import java.util.TreeSet;
15
+
16
+ public class ReportRandomnessCandidates extends GhidraScript {
17
+ private static final int MAX_MATCHES = 100;
18
+ private static final int MAX_CALLERS_PER_MATCH = 100;
19
+ private static final String[] CANDIDATE_NAMES = {
20
+ "rand", "srand", "random", "randomize", "time", "gettickcount",
21
+ "queryperformancecounter", "timegettime", "getsystemtime"
22
+ };
23
+
24
+ @Override
25
+ protected void run() throws Exception {
26
+ println("Randomness/timing candidate references for " + currentProgram.getName());
27
+ int matches = 0;
28
+ SymbolIterator symbols = currentProgram.getSymbolTable().getAllSymbols(true);
29
+ while (symbols.hasNext() && matches < MAX_MATCHES && !monitor.isCancelled()) {
30
+ Symbol symbol = symbols.next();
31
+ if (!isCandidate(symbol.getName())) {
32
+ continue;
33
+ }
34
+
35
+ matches++;
36
+ Set<String> callers = new TreeSet<>();
37
+ ReferenceIterator references = currentProgram.getReferenceManager()
38
+ .getReferencesTo(symbol.getAddress());
39
+ while (references.hasNext() && callers.size() < MAX_CALLERS_PER_MATCH
40
+ && !monitor.isCancelled()) {
41
+ Reference reference = references.next();
42
+ Function caller = currentProgram.getFunctionManager()
43
+ .getFunctionContaining(reference.getFromAddress());
44
+ callers.add(caller == null
45
+ ? reference.getFromAddress().toString()
46
+ : caller.getEntryPoint() + " " + caller.getName());
47
+ }
48
+
49
+ println(symbol.getAddress() + " " + symbol.getName(true));
50
+ for (String caller : callers) {
51
+ println(" caller " + caller);
52
+ }
53
+ if (references.hasNext()) {
54
+ println(" ... callers capped at " + MAX_CALLERS_PER_MATCH);
55
+ }
56
+ }
57
+
58
+ if (matches == 0) println("No randomness or timing candidates matched.");
59
+ else if (matches == MAX_MATCHES && symbols.hasNext()) {
60
+ println("Output capped at " + MAX_MATCHES + " candidate symbols; later symbols were not scanned.");
61
+ }
62
+ }
63
+
64
+ private static boolean isCandidate(String name) {
65
+ String lower = name.toLowerCase(Locale.ROOT);
66
+ for (String candidate : CANDIDATE_NAMES) {
67
+ if (lower.equals(candidate) || lower.endsWith("::" + candidate)
68
+ || lower.contains("_" + candidate)) {
69
+ return true;
70
+ }
71
+ }
72
+ return false;
73
+ }
74
+ }
@@ -0,0 +1,42 @@
1
+ // Reports bounded references to explicitly supplied virtual addresses.
2
+ // @category Restoration
3
+
4
+ import ghidra.app.script.GhidraScript;
5
+ import ghidra.program.model.address.Address;
6
+ import ghidra.program.model.listing.Function;
7
+ import ghidra.program.model.listing.Instruction;
8
+ import ghidra.program.model.symbol.Reference;
9
+ import ghidra.program.model.symbol.ReferenceIterator;
10
+
11
+ public class ReportReferences extends GhidraScript {
12
+ private static final int MAX_REFERENCES = 200;
13
+
14
+ @Override
15
+ protected void run() throws Exception {
16
+ String[] arguments = getScriptArgs();
17
+ if (arguments.length == 0) {
18
+ printerr("Supply one or more virtual addresses.");
19
+ return;
20
+ }
21
+
22
+ for (String argument : arguments) {
23
+ Address target = toAddr(argument);
24
+ println("===== references to " + target + " =====");
25
+ ReferenceIterator references = currentProgram.getReferenceManager().getReferencesTo(target);
26
+ int count = 0;
27
+ while (references.hasNext() && count < MAX_REFERENCES && !monitor.isCancelled()) {
28
+ Reference reference = references.next();
29
+ Instruction instruction = currentProgram.getListing().getInstructionContaining(reference.getFromAddress());
30
+ Function function = currentProgram.getFunctionManager().getFunctionContaining(reference.getFromAddress());
31
+ println(reference.getFromAddress()
32
+ + (function == null ? "" : " in " + function.getEntryPoint() + " " + function.getName())
33
+ + " " + reference.getReferenceType()
34
+ + (instruction == null ? "" : ": " + instruction));
35
+ count++;
36
+ }
37
+
38
+ if (count == 0) println("No references found.");
39
+ else if (count == MAX_REFERENCES && references.hasNext()) println("Output capped at " + MAX_REFERENCES + " references.");
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,55 @@
1
+ // Reports bounded instruction references to explicitly supplied scalar values.
2
+ // @category Restoration
3
+
4
+ import ghidra.app.script.GhidraScript;
5
+ import ghidra.program.model.listing.Function;
6
+ import ghidra.program.model.listing.Instruction;
7
+ import ghidra.program.model.listing.InstructionIterator;
8
+ import ghidra.program.model.scalar.Scalar;
9
+
10
+ import java.util.HashSet;
11
+ import java.util.Set;
12
+
13
+ public class ReportScalarConstants extends GhidraScript {
14
+ private static final int MAX_MATCHES = 300;
15
+
16
+ @Override
17
+ protected void run() throws Exception {
18
+ String[] arguments = getScriptArgs();
19
+ if (arguments.length == 0) {
20
+ printerr("Supply one or more scalar values (decimal or 0x-prefixed). ");
21
+ return;
22
+ }
23
+
24
+ Set<Long> requested = new HashSet<>();
25
+ for (String argument : arguments) requested.add(Long.decode(argument));
26
+ int matches = 0;
27
+ InstructionIterator instructions = currentProgram.getListing().getInstructions(true);
28
+ while (instructions.hasNext() && !monitor.isCancelled()) {
29
+ Instruction instruction = instructions.next();
30
+ for (int operand = 0; operand < instruction.getNumOperands(); operand++) {
31
+ for (Object object : instruction.getOpObjects(operand)) {
32
+ if (!(object instanceof Scalar scalar)
33
+ || !matchesRequested(scalar, requested)) continue;
34
+ Function function = currentProgram.getFunctionManager()
35
+ .getFunctionContaining(instruction.getAddress());
36
+ println(scalar.getUnsignedValue() + " at " + instruction.getAddress()
37
+ + (function == null ? "" : " in " + function.getEntryPoint()
38
+ + " " + function.getName()));
39
+ if (++matches >= MAX_MATCHES) {
40
+ println("... output capped at " + MAX_MATCHES + " matches");
41
+ return;
42
+ }
43
+ }
44
+ }
45
+ }
46
+ if (matches == 0) println("No requested scalar constants matched.");
47
+ }
48
+
49
+ // Arguments are decoded as signed longs while operands are reported unsigned, so a request
50
+ // such as -1 must also be compared against the operand's signed value to match at all.
51
+ private static boolean matchesRequested(Scalar scalar, Set<Long> requested) {
52
+ return requested.contains(scalar.getUnsignedValue())
53
+ || requested.contains(scalar.getSignedValue());
54
+ }
55
+ }