106 lines
11 KiB
Java
106 lines
11 KiB
Java
import ghidra.app.script.GhidraScript;
|
|
import ghidra.program.model.address.*;
|
|
import ghidra.program.model.mem.*;
|
|
import ghidra.program.model.listing.*;
|
|
import ghidra.program.model.symbol.*;
|
|
import ghidra.program.model.scalar.*;
|
|
import ghidra.program.model.lang.*;
|
|
import ghidra.app.decompiler.*;
|
|
import java.util.*;
|
|
import java.util.regex.*;
|
|
import java.io.*;
|
|
|
|
// Pass 2: every immediate 10000..10196 (tech ids) anywhere in code, ServerPlayer accessor stubs, and their callers.
|
|
public class TechFx2 extends GhidraScript {
|
|
DecompInterface decomp; Memory mem; ReferenceManager rm;
|
|
static final String OUT = "/tmp/techfx2";
|
|
String cstr(Address a) { try { byte[] b = new byte[80]; mem.getBytes(a, b); int i = 0; while (i < 80 && b[i] != 0 && (b[i]&0xff) >= 0x20 && (b[i]&0xff) < 0x7f) i++; if (i >= 1 && i < 80 && b[i] == 0) return new String(b, 0, i, "ISO-8859-1"); } catch (Exception e) {} return null; }
|
|
String constAt(long a) {
|
|
try { Address ad = toAddr(a); int iv = mem.getInt(ad); long lv = mem.getLong(ad);
|
|
float f = Float.intBitsToFloat(iv); double d = Double.longBitsToDouble(lv);
|
|
String s = cstr(ad); if (s != null) return "\"" + s + "\"";
|
|
StringBuilder sb = new StringBuilder(); sb.append("i=" + iv);
|
|
if (!Float.isNaN(f) && Math.abs(f) < 1e12 && (Math.abs(f) > 1e-7 || f == 0)) sb.append(" f=" + f);
|
|
if (!Double.isNaN(d) && Math.abs(d) < 1e12 && (Math.abs(d) > 1e-7 || d == 0)) sb.append(" d=" + d);
|
|
return sb.toString(); } catch (Exception e) { return "?"; }
|
|
}
|
|
String subst(String c) {
|
|
Matcher m = Pattern.compile("&?(DAT|PTR_s_|s_[A-Za-z0-9_]*|PTR_DAT|u_[A-Za-z0-9_]*|_DAT|PTR_PTR|_PTR)_([0-9a-f]{8})").matcher(c);
|
|
StringBuffer sb = new StringBuffer();
|
|
while (m.find()) { long a = Long.parseLong(m.group(2), 16); Address ad = toAddr(a); String s = cstr(ad); String rep = m.group(0);
|
|
if (s != null && s.length() <= 64 && !m.group(1).equals("PTR_DAT")) rep = "\"" + s + "\"";
|
|
else if (a >= 0x9dd000L && a < 0xb40000L) { rep = m.group(0) + "/*" + constAt(a) + "*/"; }
|
|
m.appendReplacement(sb, Matcher.quoteReplacement(rep)); }
|
|
m.appendTail(sb); return sb.toString();
|
|
}
|
|
String decompRaw(Function f) { try { DecompileResults res = decomp.decompileFunction(f, 600, monitor); if (res == null || !res.decompileCompleted()) return "[decompile failed]"; return res.getDecompiledFunction().getC(); } catch (Exception e) { return "[exception " + e.getMessage() + "]"; } }
|
|
Set<Long> done = new HashSet<>();
|
|
void dumpFunc(Function f, PrintWriter idx, boolean disasm) throws Exception {
|
|
if (f == null || done.contains(f.getEntryPoint().getOffset())) return;
|
|
done.add(f.getEntryPoint().getOffset());
|
|
String t = String.format("%08x", f.getEntryPoint().getOffset());
|
|
PrintWriter w = new PrintWriter(new FileWriter(OUT + "/" + t + ".c"));
|
|
w.println("// " + f.getName(true) + " @ " + f.getEntryPoint() + " size=" + f.getBody().getNumAddresses());
|
|
StringBuilder calls = new StringBuilder(); for (Function cf : f.getCalledFunctions(monitor)) calls.append(cf.getName(true) + "@" + cf.getEntryPoint() + " ");
|
|
w.println("// CALLS: " + calls);
|
|
StringBuilder callers = new StringBuilder(); for (Function cf : f.getCallingFunctions(monitor)) callers.append(cf.getName(true) + "@" + cf.getEntryPoint() + " ");
|
|
w.println("// CALLERS: " + callers);
|
|
w.println(subst(decompRaw(f)));
|
|
if (disasm) { w.println("// ---- DISASM ----"); Listing l = currentProgram.getListing(); InstructionIterator ii = l.getInstructions(f.getBody(), true);
|
|
while (ii.hasNext()) { Instruction in = ii.next(); StringBuilder ann = new StringBuilder();
|
|
for (Reference r : in.getReferencesFrom()) { long ta = r.getToAddress().getOffset(); if (ta >= 0x9dd000L && ta < 0xb40000L) { Symbol s = currentProgram.getSymbolTable().getPrimarySymbol(r.getToAddress()); ann.append(" ; " + (s != null ? s.getName() : String.format("%08x", ta)) + "=" + constAt(ta)); } else if (r.getReferenceType().isCall()) { Function cf = getFunctionAt(r.getToAddress()); if (cf != null) ann.append(" ; ->" + cf.getName(true)); } }
|
|
w.println(String.format("%08x %-40s%s", in.getAddress().getOffset(), in.toString(), ann)); } }
|
|
w.close();
|
|
idx.println("decompiled " + t + " " + f.getName(true) + " size=" + f.getBody().getNumAddresses()); idx.flush();
|
|
}
|
|
|
|
@Override public void run() throws Exception {
|
|
mem = currentProgram.getMemory(); rm = currentProgram.getReferenceManager();
|
|
decomp = new DecompInterface(); decomp.openProgram(currentProgram);
|
|
new File(OUT).mkdirs();
|
|
PrintWriter idx = new PrintWriter(new FileWriter(OUT + "/index.txt"));
|
|
List<String> names = new ArrayList<>();
|
|
for (int i = 0; i < 196; i++) { long a = 0x009ff9e4L + 8*i; int p = mem.getInt(toAddr(a)); String s = (p > 0x9dd000 && p < 0xad9000) ? cstr(toAddr(p & 0xffffffffL)) : null; names.add(s == null ? "?" : s); }
|
|
|
|
// 1. immediates 10000..10196 (+ 0xc5 sentinel ignored) anywhere
|
|
PrintWriter iw = new PrintWriter(new FileWriter(OUT + "/techids.txt"));
|
|
Map<Integer, Set<String>> byId = new TreeMap<>();
|
|
Set<Function> idFns = new LinkedHashSet<>();
|
|
Listing l = currentProgram.getListing();
|
|
InstructionIterator all = l.getInstructions(true);
|
|
while (all.hasNext()) { Instruction in = all.next();
|
|
for (int oi = 0; oi < in.getNumOperands(); oi++) { if ((in.getOperandType(oi) & OperandType.SCALAR) == 0) continue; for (Object o : in.getOpObjects(oi)) { if (!(o instanceof Scalar)) continue; long v = ((Scalar)o).getSignedValue(); if (v < 10000 || v > 10196) continue; int id = (int)(v - 10000);
|
|
Function f = getFunctionContaining(in.getAddress());
|
|
// find next call within 8 instructions
|
|
String nextCall = "-"; Instruction n = in; for (int k = 0; k < 8 && n != null; k++) { n = n.getNext(); if (n == null) break; if (n.getMnemonicString().equals("CALL")) { for (Reference r : n.getReferencesFrom()) if (r.getReferenceType().isCall()) { Function cf = getFunctionAt(r.getToAddress()); nextCall = cf == null ? r.getToAddress().toString() : cf.getName(true) + "@" + cf.getEntryPoint(); } if (nextCall.equals("-")) nextCall = n.toString(); break; } }
|
|
String line = String.format("%08x %-8s id=%d %-16s in %s -> %s", in.getAddress().getOffset(), in.getMnemonicString(), (int)v, names.get(id), f == null ? "(nofunc)" : f.getName(true) + "@" + f.getEntryPoint(), nextCall);
|
|
iw.println(line); byId.computeIfAbsent(id, k -> new TreeSet<>()).add(f == null ? "(nofunc)" : f.getName(true) + "@" + f.getEntryPoint() + "(" + String.format("%08x", in.getAddress().getOffset()) + ")"); if (f != null) idFns.add(f); } } }
|
|
iw.println("== by id =="); for (int i = 0; i < 196; i++) iw.println(String.format("%3d %-16s %s", i, names.get(i), byId.containsKey(i) ? String.join(" ", byId.get(i)) : "(none)"));
|
|
iw.close();
|
|
// also pointer-table refs: PTR_s_<name> entries at 0x009ff9e4 + 8i referenced from code (e.g. switch tables)
|
|
for (Function f : idFns) if (f.getBody().getNumAddresses() < 20000) dumpFunc(f, idx, f.getBody().getNumAddresses() < 5000);
|
|
|
|
// 2. accessor stubs in ServerPlayer/StrategyServer code range reading small [reg+disp]
|
|
long[] disp = {0xb4,0xb8,0xfc,0xfd,0xfe,0xff,0x100,0x101,0x102,0x103,0x104,0x108,0x10c,0x110,0x114,0x118,0x11c,0x120,0x124,0x128,0x12c,0x130,0x134,0x138,0x13c,0x140,0x144,0x148,0x14c,0x150,0x154,0x158,0x15c,0x160,0x190,0x194,0x224,0x228,0x22c,0x288,0x28c,0x290,0x330,0x348};
|
|
Set<Long> dset = new HashSet<>(); for (long d : disp) dset.add(d);
|
|
PrintWriter aw = new PrintWriter(new FileWriter(OUT + "/accessors.txt"));
|
|
PrintWriter fw = new PrintWriter(new FileWriter(OUT + "/fieldrefs.txt"));
|
|
Map<Long, Map<String,Integer>> hits = new TreeMap<>();
|
|
FunctionIterator fi = l.getFunctions(toAddr(0x00740000L), true);
|
|
Set<Function> accCallers = new LinkedHashSet<>();
|
|
while (fi.hasNext()) { Function f = fi.next(); if (f.getEntryPoint().getOffset() >= 0x008a0000L) break; long sz = f.getBody().getNumAddresses();
|
|
Set<Long> seenD = new TreeSet<>();
|
|
InstructionIterator ii = l.getInstructions(f.getBody(), true);
|
|
while (ii.hasNext()) { Instruction in = ii.next(); for (int oi = 0; oi < in.getNumOperands(); oi++) { boolean hasReg = false; Scalar sc = null; for (Object o : in.getOpObjects(oi)) { if (o instanceof Register) hasReg = true; if (o instanceof Scalar) sc = (Scalar)o; } if (!hasReg || sc == null) continue; if ((in.getOperandType(oi) & (OperandType.ADDRESS|OperandType.DYNAMIC)) == 0) continue; long v = sc.getSignedValue(); if (dset.contains(v)) { seenD.add(v); hits.computeIfAbsent(v, k -> new TreeMap<>()).merge(f.getName(true) + "@" + f.getEntryPoint() + " sz=" + sz, 1, Integer::sum); } } }
|
|
if (sz <= 64 && !seenD.isEmpty()) { StringBuilder cs = new StringBuilder(); for (Function c : f.getCallingFunctions(monitor)) { cs.append(c.getName(true) + "@" + c.getEntryPoint() + " "); accCallers.add(c); } aw.println(String.format("%s@%s sz=%d disp=%s callers: %s", f.getName(true), f.getEntryPoint(), sz, seenD, cs)); dumpFunc(f, idx, true); } }
|
|
aw.close();
|
|
for (Map.Entry<Long, Map<String,Integer>> e : hits.entrySet()) { fw.println(String.format("== disp 0x%x ==", e.getKey())); for (Map.Entry<String,Integer> e2 : e.getValue().entrySet()) fw.println(" " + e2.getKey() + " x" + e2.getValue()); }
|
|
fw.close();
|
|
for (Function c : accCallers) if (c.getBody().getNumAddresses() < 20000) dumpFunc(c, idx, false);
|
|
|
|
// 3. specific helpers
|
|
String[] extra = {"0082bf10","00537240","0080e410","0080e330","008186b0","004d7960","0074d070","0074a870","00745e40","00743800","004231c0","0080dad0","0080e490","0080e4d0","0080e510","0082bbf0","0082bc20","0082bc60","0082bc80","0082bcd0","0082bd10","0082bdb0","0082bdf0","0082bf00","0082c190","0082d220","0080b730","0081e9d0","008672c0","007870d0","006fe0e0","006ff370","0080b820","0080b850","0080ba30","0081eb30","0081ede0","00827540","00826af0","004d7890","0053bd40","004d77c0","00690f70","006962b0","0069d2c0","005862d0","0088df20","008182c0","0078c7e0","0074b110","0074b610","0074c400","0074ef70","00758170","0075bde0","0075fb20","0080f060","00812eb0","0081adf0","0081e3d0","00833fa0","008340a0","0083bed0","008402b0","00868400","00878060","0087fac0","008822c0","0088f9b0","008924d0","0059c7d0","0059dd00","00761930","00762170","00764e30","00766560","0077b510","0077c1f0","0078dac0","007e8fc0","007e9b60","0073ec20","0074ad90","0074b230","006b3ed0"};
|
|
for (String t : extra) { Function f = getFunctionAt(toAddr(Long.parseLong(t, 16))); if (f != null && f.getBody().getNumAddresses() < 25000) dumpFunc(f, idx, f.getBody().getNumAddresses() < 2500); }
|
|
idx.close(); decomp.dispose(); println("done");
|
|
}
|
|
}
|