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,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()
|