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.
Files changed (190) hide show
  1. package/CODEX_INSTALLATION.md +69 -0
  2. package/COPILOT_INSTALLATION.md +77 -0
  3. package/LICENSE +21 -0
  4. package/README.md +314 -0
  5. package/bin/windows-exe-decompiler-mcp-server.js +3 -0
  6. package/dist/analysis-provenance.d.ts +184 -0
  7. package/dist/analysis-provenance.js +74 -0
  8. package/dist/analysis-task-runner.d.ts +31 -0
  9. package/dist/analysis-task-runner.js +160 -0
  10. package/dist/artifact-inventory.d.ts +23 -0
  11. package/dist/artifact-inventory.js +175 -0
  12. package/dist/cache-manager.d.ts +128 -0
  13. package/dist/cache-manager.js +454 -0
  14. package/dist/confidence-semantics.d.ts +66 -0
  15. package/dist/confidence-semantics.js +122 -0
  16. package/dist/config.d.ts +335 -0
  17. package/dist/config.js +193 -0
  18. package/dist/database.d.ts +227 -0
  19. package/dist/database.js +601 -0
  20. package/dist/decompiler-worker.d.ts +441 -0
  21. package/dist/decompiler-worker.js +1962 -0
  22. package/dist/dynamic-trace.d.ts +95 -0
  23. package/dist/dynamic-trace.js +629 -0
  24. package/dist/env-validator.d.ts +15 -0
  25. package/dist/env-validator.js +249 -0
  26. package/dist/error-handler.d.ts +28 -0
  27. package/dist/error-handler.example.d.ts +22 -0
  28. package/dist/error-handler.example.js +141 -0
  29. package/dist/error-handler.js +139 -0
  30. package/dist/ghidra-analysis-status.d.ts +49 -0
  31. package/dist/ghidra-analysis-status.js +178 -0
  32. package/dist/ghidra-config.d.ts +134 -0
  33. package/dist/ghidra-config.js +464 -0
  34. package/dist/index.d.ts +9 -0
  35. package/dist/index.js +200 -0
  36. package/dist/job-queue.d.ts +169 -0
  37. package/dist/job-queue.js +407 -0
  38. package/dist/logger.d.ts +106 -0
  39. package/dist/logger.js +176 -0
  40. package/dist/policy-guard.d.ts +115 -0
  41. package/dist/policy-guard.js +243 -0
  42. package/dist/process-output.d.ts +15 -0
  43. package/dist/process-output.js +90 -0
  44. package/dist/prompts/function-explanation-review.d.ts +5 -0
  45. package/dist/prompts/function-explanation-review.js +64 -0
  46. package/dist/prompts/semantic-name-review.d.ts +5 -0
  47. package/dist/prompts/semantic-name-review.js +63 -0
  48. package/dist/runtime-correlation.d.ts +34 -0
  49. package/dist/runtime-correlation.js +279 -0
  50. package/dist/runtime-paths.d.ts +3 -0
  51. package/dist/runtime-paths.js +11 -0
  52. package/dist/selection-diff.d.ts +667 -0
  53. package/dist/selection-diff.js +53 -0
  54. package/dist/semantic-name-suggestion-artifacts.d.ts +116 -0
  55. package/dist/semantic-name-suggestion-artifacts.js +314 -0
  56. package/dist/server.d.ts +129 -0
  57. package/dist/server.js +578 -0
  58. package/dist/tools/artifact-read.d.ts +235 -0
  59. package/dist/tools/artifact-read.js +317 -0
  60. package/dist/tools/artifacts-diff.d.ts +728 -0
  61. package/dist/tools/artifacts-diff.js +304 -0
  62. package/dist/tools/artifacts-list.d.ts +515 -0
  63. package/dist/tools/artifacts-list.js +389 -0
  64. package/dist/tools/attack-map.d.ts +290 -0
  65. package/dist/tools/attack-map.js +519 -0
  66. package/dist/tools/cache-observability.d.ts +4 -0
  67. package/dist/tools/cache-observability.js +36 -0
  68. package/dist/tools/code-function-cfg.d.ts +50 -0
  69. package/dist/tools/code-function-cfg.js +102 -0
  70. package/dist/tools/code-function-decompile.d.ts +55 -0
  71. package/dist/tools/code-function-decompile.js +103 -0
  72. package/dist/tools/code-function-disassemble.d.ts +43 -0
  73. package/dist/tools/code-function-disassemble.js +185 -0
  74. package/dist/tools/code-function-explain-apply.d.ts +255 -0
  75. package/dist/tools/code-function-explain-apply.js +225 -0
  76. package/dist/tools/code-function-explain-prepare.d.ts +535 -0
  77. package/dist/tools/code-function-explain-prepare.js +276 -0
  78. package/dist/tools/code-function-explain-review.d.ts +397 -0
  79. package/dist/tools/code-function-explain-review.js +589 -0
  80. package/dist/tools/code-function-rename-apply.d.ts +248 -0
  81. package/dist/tools/code-function-rename-apply.js +220 -0
  82. package/dist/tools/code-function-rename-prepare.d.ts +506 -0
  83. package/dist/tools/code-function-rename-prepare.js +279 -0
  84. package/dist/tools/code-function-rename-review.d.ts +574 -0
  85. package/dist/tools/code-function-rename-review.js +761 -0
  86. package/dist/tools/code-functions-list.d.ts +37 -0
  87. package/dist/tools/code-functions-list.js +91 -0
  88. package/dist/tools/code-functions-rank.d.ts +34 -0
  89. package/dist/tools/code-functions-rank.js +90 -0
  90. package/dist/tools/code-functions-reconstruct.d.ts +2725 -0
  91. package/dist/tools/code-functions-reconstruct.js +2807 -0
  92. package/dist/tools/code-functions-search.d.ts +39 -0
  93. package/dist/tools/code-functions-search.js +90 -0
  94. package/dist/tools/code-reconstruct-export.d.ts +1212 -0
  95. package/dist/tools/code-reconstruct-export.js +4002 -0
  96. package/dist/tools/code-reconstruct-plan.d.ts +274 -0
  97. package/dist/tools/code-reconstruct-plan.js +342 -0
  98. package/dist/tools/dotnet-metadata-extract.d.ts +541 -0
  99. package/dist/tools/dotnet-metadata-extract.js +355 -0
  100. package/dist/tools/dotnet-reconstruct-export.d.ts +567 -0
  101. package/dist/tools/dotnet-reconstruct-export.js +1151 -0
  102. package/dist/tools/dotnet-types-list.d.ts +325 -0
  103. package/dist/tools/dotnet-types-list.js +201 -0
  104. package/dist/tools/dynamic-dependencies.d.ts +115 -0
  105. package/dist/tools/dynamic-dependencies.js +213 -0
  106. package/dist/tools/dynamic-memory-import.d.ts +10 -0
  107. package/dist/tools/dynamic-memory-import.js +567 -0
  108. package/dist/tools/dynamic-trace-import.d.ts +10 -0
  109. package/dist/tools/dynamic-trace-import.js +235 -0
  110. package/dist/tools/entrypoint-fallback-disasm.d.ts +30 -0
  111. package/dist/tools/entrypoint-fallback-disasm.js +89 -0
  112. package/dist/tools/ghidra-analyze.d.ts +88 -0
  113. package/dist/tools/ghidra-analyze.js +208 -0
  114. package/dist/tools/ghidra-health.d.ts +37 -0
  115. package/dist/tools/ghidra-health.js +212 -0
  116. package/dist/tools/ioc-export.d.ts +209 -0
  117. package/dist/tools/ioc-export.js +542 -0
  118. package/dist/tools/packer-detect.d.ts +165 -0
  119. package/dist/tools/packer-detect.js +284 -0
  120. package/dist/tools/pe-exports-extract.d.ts +175 -0
  121. package/dist/tools/pe-exports-extract.js +253 -0
  122. package/dist/tools/pe-fingerprint.d.ts +234 -0
  123. package/dist/tools/pe-fingerprint.js +269 -0
  124. package/dist/tools/pe-imports-extract.d.ts +105 -0
  125. package/dist/tools/pe-imports-extract.js +245 -0
  126. package/dist/tools/report-generate.d.ts +157 -0
  127. package/dist/tools/report-generate.js +457 -0
  128. package/dist/tools/report-summarize.d.ts +2131 -0
  129. package/dist/tools/report-summarize.js +596 -0
  130. package/dist/tools/runtime-detect.d.ts +135 -0
  131. package/dist/tools/runtime-detect.js +247 -0
  132. package/dist/tools/sample-ingest.d.ts +94 -0
  133. package/dist/tools/sample-ingest.js +327 -0
  134. package/dist/tools/sample-profile-get.d.ts +183 -0
  135. package/dist/tools/sample-profile-get.js +121 -0
  136. package/dist/tools/sandbox-execute.d.ts +441 -0
  137. package/dist/tools/sandbox-execute.js +392 -0
  138. package/dist/tools/strings-extract.d.ts +375 -0
  139. package/dist/tools/strings-extract.js +314 -0
  140. package/dist/tools/strings-floss-decode.d.ts +143 -0
  141. package/dist/tools/strings-floss-decode.js +259 -0
  142. package/dist/tools/system-health.d.ts +434 -0
  143. package/dist/tools/system-health.js +446 -0
  144. package/dist/tools/task-cancel.d.ts +21 -0
  145. package/dist/tools/task-cancel.js +70 -0
  146. package/dist/tools/task-status.d.ts +27 -0
  147. package/dist/tools/task-status.js +106 -0
  148. package/dist/tools/task-sweep.d.ts +22 -0
  149. package/dist/tools/task-sweep.js +77 -0
  150. package/dist/tools/tool-help.d.ts +340 -0
  151. package/dist/tools/tool-help.js +261 -0
  152. package/dist/tools/yara-scan.d.ts +554 -0
  153. package/dist/tools/yara-scan.js +313 -0
  154. package/dist/types.d.ts +266 -0
  155. package/dist/types.js +41 -0
  156. package/dist/worker-pool.d.ts +204 -0
  157. package/dist/worker-pool.js +650 -0
  158. package/dist/workflows/deep-static.d.ts +104 -0
  159. package/dist/workflows/deep-static.js +276 -0
  160. package/dist/workflows/function-explanation-review.d.ts +655 -0
  161. package/dist/workflows/function-explanation-review.js +440 -0
  162. package/dist/workflows/reconstruct.d.ts +2053 -0
  163. package/dist/workflows/reconstruct.js +666 -0
  164. package/dist/workflows/semantic-name-review.d.ts +2418 -0
  165. package/dist/workflows/semantic-name-review.js +521 -0
  166. package/dist/workflows/triage.d.ts +659 -0
  167. package/dist/workflows/triage.js +1374 -0
  168. package/dist/workspace-manager.d.ts +150 -0
  169. package/dist/workspace-manager.js +411 -0
  170. package/ghidra_scripts/DecompileFunction.java +487 -0
  171. package/ghidra_scripts/DecompileFunction.py +150 -0
  172. package/ghidra_scripts/ExtractCFG.java +256 -0
  173. package/ghidra_scripts/ExtractCFG.py +233 -0
  174. package/ghidra_scripts/ExtractFunctions.java +442 -0
  175. package/ghidra_scripts/ExtractFunctions.py +101 -0
  176. package/ghidra_scripts/README.md +125 -0
  177. package/ghidra_scripts/SearchFunctionReferences.java +380 -0
  178. package/helpers/DotNetMetadataProbe/DotNetMetadataProbe.csproj +9 -0
  179. package/helpers/DotNetMetadataProbe/Program.cs +566 -0
  180. package/install-to-codex.ps1 +178 -0
  181. package/install-to-copilot.ps1 +303 -0
  182. package/package.json +101 -0
  183. package/requirements.txt +9 -0
  184. package/workers/requirements-dynamic.txt +11 -0
  185. package/workers/requirements.txt +8 -0
  186. package/workers/speakeasy_compat.py +175 -0
  187. package/workers/static_worker.py +5183 -0
  188. package/workers/yara_rules/default.yar +33 -0
  189. package/workers/yara_rules/malware_families.yar +93 -0
  190. 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))