Initial public source release
Split reusable rendering and format support into vectorail-core and vectorail-gc.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
// GhidraScript: find decompiled functions containing text, optionally limiting
|
||||
// qualified function names to a second substring.
|
||||
// Usage: DecompileAllSearch.java 0x6458 GameScene
|
||||
|
||||
import ghidra.app.decompiler.DecompInterface;
|
||||
import ghidra.app.decompiler.DecompileResults;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionIterator;
|
||||
|
||||
public class DecompileAllSearch extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("DecompileAllSearch: needs text and optional function-name substring");
|
||||
return;
|
||||
}
|
||||
String needle = args[0];
|
||||
String nameNeedle = args.length > 1 ? args[1] : "";
|
||||
DecompInterface decompiler = new DecompInterface();
|
||||
decompiler.openProgram(currentProgram);
|
||||
FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
|
||||
while (functions.hasNext() && !monitor.isCancelled()) {
|
||||
Function function = functions.next();
|
||||
if (!nameNeedle.isEmpty() && !function.getName(true).contains(nameNeedle)) continue;
|
||||
DecompileResults result = decompiler.decompileFunction(function, 20, monitor);
|
||||
if (!result.decompileCompleted() || result.getDecompiledFunction() == null) continue;
|
||||
String code = result.getDecompiledFunction().getC();
|
||||
if (code == null || !code.contains(needle)) continue;
|
||||
println(function.getEntryPoint() + " " + function.getName(true));
|
||||
for (String line : code.split("\\R")) {
|
||||
if (line.contains(needle)) println(" " + line.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// GhidraScript: DecompileByAddr.java
|
||||
// Usage (headless):
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath <path> -postScript DecompileByAddr.java 0x401000 0x...
|
||||
//
|
||||
// Prints decompiled C for the function at (or containing) each address.
|
||||
|
||||
import ghidra.app.decompiler.DecompInterface;
|
||||
import ghidra.app.decompiler.DecompileResults;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
||||
public class DecompileByAddr extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("DecompileByAddr: needs one or more addresses, e.g. 0x63ea70");
|
||||
return;
|
||||
}
|
||||
|
||||
DecompInterface decomp = new DecompInterface();
|
||||
decomp.openProgram(currentProgram);
|
||||
|
||||
for (String a : args) {
|
||||
long va;
|
||||
try {
|
||||
va = Long.decode(a);
|
||||
} catch (Exception e) {
|
||||
printerr("bad address: " + a);
|
||||
continue;
|
||||
}
|
||||
|
||||
Address addr = toAddr(va);
|
||||
if (addr == null) {
|
||||
printerr("addr not in program: " + a);
|
||||
continue;
|
||||
}
|
||||
|
||||
Function f = getFunctionContaining(addr);
|
||||
if (f == null) f = getFunctionAt(addr);
|
||||
|
||||
println("================================================================================");
|
||||
println("addr: " + addr);
|
||||
if (f == null) {
|
||||
println("(no function found)");
|
||||
continue;
|
||||
}
|
||||
println("function: " + f.getName() + " @ " + f.getEntryPoint());
|
||||
|
||||
DecompileResults res = decomp.decompileFunction(f, 60, monitor);
|
||||
if (!res.decompileCompleted()) {
|
||||
println("(decompile failed)");
|
||||
continue;
|
||||
}
|
||||
String c = res.getDecompiledFunction().getC();
|
||||
// Keep output reasonable in headless logs; truncate if huge.
|
||||
if (c != null && c.length() > 120000) {
|
||||
c = c.substring(0, 120000) + "\n/* ... truncated ... */\n";
|
||||
}
|
||||
println(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// GhidraScript: decompile every caller of a function and print call-site context.
|
||||
// Usage: DecompileCallContexts.java 0x0063c0f0 [context-lines]
|
||||
|
||||
import ghidra.app.decompiler.DecompInterface;
|
||||
import ghidra.app.decompiler.DecompileResults;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceIterator;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DecompileCallContexts extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("DecompileCallContexts: needs a function address");
|
||||
return;
|
||||
}
|
||||
Address target = toAddr(Long.decode(args[0]));
|
||||
Function targetFunction = getFunctionAt(target);
|
||||
if (targetFunction == null) {
|
||||
printerr("target is not a function");
|
||||
return;
|
||||
}
|
||||
int context = args.length > 1 ? Integer.decode(args[1]) : 5;
|
||||
Map<Address, Function> callers = new LinkedHashMap<>();
|
||||
ReferenceIterator references =
|
||||
currentProgram.getReferenceManager().getReferencesTo(target);
|
||||
while (references.hasNext()) {
|
||||
Reference reference = references.next();
|
||||
Function caller = getFunctionContaining(reference.getFromAddress());
|
||||
if (caller != null) callers.put(caller.getEntryPoint(), caller);
|
||||
}
|
||||
|
||||
DecompInterface decompiler = new DecompInterface();
|
||||
decompiler.openProgram(currentProgram);
|
||||
String needle = targetFunction.getName();
|
||||
for (Function caller : callers.values()) {
|
||||
if (monitor.isCancelled()) break;
|
||||
DecompileResults result = decompiler.decompileFunction(caller, 90, monitor);
|
||||
if (!result.decompileCompleted() || result.getDecompiledFunction() == null) continue;
|
||||
String[] lines = result.getDecompiledFunction().getC().split("\\R");
|
||||
boolean[] selected = new boolean[lines.length];
|
||||
for (int i = 0; i < lines.length; ++i) {
|
||||
if (!lines[i].contains(needle)) continue;
|
||||
for (int j = Math.max(0, i - context);
|
||||
j <= Math.min(lines.length - 1, i + context); ++j) {
|
||||
selected[j] = true;
|
||||
}
|
||||
}
|
||||
println("================================================================================");
|
||||
println(caller.getEntryPoint() + " " + caller.getName(true));
|
||||
boolean gap = false;
|
||||
for (int i = 0; i < lines.length; ++i) {
|
||||
if (selected[i]) {
|
||||
if (gap) println("...");
|
||||
println(String.format("%5d %s", i + 1, lines[i]));
|
||||
gap = false;
|
||||
} else if (i > 0 && selected[i - 1]) {
|
||||
gap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// GhidraScript: decompile one function and print matching lines with context.
|
||||
// Usage: DecompileSearch.java 0x005ed4c0 0x104 8
|
||||
|
||||
import ghidra.app.decompiler.DecompInterface;
|
||||
import ghidra.app.decompiler.DecompileResults;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
||||
public class DecompileSearch extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length < 2) {
|
||||
printerr("DecompileSearch: needs address, text, and optional context-line count");
|
||||
return;
|
||||
}
|
||||
Function function = getFunctionContaining(toAddr(Long.decode(args[0])));
|
||||
if (function == null) {
|
||||
printerr("function not found");
|
||||
return;
|
||||
}
|
||||
int context = args.length > 2 ? Integer.decode(args[2]) : 5;
|
||||
DecompInterface decomp = new DecompInterface();
|
||||
decomp.openProgram(currentProgram);
|
||||
DecompileResults result = decomp.decompileFunction(function, 120, monitor);
|
||||
if (!result.decompileCompleted()) {
|
||||
printerr("decompile failed");
|
||||
return;
|
||||
}
|
||||
String[] lines = result.getDecompiledFunction().getC().split("\\R");
|
||||
boolean[] selected = new boolean[lines.length];
|
||||
for (int i = 0; i < lines.length; ++i) {
|
||||
if (!lines[i].contains(args[1])) continue;
|
||||
for (int j = Math.max(0, i - context); j <= Math.min(lines.length - 1, i + context); ++j) {
|
||||
selected[j] = true;
|
||||
}
|
||||
}
|
||||
println("function: " + function.getName() + " @ " + function.getEntryPoint());
|
||||
boolean gap = false;
|
||||
for (int i = 0; i < lines.length; ++i) {
|
||||
if (selected[i]) {
|
||||
if (gap) println("...");
|
||||
println(String.format("%5d %s", i + 1, lines[i]));
|
||||
gap = false;
|
||||
} else if (i > 0 && selected[i - 1]) {
|
||||
gap = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// GhidraScript: print little-endian dwords as hex, signed integers, and floats.
|
||||
// Usage: DumpData.java 0x006fcbf0 32
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public class DumpData extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length != 2) {
|
||||
printerr("DumpData: needs address and dword count");
|
||||
return;
|
||||
}
|
||||
Address base = toAddr(Long.decode(args[0]));
|
||||
int count = Integer.decode(args[1]);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
Address address = base.add(i * 4L);
|
||||
int value = getInt(address);
|
||||
println(address + " 0x" + String.format("%08x", value) +
|
||||
" int=" + value + " float=" + Float.intBitsToFloat(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// GhidraScript: print instructions around one or more addresses.
|
||||
// Usage: DumpInstructions.java 0x001b184c 0x001b1870
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
|
||||
public class DumpInstructions extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("DumpInstructions: needs one or more addresses");
|
||||
return;
|
||||
}
|
||||
for (String arg : args) {
|
||||
Instruction center = getInstructionContaining(toAddr(Long.decode(arg)));
|
||||
println("=== " + arg + " ===");
|
||||
if (center == null) {
|
||||
println("(no instruction)");
|
||||
continue;
|
||||
}
|
||||
Instruction cursor = center;
|
||||
for (int i = 0; i < 18; ++i) {
|
||||
Instruction previous = cursor.getPrevious();
|
||||
if (previous == null) break;
|
||||
cursor = previous;
|
||||
}
|
||||
for (int i = 0; i < 40 && cursor != null; ++i) {
|
||||
Function function = getFunctionContaining(cursor.getAddress());
|
||||
String marker = cursor.getAddress().equals(center.getAddress()) ? "=>" : " ";
|
||||
println(marker + " " + cursor.getAddress() + " " +
|
||||
(function == null ? "" : function.getName() + " ") + cursor);
|
||||
cursor = cursor.getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// GhidraScript: dump an array of 32-bit pointers as ASCII strings.
|
||||
// Usage: DumpPointerStrings.java <address> [count]
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
|
||||
public class DumpPointerStrings extends GhidraScript {
|
||||
private String readAscii(Address at) throws MemoryAccessException {
|
||||
StringBuilder out = new StringBuilder();
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
int value = getByte(at.add(i)) & 0xff;
|
||||
if (value == 0) break;
|
||||
if (value < 0x20 || value > 0x7e) return "<non-ascii>";
|
||||
out.append((char)value);
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("DumpPointerStrings: needs address and optional count");
|
||||
return;
|
||||
}
|
||||
Address start = toAddr(Long.decode(args[0]));
|
||||
int count = args.length > 1 ? Integer.decode(args[1]) : 32;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
Address slot = start.add(i * 4L);
|
||||
long raw = getInt(slot) & 0xffffffffL;
|
||||
Address target = toAddr(raw);
|
||||
String value;
|
||||
try {
|
||||
value = currentProgram.getMemory().contains(target) ? readAscii(target) : "<outside>";
|
||||
} catch (Exception exc) {
|
||||
value = "<invalid>";
|
||||
}
|
||||
println(String.format("%4d %s -> %08x %s", i, slot, raw, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// GhidraScript: DumpPointers.java
|
||||
// Usage:
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath tools/ghidra_scripts -postScript DumpPointers.java 0x006f8990 16
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
||||
public class DumpPointers extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length < 1) {
|
||||
printerr("DumpPointers: needs address and optional count");
|
||||
return;
|
||||
}
|
||||
Address at = toAddr(Long.decode(args[0]));
|
||||
int count = args.length >= 2 ? Integer.decode(args[1]) : 32;
|
||||
for (int i = 0; i < count; i++) {
|
||||
Address slot = at.add(i * 4L);
|
||||
long ptr = getInt(slot) & 0xffffffffL;
|
||||
Address dst = toAddr(ptr);
|
||||
Function f = getFunctionAt(dst);
|
||||
if (f == null) f = getFunctionContaining(dst);
|
||||
String name = f == null ? "" : f.getName() + " @ " + f.getEntryPoint();
|
||||
println(String.format("%s +0x%02x -> %08x %s", slot, i * 4, ptr, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// GhidraScript: dump 32-bit words as hex, signed integer, and IEEE-754 float.
|
||||
// Usage: DumpScalars.java <address> [count]
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public class DumpScalars extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("DumpScalars: needs address and optional count");
|
||||
return;
|
||||
}
|
||||
|
||||
Address start = toAddr(Long.decode(args[0]));
|
||||
int count = args.length > 1 ? Integer.decode(args[1]) : 1;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
Address slot = start.add(i * 4L);
|
||||
int raw = getInt(slot);
|
||||
println(String.format(
|
||||
"%s hex=%08x int=%d float=%.9g",
|
||||
slot, raw, raw, Float.intBitsToFloat(raw)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// GhidraScript: FindAddressRefs.java
|
||||
// Usage: ... -postScript FindAddressRefs.java 0x401000 [...]
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
|
||||
public class FindAddressRefs extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
for (String arg : getScriptArgs()) {
|
||||
Address target = toAddr(Long.decode(arg));
|
||||
println("================================================================================");
|
||||
println("target: " + target);
|
||||
Reference[] refs = getReferencesTo(target);
|
||||
println("refs: " + refs.length);
|
||||
for (Reference ref : refs) {
|
||||
Address from = ref.getFromAddress();
|
||||
Function function = getFunctionContaining(from);
|
||||
println(" " + from + " -> " +
|
||||
(function == null ? "(no function)" :
|
||||
function.getName() + " @ " + function.getEntryPoint()) +
|
||||
" type=" + ref.getReferenceType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// GhidraScript: FindD3DSetTransformSites.java
|
||||
// Usage:
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath tools/ghidra_scripts -postScript FindD3DSetTransformSites.java [minAddr] [maxAddr]
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
|
||||
public class FindD3DSetTransformSites extends GhidraScript {
|
||||
private boolean hasScalar(Instruction ins, long want) {
|
||||
for (int op = 0; op < ins.getNumOperands(); op++) {
|
||||
for (Object obj : ins.getOpObjects(op)) {
|
||||
if (obj instanceof Scalar) {
|
||||
Scalar s = (Scalar)obj;
|
||||
if (s.getUnsignedValue() == want || s.getSignedValue() == want) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean inRange(Address addr, Address min, Address max) {
|
||||
if (min != null && addr.compareTo(min) < 0) return false;
|
||||
if (max != null && addr.compareTo(max) > 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean followedByCall(Instruction ins, int maxSteps) {
|
||||
Instruction cur = ins;
|
||||
for (int i = 0; i < maxSteps; i++) {
|
||||
cur = cur.getNext();
|
||||
if (cur == null) return false;
|
||||
if ("CALL".equals(cur.getMnemonicString())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean nearbyPushState(Instruction ins) {
|
||||
Instruction cur = ins;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
cur = cur.getPrevious();
|
||||
if (cur == null) break;
|
||||
if (!"PUSH".equals(cur.getMnemonicString())) continue;
|
||||
if (hasScalar(cur, 2) || hasScalar(cur, 3)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
Address min = args.length > 0 ? toAddr(Long.decode(args[0])) : null;
|
||||
Address max = args.length > 1 ? toAddr(Long.decode(args[1])) : null;
|
||||
|
||||
println("=== D3D SetTransform VIEW/PROJ candidates ===");
|
||||
int hits = 0;
|
||||
for (Instruction ins : currentProgram.getListing().getInstructions(true)) {
|
||||
if (!inRange(ins.getAddress(), min, max)) continue;
|
||||
if (!hasScalar(ins, 0xb0)) continue;
|
||||
if (!followedByCall(ins, 3)) continue;
|
||||
if (!nearbyPushState(ins)) continue;
|
||||
|
||||
hits++;
|
||||
Function f = getFunctionContaining(ins.getAddress());
|
||||
String fs = f == null ? "(no func)" : f.getName() + " @ " + f.getEntryPoint();
|
||||
println("");
|
||||
println("HIT " + hits + " " + ins.getAddress() + " " + fs);
|
||||
|
||||
Instruction cur = ins;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Instruction prev = cur.getPrevious();
|
||||
if (prev == null) break;
|
||||
cur = prev;
|
||||
}
|
||||
for (int i = 0; i < 18 && cur != null; i++) {
|
||||
String mark = cur.getAddress().equals(ins.getAddress()) ? "=>" : " ";
|
||||
println(mark + " " + cur.getAddress() + " " + cur);
|
||||
cur = cur.getNext();
|
||||
}
|
||||
if (monitor.isCancelled()) break;
|
||||
}
|
||||
println("hits: " + hits);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// GhidraScript: FindFunctionCallers.java
|
||||
// Usage (headless):
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath <path> -postScript FindFunctionCallers.java 0x0063ea70
|
||||
//
|
||||
// Prints the functions which contain callsites (or any references) to the given function entry.
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceIterator;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
public class FindFunctionCallers extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("FindFunctionCallers: needs one or more function entry addresses, e.g. 0x613710");
|
||||
return;
|
||||
}
|
||||
|
||||
for (String a : args) {
|
||||
long va;
|
||||
try {
|
||||
va = Long.decode(a);
|
||||
} catch (Exception e) {
|
||||
printerr("bad address: " + a);
|
||||
continue;
|
||||
}
|
||||
Address entry = toAddr(va);
|
||||
if (entry == null) {
|
||||
printerr("addr not in program: " + a);
|
||||
continue;
|
||||
}
|
||||
|
||||
println("=== callers for " + entry + " ===");
|
||||
LinkedHashSet<String> callers = new LinkedHashSet<>();
|
||||
ReferenceIterator it = currentProgram.getReferenceManager().getReferencesTo(entry);
|
||||
int n = 0;
|
||||
while (it.hasNext()) {
|
||||
Reference r = it.next();
|
||||
n++;
|
||||
Function f = getFunctionContaining(r.getFromAddress());
|
||||
if (f != null) {
|
||||
callers.add(f.getName() + " @ " + f.getEntryPoint() + " (from " + r.getFromAddress() + ")");
|
||||
} else {
|
||||
callers.add("(no func) from " + r.getFromAddress());
|
||||
}
|
||||
if (n > 5000) break;
|
||||
}
|
||||
println("refs: " + n);
|
||||
if (callers.isEmpty()) {
|
||||
println("(none)");
|
||||
} else {
|
||||
for (String s : callers) println(" " + s);
|
||||
}
|
||||
println("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// GhidraScript: list functions whose symbol name contains a substring.
|
||||
// Usage: FindFunctions.java DrawMark
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionIterator;
|
||||
|
||||
public class FindFunctions extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("FindFunctions: needs a case-sensitive name substring");
|
||||
return;
|
||||
}
|
||||
String needle = args[0];
|
||||
FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
|
||||
while (functions.hasNext() && !monitor.isCancelled()) {
|
||||
Function function = functions.next();
|
||||
if (function.getName(true).contains(needle)) {
|
||||
println(function.getEntryPoint() + " " + function.getName(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// GhidraScript: FindScalarOps.java
|
||||
// Usage:
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath tools/ghidra_scripts -postScript FindScalarOps.java 0x3b 0x3f800000
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
public class FindScalarOps extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("FindScalarOps: needs one or more scalar values");
|
||||
return;
|
||||
}
|
||||
|
||||
for (String arg : args) {
|
||||
long want;
|
||||
try {
|
||||
want = Long.decode(arg);
|
||||
} catch (Exception e) {
|
||||
printerr("bad scalar: " + arg);
|
||||
continue;
|
||||
}
|
||||
|
||||
println("=== scalar " + arg + " ===");
|
||||
int hits = 0;
|
||||
LinkedHashSet<String> funcs = new LinkedHashSet<>();
|
||||
for (Instruction ins : currentProgram.getListing().getInstructions(true)) {
|
||||
int n = ins.getNumOperands();
|
||||
boolean matched = false;
|
||||
for (int op = 0; op < n; op++) {
|
||||
for (Object obj : ins.getOpObjects(op)) {
|
||||
if (obj instanceof Scalar) {
|
||||
Scalar s = (Scalar)obj;
|
||||
long v = s.getUnsignedValue();
|
||||
long sv = s.getSignedValue();
|
||||
if (v == want || sv == want) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matched) break;
|
||||
}
|
||||
if (!matched) continue;
|
||||
hits++;
|
||||
Function f = getFunctionContaining(ins.getAddress());
|
||||
String fs = f == null ? "(no func)" : f.getName() + " @ " + f.getEntryPoint();
|
||||
funcs.add(fs);
|
||||
if (hits <= 250) println(ins.getAddress() + " " + fs + " " + ins);
|
||||
if (monitor.isCancelled()) break;
|
||||
}
|
||||
println("hits: " + hits);
|
||||
for (String fs : funcs) println(" " + fs);
|
||||
println("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// GhidraScript: FindScalarWindow.java
|
||||
// Usage:
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath tools/ghidra_scripts -postScript FindScalarWindow.java 0xb0 [minAddr] [maxAddr] [maxHits]
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
|
||||
public class FindScalarWindow extends GhidraScript {
|
||||
private boolean hasScalar(Instruction ins, long want) {
|
||||
for (int op = 0; op < ins.getNumOperands(); op++) {
|
||||
for (Object obj : ins.getOpObjects(op)) {
|
||||
if (obj instanceof Scalar) {
|
||||
Scalar s = (Scalar)obj;
|
||||
if (s.getUnsignedValue() == want || s.getSignedValue() == want) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean inRange(Address addr, Address min, Address max) {
|
||||
if (min != null && addr.compareTo(min) < 0) return false;
|
||||
if (max != null && addr.compareTo(max) > 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("FindScalarWindow: needs scalar, optional minAddr maxAddr maxHits");
|
||||
return;
|
||||
}
|
||||
|
||||
long want = Long.decode(args[0]);
|
||||
Address min = args.length > 1 ? toAddr(Long.decode(args[1])) : null;
|
||||
Address max = args.length > 2 ? toAddr(Long.decode(args[2])) : null;
|
||||
int maxHits = args.length > 3 ? Integer.decode(args[3]) : 120;
|
||||
|
||||
println("=== scalar window " + args[0] + " ===");
|
||||
int hits = 0;
|
||||
for (Instruction ins : currentProgram.getListing().getInstructions(true)) {
|
||||
if (!inRange(ins.getAddress(), min, max)) continue;
|
||||
if (!hasScalar(ins, want)) continue;
|
||||
|
||||
hits++;
|
||||
Function f = getFunctionContaining(ins.getAddress());
|
||||
String fs = f == null ? "(no func)" : f.getName() + " @ " + f.getEntryPoint();
|
||||
println("");
|
||||
println("HIT " + hits + " " + ins.getAddress() + " " + fs);
|
||||
|
||||
Instruction cur = ins;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Instruction prev = cur.getPrevious();
|
||||
if (prev == null) break;
|
||||
cur = prev;
|
||||
}
|
||||
for (int i = 0; i < 17 && cur != null; i++) {
|
||||
String mark = cur.getAddress().equals(ins.getAddress()) ? "=>" : " ";
|
||||
println(mark + " " + cur.getAddress() + " " + cur);
|
||||
cur = cur.getNext();
|
||||
}
|
||||
|
||||
if (hits >= maxHits) {
|
||||
println("(truncated at " + maxHits + " hits)");
|
||||
break;
|
||||
}
|
||||
if (monitor.isCancelled()) break;
|
||||
}
|
||||
println("hits: " + hits);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// GhidraScript: FindStringXrefs.java
|
||||
// Usage (headless):
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> \
|
||||
// -scriptPath <path> -postScript FindStringXrefs.java "<needle1>" ["<needle2>" ...]
|
||||
//
|
||||
// Prints the addresses where the ASCII needle is found (optionally with NUL terminator)
|
||||
// and lists the functions that reference the string address.
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceIterator;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
public class FindStringXrefs extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("FindStringXrefs: needs at least one ASCII needle arg");
|
||||
return;
|
||||
}
|
||||
|
||||
Memory mem = currentProgram.getMemory();
|
||||
Address min = mem.getMinAddress();
|
||||
Address max = mem.getMaxAddress();
|
||||
|
||||
for (String needle : args) {
|
||||
if (needle == null) continue;
|
||||
if (needle.isEmpty()) continue;
|
||||
|
||||
println("=== needle: " + needle + " ===");
|
||||
|
||||
byte[] pat0 = needle.getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] pat1 = new byte[pat0.length + 1];
|
||||
System.arraycopy(pat0, 0, pat1, 0, pat0.length);
|
||||
pat1[pat1.length - 1] = 0;
|
||||
|
||||
// First try NUL-terminated.
|
||||
int hits = 0;
|
||||
Address at = min;
|
||||
while (true) {
|
||||
Address found = mem.findBytes(at, max, pat1, null, true, monitor);
|
||||
if (found == null) break;
|
||||
hits++;
|
||||
dumpHit(found);
|
||||
at = found.add(1);
|
||||
}
|
||||
|
||||
// If no NUL-terminated matches, fall back to raw bytes search.
|
||||
if (hits == 0) {
|
||||
at = min;
|
||||
while (true) {
|
||||
Address found = mem.findBytes(at, max, pat0, null, true, monitor);
|
||||
if (found == null) break;
|
||||
hits++;
|
||||
dumpHit(found);
|
||||
at = found.add(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (hits == 0) {
|
||||
println("(no hits)");
|
||||
}
|
||||
println("");
|
||||
}
|
||||
}
|
||||
|
||||
private void dumpHit(Address strAddr) {
|
||||
println("hit @ " + strAddr);
|
||||
|
||||
LinkedHashSet<String> funcs = new LinkedHashSet<>();
|
||||
ReferenceIterator it = currentProgram.getReferenceManager().getReferencesTo(strAddr);
|
||||
int nref = 0;
|
||||
while (it.hasNext()) {
|
||||
Reference r = it.next();
|
||||
nref++;
|
||||
Function f = getFunctionContaining(r.getFromAddress());
|
||||
if (f != null) {
|
||||
funcs.add(f.getName() + " @ " + f.getEntryPoint());
|
||||
} else {
|
||||
funcs.add("(no func) from " + r.getFromAddress());
|
||||
}
|
||||
if (nref > 2000) break; // avoid pathological spam
|
||||
}
|
||||
println("refs: " + nref);
|
||||
for (String s : funcs) {
|
||||
println(" " + s);
|
||||
if (funcs.size() > 200) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// GhidraScript: FindSymbolRefs.java
|
||||
// Usage:
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath <path> -postScript FindSymbolRefs.java D3DXMatrixLookAtLH ...
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolIterator;
|
||||
|
||||
public class FindSymbolRefs extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("FindSymbolRefs: needs symbol names");
|
||||
return;
|
||||
}
|
||||
|
||||
for (String needle : args) {
|
||||
println("================================================================================");
|
||||
println("symbol needle: " + needle);
|
||||
boolean any = false;
|
||||
SymbolIterator it = currentProgram.getSymbolTable().getAllSymbols(true);
|
||||
while (it.hasNext() && !monitor.isCancelled()) {
|
||||
Symbol s = it.next();
|
||||
if (!s.getName(true).contains(needle)) continue;
|
||||
any = true;
|
||||
Address addr = s.getAddress();
|
||||
println("symbol: " + s.getName(true) + " @ " + addr);
|
||||
Reference[] refs = getReferencesTo(addr);
|
||||
println("refs: " + refs.length);
|
||||
for (Reference r : refs) {
|
||||
Address from = r.getFromAddress();
|
||||
Function f = getFunctionContaining(from);
|
||||
String fn = f == null ? "(no function)" : f.getName() + " @ " + f.getEntryPoint();
|
||||
println(" " + from + " -> " + fn + " type=" + r.getReferenceType());
|
||||
}
|
||||
}
|
||||
if (!any) println("(no symbol hits)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// GhidraScript: FindVirtualCallOffset.java
|
||||
// Usage:
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath tools/ghidra_scripts -postScript FindVirtualCallOffset.java 0xc4 [minAddr] [maxAddr]
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
|
||||
public class FindVirtualCallOffset extends GhidraScript {
|
||||
private boolean hasScalar(Instruction ins, long want) {
|
||||
for (int op = 0; op < ins.getNumOperands(); op++) {
|
||||
for (Object obj : ins.getOpObjects(op)) {
|
||||
if (obj instanceof Scalar) {
|
||||
Scalar s = (Scalar)obj;
|
||||
if (s.getUnsignedValue() == want || s.getSignedValue() == want) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean inRange(Address addr, Address min, Address max) {
|
||||
if (min != null && addr.compareTo(min) < 0) return false;
|
||||
if (max != null && addr.compareTo(max) > 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean followedByCall(Instruction ins) {
|
||||
Instruction n = ins.getNext();
|
||||
if (n != null && "CALL".equals(n.getMnemonicString())) return true;
|
||||
n = n == null ? null : n.getNext();
|
||||
return n != null && "CALL".equals(n.getMnemonicString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("FindVirtualCallOffset: needs vtable offset");
|
||||
return;
|
||||
}
|
||||
long want = Long.decode(args[0]);
|
||||
Address min = args.length > 1 ? toAddr(Long.decode(args[1])) : null;
|
||||
Address max = args.length > 2 ? toAddr(Long.decode(args[2])) : null;
|
||||
|
||||
println("=== virtual call offset " + args[0] + " ===");
|
||||
int hits = 0;
|
||||
for (Instruction ins : currentProgram.getListing().getInstructions(true)) {
|
||||
if (!inRange(ins.getAddress(), min, max)) continue;
|
||||
if (!"MOV".equals(ins.getMnemonicString())) continue;
|
||||
if (!hasScalar(ins, want)) continue;
|
||||
if (!followedByCall(ins)) continue;
|
||||
|
||||
hits++;
|
||||
Function f = getFunctionContaining(ins.getAddress());
|
||||
String fs = f == null ? "(no func)" : f.getName() + " @ " + f.getEntryPoint();
|
||||
println("");
|
||||
println("HIT " + hits + " " + ins.getAddress() + " " + fs);
|
||||
|
||||
Instruction cur = ins;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
Instruction prev = cur.getPrevious();
|
||||
if (prev == null) break;
|
||||
cur = prev;
|
||||
}
|
||||
for (int i = 0; i < 14 && cur != null; i++) {
|
||||
String mark = cur.getAddress().equals(ins.getAddress()) ? "=>" : " ";
|
||||
println(mark + " " + cur.getAddress() + " " + cur);
|
||||
cur = cur.getNext();
|
||||
}
|
||||
if (monitor.isCancelled()) break;
|
||||
}
|
||||
println("hits: " + hits);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// GhidraScript: FindVtableOffsetCalls.java
|
||||
// Usage:
|
||||
// analyzeHeadless <projDir> <projName> -process <progName> -noanalysis -readOnly \
|
||||
// -scriptPath tools/ghidra_scripts -postScript FindVtableOffsetCalls.java 0xb0
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.scalar.Scalar;
|
||||
|
||||
public class FindVtableOffsetCalls extends GhidraScript {
|
||||
private boolean hasScalar(Instruction ins, long want) {
|
||||
for (int op = 0; op < ins.getNumOperands(); op++) {
|
||||
for (Object obj : ins.getOpObjects(op)) {
|
||||
if (obj instanceof Scalar) {
|
||||
Scalar s = (Scalar)obj;
|
||||
if (s.getUnsignedValue() == want || s.getSignedValue() == want) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0) {
|
||||
printerr("FindVtableOffsetCalls: needs one or more vtable offsets, e.g. 0xb0");
|
||||
return;
|
||||
}
|
||||
|
||||
for (String arg : args) {
|
||||
long want = Long.decode(arg);
|
||||
println("=== vtable call offset " + arg + " ===");
|
||||
int hits = 0;
|
||||
for (Instruction ins : currentProgram.getListing().getInstructions(true)) {
|
||||
if (!"CALL".equals(ins.getMnemonicString())) continue;
|
||||
if (!hasScalar(ins, want)) continue;
|
||||
|
||||
hits++;
|
||||
Function f = getFunctionContaining(ins.getAddress());
|
||||
String fs = f == null ? "(no func)" : f.getName() + " @ " + f.getEntryPoint();
|
||||
println("");
|
||||
println(ins.getAddress() + " " + fs + " " + ins);
|
||||
|
||||
Instruction prev = ins;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
prev = prev.getPrevious();
|
||||
if (prev == null) break;
|
||||
println(" " + prev.getAddress() + " " + prev);
|
||||
}
|
||||
if (hits >= 300) {
|
||||
println("(truncated at 300 hits)");
|
||||
break;
|
||||
}
|
||||
if (monitor.isCancelled()) break;
|
||||
}
|
||||
println("hits: " + hits);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// GhidraScript: list functions whose entry points fall inside an address range.
|
||||
// Usage: ListFunctionsRange.java 0x005b6700 0x005b8600
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.FunctionIterator;
|
||||
|
||||
public class ListFunctionsRange extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length != 2) {
|
||||
printerr("ListFunctionsRange: needs minAddr maxAddr");
|
||||
return;
|
||||
}
|
||||
Address min = toAddr(Long.decode(args[0]));
|
||||
Address max = toAddr(Long.decode(args[1]));
|
||||
FunctionIterator it = currentProgram.getFunctionManager().getFunctions(min, true);
|
||||
while (it.hasNext() && !monitor.isCancelled()) {
|
||||
Function function = it.next();
|
||||
Address entry = function.getEntryPoint();
|
||||
if (entry.compareTo(max) > 0) break;
|
||||
println(entry + " " + function.getName() + " size=0x" +
|
||||
Long.toHexString(function.getBody().getNumAddresses()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// GhidraScript: print little-endian 32-bit words and their float view.
|
||||
// Usage: ReadScalars.java 0x2703f8 0x2703fc
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public class ReadScalars extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
for (String arg : getScriptArgs()) {
|
||||
Address address = toAddr(Long.decode(arg));
|
||||
int word = getInt(address);
|
||||
println(address + " u32=0x" + Integer.toHexString(word) +
|
||||
" f32=" + Float.intBitsToFloat(word));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// GhidraScript: rename functions from address/name pairs.
|
||||
// Usage: RenameFunctions.java 0x00100000 FunctionName 0x00100100 OtherName
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
|
||||
public class RenameFunctions extends GhidraScript {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
String[] args = getScriptArgs();
|
||||
if (args == null || args.length == 0 || (args.length & 1) != 0) {
|
||||
printerr("RenameFunctions: needs address/name pairs");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < args.length; i += 2) {
|
||||
Address address = toAddr(Long.decode(args[i]));
|
||||
Function function = getFunctionAt(address);
|
||||
if (function == null) {
|
||||
printerr("no function at " + address);
|
||||
continue;
|
||||
}
|
||||
function.setName(args[i + 1], SourceType.USER_DEFINED);
|
||||
println(address + " " + function.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user