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.
- scientific_method_engine/__init__.py +5 -0
- scientific_method_engine/__main__.py +3 -0
- scientific_method_engine/cli.py +75 -0
- scientific_method_engine/ghidra/ClearNoReturnFunctions.java +22 -0
- scientific_method_engine/ghidra/CreateFunctions.java +27 -0
- scientific_method_engine/ghidra/ExportBoundedFlow.java +59 -0
- scientific_method_engine/ghidra/ExportFunctionFingerprints.java +135 -0
- scientific_method_engine/ghidra/ExportFunctionInventory.java +50 -0
- scientific_method_engine/ghidra/MergeFallThroughFragment.java +63 -0
- scientific_method_engine/ghidra/RecoverCitedFunctions.java +93 -0
- scientific_method_engine/ghidra/RepairReturningCallers.java +76 -0
- scientific_method_engine/ghidra/ReportCallArguments.java +65 -0
- scientific_method_engine/ghidra/ReportCallPaths.java +106 -0
- scientific_method_engine/ghidra/ReportCallSitesWithScalars.java +95 -0
- scientific_method_engine/ghidra/ReportCallsToRange.java +67 -0
- scientific_method_engine/ghidra/ReportConstantFirstArgumentCalls.java +55 -0
- scientific_method_engine/ghidra/ReportDataBytes.java +36 -0
- scientific_method_engine/ghidra/ReportDecompileMatches.java +71 -0
- scientific_method_engine/ghidra/ReportDecompileWindow.java +61 -0
- scientific_method_engine/ghidra/ReportFilePatternInMemory.java +102 -0
- scientific_method_engine/ghidra/ReportFirstArgumentCallSummary.java +63 -0
- scientific_method_engine/ghidra/ReportFunctionScalarConstants.java +56 -0
- scientific_method_engine/ghidra/ReportFunctionSummary.java +64 -0
- scientific_method_engine/ghidra/ReportInstructionContext.java +64 -0
- scientific_method_engine/ghidra/ReportInstructionWindow.java +36 -0
- scientific_method_engine/ghidra/ReportMemoryBlockForFileOffset.java +105 -0
- scientific_method_engine/ghidra/ReportMemoryBlocks.java +52 -0
- scientific_method_engine/ghidra/ReportRandomnessCandidates.java +74 -0
- scientific_method_engine/ghidra/ReportReferences.java +42 -0
- scientific_method_engine/ghidra/ReportScalarConstants.java +55 -0
- scientific_method_engine/ghidra/ReportStringReferences.java +102 -0
- scientific_method_engine/ghidra/ReportSymbolReferences.java +72 -0
- scientific_method_engine/x86/__init__.py +0 -0
- scientific_method_engine/x86/dispatch.py +51 -0
- scientific_method_engine/x86/image.py +172 -0
- scientific_method_engine/x86/machine.py +659 -0
- scientific_method_engine/x86/pe.py +96 -0
- scientific_method_engine/x86/reports.py +1259 -0
- scientific_method_engine/x86/trace.py +547 -0
- scientific_method_engine/x86/values.py +123 -0
- scientific_method_engine-0.1.0.dist-info/METADATA +110 -0
- scientific_method_engine-0.1.0.dist-info/RECORD +45 -0
- scientific_method_engine-0.1.0.dist-info/WHEEL +4 -0
- scientific_method_engine-0.1.0.dist-info/entry_points.txt +2 -0
- scientific_method_engine-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Reports the three nearest pushed arguments at every direct call to a 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.symbol.Reference;
|
|
9
|
+
import ghidra.program.model.symbol.ReferenceIterator;
|
|
10
|
+
|
|
11
|
+
import java.util.ArrayList;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
|
|
14
|
+
public class ReportCallArguments extends GhidraScript {
|
|
15
|
+
private static final int ARGUMENT_COUNT = 3;
|
|
16
|
+
private static final int MAX_PRECEDING_INSTRUCTIONS = 20;
|
|
17
|
+
|
|
18
|
+
@Override
|
|
19
|
+
protected void run() throws Exception {
|
|
20
|
+
String[] arguments = getScriptArgs();
|
|
21
|
+
if (arguments.length != 1) {
|
|
22
|
+
printerr("Supply one callee virtual address.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Address callee = toAddr(arguments[0]);
|
|
27
|
+
ReferenceIterator references = currentProgram.getReferenceManager().getReferencesTo(callee);
|
|
28
|
+
while (references.hasNext() && !monitor.isCancelled()) {
|
|
29
|
+
Reference reference = references.next();
|
|
30
|
+
Instruction call = currentProgram.getListing().getInstructionAt(reference.getFromAddress());
|
|
31
|
+
if (call == null || !call.getFlowType().isCall()) continue;
|
|
32
|
+
|
|
33
|
+
Function function = currentProgram.getFunctionManager().getFunctionContaining(call.getAddress());
|
|
34
|
+
List<Instruction> pushes = nearestPushes(call, function);
|
|
35
|
+
println(call.getAddress()
|
|
36
|
+
+ (function == null ? "" : " in " + function.getEntryPoint() + " " + function.getName())
|
|
37
|
+
+ " args=" + format(pushes));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private List<Instruction> nearestPushes(Instruction call, Function function) {
|
|
42
|
+
List<Instruction> pushes = new ArrayList<>();
|
|
43
|
+
Instruction cursor = call;
|
|
44
|
+
for (int count = 0;
|
|
45
|
+
count < MAX_PRECEDING_INSTRUCTIONS && pushes.size() < ARGUMENT_COUNT;
|
|
46
|
+
count++) {
|
|
47
|
+
cursor = cursor.getPrevious();
|
|
48
|
+
if (cursor == null || (function != null
|
|
49
|
+
&& !function.getBody().contains(cursor.getAddress()))) break;
|
|
50
|
+
if (cursor.getFlowType().isCall()) break;
|
|
51
|
+
if ("PUSH".equals(cursor.getMnemonicString())) pushes.add(cursor);
|
|
52
|
+
}
|
|
53
|
+
return pushes;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private String format(List<Instruction> pushes) {
|
|
57
|
+
StringBuilder result = new StringBuilder("[");
|
|
58
|
+
for (int index = 0; index < pushes.size(); index++) {
|
|
59
|
+
if (index > 0) result.append(", ");
|
|
60
|
+
Instruction push = pushes.get(index);
|
|
61
|
+
result.append(push.getAddress()).append(": ").append(push);
|
|
62
|
+
}
|
|
63
|
+
return result.append(']').toString();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Reports bounded direct-call paths between two explicitly selected functions.
|
|
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.InstructionIterator;
|
|
9
|
+
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.HashSet;
|
|
12
|
+
import java.util.List;
|
|
13
|
+
import java.util.Set;
|
|
14
|
+
|
|
15
|
+
public class ReportCallPaths extends GhidraScript {
|
|
16
|
+
private static final int MAX_DEPTH = 12;
|
|
17
|
+
private static final int MAX_VISITED_EDGES = 2000;
|
|
18
|
+
private static final int MAX_PATHS = 100;
|
|
19
|
+
|
|
20
|
+
private Address target;
|
|
21
|
+
private int depthLimit;
|
|
22
|
+
private int visitedEdges;
|
|
23
|
+
private int reportedPaths;
|
|
24
|
+
|
|
25
|
+
@Override
|
|
26
|
+
protected void run() throws Exception {
|
|
27
|
+
String[] arguments = getScriptArgs();
|
|
28
|
+
if (arguments.length != 3) {
|
|
29
|
+
printerr("Supply start address, target address, and maximum depth.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Function start = functionAt(arguments[0]);
|
|
34
|
+
Function targetFunction = functionAt(arguments[1]);
|
|
35
|
+
depthLimit = Integer.parseInt(arguments[2]);
|
|
36
|
+
if (start == null || targetFunction == null) return;
|
|
37
|
+
if (depthLimit < 1 || depthLimit > MAX_DEPTH) {
|
|
38
|
+
printerr("Maximum depth must be between 1 and " + MAX_DEPTH + ".");
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
target = targetFunction.getEntryPoint();
|
|
43
|
+
List<Function> path = new ArrayList<>();
|
|
44
|
+
path.add(start);
|
|
45
|
+
Set<Address> active = new HashSet<>();
|
|
46
|
+
active.add(start.getEntryPoint());
|
|
47
|
+
search(start, path, active, 0);
|
|
48
|
+
if (reportedPaths == 0) println("No bounded direct-call path found.");
|
|
49
|
+
if (visitedEdges >= MAX_VISITED_EDGES)
|
|
50
|
+
println("Stopped after the " + MAX_VISITED_EDGES + "-edge safety limit.");
|
|
51
|
+
if (reportedPaths >= MAX_PATHS)
|
|
52
|
+
println("Stopped after the " + MAX_PATHS + "-path safety limit.");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private Function functionAt(String value) {
|
|
56
|
+
Address address = toAddr(value);
|
|
57
|
+
Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
|
|
58
|
+
if (function == null) printerr(value + ": no containing function");
|
|
59
|
+
return function;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private void search(Function current, List<Function> path, Set<Address> active, int depth) {
|
|
63
|
+
if (monitor.isCancelled() || depth >= depthLimit
|
|
64
|
+
|| visitedEdges >= MAX_VISITED_EDGES || reportedPaths >= MAX_PATHS) return;
|
|
65
|
+
|
|
66
|
+
// Every instruction of the body, including code before the entry and after a gap, which a
|
|
67
|
+
// walk forward from the entry would stop short of.
|
|
68
|
+
InstructionIterator instructions = currentProgram.getListing()
|
|
69
|
+
.getInstructions(current.getBody(), true);
|
|
70
|
+
while (instructions.hasNext()) {
|
|
71
|
+
Instruction instruction = instructions.next();
|
|
72
|
+
if (instruction.getFlowType().isCall()) {
|
|
73
|
+
for (Address destination : instruction.getFlows()) {
|
|
74
|
+
visitedEdges++;
|
|
75
|
+
Function callee = currentProgram.getFunctionManager()
|
|
76
|
+
.getFunctionContaining(destination);
|
|
77
|
+
if (callee == null) continue;
|
|
78
|
+
Address entry = callee.getEntryPoint();
|
|
79
|
+
if (entry.equals(target)) {
|
|
80
|
+
printPath(path, instruction.getAddress(), callee);
|
|
81
|
+
}
|
|
82
|
+
else if (!active.contains(entry)) {
|
|
83
|
+
path.add(callee);
|
|
84
|
+
active.add(entry);
|
|
85
|
+
search(callee, path, active, depth + 1);
|
|
86
|
+
active.remove(entry);
|
|
87
|
+
path.remove(path.size() - 1);
|
|
88
|
+
}
|
|
89
|
+
if (visitedEdges >= MAX_VISITED_EDGES || reportedPaths >= MAX_PATHS) return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private void printPath(List<Function> path, Address callSite, Function callee) {
|
|
96
|
+
StringBuilder result = new StringBuilder();
|
|
97
|
+
for (Function function : path) {
|
|
98
|
+
if (result.length() > 0) result.append(" -> ");
|
|
99
|
+
result.append(function.getEntryPoint());
|
|
100
|
+
}
|
|
101
|
+
result.append(" -[").append(callSite).append("]-> ")
|
|
102
|
+
.append(callee.getEntryPoint());
|
|
103
|
+
println(result.toString());
|
|
104
|
+
reportedPaths++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Reports bounded call sites whose preceding argument setup contains requested scalars.
|
|
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
|
+
import ghidra.program.model.symbol.Reference;
|
|
10
|
+
import ghidra.program.model.symbol.ReferenceIterator;
|
|
11
|
+
|
|
12
|
+
import java.util.HashSet;
|
|
13
|
+
import java.util.Set;
|
|
14
|
+
|
|
15
|
+
public class ReportCallSitesWithScalars extends GhidraScript {
|
|
16
|
+
private static final int PRECEDING_INSTRUCTIONS = 12;
|
|
17
|
+
private static final int MAX_MATCHES = 100;
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
protected void run() throws Exception {
|
|
21
|
+
String[] arguments = getScriptArgs();
|
|
22
|
+
if (arguments.length < 2) {
|
|
23
|
+
printerr("Supply a callee address followed by one or more scalar values.");
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Address callee = toAddr(arguments[0]);
|
|
28
|
+
Set<Long> requested = new HashSet<>();
|
|
29
|
+
for (int index = 1; index < arguments.length; index++) {
|
|
30
|
+
requested.add(Long.decode(arguments[index]));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
int matches = 0;
|
|
34
|
+
ReferenceIterator references = currentProgram.getReferenceManager().getReferencesTo(callee);
|
|
35
|
+
while (references.hasNext() && matches < MAX_MATCHES && !monitor.isCancelled()) {
|
|
36
|
+
Reference reference = references.next();
|
|
37
|
+
Instruction call = currentProgram.getListing()
|
|
38
|
+
.getInstructionAt(reference.getFromAddress());
|
|
39
|
+
if (call == null || !call.getFlowType().isCall()) continue;
|
|
40
|
+
|
|
41
|
+
Function function = currentProgram.getFunctionManager()
|
|
42
|
+
.getFunctionContaining(call.getAddress());
|
|
43
|
+
Instruction cursor = call;
|
|
44
|
+
boolean matched = false;
|
|
45
|
+
for (int count = 0; count < PRECEDING_INSTRUCTIONS; count++) {
|
|
46
|
+
cursor = cursor.getPrevious();
|
|
47
|
+
if (cursor == null || (function != null
|
|
48
|
+
&& !function.getBody().contains(cursor.getAddress()))) break;
|
|
49
|
+
if (cursor.getFlowType().isCall()) break;
|
|
50
|
+
if (containsRequestedScalar(cursor, requested)) matched = true;
|
|
51
|
+
}
|
|
52
|
+
if (!matched) continue;
|
|
53
|
+
|
|
54
|
+
matches++;
|
|
55
|
+
println("===== call " + call.getAddress()
|
|
56
|
+
+ (function == null ? "" : " in " + function.getEntryPoint()
|
|
57
|
+
+ " " + function.getName()) + " =====");
|
|
58
|
+
cursor = call;
|
|
59
|
+
Instruction[] before = new Instruction[PRECEDING_INSTRUCTIONS];
|
|
60
|
+
int populated = 0;
|
|
61
|
+
for (; populated < PRECEDING_INSTRUCTIONS; populated++) {
|
|
62
|
+
cursor = cursor.getPrevious();
|
|
63
|
+
if (cursor == null || (function != null
|
|
64
|
+
&& !function.getBody().contains(cursor.getAddress()))) break;
|
|
65
|
+
if (cursor.getFlowType().isCall()) break;
|
|
66
|
+
before[populated] = cursor;
|
|
67
|
+
}
|
|
68
|
+
for (int index = populated - 1; index >= 0; index--) {
|
|
69
|
+
println(" " + before[index].getAddress() + " " + before[index]);
|
|
70
|
+
}
|
|
71
|
+
println("> " + call.getAddress() + " " + call);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (matches == 0) println("No matching call sites found.");
|
|
75
|
+
else if (matches == MAX_MATCHES) println("Output capped at " + MAX_MATCHES + " calls.");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private static boolean containsRequestedScalar(Instruction instruction, Set<Long> requested) {
|
|
79
|
+
for (int operand = 0; operand < instruction.getNumOperands(); operand++) {
|
|
80
|
+
for (Object object : instruction.getOpObjects(operand)) {
|
|
81
|
+
if (object instanceof Scalar scalar && matchesRequested(scalar, requested)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Arguments are decoded as signed longs while operands are reported unsigned, so a request
|
|
90
|
+
// such as -1 must also be compared against the operand's signed value to match at all.
|
|
91
|
+
private static boolean matchesRequested(Scalar scalar, Set<Long> requested) {
|
|
92
|
+
return requested.contains(scalar.getUnsignedValue())
|
|
93
|
+
|| requested.contains(scalar.getSignedValue());
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Reports call/jump sites whose resolved flow target lies in a segment range.
|
|
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.InstructionIterator;
|
|
9
|
+
import ghidra.program.model.listing.Listing;
|
|
10
|
+
|
|
11
|
+
public class ReportCallsToRange extends GhidraScript {
|
|
12
|
+
private static final int MAX_MATCHES = 50;
|
|
13
|
+
|
|
14
|
+
@Override
|
|
15
|
+
protected void run() throws Exception {
|
|
16
|
+
String[] arguments = getScriptArgs();
|
|
17
|
+
if (arguments.length < 2) {
|
|
18
|
+
printerr("Supply start address and end address (inclusive), e.g. 1028:d820 1028:dab7");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
Address start = toAddr(arguments[0]);
|
|
23
|
+
Address end = toAddr(arguments[1]);
|
|
24
|
+
if (end.compareTo(start) < 0) {
|
|
25
|
+
printerr("End address must be >= start address.");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
println("===== flow targets in " + start + ".." + end + " =====");
|
|
30
|
+
Listing listing = currentProgram.getListing();
|
|
31
|
+
InstructionIterator instructions = listing.getInstructions(true);
|
|
32
|
+
int matches = 0;
|
|
33
|
+
while (instructions.hasNext() && matches < MAX_MATCHES && !monitor.isCancelled()) {
|
|
34
|
+
Instruction instruction = instructions.next();
|
|
35
|
+
if (!instruction.getFlowType().isCall() && !instruction.getFlowType().isJump()) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Address[] flows = instruction.getFlows();
|
|
40
|
+
if (flows == null) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (Address target : flows) {
|
|
45
|
+
if (target.compareTo(start) < 0 || target.compareTo(end) > 0) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Function fromFunction = listing.getFunctionContaining(instruction.getAddress());
|
|
50
|
+
Function toFunction = listing.getFunctionContaining(target);
|
|
51
|
+
println(instruction.getAddress()
|
|
52
|
+
+ (fromFunction == null ? "" : " in " + fromFunction.getEntryPoint() + " " + fromFunction.getName())
|
|
53
|
+
+ " -> " + target
|
|
54
|
+
+ (toFunction == null ? "" : " (" + toFunction.getEntryPoint() + " " + toFunction.getName() + ")")
|
|
55
|
+
+ " :: " + instruction);
|
|
56
|
+
matches++;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (matches == 0) {
|
|
62
|
+
println("No resolved call/jump targets in range.");
|
|
63
|
+
} else if (matches == MAX_MATCHES && instructions.hasNext()) {
|
|
64
|
+
println("Output capped at " + MAX_MATCHES + " matches.");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Reports direct x86 cdecl calls whose first argument is one requested constant.
|
|
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
|
+
import ghidra.program.model.symbol.Reference;
|
|
10
|
+
import ghidra.program.model.symbol.ReferenceIterator;
|
|
11
|
+
|
|
12
|
+
public class ReportConstantFirstArgumentCalls extends GhidraScript {
|
|
13
|
+
private static final int MAX_CALLS = 300;
|
|
14
|
+
|
|
15
|
+
@Override
|
|
16
|
+
protected void run() throws Exception {
|
|
17
|
+
String[] arguments = getScriptArgs();
|
|
18
|
+
if (arguments.length != 2) {
|
|
19
|
+
printerr("Supply a callee virtual address and constant first argument.");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Address callee = toAddr(arguments[0]);
|
|
24
|
+
long requested = Long.decode(arguments[1]);
|
|
25
|
+
ReferenceIterator references = currentProgram.getReferenceManager().getReferencesTo(callee);
|
|
26
|
+
int matches = 0;
|
|
27
|
+
while (references.hasNext() && matches < MAX_CALLS && !monitor.isCancelled()) {
|
|
28
|
+
Reference reference = references.next();
|
|
29
|
+
Instruction call = currentProgram.getListing().getInstructionAt(reference.getFromAddress());
|
|
30
|
+
if (call == null || !call.getFlowType().isCall()) continue;
|
|
31
|
+
Instruction firstPush = call.getPrevious();
|
|
32
|
+
if (!isPushOf(firstPush, requested)) continue;
|
|
33
|
+
|
|
34
|
+
Function function = currentProgram.getFunctionManager().getFunctionContaining(call.getAddress());
|
|
35
|
+
Instruction secondPush = firstPush.getPrevious();
|
|
36
|
+
println(call.getAddress()
|
|
37
|
+
+ (function == null ? "" : " in " + function.getEntryPoint() + " " + function.getName())
|
|
38
|
+
+ " first=" + firstPush
|
|
39
|
+
+ (secondPush == null ? "" : " preceding=" + secondPush));
|
|
40
|
+
matches++;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
println("Matched calls: " + matches);
|
|
44
|
+
if (matches == MAX_CALLS) println("Output capped at " + MAX_CALLS + " calls.");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private static boolean isPushOf(Instruction instruction, long requested) {
|
|
48
|
+
if (instruction == null || !"PUSH".equals(instruction.getMnemonicString())) return false;
|
|
49
|
+
for (Object object : instruction.getOpObjects(0)) {
|
|
50
|
+
if (object instanceof Scalar scalar
|
|
51
|
+
&& scalar.getUnsignedValue() == requested) return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Prints a bounded byte range at an explicitly supplied virtual address.
|
|
2
|
+
// @category Restoration
|
|
3
|
+
|
|
4
|
+
import ghidra.app.script.GhidraScript;
|
|
5
|
+
import ghidra.program.model.address.Address;
|
|
6
|
+
|
|
7
|
+
public class ReportDataBytes extends GhidraScript {
|
|
8
|
+
@Override
|
|
9
|
+
protected void run() throws Exception {
|
|
10
|
+
String[] arguments = getScriptArgs();
|
|
11
|
+
if (arguments.length != 2) {
|
|
12
|
+
printerr("Supply a virtual address and byte count, for example 0x00494818 24.");
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Address address = toAddr(arguments[0]);
|
|
17
|
+
int count = Integer.decode(arguments[1]);
|
|
18
|
+
if (count < 1 || count > 256) {
|
|
19
|
+
printerr("Byte count must be between 1 and 256.");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
byte[] bytes = new byte[count];
|
|
24
|
+
currentProgram.getMemory().getBytes(address, bytes);
|
|
25
|
+
println("===== " + address + " (" + count + " bytes) =====");
|
|
26
|
+
for (int offset = 0; offset < bytes.length; offset += 16) {
|
|
27
|
+
StringBuilder line = new StringBuilder();
|
|
28
|
+
line.append(address.add(offset)).append(":");
|
|
29
|
+
int end = Math.min(offset + 16, bytes.length);
|
|
30
|
+
for (int index = offset; index < end; index++) {
|
|
31
|
+
line.append(String.format(" %02x", bytes[index] & 0xff));
|
|
32
|
+
}
|
|
33
|
+
println(line.toString());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Prints bounded context around requested text in one focused decompilation.
|
|
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
|
+
|
|
10
|
+
import java.util.LinkedHashSet;
|
|
11
|
+
import java.util.Set;
|
|
12
|
+
|
|
13
|
+
public class ReportDecompileMatches extends GhidraScript {
|
|
14
|
+
private static final int CONTEXT_LINES = 2;
|
|
15
|
+
private static final int MAX_LINES = 240;
|
|
16
|
+
|
|
17
|
+
@Override
|
|
18
|
+
protected void run() throws Exception {
|
|
19
|
+
String[] arguments = getScriptArgs();
|
|
20
|
+
if (arguments.length < 2) {
|
|
21
|
+
printerr("Supply a virtual address followed by one or more literal text patterns.");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Address address = toAddr(arguments[0]);
|
|
26
|
+
Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
|
|
27
|
+
if (function == null) {
|
|
28
|
+
printerr(arguments[0] + ": no containing function");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
DecompInterface decompiler = new DecompInterface();
|
|
33
|
+
decompiler.openProgram(currentProgram);
|
|
34
|
+
try {
|
|
35
|
+
DecompileResults result = decompiler.decompileFunction(function, 60, monitor);
|
|
36
|
+
if (!result.decompileCompleted()) {
|
|
37
|
+
printerr("Decompiler failed: " + result.getErrorMessage());
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
String[] lines = result.getDecompiledFunction().getC().split("\\R");
|
|
42
|
+
Set<Integer> selected = new LinkedHashSet<>();
|
|
43
|
+
for (int index = 0; index < lines.length; index++) {
|
|
44
|
+
for (int pattern = 1; pattern < arguments.length; pattern++) {
|
|
45
|
+
if (lines[index].contains(arguments[pattern])) {
|
|
46
|
+
for (int context = Math.max(0, index - CONTEXT_LINES);
|
|
47
|
+
context <= Math.min(lines.length - 1, index + CONTEXT_LINES);
|
|
48
|
+
context++) selected.add(context);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
println("===== " + function.getEntryPoint() + " " + function.getName() + " =====");
|
|
54
|
+
int emitted = 0;
|
|
55
|
+
int previous = -2;
|
|
56
|
+
for (int index : selected) {
|
|
57
|
+
if (emitted++ >= MAX_LINES) {
|
|
58
|
+
println("... output capped at " + MAX_LINES + " lines");
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
if (index > previous + 1) println("...");
|
|
62
|
+
println((index + 1) + ": " + lines[index]);
|
|
63
|
+
previous = index;
|
|
64
|
+
}
|
|
65
|
+
if (selected.isEmpty()) println("No requested patterns matched.");
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
decompiler.dispose();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Prints an explicitly bounded line window from one focused decompilation.
|
|
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
|
+
|
|
10
|
+
public class ReportDecompileWindow extends GhidraScript {
|
|
11
|
+
private static final int MAX_LINES = 160;
|
|
12
|
+
|
|
13
|
+
@Override
|
|
14
|
+
protected void run() throws Exception {
|
|
15
|
+
String[] arguments = getScriptArgs();
|
|
16
|
+
if (arguments.length != 3) {
|
|
17
|
+
printerr("Supply a virtual address, one-based start line, and line count.");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Address address = toAddr(arguments[0]);
|
|
22
|
+
int requestedStart = Integer.parseInt(arguments[1]);
|
|
23
|
+
int requestedCount = Integer.parseInt(arguments[2]);
|
|
24
|
+
if (requestedStart < 1 || requestedCount < 1 || requestedCount > MAX_LINES) {
|
|
25
|
+
printerr("Start must be positive and count must be between 1 and " + MAX_LINES + ".");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Function function = currentProgram.getFunctionManager().getFunctionContaining(address);
|
|
30
|
+
if (function == null) {
|
|
31
|
+
printerr(arguments[0] + ": no containing function");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
DecompInterface decompiler = new DecompInterface();
|
|
36
|
+
decompiler.openProgram(currentProgram);
|
|
37
|
+
try {
|
|
38
|
+
DecompileResults result = decompiler.decompileFunction(function, 60, monitor);
|
|
39
|
+
if (!result.decompileCompleted()) {
|
|
40
|
+
printerr("Decompiler failed: " + result.getErrorMessage());
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
String[] lines = result.getDecompiledFunction().getC().split("\\R");
|
|
45
|
+
int start = requestedStart - 1;
|
|
46
|
+
if (start >= lines.length) {
|
|
47
|
+
printerr("Start line " + requestedStart + " exceeds " + lines.length + " lines.");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
int end = Math.min(lines.length, start + requestedCount);
|
|
51
|
+
println("===== " + function.getEntryPoint() + " " + function.getName()
|
|
52
|
+
+ " lines " + (start + 1) + "-" + end + " of " + lines.length + " =====");
|
|
53
|
+
for (int index = start; index < end; index++) {
|
|
54
|
+
println((index + 1) + ": " + lines[index]);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
decompiler.dispose();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Finds a file-offset byte pattern anywhere in Ghidra loaded memory.
|
|
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.symbol.Reference;
|
|
11
|
+
import ghidra.program.model.symbol.ReferenceIterator;
|
|
12
|
+
import ghidra.program.model.symbol.ReferenceManager;
|
|
13
|
+
|
|
14
|
+
import java.io.File;
|
|
15
|
+
import java.nio.file.Files;
|
|
16
|
+
|
|
17
|
+
public class ReportFilePatternInMemory extends GhidraScript {
|
|
18
|
+
private static final int MAX_MATCHES = 8;
|
|
19
|
+
|
|
20
|
+
@Override
|
|
21
|
+
protected void run() throws Exception {
|
|
22
|
+
String[] arguments = getScriptArgs();
|
|
23
|
+
if (arguments.length < 1) {
|
|
24
|
+
printerr("Supply file offset in hex and optional pattern length.");
|
|
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
|
+
byte[] fileBytes = Files.readAllBytes(executable.toPath());
|
|
34
|
+
long fileOffset = parseHex(arguments[0]);
|
|
35
|
+
int length = arguments.length > 1 ? Integer.decode(arguments[1]) : 8;
|
|
36
|
+
if (fileOffset < 0 || length < 1 || fileOffset + length > fileBytes.length) {
|
|
37
|
+
printerr("File offset out of range.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
byte[] pattern = new byte[length];
|
|
42
|
+
System.arraycopy(fileBytes, (int) fileOffset, pattern, 0, length);
|
|
43
|
+
println("===== searching for file offset " + arguments[0] + " pattern =====");
|
|
44
|
+
println(toHex(pattern));
|
|
45
|
+
|
|
46
|
+
Memory memory = currentProgram.getMemory();
|
|
47
|
+
Listing listing = currentProgram.getListing();
|
|
48
|
+
ReferenceManager referenceManager = currentProgram.getReferenceManager();
|
|
49
|
+
Address cursor = memory.getMinAddress();
|
|
50
|
+
int matches = 0;
|
|
51
|
+
while (cursor != null && matches < MAX_MATCHES && !monitor.isCancelled()) {
|
|
52
|
+
Address found = memory.findBytes(cursor, pattern, null, true, monitor);
|
|
53
|
+
if (found == null) break;
|
|
54
|
+
|
|
55
|
+
Function function = listing.getFunctionContaining(found);
|
|
56
|
+
Instruction instruction = listing.getInstructionContaining(found);
|
|
57
|
+
println("Match " + (matches + 1) + ": " + found
|
|
58
|
+
+ (function == null ? "" : " in " + function.getEntryPoint() + " " + function.getName())
|
|
59
|
+
+ (instruction == null ? "" : " :: " + instruction));
|
|
60
|
+
|
|
61
|
+
ReferenceIterator references = referenceManager.getReferencesTo(found);
|
|
62
|
+
int refCount = 0;
|
|
63
|
+
while (references.hasNext() && refCount < 10) {
|
|
64
|
+
Reference reference = references.next();
|
|
65
|
+
Function fromFunction = listing.getFunctionContaining(reference.getFromAddress());
|
|
66
|
+
println(" ref from " + reference.getFromAddress()
|
|
67
|
+
+ (fromFunction == null ? "" : " in " + fromFunction.getEntryPoint() + " " + fromFunction.getName())
|
|
68
|
+
+ " " + reference.getReferenceType());
|
|
69
|
+
refCount++;
|
|
70
|
+
}
|
|
71
|
+
if (refCount == 0) {
|
|
72
|
+
println(" no incoming references");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// next() is null past the last address, which ends the search.
|
|
76
|
+
cursor = found.next();
|
|
77
|
+
matches++;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (matches == 0) {
|
|
81
|
+
println("Pattern not found in loaded memory.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The offset is hex with or without 0x, as for ReportMemoryBlockForFileOffset; Long.decode alone
|
|
86
|
+
// would read a bare 76640 as decimal and a leading zero as octal.
|
|
87
|
+
private static long parseHex(String argument) {
|
|
88
|
+
String normalized = argument.trim();
|
|
89
|
+
if (normalized.startsWith("0x") || normalized.startsWith("0X")) {
|
|
90
|
+
normalized = normalized.substring(2);
|
|
91
|
+
}
|
|
92
|
+
return Long.parseLong(normalized, 16);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private static String toHex(byte[] bytes) {
|
|
96
|
+
StringBuilder builder = new StringBuilder();
|
|
97
|
+
for (byte value : bytes) {
|
|
98
|
+
builder.append(String.format("%02X ", value));
|
|
99
|
+
}
|
|
100
|
+
return builder.toString().trim();
|
|
101
|
+
}
|
|
102
|
+
}
|