feat(analysis): add type information to xref and label entries

This commit is contained in:
Rodrigo Verdiani 2026-06-08 21:07:04 -03:00
parent dcb4cf6a3c
commit b26631c5aa
11 changed files with 81 additions and 22 deletions

View File

@ -4,6 +4,7 @@ static main() {
auto ea, func_end, curr_inst, func_name, disasm, file;
auto last_inst, xref, ref, label_name;
auto entry_ea, entry_name;
auto xref_type, type_str;
Message("Aguardando fim da analise...\n");
Wait();
@ -31,12 +32,20 @@ static main() {
fprintf(file, " \"function\": \"%s\",\n", func_name);
fprintf(file, " \"address\": \"0x%08X\",\n", ea);
// --- XREFS TO (Simplificado e direto para o IDA 5) ---
// --- XREFS TO (com tipo: CALL ou JUMP) ---
fprintf(file, " \"xrefs\": \"");
xref = RfirstB(ea);
while (xref != BADADDR) {
if (GetFunctionName(xref) != "") {
fprintf(file, "%s ", GetFunctionName(xref));
xref_type = XrefType();
if (xref_type == fl_CN || xref_type == fl_CF) {
type_str = "CALL";
} else if (xref_type == fl_JN || xref_type == fl_JF) {
type_str = "JUMP";
} else {
type_str = "UNKNOWN";
}
fprintf(file, "%s:%s ", GetFunctionName(xref), type_str);
}
xref = RnextB(ea, xref);
}
@ -63,7 +72,7 @@ static main() {
}
fprintf(file, "\",\n");
// --- LABELS (code references from instructions) ---
// --- LABELS (code references with type) ---
fprintf(file, " \"labels\": \"");
curr_inst = ea;
last_inst = 0;
@ -78,7 +87,15 @@ static main() {
while (ref != BADADDR) {
label_name = Name(ref);
if (label_name != "") {
fprintf(file, "%s:0x%08X ", label_name, ref);
xref_type = XrefType();
if (xref_type == fl_CN || xref_type == fl_CF) {
type_str = "CALL";
} else if (xref_type == fl_JN || xref_type == fl_JF) {
type_str = "JUMP";
} else {
type_str = "UNKNOWN";
}
fprintf(file, "%s:0x%08X:%s ", label_name, ref, type_str);
}
ref = Rnext(curr_inst, ref);
}

View File

@ -279,7 +279,10 @@ public class ChatContextBuilder {
sb.append("Callers: ");
sb.append(
String.join(", ", callers.stream().limit(5).map(x -> x.getCaller().getName()).toList()));
callers.stream()
.limit(5)
.map(x -> x.getCaller().getName() + " [" + x.getType() + "]")
.collect(java.util.stream.Collectors.joining(", ")));
sb.append("\n");
}
@ -290,7 +293,10 @@ public class ChatContextBuilder {
sb.append("Callees: ");
sb.append(
String.join(", ", callees.stream().limit(10).map(x -> x.getCallee().getName()).toList()));
callees.stream()
.limit(10)
.map(x -> x.getCallee().getName() + " [" + x.getType() + "]")
.collect(java.util.stream.Collectors.joining(", ")));
sb.append("\n");
}

View File

@ -2,8 +2,8 @@ package ai.decompile.analysis.model.dto;
import ai.decompile.analysis.model.entity.StaticLabel;
public record LabelResponse(String name, String address) {
public record LabelResponse(String name, String address, String type) {
public static LabelResponse from(StaticLabel l) {
return new LabelResponse(l.getName(), l.getAddress());
return new LabelResponse(l.getName(), l.getAddress(), l.getType());
}
}

View File

@ -25,4 +25,8 @@ public class StaticLabel {
@Column(nullable = false, length = 20)
private String address;
@Column(nullable = false, length = 10)
@Builder.Default
private String type = "CALL";
}

View File

@ -132,6 +132,7 @@ public class AnalysisService {
.function(func)
.name(label.name())
.address(label.address())
.type(label.type())
.build());
}
}
@ -145,13 +146,13 @@ public class AnalysisService {
continue;
}
for (var calleeName : funcResult.xrefNames()) {
var calleeId = nameToId.get(calleeName);
for (var xrefEntry : funcResult.xrefs()) {
var calleeId = nameToId.get(xrefEntry.name());
if (calleeId == null) {
log.debug(
"Skipping xref from '{}' to unknown function '{}' (likely external/lib)",
funcResult.name(),
calleeName);
xrefEntry.name());
continue;
}
@ -159,7 +160,7 @@ public class AnalysisService {
StaticXref.builder()
.caller(functionRepository.getReferenceById(callerId))
.callee(functionRepository.getReferenceById(calleeId))
.type("CALL")
.type(xrefEntry.type())
.build());
}
}

