feat(ai): suggestPseudocode tool, simplified prompt, DecompiledResult.found, and address sort
This commit is contained in:
parent
6ad98e9675
commit
1b1da09ff3
@ -60,60 +60,31 @@ public class ChatContextBuilder {
|
|||||||
getFunctionCallGraph (or getFunctionAssembly + getCallers + getCallees)
|
getFunctionCallGraph (or getFunctionAssembly + getCallers + getCallees)
|
||||||
to examine the function AND the functions it calls. A thorough analysis
|
to examine the function AND the functions it calls. A thorough analysis
|
||||||
includes understanding what callees do and how they relate to the caller.
|
includes understanding what callees do and how they relate to the caller.
|
||||||
Never just read one function in isolation and suggest analyzing callees
|
Never just read one function in isolation — analyze callees now.
|
||||||
"later" — analyze them now as part of your response.
|
|
||||||
|
|
||||||
PRIORITY RULE: When the user asks for a specific mutation (rename, comment,
|
[MUTATION TOOLS — MANDATORY]
|
||||||
signature, flag), use that tool first. Do NOT respond with a different mutation
|
When your analysis reveals findings deserving a rename, comment, signature,
|
||||||
type instead (e.g., don't suggest renames when the user asked for comments).
|
flag, or label change, you MUST call the corresponding tool IMMEDIATELY.
|
||||||
This rule is about mutation tool selection ONLY — it does NOT limit your
|
A suggestion described only in text will NOT be reflected in the UI and is
|
||||||
analysis. Always do a thorough analysis including callees and callers.
|
USELESS — the user needs the tool call to see and approve it.
|
||||||
|
|
||||||
[FUNCTION RENAME]
|
For proactive suggestions (requires user approval), use the suggest* tools:
|
||||||
Use renameFunction when the user explicitly asks to rename a specific function
|
suggestRenameFunction, suggestAddComment, suggestUpdateSignature,
|
||||||
(applies immediately). Use suggestRenameFunction when you independently identify
|
suggestPseudocode, suggestRenameLabel, suggestRenameGlobalVar,
|
||||||
a function's purpose and want to suggest a better name (requires user approval).
|
suggestRenameStruct
|
||||||
Proactively suggest renames whenever you identify a function's purpose with high
|
|
||||||
confidence. For example:
|
|
||||||
- A function that decrypts data with RC4 → CALL suggestRenameFunction("DecryptRC4")
|
|
||||||
- A function that parses HTTP headers → CALL suggestRenameFunction("ParseHttpHeaders")
|
|
||||||
- A function that validates a checksum → CALL suggestRenameFunction("ValidateChecksum")
|
|
||||||
- A function that is the WinMain entry point → CALL suggestRenameFunction("WinMain")
|
|
||||||
CRITICAL: When you decide a function should be renamed, you MUST call the
|
|
||||||
suggestRenameFunction tool with the function ID and new name. NEVER just
|
|
||||||
describe the rename in your text response without calling the tool.
|
|
||||||
Always call the tool, then briefly explain your reasoning afterward.
|
|
||||||
|
|
||||||
[FUNCTION SIGNATURE]
|
For changes the user explicitly asked for (applied immediately), use:
|
||||||
Use updateSignature (apply) or suggestUpdateSignature (pending approval)
|
renameFunction, addComment, updateSignature,
|
||||||
to set a function's type signature. Infer parameter types and return type
|
addFlag, removeFlag, renameLabel,
|
||||||
from assembly analysis. Example: 'int DecryptRC4(int keyLen, const uint8_t* key)'.
|
renameGlobalVar, renameStruct
|
||||||
|
|
||||||
[FUNCTION COMMENTS]
|
Rules:
|
||||||
Use addComment (apply) or suggestAddComment (pending approval) to document
|
1. Call mutation tools BEFORE writing your analysis text.
|
||||||
analysis findings. You can attach comments to specific addresses
|
2. If you have 3 suggestions, make 3 tool calls — not 1 text block.
|
||||||
(e.g., address="0x5EB5") to document individual instructions inline.
|
3. For pseudocode: only suggestPseudocode when getFunctionDecompiled returned
|
||||||
Omit address to attach to the function. The tool appends automatically.
|
found=false and you are confident in the assembly logic.
|
||||||
CRITICAL: Pass ONLY the new comment text — never include previous comments.
|
4. When the user explicitly asks for a specific change type (e.g. "add a comment"),
|
||||||
CRITICAL: When the user asks to add comments, call addComment/suggestAddComment.
|
use that tool first. Do not respond with a different mutation type instead.
|
||||||
Do NOT respond with renames or signature changes unless the user also asked for those.
|
|
||||||
|
|
||||||
[FUNCTION FLAGS]
|
|
||||||
Use addFlag and removeFlag to categorize functions.
|
|
||||||
Valid flags: CRYPTO, NETWORK, FILE_IO, ANTI_DEBUG, ENTRY_POINT,
|
|
||||||
CALLBACK, MEMORY, PROCESS, REGISTRY, UI.
|
|
||||||
|
|
||||||
[GLOBAL VARIABLES]
|
|
||||||
Use searchGlobalVars to find data label IDs, then renameGlobalVar (apply)
|
|
||||||
or suggestRenameGlobalVar (pending approval).
|
|
||||||
|
|
||||||
[LABELS]
|
|
||||||
Use searchLabels to find label IDs within a function, then renameLabel (apply)
|
|
||||||
or suggestRenameLabel (pending approval).
|
|
||||||
|
|
||||||
[STRUCTS]
|
|
||||||
Use searchStructs to find struct IDs, then renameStruct (apply)
|
|
||||||
or suggestRenameStruct (pending approval).
|
|
||||||
%s
|
%s
|
||||||
[BINARY OVERVIEW]
|
[BINARY OVERVIEW]
|
||||||
Filename: %s
|
Filename: %s
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package ai.decompile.ai.service.tools;
|
|||||||
|
|
||||||
import ai.decompile.analysis.model.repository.StaticFunctionRepository;
|
import ai.decompile.analysis.model.repository.StaticFunctionRepository;
|
||||||
import ai.decompile.analysis.service.MutationService;
|
import ai.decompile.analysis.service.MutationService;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.ai.tool.annotation.Tool;
|
import org.springframework.ai.tool.annotation.Tool;
|
||||||
@ -217,6 +218,48 @@ public class FunctionMutationTools {
|
|||||||
mutationService.suggestComment(funcId, comment, address, "AI", sessionId));
|
mutationService.suggestComment(funcId, comment, address, "AI", sessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Tool(
|
||||||
|
description =
|
||||||
|
"""
|
||||||
|
Suggest C-like pseudocode for a function that currently has no decompiled code.
|
||||||
|
CRITICAL: Only call this when getFunctionDecompiled returns found=false and you
|
||||||
|
have thoroughly analyzed the function's assembly (call getFunctionAssembly first).
|
||||||
|
Be conservative — only suggest pseudocode when the assembly logic is clear and
|
||||||
|
you can produce accurate, idiomatic C code. Do NOT guess or fabricate logic.
|
||||||
|
The pseudocode must faithfully reproduce the exact logic shown in the assembly,
|
||||||
|
using proper C syntax (control flow, variable names derived from stack offsets,
|
||||||
|
function calls using resolved names when available).
|
||||||
|
Provide one line of code per array element. Do NOT combine multiple statements
|
||||||
|
into a single string element. A comment "Pseudocode generated by AI" will be
|
||||||
|
automatically added.
|
||||||
|
""")
|
||||||
|
public String suggestPseudocode(
|
||||||
|
@ToolParam(description = "The function ID (UUID from searchFunctions result)")
|
||||||
|
String functionId,
|
||||||
|
@ToolParam(
|
||||||
|
description =
|
||||||
|
"Array of C-like pseudocode lines. Each element is exactly ONE line of code. "
|
||||||
|
+ "Do NOT put multiple statements in one element. "
|
||||||
|
+ "Do not include markdown code fences.")
|
||||||
|
List<String> pseudocodeLines) {
|
||||||
|
|
||||||
|
if (pseudocodeLines == null || pseudocodeLines.isEmpty()) {
|
||||||
|
return error(functionId, "Pseudocode must not be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
var pseudocode = String.join("\n", pseudocodeLines);
|
||||||
|
var funcId = UUID.fromString(functionId);
|
||||||
|
if (functionRepository.findById(funcId).isEmpty()) {
|
||||||
|
return error(functionId, "Function not found: " + functionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutationService.applyComment(funcId, "Pseudocode generated by AI", "", "AI");
|
||||||
|
|
||||||
|
var sessionId = toolContext.getSessionId();
|
||||||
|
return helper.formatResult(
|
||||||
|
mutationService.suggest("FUNCTION", funcId, "decompiledCode", pseudocode, "AI", sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
@Tool(
|
@Tool(
|
||||||
description =
|
description =
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import org.springframework.ai.tool.annotation.Tool;
|
|||||||
import org.springframework.ai.tool.annotation.ToolParam;
|
import org.springframework.ai.tool.annotation.ToolParam;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
@ -155,16 +156,18 @@ public class FunctionTools {
|
|||||||
var func = functionRepository.findById(UUID.fromString(functionId)).orElse(null);
|
var func = functionRepository.findById(UUID.fromString(functionId)).orElse(null);
|
||||||
|
|
||||||
if (Objects.isNull(func)) {
|
if (Objects.isNull(func)) {
|
||||||
return new DecompiledResult("unknown", "unknown", "");
|
return new DecompiledResult("unknown", "unknown", "", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var renameMap = mutationService.buildRenameMap(func.getAnalysis().getId());
|
var renameMap = mutationService.buildRenameMap(func.getAnalysis().getId());
|
||||||
var code = func.getDecompiledCode();
|
var code = func.getDecompiledCode();
|
||||||
|
var hasDecompiled = Objects.nonNull(code) && !code.isBlank();
|
||||||
|
|
||||||
return new DecompiledResult(
|
return new DecompiledResult(
|
||||||
func.getName(),
|
func.getName(),
|
||||||
func.getAddress(),
|
func.getAddress(),
|
||||||
AnalysisService.resolveNames(Objects.nonNull(code) ? code : "", renameMap));
|
hasDecompiled ? AnalysisService.resolveNames(code, renameMap) : "",
|
||||||
|
hasDecompiled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Tool(
|
@Tool(
|
||||||
@ -291,7 +294,8 @@ public class FunctionTools {
|
|||||||
|
|
||||||
var page =
|
var page =
|
||||||
functionRepository.findByAnalysisId(
|
functionRepository.findByAnalysisId(
|
||||||
analysis.getId(), PageRequest.of(0, MAX_LIST_FUNCTIONS));
|
analysis.getId(),
|
||||||
|
PageRequest.of(0, MAX_LIST_FUNCTIONS, Sort.by("address").ascending()));
|
||||||
|
|
||||||
return page.getContent().stream().map(this::toSummary).toList();
|
return page.getContent().stream().map(this::toSummary).toList();
|
||||||
}
|
}
|
||||||
@ -345,7 +349,8 @@ public class FunctionTools {
|
|||||||
public record FunctionSummary(
|
public record FunctionSummary(
|
||||||
UUID id, String name, String address, int functionSize, String signature) {}
|
UUID id, String name, String address, int functionSize, String signature) {}
|
||||||
|
|
||||||
public record DecompiledResult(String name, String address, String decompiledCode) {}
|
public record DecompiledResult(
|
||||||
|
String name, String address, String decompiledCode, boolean found) {}
|
||||||
|
|
||||||
public record FunctionAssembly(String name, String address, String assembly) {}
|
public record FunctionAssembly(String name, String address, String assembly) {}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user