windows-exe-decompiler-mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CODEX_INSTALLATION.md +69 -0
- package/COPILOT_INSTALLATION.md +77 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/bin/windows-exe-decompiler-mcp-server.js +3 -0
- package/dist/analysis-provenance.d.ts +184 -0
- package/dist/analysis-provenance.js +74 -0
- package/dist/analysis-task-runner.d.ts +31 -0
- package/dist/analysis-task-runner.js +160 -0
- package/dist/artifact-inventory.d.ts +23 -0
- package/dist/artifact-inventory.js +175 -0
- package/dist/cache-manager.d.ts +128 -0
- package/dist/cache-manager.js +454 -0
- package/dist/confidence-semantics.d.ts +66 -0
- package/dist/confidence-semantics.js +122 -0
- package/dist/config.d.ts +335 -0
- package/dist/config.js +193 -0
- package/dist/database.d.ts +227 -0
- package/dist/database.js +601 -0
- package/dist/decompiler-worker.d.ts +441 -0
- package/dist/decompiler-worker.js +1962 -0
- package/dist/dynamic-trace.d.ts +95 -0
- package/dist/dynamic-trace.js +629 -0
- package/dist/env-validator.d.ts +15 -0
- package/dist/env-validator.js +249 -0
- package/dist/error-handler.d.ts +28 -0
- package/dist/error-handler.example.d.ts +22 -0
- package/dist/error-handler.example.js +141 -0
- package/dist/error-handler.js +139 -0
- package/dist/ghidra-analysis-status.d.ts +49 -0
- package/dist/ghidra-analysis-status.js +178 -0
- package/dist/ghidra-config.d.ts +134 -0
- package/dist/ghidra-config.js +464 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +200 -0
- package/dist/job-queue.d.ts +169 -0
- package/dist/job-queue.js +407 -0
- package/dist/logger.d.ts +106 -0
- package/dist/logger.js +176 -0
- package/dist/policy-guard.d.ts +115 -0
- package/dist/policy-guard.js +243 -0
- package/dist/process-output.d.ts +15 -0
- package/dist/process-output.js +90 -0
- package/dist/prompts/function-explanation-review.d.ts +5 -0
- package/dist/prompts/function-explanation-review.js +64 -0
- package/dist/prompts/semantic-name-review.d.ts +5 -0
- package/dist/prompts/semantic-name-review.js +63 -0
- package/dist/runtime-correlation.d.ts +34 -0
- package/dist/runtime-correlation.js +279 -0
- package/dist/runtime-paths.d.ts +3 -0
- package/dist/runtime-paths.js +11 -0
- package/dist/selection-diff.d.ts +667 -0
- package/dist/selection-diff.js +53 -0
- package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
- package/dist/semantic-name-suggestion-artifacts.js +314 -0
- package/dist/server.d.ts +129 -0
- package/dist/server.js +578 -0
- package/dist/tools/artifact-read.d.ts +235 -0
- package/dist/tools/artifact-read.js +317 -0
- package/dist/tools/artifacts-diff.d.ts +728 -0
- package/dist/tools/artifacts-diff.js +304 -0
- package/dist/tools/artifacts-list.d.ts +515 -0
- package/dist/tools/artifacts-list.js +389 -0
- package/dist/tools/attack-map.d.ts +290 -0
- package/dist/tools/attack-map.js +519 -0
- package/dist/tools/cache-observability.d.ts +4 -0
- package/dist/tools/cache-observability.js +36 -0
- package/dist/tools/code-function-cfg.d.ts +50 -0
- package/dist/tools/code-function-cfg.js +102 -0
- package/dist/tools/code-function-decompile.d.ts +55 -0
- package/dist/tools/code-function-decompile.js +103 -0
- package/dist/tools/code-function-disassemble.d.ts +43 -0
- package/dist/tools/code-function-disassemble.js +185 -0
- package/dist/tools/code-function-explain-apply.d.ts +255 -0
- package/dist/tools/code-function-explain-apply.js +225 -0
- package/dist/tools/code-function-explain-prepare.d.ts +535 -0
- package/dist/tools/code-function-explain-prepare.js +276 -0
- package/dist/tools/code-function-explain-review.d.ts +397 -0
- package/dist/tools/code-function-explain-review.js +589 -0
- package/dist/tools/code-function-rename-apply.d.ts +248 -0
- package/dist/tools/code-function-rename-apply.js +220 -0
- package/dist/tools/code-function-rename-prepare.d.ts +506 -0
- package/dist/tools/code-function-rename-prepare.js +279 -0
- package/dist/tools/code-function-rename-review.d.ts +574 -0
- package/dist/tools/code-function-rename-review.js +761 -0
- package/dist/tools/code-functions-list.d.ts +37 -0
- package/dist/tools/code-functions-list.js +91 -0
- package/dist/tools/code-functions-rank.d.ts +34 -0
- package/dist/tools/code-functions-rank.js +90 -0
- package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
- package/dist/tools/code-functions-reconstruct.js +2807 -0
- package/dist/tools/code-functions-search.d.ts +39 -0
- package/dist/tools/code-functions-search.js +90 -0
- package/dist/tools/code-reconstruct-export.d.ts +1212 -0
- package/dist/tools/code-reconstruct-export.js +4002 -0
- package/dist/tools/code-reconstruct-plan.d.ts +274 -0
- package/dist/tools/code-reconstruct-plan.js +342 -0
- package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
- package/dist/tools/dotnet-metadata-extract.js +355 -0
- package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
- package/dist/tools/dotnet-reconstruct-export.js +1151 -0
- package/dist/tools/dotnet-types-list.d.ts +325 -0
- package/dist/tools/dotnet-types-list.js +201 -0
- package/dist/tools/dynamic-dependencies.d.ts +115 -0
- package/dist/tools/dynamic-dependencies.js +213 -0
- package/dist/tools/dynamic-memory-import.d.ts +10 -0
- package/dist/tools/dynamic-memory-import.js +567 -0
- package/dist/tools/dynamic-trace-import.d.ts +10 -0
- package/dist/tools/dynamic-trace-import.js +235 -0
- package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
- package/dist/tools/entrypoint-fallback-disasm.js +89 -0
- package/dist/tools/ghidra-analyze.d.ts +88 -0
- package/dist/tools/ghidra-analyze.js +208 -0
- package/dist/tools/ghidra-health.d.ts +37 -0
- package/dist/tools/ghidra-health.js +212 -0
- package/dist/tools/ioc-export.d.ts +209 -0
- package/dist/tools/ioc-export.js +542 -0
- package/dist/tools/packer-detect.d.ts +165 -0
- package/dist/tools/packer-detect.js +284 -0
- package/dist/tools/pe-exports-extract.d.ts +175 -0
- package/dist/tools/pe-exports-extract.js +253 -0
- package/dist/tools/pe-fingerprint.d.ts +234 -0
- package/dist/tools/pe-fingerprint.js +269 -0
- package/dist/tools/pe-imports-extract.d.ts +105 -0
- package/dist/tools/pe-imports-extract.js +245 -0
- package/dist/tools/report-generate.d.ts +157 -0
- package/dist/tools/report-generate.js +457 -0
- package/dist/tools/report-summarize.d.ts +2131 -0
- package/dist/tools/report-summarize.js +596 -0
- package/dist/tools/runtime-detect.d.ts +135 -0
- package/dist/tools/runtime-detect.js +247 -0
- package/dist/tools/sample-ingest.d.ts +94 -0
- package/dist/tools/sample-ingest.js +327 -0
- package/dist/tools/sample-profile-get.d.ts +183 -0
- package/dist/tools/sample-profile-get.js +121 -0
- package/dist/tools/sandbox-execute.d.ts +441 -0
- package/dist/tools/sandbox-execute.js +392 -0
- package/dist/tools/strings-extract.d.ts +375 -0
- package/dist/tools/strings-extract.js +314 -0
- package/dist/tools/strings-floss-decode.d.ts +143 -0
- package/dist/tools/strings-floss-decode.js +259 -0
- package/dist/tools/system-health.d.ts +434 -0
- package/dist/tools/system-health.js +446 -0
- package/dist/tools/task-cancel.d.ts +21 -0
- package/dist/tools/task-cancel.js +70 -0
- package/dist/tools/task-status.d.ts +27 -0
- package/dist/tools/task-status.js +106 -0
- package/dist/tools/task-sweep.d.ts +22 -0
- package/dist/tools/task-sweep.js +77 -0
- package/dist/tools/tool-help.d.ts +340 -0
- package/dist/tools/tool-help.js +261 -0
- package/dist/tools/yara-scan.d.ts +554 -0
- package/dist/tools/yara-scan.js +313 -0
- package/dist/types.d.ts +266 -0
- package/dist/types.js +41 -0
- package/dist/worker-pool.d.ts +204 -0
- package/dist/worker-pool.js +650 -0
- package/dist/workflows/deep-static.d.ts +104 -0
- package/dist/workflows/deep-static.js +276 -0
- package/dist/workflows/function-explanation-review.d.ts +655 -0
- package/dist/workflows/function-explanation-review.js +440 -0
- package/dist/workflows/reconstruct.d.ts +2053 -0
- package/dist/workflows/reconstruct.js +666 -0
- package/dist/workflows/semantic-name-review.d.ts +2418 -0
- package/dist/workflows/semantic-name-review.js +521 -0
- package/dist/workflows/triage.d.ts +659 -0
- package/dist/workflows/triage.js +1374 -0
- package/dist/workspace-manager.d.ts +150 -0
- package/dist/workspace-manager.js +411 -0
- package/ghidra_scripts/DecompileFunction.java +487 -0
- package/ghidra_scripts/DecompileFunction.py +150 -0
- package/ghidra_scripts/ExtractCFG.java +256 -0
- package/ghidra_scripts/ExtractCFG.py +233 -0
- package/ghidra_scripts/ExtractFunctions.java +442 -0
- package/ghidra_scripts/ExtractFunctions.py +101 -0
- package/ghidra_scripts/README.md +125 -0
- package/ghidra_scripts/SearchFunctionReferences.java +380 -0
- package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
- package/helpers/DotNetMetadataProbe/Program.cs +566 -0
- package/install-to-codex.ps1 +178 -0
- package/install-to-copilot.ps1 +303 -0
- package/package.json +101 -0
- package/requirements.txt +9 -0
- package/workers/requirements-dynamic.txt +11 -0
- package/workers/requirements.txt +8 -0
- package/workers/speakeasy_compat.py +175 -0
- package/workers/static_worker.py +5183 -0
- package/workers/yara_rules/default.yar +33 -0
- package/workers/yara_rules/malware_families.yar +93 -0
- package/workers/yara_rules/packers.yar +80 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
// DecompileFunction.java - Java fallback for function decompilation
|
|
2
|
+
// @category Analysis
|
|
3
|
+
// @description Decompiles a specific function and returns pseudocode, callers, callees, and xrefs
|
|
4
|
+
|
|
5
|
+
import ghidra.app.decompiler.DecompInterface;
|
|
6
|
+
import ghidra.app.decompiler.DecompileOptions;
|
|
7
|
+
import ghidra.app.decompiler.DecompileResults;
|
|
8
|
+
import ghidra.app.script.GhidraScript;
|
|
9
|
+
import ghidra.program.model.address.Address;
|
|
10
|
+
import ghidra.program.model.address.AddressIterator;
|
|
11
|
+
import ghidra.program.model.listing.Function;
|
|
12
|
+
import ghidra.program.model.listing.FunctionIterator;
|
|
13
|
+
import ghidra.program.model.listing.FunctionManager;
|
|
14
|
+
import ghidra.program.model.listing.Instruction;
|
|
15
|
+
import ghidra.program.model.symbol.Reference;
|
|
16
|
+
import ghidra.program.model.symbol.ReferenceIterator;
|
|
17
|
+
import ghidra.program.model.symbol.Symbol;
|
|
18
|
+
import ghidra.program.model.symbol.SymbolIterator;
|
|
19
|
+
import ghidra.program.model.symbol.SymbolType;
|
|
20
|
+
import ghidra.util.task.ConsoleTaskMonitor;
|
|
21
|
+
|
|
22
|
+
import java.util.LinkedHashMap;
|
|
23
|
+
import java.util.LinkedHashSet;
|
|
24
|
+
import java.util.Map;
|
|
25
|
+
import java.util.Set;
|
|
26
|
+
|
|
27
|
+
public class DecompileFunction extends GhidraScript {
|
|
28
|
+
|
|
29
|
+
private static class ResolvedTarget {
|
|
30
|
+
Function function;
|
|
31
|
+
String address;
|
|
32
|
+
String name;
|
|
33
|
+
String resolvedBy;
|
|
34
|
+
boolean exact;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private static class RelationshipAccumulator {
|
|
38
|
+
String address;
|
|
39
|
+
String name;
|
|
40
|
+
String resolvedBy;
|
|
41
|
+
boolean exact;
|
|
42
|
+
Set<String> relationTypes = new LinkedHashSet<>();
|
|
43
|
+
Set<String> referenceTypes = new LinkedHashSet<>();
|
|
44
|
+
Set<String> referenceAddresses = new LinkedHashSet<>();
|
|
45
|
+
Set<String> targetAddresses = new LinkedHashSet<>();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private String escapeJson(String value) {
|
|
49
|
+
if (value == null) {
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
StringBuilder out = new StringBuilder(value.length() + 16);
|
|
53
|
+
for (int i = 0; i < value.length(); i++) {
|
|
54
|
+
char c = value.charAt(i);
|
|
55
|
+
switch (c) {
|
|
56
|
+
case '"':
|
|
57
|
+
out.append("\\\"");
|
|
58
|
+
break;
|
|
59
|
+
case '\\':
|
|
60
|
+
out.append("\\\\");
|
|
61
|
+
break;
|
|
62
|
+
case '\b':
|
|
63
|
+
out.append("\\b");
|
|
64
|
+
break;
|
|
65
|
+
case '\f':
|
|
66
|
+
out.append("\\f");
|
|
67
|
+
break;
|
|
68
|
+
case '\n':
|
|
69
|
+
out.append("\\n");
|
|
70
|
+
break;
|
|
71
|
+
case '\r':
|
|
72
|
+
out.append("\\r");
|
|
73
|
+
break;
|
|
74
|
+
case '\t':
|
|
75
|
+
out.append("\\t");
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
if (c < 0x20) {
|
|
79
|
+
out.append(String.format("\\u%04x", (int) c));
|
|
80
|
+
} else {
|
|
81
|
+
out.append(c);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return out.toString();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private Function resolveFunction(String addressOrSymbol) {
|
|
89
|
+
FunctionManager manager = currentProgram.getFunctionManager();
|
|
90
|
+
try {
|
|
91
|
+
Address address = currentProgram.getAddressFactory().getAddress(addressOrSymbol);
|
|
92
|
+
Function byAddress = manager.getFunctionAt(address);
|
|
93
|
+
if (byAddress != null) {
|
|
94
|
+
return byAddress;
|
|
95
|
+
}
|
|
96
|
+
Function byContaining = manager.getFunctionContaining(address);
|
|
97
|
+
if (byContaining != null) {
|
|
98
|
+
return byContaining;
|
|
99
|
+
}
|
|
100
|
+
} catch (Exception ignored) {
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
SymbolIterator iterator = currentProgram.getSymbolTable().getSymbols(addressOrSymbol);
|
|
104
|
+
while (iterator.hasNext()) {
|
|
105
|
+
Symbol symbol = iterator.next();
|
|
106
|
+
if (symbol.getSymbolType() != SymbolType.FUNCTION) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
Function bySymbol = manager.getFunctionAt(symbol.getAddress());
|
|
110
|
+
if (bySymbol != null) {
|
|
111
|
+
return bySymbol;
|
|
112
|
+
}
|
|
113
|
+
Function byContaining = manager.getFunctionContaining(symbol.getAddress());
|
|
114
|
+
if (byContaining != null) {
|
|
115
|
+
return byContaining;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
FunctionIterator functions = manager.getFunctions(true);
|
|
120
|
+
while (functions.hasNext()) {
|
|
121
|
+
Function function = functions.next();
|
|
122
|
+
if (addressOrSymbol.equals(function.getName())) {
|
|
123
|
+
return function;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private void appendNamedAddressList(StringBuilder sb, Map<String, String> entries) {
|
|
131
|
+
sb.append('[');
|
|
132
|
+
boolean first = true;
|
|
133
|
+
for (Map.Entry<String, String> entry : entries.entrySet()) {
|
|
134
|
+
if (!first) {
|
|
135
|
+
sb.append(',');
|
|
136
|
+
}
|
|
137
|
+
first = false;
|
|
138
|
+
sb.append("{\"address\":\"")
|
|
139
|
+
.append(escapeJson(entry.getKey()))
|
|
140
|
+
.append("\",\"name\":\"")
|
|
141
|
+
.append(escapeJson(entry.getValue()))
|
|
142
|
+
.append("\"}");
|
|
143
|
+
}
|
|
144
|
+
sb.append(']');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private void appendStringArray(StringBuilder sb, Set<String> values) {
|
|
148
|
+
sb.append('[');
|
|
149
|
+
boolean first = true;
|
|
150
|
+
for (String value : values) {
|
|
151
|
+
if (!first) {
|
|
152
|
+
sb.append(',');
|
|
153
|
+
}
|
|
154
|
+
first = false;
|
|
155
|
+
sb.append('"').append(escapeJson(value)).append('"');
|
|
156
|
+
}
|
|
157
|
+
sb.append(']');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private void appendRelationshipList(StringBuilder sb, Map<String, RelationshipAccumulator> relationships) {
|
|
161
|
+
sb.append('[');
|
|
162
|
+
boolean first = true;
|
|
163
|
+
for (RelationshipAccumulator relationship : relationships.values()) {
|
|
164
|
+
if (!first) {
|
|
165
|
+
sb.append(',');
|
|
166
|
+
}
|
|
167
|
+
first = false;
|
|
168
|
+
sb.append('{');
|
|
169
|
+
sb.append("\"address\":\"").append(escapeJson(relationship.address)).append("\",");
|
|
170
|
+
sb.append("\"name\":\"").append(escapeJson(relationship.name)).append("\",");
|
|
171
|
+
sb.append("\"relation_types\":");
|
|
172
|
+
appendStringArray(sb, relationship.relationTypes);
|
|
173
|
+
sb.append(",\"reference_types\":");
|
|
174
|
+
appendStringArray(sb, relationship.referenceTypes);
|
|
175
|
+
sb.append(",\"reference_addresses\":");
|
|
176
|
+
appendStringArray(sb, relationship.referenceAddresses);
|
|
177
|
+
sb.append(",\"target_addresses\":");
|
|
178
|
+
appendStringArray(sb, relationship.targetAddresses);
|
|
179
|
+
sb.append(",\"resolved_by\":\"").append(escapeJson(relationship.resolvedBy)).append("\",");
|
|
180
|
+
sb.append("\"is_exact\":").append(relationship.exact);
|
|
181
|
+
sb.append('}');
|
|
182
|
+
}
|
|
183
|
+
sb.append(']');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private String makeRelationKey(String address, String name) {
|
|
187
|
+
return address + "|" + name;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private void addRelationship(
|
|
191
|
+
Map<String, RelationshipAccumulator> relationships,
|
|
192
|
+
String relationKey,
|
|
193
|
+
String address,
|
|
194
|
+
String name,
|
|
195
|
+
String relationType,
|
|
196
|
+
String referenceType,
|
|
197
|
+
String referenceAddress,
|
|
198
|
+
String targetAddress,
|
|
199
|
+
String resolvedBy,
|
|
200
|
+
boolean exact
|
|
201
|
+
) {
|
|
202
|
+
RelationshipAccumulator relationship = relationships.get(relationKey);
|
|
203
|
+
if (relationship == null) {
|
|
204
|
+
relationship = new RelationshipAccumulator();
|
|
205
|
+
relationship.address = address;
|
|
206
|
+
relationship.name = name;
|
|
207
|
+
relationship.resolvedBy = resolvedBy;
|
|
208
|
+
relationship.exact = exact;
|
|
209
|
+
relationships.put(relationKey, relationship);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
relationship.relationTypes.add(relationType);
|
|
213
|
+
relationship.referenceTypes.add(referenceType == null ? "unknown" : referenceType);
|
|
214
|
+
relationship.referenceAddresses.add(referenceAddress);
|
|
215
|
+
relationship.targetAddresses.add(targetAddress);
|
|
216
|
+
|
|
217
|
+
if (!relationship.exact && exact) {
|
|
218
|
+
relationship.exact = true;
|
|
219
|
+
}
|
|
220
|
+
if (!"function_at".equals(relationship.resolvedBy) && "function_at".equals(resolvedBy)) {
|
|
221
|
+
relationship.resolvedBy = resolvedBy;
|
|
222
|
+
} else if ("primary_symbol".equals(relationship.resolvedBy)
|
|
223
|
+
&& !"primary_symbol".equals(resolvedBy)) {
|
|
224
|
+
relationship.resolvedBy = resolvedBy;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private Map<String, String> toNamedAddressMap(Map<String, RelationshipAccumulator> relationships) {
|
|
229
|
+
Map<String, String> entries = new LinkedHashMap<>();
|
|
230
|
+
for (RelationshipAccumulator relationship : relationships.values()) {
|
|
231
|
+
entries.put(relationship.address, relationship.name);
|
|
232
|
+
}
|
|
233
|
+
return entries;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private ResolvedTarget resolveCallableTarget(FunctionManager manager, Address address) {
|
|
237
|
+
ResolvedTarget target = new ResolvedTarget();
|
|
238
|
+
|
|
239
|
+
Function function = manager.getFunctionAt(address);
|
|
240
|
+
if (function != null) {
|
|
241
|
+
target.function = function;
|
|
242
|
+
target.address = function.getEntryPoint().toString();
|
|
243
|
+
target.name = function.getName();
|
|
244
|
+
target.resolvedBy = "function_at";
|
|
245
|
+
target.exact = address.equals(function.getEntryPoint());
|
|
246
|
+
return target;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function = manager.getFunctionContaining(address);
|
|
250
|
+
if (function != null) {
|
|
251
|
+
target.function = function;
|
|
252
|
+
target.address = function.getEntryPoint().toString();
|
|
253
|
+
target.name = function.getName();
|
|
254
|
+
target.resolvedBy = "function_containing";
|
|
255
|
+
target.exact = address.equals(function.getEntryPoint());
|
|
256
|
+
return target;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
Symbol symbol = currentProgram.getSymbolTable().getPrimarySymbol(address);
|
|
260
|
+
if (symbol != null) {
|
|
261
|
+
target.function = null;
|
|
262
|
+
target.address = symbol.getAddress().toString();
|
|
263
|
+
target.name = symbol.getName();
|
|
264
|
+
target.resolvedBy = "primary_symbol";
|
|
265
|
+
target.exact = address.equals(symbol.getAddress());
|
|
266
|
+
return target;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private boolean isTailJumpHint(Function sourceFunction, Address fromAddress) {
|
|
273
|
+
Instruction instruction = currentProgram.getListing().getInstructionContaining(fromAddress);
|
|
274
|
+
if (instruction == null || !instruction.getFlowType().isJump() || instruction.getFlowType().isConditional()) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
Instruction next = instruction.getNext();
|
|
279
|
+
return next == null || !sourceFunction.getBody().contains(next.getAddress());
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private String classifyRelationType(Function sourceFunction, ResolvedTarget target, Reference ref) {
|
|
283
|
+
if (ref == null || ref.getReferenceType() == null || target == null) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (ref.getReferenceType().isCall()) {
|
|
288
|
+
return target.exact ? "direct_call" : "direct_call_body";
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
boolean sameFunction = target.function != null
|
|
292
|
+
&& sourceFunction.getEntryPoint().equals(target.function.getEntryPoint());
|
|
293
|
+
if (sameFunction) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (ref.getReferenceType().isJump()) {
|
|
298
|
+
return isTailJumpHint(sourceFunction, ref.getFromAddress())
|
|
299
|
+
? "tail_jump_hint"
|
|
300
|
+
: "body_reference_hint";
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Instruction instruction = currentProgram.getListing().getInstructionContaining(ref.getFromAddress());
|
|
304
|
+
if (instruction != null) {
|
|
305
|
+
return "body_reference_hint";
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private Map<String, RelationshipAccumulator> collectCallerRelationships(Function function) {
|
|
312
|
+
Map<String, RelationshipAccumulator> callers = new LinkedHashMap<>();
|
|
313
|
+
FunctionManager manager = currentProgram.getFunctionManager();
|
|
314
|
+
AddressIterator addresses = function.getBody().getAddresses(true);
|
|
315
|
+
while (addresses.hasNext()) {
|
|
316
|
+
Address targetAddress = addresses.next();
|
|
317
|
+
ReferenceIterator refsTo = currentProgram.getReferenceManager().getReferencesTo(targetAddress);
|
|
318
|
+
while (refsTo.hasNext()) {
|
|
319
|
+
Reference ref = refsTo.next();
|
|
320
|
+
Function caller = manager.getFunctionContaining(ref.getFromAddress());
|
|
321
|
+
if (caller == null) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
ResolvedTarget target = new ResolvedTarget();
|
|
326
|
+
target.function = function;
|
|
327
|
+
target.address = function.getEntryPoint().toString();
|
|
328
|
+
target.name = function.getName();
|
|
329
|
+
target.resolvedBy = targetAddress.equals(function.getEntryPoint())
|
|
330
|
+
? "function_at"
|
|
331
|
+
: "function_containing";
|
|
332
|
+
target.exact = targetAddress.equals(function.getEntryPoint());
|
|
333
|
+
|
|
334
|
+
String relationType = classifyRelationType(caller, target, ref);
|
|
335
|
+
if (relationType == null) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
addRelationship(
|
|
340
|
+
callers,
|
|
341
|
+
caller.getEntryPoint().toString(),
|
|
342
|
+
caller.getEntryPoint().toString(),
|
|
343
|
+
caller.getName(),
|
|
344
|
+
relationType,
|
|
345
|
+
ref.getReferenceType().getName(),
|
|
346
|
+
ref.getFromAddress().toString(),
|
|
347
|
+
targetAddress.toString(),
|
|
348
|
+
target.resolvedBy,
|
|
349
|
+
target.exact
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return callers;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private Map<String, RelationshipAccumulator> collectCalleeRelationships(Function function) {
|
|
357
|
+
Map<String, RelationshipAccumulator> callees = new LinkedHashMap<>();
|
|
358
|
+
FunctionManager manager = currentProgram.getFunctionManager();
|
|
359
|
+
AddressIterator addresses = function.getBody().getAddresses(true);
|
|
360
|
+
while (addresses.hasNext()) {
|
|
361
|
+
Address fromAddress = addresses.next();
|
|
362
|
+
Reference[] refsFrom = currentProgram.getReferenceManager().getReferencesFrom(fromAddress);
|
|
363
|
+
for (Reference ref : refsFrom) {
|
|
364
|
+
ResolvedTarget target = resolveCallableTarget(manager, ref.getToAddress());
|
|
365
|
+
String relationType = classifyRelationType(function, target, ref);
|
|
366
|
+
if (target == null || relationType == null) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
addRelationship(
|
|
370
|
+
callees,
|
|
371
|
+
makeRelationKey(target.address, target.name),
|
|
372
|
+
target.address,
|
|
373
|
+
target.name,
|
|
374
|
+
relationType,
|
|
375
|
+
ref.getReferenceType().getName(),
|
|
376
|
+
fromAddress.toString(),
|
|
377
|
+
ref.getToAddress().toString(),
|
|
378
|
+
target.resolvedBy,
|
|
379
|
+
target.exact
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return callees;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private void appendXrefs(StringBuilder sb, Function function) {
|
|
387
|
+
sb.append('[');
|
|
388
|
+
boolean first = true;
|
|
389
|
+
FunctionManager manager = currentProgram.getFunctionManager();
|
|
390
|
+
ReferenceIterator refsTo = currentProgram.getReferenceManager().getReferencesTo(function.getEntryPoint());
|
|
391
|
+
while (refsTo.hasNext()) {
|
|
392
|
+
Reference ref = refsTo.next();
|
|
393
|
+
if (!first) {
|
|
394
|
+
sb.append(',');
|
|
395
|
+
}
|
|
396
|
+
first = false;
|
|
397
|
+
sb.append("{\"from_address\":\"")
|
|
398
|
+
.append(escapeJson(ref.getFromAddress().toString()))
|
|
399
|
+
.append("\",\"type\":\"")
|
|
400
|
+
.append(escapeJson(ref.getReferenceType().getName()))
|
|
401
|
+
.append("\",\"is_call\":")
|
|
402
|
+
.append(ref.getReferenceType().isCall())
|
|
403
|
+
.append(",\"is_data\":")
|
|
404
|
+
.append(ref.getReferenceType().isData());
|
|
405
|
+
|
|
406
|
+
Function fromFunction = manager.getFunctionContaining(ref.getFromAddress());
|
|
407
|
+
if (fromFunction != null) {
|
|
408
|
+
sb.append(",\"from_function\":\"")
|
|
409
|
+
.append(escapeJson(fromFunction.getName()))
|
|
410
|
+
.append("\"");
|
|
411
|
+
}
|
|
412
|
+
sb.append('}');
|
|
413
|
+
}
|
|
414
|
+
sb.append(']');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
@Override
|
|
418
|
+
protected void run() throws Exception {
|
|
419
|
+
if (currentProgram == null) {
|
|
420
|
+
println("{\"error\":\"No program loaded\"}");
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
String[] args = getScriptArgs();
|
|
425
|
+
if (args.length < 1) {
|
|
426
|
+
println("{\"error\":\"Usage: DecompileFunction.java <address|symbol> [include_xrefs]\"}");
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
String addressOrSymbol = args[0];
|
|
431
|
+
boolean includeXrefs = args.length > 1 && "true".equalsIgnoreCase(args[1]);
|
|
432
|
+
|
|
433
|
+
Function function = resolveFunction(addressOrSymbol);
|
|
434
|
+
if (function == null) {
|
|
435
|
+
println("{\"error\":\"Function not found: " + escapeJson(addressOrSymbol) + "\"}");
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
Map<String, RelationshipAccumulator> callerRelationships = collectCallerRelationships(function);
|
|
440
|
+
Map<String, RelationshipAccumulator> calleeRelationships = collectCalleeRelationships(function);
|
|
441
|
+
Map<String, String> callers = toNamedAddressMap(callerRelationships);
|
|
442
|
+
Map<String, String> callees = toNamedAddressMap(calleeRelationships);
|
|
443
|
+
|
|
444
|
+
DecompInterface decompiler = new DecompInterface();
|
|
445
|
+
DecompileOptions options = new DecompileOptions();
|
|
446
|
+
decompiler.setOptions(options);
|
|
447
|
+
decompiler.openProgram(currentProgram);
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
DecompileResults result = decompiler.decompileFunction(function, 30, new ConsoleTaskMonitor());
|
|
451
|
+
if (result == null || !result.decompileCompleted()) {
|
|
452
|
+
String error = result == null ? "Decompilation failed" : result.getErrorMessage();
|
|
453
|
+
println("{\"error\":\"" + escapeJson(error) + "\"}");
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
String pseudocode = "";
|
|
458
|
+
if (result.getDecompiledFunction() != null) {
|
|
459
|
+
pseudocode = result.getDecompiledFunction().getC();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
StringBuilder sb = new StringBuilder(32768);
|
|
463
|
+
sb.append('{');
|
|
464
|
+
sb.append("\"function\":\"").append(escapeJson(function.getName())).append("\",");
|
|
465
|
+
sb.append("\"address\":\"").append(escapeJson(function.getEntryPoint().toString())).append("\",");
|
|
466
|
+
sb.append("\"pseudocode\":\"").append(escapeJson(pseudocode)).append("\",");
|
|
467
|
+
sb.append("\"callers\":");
|
|
468
|
+
appendNamedAddressList(sb, callers);
|
|
469
|
+
sb.append(",\"caller_relationships\":");
|
|
470
|
+
appendRelationshipList(sb, callerRelationships);
|
|
471
|
+
sb.append(",\"callees\":");
|
|
472
|
+
appendNamedAddressList(sb, callees);
|
|
473
|
+
sb.append(",\"callee_relationships\":");
|
|
474
|
+
appendRelationshipList(sb, calleeRelationships);
|
|
475
|
+
|
|
476
|
+
if (includeXrefs) {
|
|
477
|
+
sb.append(",\"xrefs\":");
|
|
478
|
+
appendXrefs(sb, function);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
sb.append('}');
|
|
482
|
+
println(sb.toString());
|
|
483
|
+
} finally {
|
|
484
|
+
decompiler.dispose();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# DecompileFunction.py - Ghidra script to decompile a specific function
|
|
2
|
+
# @category Analysis
|
|
3
|
+
# @description Decompiles a specific function and returns pseudocode, callers, callees, and xrefs
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from ghidra.app.decompiler import DecompInterface, DecompileOptions
|
|
8
|
+
from ghidra.util.task import ConsoleTaskMonitor
|
|
9
|
+
|
|
10
|
+
def decompile_function(address_str, include_xrefs=False):
|
|
11
|
+
"""
|
|
12
|
+
Decompile a specific function by address or symbol name
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
address_str: Function address (hex string) or symbol name
|
|
16
|
+
include_xrefs: Whether to include cross-references
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
JSON object with decompilation results
|
|
20
|
+
"""
|
|
21
|
+
program = getCurrentProgram()
|
|
22
|
+
if program is None:
|
|
23
|
+
return {"error": "No program loaded"}
|
|
24
|
+
|
|
25
|
+
function_manager = program.getFunctionManager()
|
|
26
|
+
|
|
27
|
+
# Try to find function by address or name
|
|
28
|
+
target_function = None
|
|
29
|
+
|
|
30
|
+
# First try as address
|
|
31
|
+
try:
|
|
32
|
+
addr = program.getAddressFactory().getAddress(address_str)
|
|
33
|
+
target_function = function_manager.getFunctionAt(addr)
|
|
34
|
+
except:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
# If not found, try as symbol name
|
|
38
|
+
if target_function is None:
|
|
39
|
+
for function in function_manager.getFunctions(True):
|
|
40
|
+
if function.getName() == address_str:
|
|
41
|
+
target_function = function
|
|
42
|
+
break
|
|
43
|
+
|
|
44
|
+
if target_function is None:
|
|
45
|
+
return {"error": "Function not found: {}".format(address_str)}
|
|
46
|
+
|
|
47
|
+
# Initialize decompiler
|
|
48
|
+
decompiler = DecompInterface()
|
|
49
|
+
decompiler.openProgram(program)
|
|
50
|
+
|
|
51
|
+
# Set decompiler options
|
|
52
|
+
options = DecompileOptions()
|
|
53
|
+
decompiler.setOptions(options)
|
|
54
|
+
|
|
55
|
+
# Set timeout (30 seconds default)
|
|
56
|
+
monitor = ConsoleTaskMonitor()
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
# Decompile the function
|
|
60
|
+
decompile_results = decompiler.decompileFunction(target_function, 30, monitor)
|
|
61
|
+
|
|
62
|
+
if decompile_results is None or not decompile_results.decompileCompleted():
|
|
63
|
+
error_msg = "Decompilation failed"
|
|
64
|
+
if decompile_results:
|
|
65
|
+
error_msg = decompile_results.getErrorMessage()
|
|
66
|
+
return {"error": error_msg}
|
|
67
|
+
|
|
68
|
+
# Get pseudocode
|
|
69
|
+
decomp_code = decompile_results.getDecompiledFunction()
|
|
70
|
+
pseudocode = decomp_code.getC() if decomp_code else ""
|
|
71
|
+
|
|
72
|
+
# Get callers
|
|
73
|
+
callers = []
|
|
74
|
+
for caller_ref in target_function.getSymbol().getReferences():
|
|
75
|
+
if caller_ref.getReferenceType().isCall():
|
|
76
|
+
from_addr = caller_ref.getFromAddress()
|
|
77
|
+
caller_func = function_manager.getFunctionContaining(from_addr)
|
|
78
|
+
if caller_func:
|
|
79
|
+
callers.append({
|
|
80
|
+
"address": caller_func.getEntryPoint().toString(),
|
|
81
|
+
"name": caller_func.getName()
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
# Get callees
|
|
85
|
+
callees = []
|
|
86
|
+
for ref in target_function.getBody().getAddresses(True):
|
|
87
|
+
refs_from = program.getReferenceManager().getReferencesFrom(ref)
|
|
88
|
+
for ref_from in refs_from:
|
|
89
|
+
if ref_from.getReferenceType().isCall():
|
|
90
|
+
to_addr = ref_from.getToAddress()
|
|
91
|
+
callee_func = function_manager.getFunctionAt(to_addr)
|
|
92
|
+
if callee_func:
|
|
93
|
+
callees.append({
|
|
94
|
+
"address": callee_func.getEntryPoint().toString(),
|
|
95
|
+
"name": callee_func.getName()
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
# Build result
|
|
99
|
+
result = {
|
|
100
|
+
"function": target_function.getName(),
|
|
101
|
+
"address": target_function.getEntryPoint().toString(),
|
|
102
|
+
"pseudocode": pseudocode,
|
|
103
|
+
"callers": callers,
|
|
104
|
+
"callees": callees
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Add cross-references if requested
|
|
108
|
+
if include_xrefs:
|
|
109
|
+
xrefs = []
|
|
110
|
+
ref_manager = program.getReferenceManager()
|
|
111
|
+
|
|
112
|
+
# Get all references to this function
|
|
113
|
+
for ref in ref_manager.getReferencesTo(target_function.getEntryPoint()):
|
|
114
|
+
xref_info = {
|
|
115
|
+
"from_address": ref.getFromAddress().toString(),
|
|
116
|
+
"type": ref.getReferenceType().getName(),
|
|
117
|
+
"is_call": ref.getReferenceType().isCall(),
|
|
118
|
+
"is_data": ref.getReferenceType().isData()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Try to get the containing function
|
|
122
|
+
from_func = function_manager.getFunctionContaining(ref.getFromAddress())
|
|
123
|
+
if from_func:
|
|
124
|
+
xref_info["from_function"] = from_func.getName()
|
|
125
|
+
|
|
126
|
+
xrefs.append(xref_info)
|
|
127
|
+
|
|
128
|
+
result["xrefs"] = xrefs
|
|
129
|
+
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
return {"error": "Decompilation error: {}".format(str(e))}
|
|
134
|
+
|
|
135
|
+
finally:
|
|
136
|
+
decompiler.dispose()
|
|
137
|
+
|
|
138
|
+
# Main execution
|
|
139
|
+
if __name__ == "__main__":
|
|
140
|
+
# Get arguments from command line
|
|
141
|
+
# Expected format: address [include_xrefs]
|
|
142
|
+
if len(sys.argv) < 2:
|
|
143
|
+
print(json.dumps({"error": "Usage: DecompileFunction.py <address|symbol> [include_xrefs]"}))
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
|
|
146
|
+
address_arg = sys.argv[1]
|
|
147
|
+
include_xrefs_arg = len(sys.argv) > 2 and sys.argv[2].lower() == "true"
|
|
148
|
+
|
|
149
|
+
result = decompile_function(address_arg, include_xrefs_arg)
|
|
150
|
+
print(json.dumps(result, indent=2))
|