99 lines
9.5 KiB
Java
99 lines
9.5 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 3: money-tail helpers, species-def table, tech bitmask tables, node segment builder, ComputeBudget disasm.
|
|
public class TechFx3 extends GhidraScript {
|
|
DecompInterface decomp; Memory mem; ReferenceManager rm;
|
|
static final String OUT = "/tmp/techfx3";
|
|
List<String> names = new ArrayList<>();
|
|
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 techName(long v) { return (v >= 10000 && v <= 10195) ? names.get((int)(v - 10000)) : (v == 0xc5 ? "NONE" : 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); String tn = techName(iv & 0xffffffffL); if (tn != null) sb.append("(" + tn + ")");
|
|
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);
|
|
// annotate bare tech-id literals
|
|
Matcher m2 = Pattern.compile("0x27([0-9a-f]{2})\\b").matcher(sb.toString()); StringBuffer sb2 = new StringBuffer();
|
|
while (m2.find()) { long v = Long.parseLong("27" + m2.group(1), 16); String tn = techName(v); m2.appendReplacement(sb2, Matcher.quoteReplacement(m2.group(0) + (tn != null ? "/*" + tn + "*/" : ""))); }
|
|
m2.appendTail(sb2); return sb2.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)); } }
|
|
for (int oi = 0; oi < in.getNumOperands(); oi++) for (Object o : in.getOpObjects(oi)) if (o instanceof Scalar) { String tn = techName(((Scalar)o).getUnsignedValue()); if (tn != null) ann.append(" ; tech " + tn); }
|
|
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();
|
|
}
|
|
void dumpRegion(PrintWriter w, String title, long start, long end) throws Exception {
|
|
w.println("== " + title + String.format(" %08x..%08x ==", start, end));
|
|
for (long a = start; a < end; a += 4) { int iv = mem.getInt(toAddr(a)); String s = null; if (iv > 0x9dd000 && iv < 0xb40000) s = cstr(toAddr(iv & 0xffffffffL)); Symbol sym = currentProgram.getSymbolTable().getPrimarySymbol(toAddr(a));
|
|
w.println(String.format("%08x %s%s%s", a, constAt(a), s != null ? " ->\"" + s + "\"" : "", sym != null ? " [" + sym.getName() + "]" : "")); }
|
|
}
|
|
|
|
@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"));
|
|
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); }
|
|
PrintWriter rep = new PrintWriter(new FileWriter(OUT + "/report.txt"));
|
|
|
|
// species def accessor and its table
|
|
Function sp = getFunctionAt(toAddr(0x00545cc0L)); dumpFunc(sp, idx, true);
|
|
Set<Long> bases = new TreeSet<>();
|
|
if (sp != null) { InstructionIterator ii = currentProgram.getListing().getInstructions(sp.getBody(), true); while (ii.hasNext()) { Instruction in = ii.next(); for (Reference r : in.getReferencesFrom()) { long ta = r.getToAddress().getOffset(); if (ta >= 0x9dd000L && ta < 0xb40000L) bases.add(ta); } for (int oi = 0; oi < in.getNumOperands(); oi++) for (Object o : in.getOpObjects(oi)) if (o instanceof Scalar) { long v = ((Scalar)o).getUnsignedValue(); if (v >= 0xad9000L && v < 0xb40000L) bases.add(v); rep.println(String.format(" 545cc0 scalar %08x %d", in.getAddress().getOffset(), v)); } } }
|
|
rep.println("species accessor data refs: " + bases);
|
|
for (long b : bases) dumpRegion(rep, "speciesdef candidate", b, b + 7 * 0xc0);
|
|
// tech bitmask tables used by FUN_004d7960
|
|
dumpRegion(rep, "bitmask tables", 0x00adf360L, 0x00adf5a0L);
|
|
// AI tech table near DAT_00aea2ec
|
|
dumpRegion(rep, "AI tech table", 0x00aea2d0L, 0x00aea340L);
|
|
// 116-entry table header/neighbourhood (what precedes/follows)
|
|
dumpRegion(rep, "0x00a19700 neighbourhood", 0x00a196f0L, 0x00a19720L);
|
|
dumpRegion(rep, "0x009ff9e4 neighbourhood", 0x009ff9c0L, 0x009ff9f0L);
|
|
|
|
String[] want = {"0074d760","0074b700","0080f470","007484d0","005453a0","00690f70","0074b5a0","00747740","00705280","008e8eb0","0057d220","0057d7e0","00580e30","0074b110","0074ab20","0074a4a0","0074c680","0074f1e0","007477a0","00817f90","0059b490","0080f4b0","00536c80","0074a6d0","0053bd40","004d7890","004d77c0","00690f70","006922c0","00696420","006964a0","00695be0","00696550","00694f40","0053dd60","00535f80","00535400","00535480","005352f0","00535ef0","00578920","00578d20","00552600","00552900","00555fc0","0079f050","0079f290","007911f0","007867e0","0078aa70","007b41c0","007cb080","008375d0","00875fa0","006c3600","006bb420","006da6a0","006a2bf0","006c2510","006ad5e0","006acba0","006aea70","0057c3c0","006cdbb0","0075cb20","0062d310","0080dad0","00815ab0","00815960","0075bd70","0075bde0","006e18e0","008188b0","00788ff0","0080e510","0080e490","00746390","0080e4d0","00535c40","00818860","00820380","00700920","0079b980","006d1e30","006fb1a0","006b3ed0","0074b7a0","00746830","00750480","007483b0"};
|
|
String[] dis = {"0074d760","0074b700","0080f470","007484d0","0074b5a0","00747740","0057d220","0057d7e0","00705280","008e8eb0","0080dad0","0080e510","0080e490","0080e4d0","00815ab0","00815960","0075bd70","008188b0","00818860","00746390","00535c40"};
|
|
Set<String> disSet = new HashSet<>(Arrays.asList(dis));
|
|
for (String t : want) { Function f = getFunctionAt(toAddr(Long.parseLong(t, 16))); if (f != null && f.getBody().getNumAddresses() < 25000) dumpFunc(f, idx, disSet.contains(t) || f.getBody().getNumAddresses() < 900); }
|
|
// ComputeBudget with disasm
|
|
dumpFunc(getFunctionAt(toAddr(0x00863030L)), idx, true);
|
|
rep.close(); idx.close(); decomp.dispose(); println("done");
|
|
}
|
|
}
|