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 ea, func_end, curr_inst, func_name, disasm, file;
auto last_inst, xref, ref, label_name; auto last_inst, xref, ref, label_name;
auto entry_ea, entry_name; auto entry_ea, entry_name;
auto xref_type, type_str;
Message("Aguardando fim da analise...\n"); Message("Aguardando fim da analise...\n");
Wait(); Wait();
@ -31,12 +32,20 @@ static main() {
fprintf(file, " \"function\": \"%s\",\n", func_name); fprintf(file, " \"function\": \"%s\",\n", func_name);
fprintf(file, " \"address\": \"0x%08X\",\n", ea); 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\": \""); fprintf(file, " \"xrefs\": \"");
xref = RfirstB(ea); xref = RfirstB(ea);
while (xref != BADADDR) { while (xref != BADADDR) {
if (GetFunctionName(xref) != "") { 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); xref = RnextB(ea, xref);
} }
@ -63,7 +72,7 @@ static main() {
} }
fprintf(file, "\",\n"); fprintf(file, "\",\n");
// --- LABELS (code references from instructions) --- // --- LABELS (code references with type) ---
fprintf(file, " \"labels\": \""); fprintf(file, " \"labels\": \"");
curr_inst = ea; curr_inst = ea;
last_inst = 0; last_inst = 0;
@ -78,7 +87,15 @@ static main() {
while (ref != BADADDR) { while (ref != BADADDR) {
label_name = Name(ref); label_name = Name(ref);
if (label_name != "") { 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); ref = Rnext(curr_inst, ref);
} }

View File

@ -279,7 +279,10 @@ public class ChatContextBuilder {
sb.append("Callers: "); sb.append("Callers: ");
sb.append( 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"); sb.append("\n");
} }
@ -290,7 +293,10 @@ public class ChatContextBuilder {
sb.append("Callees: "); sb.append("Callees: ");
sb.append( 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"); sb.append("\n");
} }

View File

@ -2,8 +2,8 @@ package ai.decompile.analysis.model.dto;
import ai.decompile.analysis.model.entity.StaticLabel; 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) { 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) @Column(nullable = false, length = 20)
private String address; 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) .function(func)
.name(label.name()) .name(label.name())
.address(label.address()) .address(label.address())
.type(label.type())
.build()); .build());
} }
} }
@ -145,13 +146,13 @@ public class AnalysisService {
continue; continue;
} }
for (var calleeName : funcResult.xrefNames()) { for (var xrefEntry : funcResult.xrefs()) {
var calleeId = nameToId.get(calleeName); var calleeId = nameToId.get(xrefEntry.name());
if (calleeId == null) { if (calleeId == null) {
log.debug( log.debug(
"Skipping xref from '{}' to unknown function '{}' (likely external/lib)", "Skipping xref from '{}' to unknown function '{}' (likely external/lib)",
funcResult.name(), funcResult.name(),
calleeName); xrefEntry.name());
continue; continue;
} }
@ -159,7 +160,7 @@ public class AnalysisService {
StaticXref.builder() StaticXref.builder()
.caller(functionRepository.getReferenceById(callerId)) .caller(functionRepository.getReferenceById(callerId))
.callee(functionRepository.getReferenceById(calleeId)) .callee(functionRepository.getReferenceById(calleeId))
.type("CALL") .type(xrefEntry.type())
.build()); .build());
} }
} }

View File

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

View File

@ -1,3 +1,3 @@
package ai.decompile.engine.model; 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.EngineType;
import ai.decompile.engine.model.FunctionResult; import ai.decompile.engine.model.FunctionResult;
import ai.decompile.engine.model.LabelEntry; import ai.decompile.engine.model.LabelEntry;
import ai.decompile.engine.model.XrefEntry;
import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Bind;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -185,10 +186,9 @@ public class Ida5EngineService implements EngineService {
continue; continue;
} }
var xrefNames = var xrefs = parseXrefs(xrefsRaw.trim());
xrefsRaw.isBlank() ? List.<String>of() : Arrays.asList(xrefsRaw.trim().split("\\s+"));
var labels = parseLabels(labelsRaw.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()) { if (functions.isEmpty()) {
@ -210,6 +210,23 @@ public class Ida5EngineService implements EngineService {
return idx < 0 ? null : text.substring(idx + delim.length()); 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) { private static List<LabelEntry> parseLabels(String raw) {
if (raw == null || raw.isBlank()) { if (raw == null || raw.isBlank()) {
return List.of(); return List.of();
@ -221,10 +238,19 @@ public class Ida5EngineService implements EngineService {
} }
private static LabelEntry parseLabelEntry(String token) { private static LabelEntry parseLabelEntry(String token) {
int colon = token.lastIndexOf(':'); var lastColon = token.lastIndexOf(':');
if (colon < 0) { if (lastColon < 0) {
return new LabelEntry(token, ""); 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, id UUID PRIMARY KEY,
function_id UUID NOT NULL REFERENCES static_function (id) ON DELETE CASCADE, function_id UUID NOT NULL REFERENCES static_function (id) ON DELETE CASCADE,
name VARCHAR(500) NOT NULL, 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); CREATE INDEX IF NOT EXISTS idx_static_label_function_id ON static_label (function_id);