View File

@ -8,5 +8,5 @@ public record FunctionResult(
String assembly,
String decompiledCode,
String signature,
List<String> xrefNames,
List<XrefEntry> xrefs,
List<LabelEntry> labels) {}

View File

@ -1,3 +1,3 @@
package ai.decompile.engine.model;
public record LabelEntry(String name, String address) {}
public record LabelEntry(String name, String address, String type) {}

View File

@ -0,0 +1,3 @@
package ai.decompile.engine.model;
public record XrefEntry(String name, String type) {}

View File

@ -6,6 +6,7 @@ import ai.decompile.engine.model.AnalysisResult;
import ai.decompile.engine.model.EngineType;
import ai.decompile.engine.model.FunctionResult;
import ai.decompile.engine.model.LabelEntry;
import ai.decompile.engine.model.XrefEntry;
import com.github.dockerjava.api.model.Bind;
import java.io.File;
import java.io.IOException;
@ -185,10 +186,9 @@ public class Ida5EngineService implements EngineService {
continue;
}
var xrefNames =
xrefsRaw.isBlank() ? List.<String>of() : Arrays.asList(xrefsRaw.trim().split("\\s+"));
var xrefs = parseXrefs(xrefsRaw.trim());
var labels = parseLabels(labelsRaw.trim());
functions.add(new FunctionResult(name, address, assembly, null, null, xrefNames, labels));
functions.add(new FunctionResult(name, address, assembly, null, null, xrefs, labels));
}
if (functions.isEmpty()) {
@ -210,6 +210,23 @@ public class Ida5EngineService implements EngineService {
return idx < 0 ? null : text.substring(idx + delim.length());
}
private static List<XrefEntry> parseXrefs(String raw) {
if (Objects.isNull(raw) || raw.isBlank()) {
return List.of();
}
return Arrays.stream(raw.trim().split("\\s+")).map(Ida5EngineService::parseXrefEntry).toList();
}
private static XrefEntry parseXrefEntry(String token) {
var colon = token.lastIndexOf(':');
if (colon < 0) {
return new XrefEntry(token, "CALL");
}
return new XrefEntry(token.substring(0, colon), token.substring(colon + 1));
}
private static List<LabelEntry> parseLabels(String raw) {
if (raw == null || raw.isBlank()) {
return List.of();
@ -221,10 +238,19 @@ public class Ida5EngineService implements EngineService {
}
private static LabelEntry parseLabelEntry(String token) {
int colon = token.lastIndexOf(':');
if (colon < 0) {
return new LabelEntry(token, "");
var lastColon = token.lastIndexOf(':');
if (lastColon < 0) {
return new LabelEntry(token, "", "CALL");
}
return new LabelEntry(token.substring(0, colon), token.substring(colon + 1));
var type = token.substring(lastColon + 1);
var nameAddr = token.substring(0, lastColon);
var addrColon = nameAddr.lastIndexOf(':');
if (addrColon < 0) {
return new LabelEntry(nameAddr, "", type);
}
return new LabelEntry(
nameAddr.substring(0, addrColon), nameAddr.substring(addrColon + 1), type);
}
}

View File

@ -0,0 +1 @@
ALTER TABLE static_label ADD COLUMN IF NOT EXISTS type VARCHAR(10) NOT NULL DEFAULT 'CALL';

View File

@ -114,6 +114,7 @@ CREATE TABLE IF NOT EXISTS static_label
id UUID PRIMARY KEY,
function_id UUID NOT NULL REFERENCES static_function (id) ON DELETE CASCADE,
name VARCHAR(500) NOT NULL,
address VARCHAR(20) NOT NULL
address VARCHAR(20) NOT NULL,
type VARCHAR(10) NOT NULL DEFAULT 'CALL'
);
CREATE INDEX IF NOT EXISTS idx_static_label_function_id ON static_label (function_id);