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,256 @@
1
+ // ExtractCFG.java - Java fallback for CFG extraction
2
+ // @category Analysis
3
+ // @description Extracts a control-flow graph for a function and returns JSON
4
+
5
+ import ghidra.app.script.GhidraScript;
6
+ import ghidra.program.model.address.Address;
7
+ import ghidra.program.model.block.BasicBlockModel;
8
+ import ghidra.program.model.block.CodeBlock;
9
+ import ghidra.program.model.block.CodeBlockIterator;
10
+ import ghidra.program.model.block.CodeBlockReference;
11
+ import ghidra.program.model.block.CodeBlockReferenceIterator;
12
+ import ghidra.program.model.listing.Function;
13
+ import ghidra.program.model.listing.FunctionIterator;
14
+ import ghidra.program.model.listing.FunctionManager;
15
+ import ghidra.program.model.listing.Instruction;
16
+ import ghidra.program.model.symbol.Symbol;
17
+ import ghidra.program.model.symbol.SymbolIterator;
18
+ import ghidra.program.model.symbol.SymbolType;
19
+
20
+ import java.util.LinkedHashMap;
21
+ import java.util.Map;
22
+
23
+ public class ExtractCFG extends GhidraScript {
24
+
25
+ private String escapeJson(String value) {
26
+ if (value == null) {
27
+ return "";
28
+ }
29
+ StringBuilder out = new StringBuilder(value.length() + 16);
30
+ for (int i = 0; i < value.length(); i++) {
31
+ char c = value.charAt(i);
32
+ switch (c) {
33
+ case '"':
34
+ out.append("\\\"");
35
+ break;
36
+ case '\\':
37
+ out.append("\\\\");
38
+ break;
39
+ case '\b':
40
+ out.append("\\b");
41
+ break;
42
+ case '\f':
43
+ out.append("\\f");
44
+ break;
45
+ case '\n':
46
+ out.append("\\n");
47
+ break;
48
+ case '\r':
49
+ out.append("\\r");
50
+ break;
51
+ case '\t':
52
+ out.append("\\t");
53
+ break;
54
+ default:
55
+ if (c < 0x20) {
56
+ out.append(String.format("\\u%04x", (int) c));
57
+ } else {
58
+ out.append(c);
59
+ }
60
+ }
61
+ }
62
+ return out.toString();
63
+ }
64
+
65
+ private Function resolveFunction(String addressOrSymbol) {
66
+ FunctionManager manager = currentProgram.getFunctionManager();
67
+ try {
68
+ Address address = currentProgram.getAddressFactory().getAddress(addressOrSymbol);
69
+ Function byAddress = manager.getFunctionAt(address);
70
+ if (byAddress != null) {
71
+ return byAddress;
72
+ }
73
+ } catch (Exception ignored) {
74
+ }
75
+
76
+ SymbolIterator iterator = currentProgram.getSymbolTable().getSymbols(addressOrSymbol);
77
+ while (iterator.hasNext()) {
78
+ Symbol symbol = iterator.next();
79
+ if (symbol.getSymbolType() != SymbolType.FUNCTION) {
80
+ continue;
81
+ }
82
+ Function bySymbol = manager.getFunctionAt(symbol.getAddress());
83
+ if (bySymbol != null) {
84
+ return bySymbol;
85
+ }
86
+ }
87
+
88
+ FunctionIterator functions = manager.getFunctions(true);
89
+ while (functions.hasNext()) {
90
+ Function function = functions.next();
91
+ if (addressOrSymbol.equals(function.getName())) {
92
+ return function;
93
+ }
94
+ }
95
+
96
+ return null;
97
+ }
98
+
99
+ private String getBlockType(CodeBlock block, Function function) throws Exception {
100
+ if (block.getFirstStartAddress().equals(function.getEntryPoint())) {
101
+ return "entry";
102
+ }
103
+
104
+ CodeBlockReferenceIterator destinations = block.getDestinations(monitor);
105
+ if (!destinations.hasNext()) {
106
+ return "exit";
107
+ }
108
+
109
+ Instruction instruction = currentProgram.getListing().getInstructionAt(block.getFirstStartAddress());
110
+ while (instruction != null && block.contains(instruction.getAddress())) {
111
+ if (instruction.getFlowType().isCall()) {
112
+ return "call";
113
+ }
114
+ if (instruction.getFlowType().isTerminal()) {
115
+ return "return";
116
+ }
117
+ instruction = instruction.getNext();
118
+ }
119
+
120
+ return "basic";
121
+ }
122
+
123
+ private String getEdgeType(CodeBlock sourceBlock) {
124
+ Instruction instruction = currentProgram.getListing().getInstructionAt(sourceBlock.getMaxAddress());
125
+ if (instruction == null) {
126
+ return "fallthrough";
127
+ }
128
+ if (instruction.getFlowType().isCall()) {
129
+ return "call";
130
+ }
131
+ if (instruction.getFlowType().isJump()) {
132
+ return "jump";
133
+ }
134
+ if (instruction.getFlowType().isTerminal()) {
135
+ return "return";
136
+ }
137
+ return "fallthrough";
138
+ }
139
+
140
+ @Override
141
+ protected void run() throws Exception {
142
+ if (currentProgram == null) {
143
+ println("{\"error\":\"No program loaded\"}");
144
+ return;
145
+ }
146
+
147
+ String[] args = getScriptArgs();
148
+ if (args.length < 1) {
149
+ println("{\"error\":\"Usage: ExtractCFG.java <address|symbol>\"}");
150
+ return;
151
+ }
152
+
153
+ Function function = resolveFunction(args[0]);
154
+ if (function == null) {
155
+ println("{\"error\":\"Function not found: " + escapeJson(args[0]) + "\"}");
156
+ return;
157
+ }
158
+
159
+ BasicBlockModel blockModel = new BasicBlockModel(currentProgram);
160
+ Map<String, String> blockIds = new LinkedHashMap<>();
161
+ StringBuilder nodes = new StringBuilder(16384);
162
+ StringBuilder edges = new StringBuilder(16384);
163
+
164
+ boolean firstNode = true;
165
+ int blockIndex = 0;
166
+ CodeBlockIterator blocks = blockModel.getCodeBlocksContaining(function.getBody(), monitor);
167
+ while (blocks.hasNext()) {
168
+ CodeBlock block = blocks.next();
169
+ String blockAddress = block.getFirstStartAddress().toString();
170
+ String blockId = "block_" + blockIndex++;
171
+ blockIds.put(blockAddress, blockId);
172
+
173
+ if (!firstNode) {
174
+ nodes.append(',');
175
+ }
176
+ firstNode = false;
177
+
178
+ nodes.append('{');
179
+ nodes.append("\"id\":\"").append(escapeJson(blockId)).append("\",");
180
+ nodes.append("\"address\":\"").append(escapeJson(blockAddress)).append("\",");
181
+ nodes.append("\"instructions\":[");
182
+
183
+ boolean firstInstruction = true;
184
+ Instruction instruction = currentProgram.getListing().getInstructionAt(block.getFirstStartAddress());
185
+ while (instruction != null && block.contains(instruction.getAddress())) {
186
+ if (!firstInstruction) {
187
+ nodes.append(',');
188
+ }
189
+ firstInstruction = false;
190
+ String operand =
191
+ instruction.getNumOperands() > 0
192
+ ? instruction.getDefaultOperandRepresentation(0)
193
+ : "";
194
+ nodes.append("\"")
195
+ .append(escapeJson(
196
+ instruction.getAddress().toString()
197
+ + ": "
198
+ + instruction.getMnemonicString()
199
+ + (operand.length() > 0 ? " " + operand : "")
200
+ ))
201
+ .append("\"");
202
+ instruction = instruction.getNext();
203
+ }
204
+
205
+ nodes.append("],\"type\":\"")
206
+ .append(escapeJson(getBlockType(block, function)))
207
+ .append("\"}");
208
+ }
209
+
210
+ boolean firstEdge = true;
211
+ blocks = blockModel.getCodeBlocksContaining(function.getBody(), monitor);
212
+ while (blocks.hasNext()) {
213
+ CodeBlock block = blocks.next();
214
+ String fromId = blockIds.get(block.getFirstStartAddress().toString());
215
+ if (fromId == null) {
216
+ continue;
217
+ }
218
+
219
+ CodeBlockReferenceIterator destinations = block.getDestinations(monitor);
220
+ while (destinations.hasNext()) {
221
+ CodeBlockReference destination = destinations.next();
222
+ if (!function.getBody().contains(destination.getDestinationAddress())) {
223
+ continue;
224
+ }
225
+
226
+ String toId = blockIds.get(destination.getDestinationAddress().toString());
227
+ if (toId == null) {
228
+ continue;
229
+ }
230
+
231
+ if (!firstEdge) {
232
+ edges.append(',');
233
+ }
234
+ firstEdge = false;
235
+
236
+ edges.append("{\"from\":\"")
237
+ .append(escapeJson(fromId))
238
+ .append("\",\"to\":\"")
239
+ .append(escapeJson(toId))
240
+ .append("\",\"type\":\"")
241
+ .append(escapeJson(getEdgeType(block)))
242
+ .append("\"}");
243
+ }
244
+ }
245
+
246
+ StringBuilder output = new StringBuilder(32768);
247
+ output.append('{');
248
+ output.append("\"function\":\"").append(escapeJson(function.getName())).append("\",");
249
+ output.append("\"address\":\"").append(escapeJson(function.getEntryPoint().toString())).append("\",");
250
+ output.append("\"nodes\":[").append(nodes).append("],");
251
+ output.append("\"edges\":[").append(edges).append("]");
252
+ output.append('}');
253
+
254
+ println(output.toString());
255
+ }
256
+ }
@@ -0,0 +1,233 @@
1
+ # ExtractCFG.py - Extract Control Flow Graph for a function
2
+ # Requirements: 11.1, 11.2, 11.3, 11.4, 11.5
3
+ #
4
+ # Usage: analyzeHeadless <project_path> <project_name> -process <binary> \
5
+ # -postScript ExtractCFG.py <address_or_symbol> -noanalysis
6
+ #
7
+ # Output: JSON with CFG nodes and edges
8
+
9
+ from ghidra.program.model.block import BasicBlockModel
10
+ from ghidra.program.model.symbol import SymbolType
11
+ from ghidra.program.model.address import AddressSet
12
+ import json
13
+ import sys
14
+
15
+ def get_function_by_address_or_symbol(program, address_or_symbol):
16
+ """
17
+ Find function by address (hex string) or symbol name
18
+ """
19
+ function_manager = program.getFunctionManager()
20
+
21
+ # Try as address first
22
+ try:
23
+ addr = program.getAddressFactory().getAddress(address_or_symbol)
24
+ func = function_manager.getFunctionAt(addr)
25
+ if func:
26
+ return func
27
+ except:
28
+ pass
29
+
30
+ # Try as symbol name
31
+ symbol_table = program.getSymbolTable()
32
+ symbols = symbol_table.getSymbols(address_or_symbol)
33
+
34
+ for symbol in symbols:
35
+ if symbol.getSymbolType() == SymbolType.FUNCTION:
36
+ func = function_manager.getFunctionAt(symbol.getAddress())
37
+ if func:
38
+ return func
39
+
40
+ return None
41
+
42
+ def get_block_type(block, function):
43
+ """
44
+ Determine the type of a basic block
45
+ Requirements: 11.3
46
+ """
47
+ entry_point = function.getEntryPoint()
48
+
49
+ # Check if this is the entry block
50
+ if block.getFirstStartAddress().equals(entry_point):
51
+ return "entry"
52
+
53
+ # Check if this is an exit block (has no outgoing edges or returns)
54
+ destinations = block.getDestinations(monitor)
55
+ if not destinations.hasNext():
56
+ return "exit"
57
+
58
+ # Check if block contains a call instruction
59
+ listing = currentProgram.getListing()
60
+ instruction = listing.getInstructionAt(block.getFirstStartAddress())
61
+
62
+ while instruction and block.contains(instruction.getAddress()):
63
+ flow_type = instruction.getFlowType()
64
+ if flow_type.isCall():
65
+ return "call"
66
+ if flow_type.isTerminal():
67
+ return "return"
68
+ instruction = instruction.getNext()
69
+
70
+ return "basic"
71
+
72
+ def get_edge_type(source_block, dest_block):
73
+ """
74
+ Determine the type of a control flow edge
75
+ Requirements: 11.4
76
+ """
77
+ listing = currentProgram.getListing()
78
+
79
+ # Get the last instruction in the source block
80
+ last_addr = source_block.getMaxAddress()
81
+ instruction = listing.getInstructionBefore(last_addr.add(1))
82
+
83
+ if not instruction:
84
+ return "fallthrough"
85
+
86
+ flow_type = instruction.getFlowType()
87
+
88
+ if flow_type.isCall():
89
+ return "call"
90
+ elif flow_type.isJump():
91
+ return "jump"
92
+ elif flow_type.isTerminal():
93
+ return "return"
94
+ elif flow_type.isFallthrough():
95
+ return "fallthrough"
96
+ else:
97
+ return "fallthrough"
98
+
99
+ def extract_cfg(function):
100
+ """
101
+ Extract control flow graph for a function
102
+ Requirements: 11.1, 11.2, 11.3, 11.4, 11.5
103
+ """
104
+ # Create basic block model
105
+ block_model = BasicBlockModel(currentProgram)
106
+ listing = currentProgram.getListing()
107
+
108
+ # Get all basic blocks in the function
109
+ function_body = function.getBody()
110
+ code_blocks = block_model.getCodeBlocksContaining(function_body, monitor)
111
+
112
+ nodes = []
113
+ edges = []
114
+ block_id_map = {}
115
+
116
+ # First pass: create nodes
117
+ block_index = 0
118
+ while code_blocks.hasNext():
119
+ block = code_blocks.next()
120
+
121
+ # Generate unique block ID
122
+ block_id = "block_{}".format(block_index)
123
+ block_id_map[block.getFirstStartAddress().toString()] = block_id
124
+
125
+ # Extract instructions in this block
126
+ instructions = []
127
+ instruction = listing.getInstructionAt(block.getFirstStartAddress())
128
+
129
+ while instruction and block.contains(instruction.getAddress()):
130
+ # Format: address: mnemonic operands
131
+ instr_str = "{}: {} {}".format(
132
+ instruction.getAddress().toString(),
133
+ instruction.getMnemonicString(),
134
+ instruction.getDefaultOperandRepresentation(0) if instruction.getNumOperands() > 0 else ""
135
+ )
136
+ instructions.append(instr_str.strip())
137
+ instruction = instruction.getNext()
138
+
139
+ # Determine block type
140
+ block_type = get_block_type(block, function)
141
+
142
+ # Create node
143
+ node = {
144
+ "id": block_id,
145
+ "address": block.getFirstStartAddress().toString(),
146
+ "instructions": instructions,
147
+ "type": block_type
148
+ }
149
+ nodes.append(node)
150
+
151
+ block_index += 1
152
+
153
+ # Second pass: create edges
154
+ code_blocks = block_model.getCodeBlocksContaining(function_body, monitor)
155
+
156
+ while code_blocks.hasNext():
157
+ block = code_blocks.next()
158
+ source_id = block_id_map.get(block.getFirstStartAddress().toString())
159
+
160
+ if not source_id:
161
+ continue
162
+
163
+ # Get all destination blocks
164
+ destinations = block.getDestinations(monitor)
165
+
166
+ while destinations.hasNext():
167
+ dest_ref = destinations.next()
168
+ dest_addr = dest_ref.getDestinationAddress()
169
+
170
+ # Check if destination is within the function
171
+ if function_body.contains(dest_addr):
172
+ dest_id = block_id_map.get(dest_addr.toString())
173
+
174
+ if dest_id:
175
+ # Determine edge type
176
+ edge_type = get_edge_type(block, None)
177
+
178
+ edge = {
179
+ "from": source_id,
180
+ "to": dest_id,
181
+ "type": edge_type
182
+ }
183
+ edges.append(edge)
184
+
185
+ return {
186
+ "nodes": nodes,
187
+ "edges": edges
188
+ }
189
+
190
+ def main():
191
+ """
192
+ Main entry point
193
+ """
194
+ if len(sys.argv) < 2:
195
+ result = {
196
+ "error": "Usage: ExtractCFG.py <address_or_symbol>"
197
+ }
198
+ print(json.dumps(result))
199
+ return
200
+
201
+ address_or_symbol = sys.argv[1]
202
+
203
+ # Find the function
204
+ function = get_function_by_address_or_symbol(currentProgram, address_or_symbol)
205
+
206
+ if not function:
207
+ result = {
208
+ "error": "Function not found: {}".format(address_or_symbol)
209
+ }
210
+ print(json.dumps(result))
211
+ return
212
+
213
+ # Extract CFG
214
+ try:
215
+ cfg = extract_cfg(function)
216
+
217
+ result = {
218
+ "function": function.getName(),
219
+ "address": function.getEntryPoint().toString(),
220
+ "nodes": cfg["nodes"],
221
+ "edges": cfg["edges"]
222
+ }
223
+
224
+ print(json.dumps(result, indent=2))
225
+
226
+ except Exception as e:
227
+ result = {
228
+ "error": "Failed to extract CFG: {}".format(str(e))
229
+ }
230
+ print(json.dumps(result))
231
+
232
+ if __name__ == "__main__":
233
+ main()