mirror of
https://github.com/neoStudiosLCE/neoLegacy.git
synced 2026-06-09 14:42:58 +00:00
Tools for comparing 4JLibs binary changes between git refs: - compare-4jlibs.py: Extracts libs, parses symbol tables via dumpbin, demangles with undname, generates structured diff reports - extract_lib.py: Extracts .obj members from COFF .lib archives - ExportLibInfo.java: Ghidra headless script for non-LTCG object files - list-lib-symbols.sh / compare-4jlibs.sh: Shell wrappers
140 lines
5.3 KiB
Java
140 lines
5.3 KiB
Java
// Export symbols, functions, and external references from a COFF .lib to a JSON report.
|
|
// Designed for headless mode: pass output path as first script argument.
|
|
//
|
|
// Usage with analyzeHeadless:
|
|
// analyzeHeadless <projDir> <projName> -import <file.lib> \
|
|
// -postScript ExportLibInfo.java <output.json> -deleteProject
|
|
//
|
|
//@category 4JLibs
|
|
|
|
import java.io.File;
|
|
import java.io.FileWriter;
|
|
import java.io.PrintWriter;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.program.model.address.Address;
|
|
import ghidra.program.model.listing.*;
|
|
import ghidra.program.model.symbol.*;
|
|
|
|
public class ExportLibInfo extends GhidraScript {
|
|
|
|
@Override
|
|
public void run() throws Exception {
|
|
String[] args = getScriptArgs();
|
|
if (args.length < 1) {
|
|
printerr("Usage: ExportLibInfo.java <output.json>");
|
|
return;
|
|
}
|
|
|
|
File outputFile = new File(args[0]);
|
|
outputFile.getParentFile().mkdirs();
|
|
|
|
PrintWriter pw = new PrintWriter(new FileWriter(outputFile, true));
|
|
|
|
String programName = currentProgram.getName();
|
|
Listing listing = currentProgram.getListing();
|
|
SymbolTable symbolTable = currentProgram.getSymbolTable();
|
|
ExternalManager extMgr = currentProgram.getExternalManager();
|
|
|
|
// Collect functions
|
|
List<String> functions = new ArrayList<>();
|
|
FunctionIterator funcIter = listing.getFunctions(true);
|
|
while (funcIter.hasNext() && !monitor.isCancelled()) {
|
|
Function f = funcIter.next();
|
|
String sig = f.getPrototypeString(false, false);
|
|
String callingConv = f.getCallingConventionName();
|
|
long size = f.getBody().getNumAddresses();
|
|
|
|
functions.add(String.format(
|
|
" {\"name\": %s, \"entry\": %s, \"signature\": %s, \"callingConvention\": %s, \"size\": %d, \"paramCount\": %d}",
|
|
jsonStr(f.getName()),
|
|
jsonStr(f.getEntryPoint().toString()),
|
|
jsonStr(sig),
|
|
jsonStr(callingConv),
|
|
size,
|
|
f.getParameterCount()
|
|
));
|
|
}
|
|
|
|
// Collect all symbols (non-function)
|
|
List<String> symbols = new ArrayList<>();
|
|
SymbolIterator symIter = symbolTable.getAllSymbols(true);
|
|
while (symIter.hasNext() && !monitor.isCancelled()) {
|
|
Symbol sym = symIter.next();
|
|
if (sym.getSymbolType() == SymbolType.FUNCTION) {
|
|
continue; // already captured above
|
|
}
|
|
if (sym.isExternal()) {
|
|
continue; // captured below
|
|
}
|
|
symbols.add(String.format(
|
|
" {\"name\": %s, \"type\": %s, \"address\": %s, \"source\": %s}",
|
|
jsonStr(sym.getName(true)),
|
|
jsonStr(sym.getSymbolType().toString()),
|
|
jsonStr(sym.getAddress().toString()),
|
|
jsonStr(sym.getSource().toString())
|
|
));
|
|
}
|
|
|
|
// Collect external symbols (imports from other libraries)
|
|
List<String> externals = new ArrayList<>();
|
|
symIter = symbolTable.getExternalSymbols();
|
|
while (symIter.hasNext() && !monitor.isCancelled()) {
|
|
Symbol sym = symIter.next();
|
|
String extLib = "";
|
|
ExternalLocation extLoc = extMgr.getExternalLocation(sym);
|
|
if (extLoc != null && extLoc.getLibraryName() != null) {
|
|
extLib = extLoc.getLibraryName();
|
|
}
|
|
externals.add(String.format(
|
|
" {\"name\": %s, \"type\": %s, \"library\": %s}",
|
|
jsonStr(sym.getName(true)),
|
|
jsonStr(sym.getSymbolType().toString()),
|
|
jsonStr(extLib)
|
|
));
|
|
}
|
|
|
|
// Write JSON object for this program/object-file
|
|
pw.println("{");
|
|
pw.println(" \"program\": " + jsonStr(programName) + ",");
|
|
pw.println(" \"language\": " + jsonStr(currentProgram.getLanguageID().toString()) + ",");
|
|
pw.println(" \"compiler\": " + jsonStr(currentProgram.getCompilerSpec().getCompilerSpecID().toString()) + ",");
|
|
|
|
pw.println(" \"functionCount\": " + functions.size() + ",");
|
|
pw.println(" \"functions\": [");
|
|
pw.println(String.join(",\n", functions));
|
|
pw.println(" ],");
|
|
|
|
pw.println(" \"symbolCount\": " + symbols.size() + ",");
|
|
pw.println(" \"symbols\": [");
|
|
pw.println(String.join(",\n", symbols));
|
|
pw.println(" ],");
|
|
|
|
pw.println(" \"externalCount\": " + externals.size() + ",");
|
|
pw.println(" \"externals\": [");
|
|
pw.println(String.join(",\n", externals));
|
|
pw.println(" ]");
|
|
|
|
pw.println("}");
|
|
|
|
pw.flush();
|
|
pw.close();
|
|
|
|
println("ExportLibInfo: wrote " + functions.size() + " functions, " +
|
|
symbols.size() + " symbols, " + externals.size() + " externals for " +
|
|
programName + " -> " + outputFile.getAbsolutePath());
|
|
}
|
|
|
|
private String jsonStr(String s) {
|
|
if (s == null) return "null";
|
|
return "\"" + s.replace("\\", "\\\\")
|
|
.replace("\"", "\\\"")
|
|
.replace("\n", "\\n")
|
|
.replace("\r", "\\r")
|
|
.replace("\t", "\\t") + "\"";
|
|
}
|
|
}
|