Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import ai.reveng.toolkit.ghidra.core.services.api.GhidraRevengService;
import ai.reveng.toolkit.ghidra.core.services.api.types.AnalysisID;
import ai.reveng.toolkit.ghidra.core.services.api.types.AutoUnstripResponse;
import ai.reveng.toolkit.ghidra.core.services.api.types.FunctionID;
import ai.reveng.toolkit.ghidra.core.types.ProgramWithBinaryID;
import ai.reveng.toolkit.ghidra.plugins.ReaiPluginPackage;
import ghidra.framework.plugintool.PluginTool;
Expand Down Expand Up @@ -107,6 +108,9 @@ private void importFunctionNames(AutoUnstripResponse autoUnstripResponse) {
// Retrieve the mangled names map once outside the transaction
var mangledNameMapOpt = revengService.getFunctionMangledNamesMap(program);

// Retrieve the function ID map once outside the transaction
var functionMap = revengService.getFunctionMap(program);

program.withTransaction("Apply Auto-Unstrip Function Names", () -> {
try {
var revengMatchNamespace = program.getSymbolTable().getOrCreateNameSpace(
Expand All @@ -121,6 +125,7 @@ private void importFunctionNames(AutoUnstripResponse autoUnstripResponse) {

var revEngMangledName = match.suggested_name();
var revEngDemangledName = match.suggested_demangled_name();
var functionID = functionMap.get(new FunctionID(match.function_id().value()));

if (
func != null &&
Expand All @@ -132,6 +137,8 @@ private void importFunctionNames(AutoUnstripResponse autoUnstripResponse) {
// Only accept valid names (no spaces)
!revEngMangledName.contains(" ") &&
!revEngDemangledName.contains(" ")
// Only rename if the function ID is known (boundaries matched)
&& functionID != null
) {
try {
// Capture original name before renaming
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ protected void processFunctionMatchingResults(FunctionMatchingBatchResponse resp
Function localFunction = functionMap.get(new FunctionID(matchResult.getFunctionId()));

if (localFunction == null) {
// If we can't find the local function, skip this match
// If we can't find the local function, skip this match (boundaries do not match the remote ones)
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,6 @@ protected void doLoad(Accumulator<LegacyAnalysisResult> accumulator, TaskMonitor
return;
}

// Filter out analyses where the function boundaries hash does not match our program
var functionBoundariesHash = functionBoundariesService.getFunctionBoundariesHash();
if (!result.function_boundaries_hash().equals(functionBoundariesHash)) {
loggingService.info(
"[RevEng] Skipping analysis for " + result.binary_id() + " as function boundaries hash does" +
" not match. Expected " + functionBoundariesHash + " but got " +
result.function_boundaries_hash());
return;
}

accumulator.add(result);
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ private void loadFunctionInfo(Program program, BinaryID binID) throws ApiExcepti
StringPropertyMap finalMangledNameMap = mangledNameMap;

AtomicInteger ghidraRenamedFunctions = new AtomicInteger();
AtomicInteger ghidraBoundariesMatchedFunction = new AtomicInteger();
functionInfo.forEach(
info -> {
var oFunc = getFunctionFor(info, program);
Expand Down Expand Up @@ -215,9 +216,11 @@ private void loadFunctionInfo(Program program, BinaryID binID) throws ApiExcepti
}

var funcSize = func.getBody().getNumAddresses();

// For unclear reasons the func size is off by one
if (funcSize - 1 != info.functionSize() && funcSize != info.functionSize()){
Msg.warn(this, "Function size mismatch for function %s: %d vs %d".formatted(ghidraMangledName, funcSize, info.functionSize()));
return;
}

// Source types:
Expand All @@ -238,6 +241,8 @@ private void loadFunctionInfo(Program program, BinaryID binID) throws ApiExcepti
}
finalFunctionIDMap.add(func.getEntryPoint(), info.functionID().value());
finalMangledNameMap.add(func.getEntryPoint(), revEngMangledName);

ghidraBoundariesMatchedFunction.getAndIncrement();
}
);

Expand All @@ -256,14 +261,14 @@ private void loadFunctionInfo(Program program, BinaryID binID) throws ApiExcepti
program.endTransaction(transactionID, true);

// Print summary
Msg.debug(
this,
"Loaded %d functions from RevEng.AI, renamed %d, Ghidra has %d functions".formatted(
Msg.showInfo(this, null, ReaiPluginPackage.WINDOW_PREFIX + "Function loading summary",
("Found %d functions from RevEng.AI. Renamed %d. Your local Ghidra instance has %d/%d matching function " +
"boundaries. For better results, please start a new analysis from this plugin.").formatted(
functionInfo.size(),
ghidraRenamedFunctions.get(),
ghidraBoundariesMatchedFunction.get(),
ghidraFunctionCount.get()
)
);
));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,4 @@ public interface ExportFunctionBoundariesService {
* @return
*/
public JSONArray getFunctionsArray();

/**
* Return a hash of the function boundaries for change detection
* Note that this algorithm must match that used on the API server side!
* @return
*/
public String getFunctionBoundariesHash();
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,58 +80,4 @@ public JSONArray getFunctionsArray() {
}
return fArray;
}

@Override
public String getFunctionBoundariesHash() {
if (!isReady)
init();

// Collect all function boundaries into a list
List<JSONObject> boundaries = new ArrayList<>();
for (Function f : fm.getFunctions(true)) {
boundaries.add(getFunctionAt(f.getEntryPoint()));
}

// Sort the boundaries by start address (convert hex string to long for proper sorting)
boundaries.sort(Comparator.comparingLong(b -> Long.parseUnsignedLong(
b.getString("start_addr").substring(2), 16)));

// Create a formatted string representation of the boundaries
StringBuilder boundariesStr = new StringBuilder();
for (int i = 0; i < boundaries.size(); i++) {
JSONObject b = boundaries.get(i);
if (i > 0) {
boundariesStr.append(",");
}

// Convert hex addresses to integer representation
String startAddrHex = b.getString("start_addr");
String endAddrHex = b.getString("end_addr");
long startAddrInt = Long.parseUnsignedLong(startAddrHex.substring(2), 16);
long endAddrInt = Long.parseUnsignedLong(endAddrHex.substring(2), 16);

boundariesStr.append(startAddrInt)
.append("-")
.append(endAddrInt);
}

// Generate SHA-256 hash of the boundaries string
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(boundariesStr.toString().getBytes());

// Convert to hexadecimal string
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256 algorithm not available", e);
}
}
}
Loading