feat(analysis): add function signature parsing support

This commit is contained in:
Rodrigo Verdiani 2026-06-10 09:46:30 -03:00
parent 6f886f961f
commit 52d75600c0
4 changed files with 89 additions and 4 deletions

View File

@ -239,7 +239,13 @@ def extract_func(f, ea):
"repeatable": True
})
curr_inst = curr_inst + idc.ItemSize(curr_inst)
f.write(" \"comments\": %s\n" % _json_string(json.dumps(comment_entries)))
f.write(" \"comments\": %s,\n" % _json_string(json.dumps(comment_entries)))
# ---- signature ----
sig = idc.GetType(ea)
if not sig:
sig = idc.GuessType(ea)
f.write(" \"signature\": %s\n" % _json_string(sig if sig else ""))
f.write(" }")
f.flush()
return True

View File

@ -303,6 +303,9 @@ public class EmbeddingChunkBuilder {
func.getName(),
func.getAddress(),
Objects.nonNull(func.getFunctionSize()) ? func.getFunctionSize() : 0));
if (Objects.nonNull(func.getSignature()) && !func.getSignature().isBlank()) {
sb.append("Signature: ").append(func.getSignature()).append("\n");
}
sb.append(
String.format(
"Binary: %s | %s | %s | %s\n",

View File

@ -46,6 +46,7 @@ public abstract class AbstractIdaEngineService implements EngineService {
private static final String KEY_STRING_REFERENCED_BY = ", \"referencedBy\": \"";
private static final String KEY_STACK_FRAME = "\",\n \"stackFrame\": \"";
private static final String KEY_COMMENTS = "\",\n \"comments\": \"";
private static final String KEY_SIGNATURE = "\",\n \"signature\": \"";
private static final String KEY_IMPORTS_START = "\"imports\": [";
private static final String KEY_IMPORT_DLL = "\"dll\": \"";
private static final String KEY_IMPORT_NAME = "\", \"name\": \"";
@ -277,9 +278,22 @@ public abstract class AbstractIdaEngineService implements EngineService {
if (Objects.isNull(cmtRemainder)) {
return null;
}
var comments = extractField(cmtRemainder, KEY_ENTRY_END);
var comments = extractField(cmtRemainder, KEY_SIGNATURE);
var signature = "";
if (Objects.isNull(comments)) {
return null;
comments = extractField(cmtRemainder, KEY_ENTRY_END);
if (Objects.isNull(comments)) {
return null;
}
} else {
var sigRemainder = after(cmtRemainder, KEY_SIGNATURE);
if (Objects.nonNull(sigRemainder)) {
var sig = extractField(sigRemainder, KEY_ENTRY_END);
if (Objects.nonNull(sig)) {
signature = sig;
}
}
}
var xrefs = parseXrefs(xrefsRaw.trim());
@ -291,7 +305,7 @@ public abstract class AbstractIdaEngineService implements EngineService {
flags.endsWith("|") ? flags.substring(0, flags.length() - 1) : flags,
assembly,
null,
null,
signature,
xrefs,
labels,
stackFrame,

View File

@ -131,6 +131,68 @@ class AbstractIdaEngineServiceTest {
assertTrue(func.comments().contains("repeatable"));
}
@Test
void shouldParseFunctionWithSignature() {
var json =
"""
{
"entry_point": "0x00401000:start",
"segments": [],
"strings": [],
"imports": [],
"functions": [
{
"function": "sub_1000",
"address": "0x00401000",
"size": 64,
"flags": "",
"xrefs": "",
"assembly": "0x00401000 push ebp",
"labels": "",
"stackFrame": "[]",
"comments": "[]",
"signature": "int __cdecl sub_1000(HWND hWnd, LPCSTR lpText)"
}
]
}
""";
var result = invokeParseOutput(json);
assertEquals(
"int __cdecl sub_1000(HWND hWnd, LPCSTR lpText)", result.functions().get(0).signature());
}
@Test
void shouldDefaultSignatureToEmptyWhenFieldMissing() {
var json =
"""
{
"entry_point": "0x00401000:start",
"segments": [],
"strings": [],
"imports": [],
"functions": [
{
"function": "sub_2000",
"address": "0x00402000",
"size": 32,
"flags": "",
"xrefs": "",
"assembly": "0x00402000 ret",
"labels": "",
"stackFrame": "[]",
"comments": "[]"
}
]
}
""";
var result = invokeParseOutput(json);
assertEquals("", result.functions().get(0).signature());
}
@Test
void shouldDefaultReferencedByToEmptyWhenFieldMissing() {
var json =