Skip to content

Commit

Permalink
Experimental: process Pascal/Unicode strings from IDC
Browse files Browse the repository at this point in the history
  • Loading branch information
sarog committed Aug 4, 2023
1 parent 4e3d4f5 commit 2545498
Showing 1 changed file with 124 additions and 13 deletions.
137 changes: 124 additions & 13 deletions DhrakeInit.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import ghidra.app.decompiler.DecompileResults;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
Expand All @@ -17,6 +18,7 @@
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
Expand Down Expand Up @@ -199,6 +201,9 @@ private boolean importSymbolsFromIDC() {
File idc;
long progress = 0;
int linecount = 0;
int countSymbols = 0;
int countStrings = 0;
int countVMTs = 0;

monitor.setMessage("Loading symbols from IDC");

Expand All @@ -208,7 +213,13 @@ private boolean importSymbolsFromIDC() {
return false;
}

Pattern pattern = Pattern.compile("^\\s*MakeNameEx\\((?:0x)?([A-Fa-f0-9]+),\\s*\"([^\"]*)\",\\s*([xA-Fa-f0-9]+)\\);\\s*$");
Pattern makeName = Pattern.compile("^\\s*MakeNameEx\\((?:0x)?([A-Fa-f0-9]+),\\s*\"([^\"]*)\",\\s*([xA-Fa-f0-9]+)\\);\\s*$");

// String Type: group 1
Pattern stringType = Pattern.compile("^\\s*SetLongPrm\\(INF_STRTYPE,\\s*ASCSTR_(PASCAL|TERMCHR|UNICODE)\\);\\s*$");

// Both combined: group 1 = position, group 2 = end or null
Pattern makeString = Pattern.compile("^\\s*MakeStr\\((?:0x)?([A-Fa-f0-9]+),\\s*(?:0x)?([-1|A-Fa-f0-9]+)\\s*\\);\\s*$");

try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(idc)); Scanner sc = new Scanner(inputStream, StandardCharsets.UTF_8)) {
// Count lines
Expand All @@ -226,37 +237,137 @@ private boolean importSymbolsFromIDC() {
monitor.setMessage("Processing IDC file");
this.logMsg("Processing IDC file %s", idc.toPath());

String foundStringType = "";

while (sc.hasNextLine()) {
if (monitor.isCancelled()) break;

String line = sc.nextLine();

monitor.setProgress(progress++);

if (!line.contains("MakeNameEx")) continue;
if (!line.contains("MakeNameEx") && !line.contains("SetLongPrm")) continue;

Matcher match = pattern.matcher(line);
Matcher funcMatch = makeName.matcher(line);
Matcher stringTypeMatch = stringType.matcher(line);

if (!match.matches()) continue;
if (!funcMatch.matches() && !stringTypeMatch.matches()) continue;

Integer offset = Integer.parseUnsignedInt(match.group(1), 16);
Address entryPoint = this.toAddr(offset);
String functionName = match.group(2);
monitor.setMessage(functionName);
if (functionName.strip().length() > 0) {
try {
this.renameSymbol(entryPoint, functionName);
} catch (InvalidInputException e) {
this.logMsg("Renaming failed for: %s", functionName);
// Match: Function
if (funcMatch.matches()) {
long offset = Long.parseUnsignedLong(funcMatch.group(1), 16);
Address entryPoint = this.toAddr(offset);
String symName = funcMatch.group(2);
monitor.setMessage(symName);

if (symName.strip().length() > 0) {
try {
this.logMsg("Renaming symbol at %s to %s", entryPoint, symName);
this.renameSymbol(entryPoint, symName);
countSymbols++;
} catch (InvalidInputException e) {
this.logMsg("Renaming failed for: %s", symName);
}
}
continue;
}

// Match: String
if (stringTypeMatch.matches()) {
foundStringType = stringTypeMatch.group(1);
line = sc.nextLine();
Matcher stringMatch = makeString.matcher(line);

if (stringMatch.matches()) {
long start = Long.parseUnsignedLong(stringMatch.group(1), 16);
long end = Long.parseLong(stringMatch.group(2), 16);
long length = end > start ? end - start : -1;

Address startAddr = this.toAddr(start);
Address endAddr = this.toAddr(end);

// Debug:
// monitor.setMessage(String.format("Found %s string at %s", foundStringType, startAddr.toString()));
// logMsg(String.format("Line %s found %s string at %s", progress, foundStringType, startAddr));

// Process Unicode strings for now
if (foundStringType.equals("UNICODE")) {
createPascalString(foundStringType, startAddr, endAddr, length);
countStrings++;
}

// Notes to self:
// PASCAL = ShortString => 'PascalString255' (ikString)
// TERMCHR = String (L) = Pointer String, AnsiString (L,R) => 'PascalString'
// If TERMCHR has a length, can be: PChar, PAnsiChar (ikCString)
// If TERMCHR has a length of -1, it's an AnsiString (ikLString)
// UNICODE = WideString (L), Unicode (L,R,W,C) => 'PascalUnicode'
}
}
}
} catch (IOException e) {
this.logMsg("file not found: %s", idc.toPath());
return false;
}

this.logMsg("Total symbols: %d", countSymbols);
this.logMsg("Total strings: %d", countStrings);
this.logMsg("Total VMTs : %d", countVMTs);

return true;
}

private void createPascalString(String type, Address start, Address end, long length) {
try {
if (this.getReferencesFrom(start).length > 0) {
this.logMsg("Found references from %s", start);
if (this.getReferencesTo(start).length > 0) {
this.logMsg("Found references to %s", start);
}
return;
}

// move this up
if (this.getDataAt(start) != null) {
if (this.getDataAt(start).isDefined()) {
this.logMsg("Data is defined at %s", start);
}

if (this.getDataAt(start).isPointer()) {
this.logMsg("Data defined at %s is a pointer", start);
return;
}
}

if (this.getDataAfter(start).hasStringValue()) {
this.logMsg("Data after %s => %s has a string value", start, start.next());
if (length > 0) {
this.clearListing(new AddressSet(start, end));
} else {
this.clearListing(start);
}
}

// todo: allow user to adjust the minimum length
if (length > 5) {
if (this.getDataAt(start) == null) {
this.logMsg("Found %s string at %s", type, start);

switch (type) {
case "PASCAL" -> this.createData(start, new PascalString255DataType());
case "TERMCHR" -> this.createData(start, new PascalStringDataType());
case "UNICODE" -> this.createData(start, new UnicodeDataType());
// case "UNICODE" -> this.createData(start, new PascalUnicodeDataType());
}
}
}
} catch (CodeUnitInsertionException e) {
this.logMsg("Cannot create string, conflicting data at address %s", start);
} catch (Exception e) {
this.logMsg("Cannot create string at %s, exception: %s", start, e);
}
}

private void repairStringCompareFunctions() {
try {
monitor.setMessage("Repairing known function signatures");
Expand Down

0 comments on commit 2545498

Please sign in to comment.