mitsupi 1.0.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/LICENSE +201 -0
- package/README.md +95 -0
- package/TODO.md +11 -0
- package/commands/handoff.md +100 -0
- package/commands/make-release.md +75 -0
- package/commands/pickup.md +30 -0
- package/commands/update-changelog.md +78 -0
- package/package.json +22 -0
- package/pi-extensions/answer.ts +527 -0
- package/pi-extensions/codex-tuning.ts +632 -0
- package/pi-extensions/commit.ts +248 -0
- package/pi-extensions/cwd-history.ts +237 -0
- package/pi-extensions/issues.ts +548 -0
- package/pi-extensions/loop.ts +446 -0
- package/pi-extensions/qna.ts +167 -0
- package/pi-extensions/reveal.ts +689 -0
- package/pi-extensions/review.ts +807 -0
- package/pi-themes/armin.json +81 -0
- package/pi-themes/nightowl.json +82 -0
- package/skills/anachb/SKILL.md +183 -0
- package/skills/anachb/departures.sh +79 -0
- package/skills/anachb/disruptions.sh +53 -0
- package/skills/anachb/route.sh +87 -0
- package/skills/anachb/search.sh +43 -0
- package/skills/ghidra/SKILL.md +254 -0
- package/skills/ghidra/scripts/find-ghidra.sh +54 -0
- package/skills/ghidra/scripts/ghidra-analyze.sh +239 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportAll.java +278 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportCalls.java +148 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportDecompiled.java +84 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportFunctions.java +114 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportStrings.java +123 -0
- package/skills/ghidra/scripts/ghidra_scripts/ExportSymbols.java +135 -0
- package/skills/github/SKILL.md +47 -0
- package/skills/improve-skill/SKILL.md +155 -0
- package/skills/improve-skill/scripts/extract-session.js +349 -0
- package/skills/oebb-scotty/SKILL.md +429 -0
- package/skills/oebb-scotty/arrivals.sh +83 -0
- package/skills/oebb-scotty/departures.sh +83 -0
- package/skills/oebb-scotty/disruptions.sh +33 -0
- package/skills/oebb-scotty/search-station.sh +36 -0
- package/skills/oebb-scotty/trip.sh +119 -0
- package/skills/openscad/SKILL.md +232 -0
- package/skills/openscad/examples/parametric_box.scad +92 -0
- package/skills/openscad/examples/phone_stand.scad +95 -0
- package/skills/openscad/tools/common.sh +50 -0
- package/skills/openscad/tools/export-stl.sh +56 -0
- package/skills/openscad/tools/extract-params.sh +147 -0
- package/skills/openscad/tools/multi-preview.sh +68 -0
- package/skills/openscad/tools/preview.sh +74 -0
- package/skills/openscad/tools/render-with-params.sh +91 -0
- package/skills/openscad/tools/validate.sh +46 -0
- package/skills/pi-share/SKILL.md +105 -0
- package/skills/pi-share/fetch-session.mjs +322 -0
- package/skills/sentry/SKILL.md +239 -0
- package/skills/sentry/lib/auth.js +99 -0
- package/skills/sentry/scripts/fetch-event.js +329 -0
- package/skills/sentry/scripts/fetch-issue.js +356 -0
- package/skills/sentry/scripts/list-issues.js +239 -0
- package/skills/sentry/scripts/search-events.js +291 -0
- package/skills/sentry/scripts/search-logs.js +240 -0
- package/skills/tmux/SKILL.md +105 -0
- package/skills/tmux/scripts/find-sessions.sh +112 -0
- package/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/skills/web-browser/SKILL.md +91 -0
- package/skills/web-browser/scripts/cdp.js +210 -0
- package/skills/web-browser/scripts/dismiss-cookies.js +373 -0
- package/skills/web-browser/scripts/eval.js +68 -0
- package/skills/web-browser/scripts/logs-tail.js +69 -0
- package/skills/web-browser/scripts/nav.js +65 -0
- package/skills/web-browser/scripts/net-summary.js +94 -0
- package/skills/web-browser/scripts/package-lock.json +33 -0
- package/skills/web-browser/scripts/package.json +6 -0
- package/skills/web-browser/scripts/pick.js +165 -0
- package/skills/web-browser/scripts/screenshot.js +52 -0
- package/skills/web-browser/scripts/start.js +80 -0
- package/skills/web-browser/scripts/watch.js +266 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/* ###
|
|
2
|
+
* Export comprehensive analysis: decompiled code, functions, strings, calls, and symbols
|
|
3
|
+
* @category Export
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import ghidra.app.decompiler.DecompInterface;
|
|
7
|
+
import ghidra.app.decompiler.DecompileOptions;
|
|
8
|
+
import ghidra.app.decompiler.DecompileResults;
|
|
9
|
+
import ghidra.app.script.GhidraScript;
|
|
10
|
+
import ghidra.program.model.data.DataType;
|
|
11
|
+
import ghidra.program.model.listing.*;
|
|
12
|
+
import ghidra.program.model.symbol.*;
|
|
13
|
+
|
|
14
|
+
import java.io.File;
|
|
15
|
+
import java.io.FileWriter;
|
|
16
|
+
import java.io.PrintWriter;
|
|
17
|
+
import java.util.*;
|
|
18
|
+
|
|
19
|
+
public class ExportAll extends GhidraScript {
|
|
20
|
+
|
|
21
|
+
private String outputDir;
|
|
22
|
+
private String programName;
|
|
23
|
+
|
|
24
|
+
@Override
|
|
25
|
+
public void run() throws Exception {
|
|
26
|
+
outputDir = System.getenv("GHIDRA_OUTPUT_DIR");
|
|
27
|
+
if (outputDir == null || outputDir.isEmpty()) {
|
|
28
|
+
outputDir = ".";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
programName = currentProgram.getName().replaceAll("[^a-zA-Z0-9._-]", "_");
|
|
32
|
+
|
|
33
|
+
println("=== Starting comprehensive export ===");
|
|
34
|
+
println("Output directory: " + outputDir);
|
|
35
|
+
println("Program: " + currentProgram.getName());
|
|
36
|
+
println("Architecture: " + currentProgram.getLanguage().getProcessor());
|
|
37
|
+
println("");
|
|
38
|
+
|
|
39
|
+
// Export summary first
|
|
40
|
+
exportSummary();
|
|
41
|
+
|
|
42
|
+
// Export decompiled code
|
|
43
|
+
exportDecompiled();
|
|
44
|
+
|
|
45
|
+
// Export functions
|
|
46
|
+
exportFunctions();
|
|
47
|
+
|
|
48
|
+
// Export strings
|
|
49
|
+
exportStrings();
|
|
50
|
+
|
|
51
|
+
// Export interesting patterns
|
|
52
|
+
exportInteresting();
|
|
53
|
+
|
|
54
|
+
println("");
|
|
55
|
+
println("=== Export complete ===");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private void exportSummary() throws Exception {
|
|
59
|
+
File outputFile = new File(outputDir, programName + "_summary.txt");
|
|
60
|
+
println("Exporting summary to: " + outputFile.getName());
|
|
61
|
+
|
|
62
|
+
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
|
63
|
+
writer.println("Binary Analysis Summary");
|
|
64
|
+
writer.println("=======================");
|
|
65
|
+
writer.println("");
|
|
66
|
+
writer.println("File: " + currentProgram.getName());
|
|
67
|
+
writer.println("Architecture: " + currentProgram.getLanguage().getProcessor());
|
|
68
|
+
writer.println("Address Size: " + currentProgram.getLanguage().getLanguageDescription().getSize() + " bit");
|
|
69
|
+
writer.println("Endianness: " + currentProgram.getLanguage().isBigEndian() + " (big endian)");
|
|
70
|
+
writer.println("Compiler: " + currentProgram.getCompilerSpec().getCompilerSpecID());
|
|
71
|
+
writer.println("");
|
|
72
|
+
|
|
73
|
+
// Count functions
|
|
74
|
+
int totalFuncs = 0, externalFuncs = 0, thunkFuncs = 0;
|
|
75
|
+
FunctionIterator funcs = currentProgram.getFunctionManager().getFunctions(true);
|
|
76
|
+
while (funcs.hasNext()) {
|
|
77
|
+
Function f = funcs.next();
|
|
78
|
+
totalFuncs++;
|
|
79
|
+
if (f.isExternal()) externalFuncs++;
|
|
80
|
+
if (f.isThunk()) thunkFuncs++;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
writer.println("Functions:");
|
|
84
|
+
writer.println(" Total: " + totalFuncs);
|
|
85
|
+
writer.println(" External: " + externalFuncs);
|
|
86
|
+
writer.println(" Thunks: " + thunkFuncs);
|
|
87
|
+
writer.println(" User-defined: " + (totalFuncs - externalFuncs - thunkFuncs));
|
|
88
|
+
writer.println("");
|
|
89
|
+
|
|
90
|
+
// Memory sections
|
|
91
|
+
writer.println("Memory Sections:");
|
|
92
|
+
for (var block : currentProgram.getMemory().getBlocks()) {
|
|
93
|
+
writer.println(" " + block.getName() + ": " + block.getStart() + " - " + block.getEnd() +
|
|
94
|
+
" (" + block.getSize() + " bytes)" +
|
|
95
|
+
(block.isExecute() ? " [X]" : "") +
|
|
96
|
+
(block.isWrite() ? " [W]" : "") +
|
|
97
|
+
(block.isRead() ? " [R]" : ""));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private void exportDecompiled() throws Exception {
|
|
103
|
+
File outputFile = new File(outputDir, programName + "_decompiled.c");
|
|
104
|
+
println("Exporting decompiled code to: " + outputFile.getName());
|
|
105
|
+
|
|
106
|
+
DecompInterface decompiler = new DecompInterface();
|
|
107
|
+
DecompileOptions options = new DecompileOptions();
|
|
108
|
+
decompiler.setOptions(options);
|
|
109
|
+
|
|
110
|
+
if (!decompiler.openProgram(currentProgram)) {
|
|
111
|
+
printerr("Failed to initialize decompiler");
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
|
116
|
+
writer.println("/* Decompiled from: " + currentProgram.getName() + " */");
|
|
117
|
+
writer.println("");
|
|
118
|
+
|
|
119
|
+
FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
|
|
120
|
+
int count = 0;
|
|
121
|
+
|
|
122
|
+
while (functions.hasNext() && !monitor.isCancelled()) {
|
|
123
|
+
Function func = functions.next();
|
|
124
|
+
if (func.isExternal() || func.isThunk()) continue;
|
|
125
|
+
|
|
126
|
+
DecompileResults results = decompiler.decompileFunction(func, 30, monitor);
|
|
127
|
+
if (results.decompileCompleted()) {
|
|
128
|
+
writer.println("/* " + func.getName() + " @ " + func.getEntryPoint() + " */");
|
|
129
|
+
writer.println(results.getDecompiledFunction().getC());
|
|
130
|
+
writer.println("");
|
|
131
|
+
count++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
println(" Decompiled " + count + " functions");
|
|
135
|
+
} finally {
|
|
136
|
+
decompiler.dispose();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private void exportFunctions() throws Exception {
|
|
141
|
+
File outputFile = new File(outputDir, programName + "_functions.json");
|
|
142
|
+
println("Exporting functions to: " + outputFile.getName());
|
|
143
|
+
|
|
144
|
+
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
|
145
|
+
writer.println("[");
|
|
146
|
+
|
|
147
|
+
FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
|
|
148
|
+
boolean first = true;
|
|
149
|
+
int count = 0;
|
|
150
|
+
|
|
151
|
+
while (functions.hasNext() && !monitor.isCancelled()) {
|
|
152
|
+
Function func = functions.next();
|
|
153
|
+
if (!first) writer.println(",");
|
|
154
|
+
first = false;
|
|
155
|
+
|
|
156
|
+
writer.println(" {");
|
|
157
|
+
writer.println(" \"name\": \"" + escapeJson(func.getName()) + "\",");
|
|
158
|
+
writer.println(" \"address\": \"" + func.getEntryPoint() + "\",");
|
|
159
|
+
writer.println(" \"signature\": \"" + escapeJson(func.getPrototypeString(false, false)) + "\",");
|
|
160
|
+
writer.println(" \"external\": " + func.isExternal() + ",");
|
|
161
|
+
|
|
162
|
+
// Get calls
|
|
163
|
+
java.util.Set<Function> calls = func.getCalledFunctions(monitor);
|
|
164
|
+
writer.print(" \"calls\": [");
|
|
165
|
+
int callIdx = 0;
|
|
166
|
+
for (Function calledFunc : calls) {
|
|
167
|
+
if (callIdx >= 20) break;
|
|
168
|
+
if (callIdx > 0) writer.print(", ");
|
|
169
|
+
writer.print("\"" + escapeJson(calledFunc.getName()) + "\"");
|
|
170
|
+
callIdx++;
|
|
171
|
+
}
|
|
172
|
+
writer.println("]");
|
|
173
|
+
writer.print(" }");
|
|
174
|
+
count++;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
writer.println();
|
|
178
|
+
writer.println("]");
|
|
179
|
+
println(" Exported " + count + " functions");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private void exportStrings() throws Exception {
|
|
184
|
+
File outputFile = new File(outputDir, programName + "_strings.txt");
|
|
185
|
+
println("Exporting strings to: " + outputFile.getName());
|
|
186
|
+
|
|
187
|
+
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
|
188
|
+
DataIterator dataIterator = currentProgram.getListing().getDefinedData(true);
|
|
189
|
+
int count = 0;
|
|
190
|
+
|
|
191
|
+
while (dataIterator.hasNext() && !monitor.isCancelled()) {
|
|
192
|
+
Data data = dataIterator.next();
|
|
193
|
+
DataType dt = data.getBaseDataType();
|
|
194
|
+
String typeName = dt.getName().toLowerCase();
|
|
195
|
+
|
|
196
|
+
if (typeName.contains("string") || typeName.contains("unicode")) {
|
|
197
|
+
Object value = data.getValue();
|
|
198
|
+
if (value instanceof String) {
|
|
199
|
+
String str = (String) value;
|
|
200
|
+
if (str.length() >= 4) {
|
|
201
|
+
writer.println(data.getAddress() + ": " + str);
|
|
202
|
+
count++;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
println(" Exported " + count + " strings");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private void exportInteresting() throws Exception {
|
|
212
|
+
File outputFile = new File(outputDir, programName + "_interesting.txt");
|
|
213
|
+
println("Analyzing interesting patterns...");
|
|
214
|
+
|
|
215
|
+
// Interesting function name patterns
|
|
216
|
+
String[] interestingPatterns = {
|
|
217
|
+
"crypt", "encrypt", "decrypt", "aes", "des", "rsa", "md5", "sha",
|
|
218
|
+
"password", "passwd", "secret", "key", "token", "auth",
|
|
219
|
+
"socket", "connect", "send", "recv", "http", "url", "dns",
|
|
220
|
+
"file", "open", "read", "write", "exec", "system", "shell", "cmd",
|
|
221
|
+
"malloc", "free", "alloc", "memcpy", "strcpy", "sprintf",
|
|
222
|
+
"debug", "log", "print", "error", "fail"
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
|
226
|
+
writer.println("Interesting Functions and Patterns");
|
|
227
|
+
writer.println("===================================");
|
|
228
|
+
writer.println("");
|
|
229
|
+
|
|
230
|
+
// Find functions matching patterns
|
|
231
|
+
Map<String, List<String>> categorized = new LinkedHashMap<>();
|
|
232
|
+
for (String pattern : interestingPatterns) {
|
|
233
|
+
categorized.put(pattern, new ArrayList<>());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
|
|
237
|
+
while (functions.hasNext()) {
|
|
238
|
+
Function func = functions.next();
|
|
239
|
+
String name = func.getName().toLowerCase();
|
|
240
|
+
for (String pattern : interestingPatterns) {
|
|
241
|
+
if (name.contains(pattern)) {
|
|
242
|
+
categorized.get(pattern).add(func.getName() + " @ " + func.getEntryPoint());
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (Map.Entry<String, List<String>> entry : categorized.entrySet()) {
|
|
248
|
+
if (!entry.getValue().isEmpty()) {
|
|
249
|
+
writer.println("[" + entry.getKey().toUpperCase() + " related]");
|
|
250
|
+
for (String func : entry.getValue()) {
|
|
251
|
+
writer.println(" " + func);
|
|
252
|
+
}
|
|
253
|
+
writer.println("");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Find potential vulnerabilities (dangerous function calls)
|
|
258
|
+
writer.println("[POTENTIALLY DANGEROUS FUNCTIONS]");
|
|
259
|
+
String[] dangerous = {"strcpy", "sprintf", "gets", "scanf", "strcat", "system", "exec"};
|
|
260
|
+
for (String pattern : dangerous) {
|
|
261
|
+
SymbolIterator symbols = currentProgram.getSymbolTable().getSymbols(pattern);
|
|
262
|
+
while (symbols.hasNext()) {
|
|
263
|
+
Symbol sym = symbols.next();
|
|
264
|
+
writer.println(" " + sym.getName() + " @ " + sym.getAddress());
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private String escapeJson(String s) {
|
|
271
|
+
if (s == null) return "";
|
|
272
|
+
return s.replace("\\", "\\\\")
|
|
273
|
+
.replace("\"", "\\\"")
|
|
274
|
+
.replace("\n", "\\n")
|
|
275
|
+
.replace("\r", "\\r")
|
|
276
|
+
.replace("\t", "\\t");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/* ###
|
|
2
|
+
* Export function call graph
|
|
3
|
+
* @category Export
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import ghidra.app.script.GhidraScript;
|
|
7
|
+
import ghidra.program.model.listing.Function;
|
|
8
|
+
import ghidra.program.model.listing.FunctionIterator;
|
|
9
|
+
import ghidra.program.model.symbol.Reference;
|
|
10
|
+
import ghidra.program.model.symbol.ReferenceIterator;
|
|
11
|
+
|
|
12
|
+
import java.io.File;
|
|
13
|
+
import java.io.FileWriter;
|
|
14
|
+
import java.io.PrintWriter;
|
|
15
|
+
import java.util.*;
|
|
16
|
+
|
|
17
|
+
public class ExportCalls extends GhidraScript {
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
public void run() throws Exception {
|
|
21
|
+
String outputDir = System.getenv("GHIDRA_OUTPUT_DIR");
|
|
22
|
+
if (outputDir == null || outputDir.isEmpty()) {
|
|
23
|
+
outputDir = ".";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
String programName = currentProgram.getName().replaceAll("[^a-zA-Z0-9._-]", "_");
|
|
27
|
+
File outputFile = new File(outputDir, programName + "_calls.json");
|
|
28
|
+
|
|
29
|
+
println("Exporting call graph to: " + outputFile.getAbsolutePath());
|
|
30
|
+
|
|
31
|
+
// Build call graph
|
|
32
|
+
Map<String, Set<String>> callGraph = new LinkedHashMap<>();
|
|
33
|
+
Map<String, String> functionAddresses = new LinkedHashMap<>();
|
|
34
|
+
|
|
35
|
+
FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
|
|
36
|
+
|
|
37
|
+
while (functions.hasNext() && !monitor.isCancelled()) {
|
|
38
|
+
Function func = functions.next();
|
|
39
|
+
String funcName = func.getName();
|
|
40
|
+
functionAddresses.put(funcName, func.getEntryPoint().toString());
|
|
41
|
+
|
|
42
|
+
Set<String> calls = new TreeSet<>();
|
|
43
|
+
Set<Function> calledFunctions = func.getCalledFunctions(monitor);
|
|
44
|
+
|
|
45
|
+
for (Function called : calledFunctions) {
|
|
46
|
+
calls.add(called.getName());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
callGraph.put(funcName, calls);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Write output
|
|
53
|
+
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
|
54
|
+
writer.println("{");
|
|
55
|
+
writer.println(" \"program\": \"" + escapeJson(currentProgram.getName()) + "\",");
|
|
56
|
+
writer.println(" \"totalFunctions\": " + callGraph.size() + ",");
|
|
57
|
+
writer.println(" \"callGraph\": {");
|
|
58
|
+
|
|
59
|
+
boolean firstFunc = true;
|
|
60
|
+
for (Map.Entry<String, Set<String>> entry : callGraph.entrySet()) {
|
|
61
|
+
if (!firstFunc) {
|
|
62
|
+
writer.println(",");
|
|
63
|
+
}
|
|
64
|
+
firstFunc = false;
|
|
65
|
+
|
|
66
|
+
String funcName = entry.getKey();
|
|
67
|
+
Set<String> calls = entry.getValue();
|
|
68
|
+
|
|
69
|
+
writer.print(" \"" + escapeJson(funcName) + "\": {");
|
|
70
|
+
writer.print("\"address\": \"" + functionAddresses.get(funcName) + "\", ");
|
|
71
|
+
writer.print("\"calls\": [");
|
|
72
|
+
|
|
73
|
+
boolean firstCall = true;
|
|
74
|
+
for (String call : calls) {
|
|
75
|
+
if (!firstCall) {
|
|
76
|
+
writer.print(", ");
|
|
77
|
+
}
|
|
78
|
+
firstCall = false;
|
|
79
|
+
writer.print("\"" + escapeJson(call) + "\"");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
writer.print("]}");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
writer.println();
|
|
86
|
+
writer.println(" },");
|
|
87
|
+
|
|
88
|
+
// Also export interesting functions (potential entry points, etc.)
|
|
89
|
+
writer.println(" \"interestingFunctions\": {");
|
|
90
|
+
|
|
91
|
+
// Find functions with no callers (potential entry points)
|
|
92
|
+
Set<String> noCaller = new TreeSet<>();
|
|
93
|
+
Set<String> allCalled = new TreeSet<>();
|
|
94
|
+
for (Set<String> calls : callGraph.values()) {
|
|
95
|
+
allCalled.addAll(calls);
|
|
96
|
+
}
|
|
97
|
+
for (String func : callGraph.keySet()) {
|
|
98
|
+
if (!allCalled.contains(func)) {
|
|
99
|
+
noCaller.add(func);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
writer.print(" \"potentialEntryPoints\": [");
|
|
104
|
+
boolean first = true;
|
|
105
|
+
for (String func : noCaller) {
|
|
106
|
+
if (!first) writer.print(", ");
|
|
107
|
+
first = false;
|
|
108
|
+
writer.print("\"" + escapeJson(func) + "\"");
|
|
109
|
+
}
|
|
110
|
+
writer.println("],");
|
|
111
|
+
|
|
112
|
+
// Find functions with many callers (commonly used)
|
|
113
|
+
Map<String, Integer> callerCount = new HashMap<>();
|
|
114
|
+
for (Set<String> calls : callGraph.values()) {
|
|
115
|
+
for (String call : calls) {
|
|
116
|
+
callerCount.merge(call, 1, Integer::sum);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
List<Map.Entry<String, Integer>> sorted = new ArrayList<>(callerCount.entrySet());
|
|
121
|
+
sorted.sort((a, b) -> b.getValue().compareTo(a.getValue()));
|
|
122
|
+
|
|
123
|
+
writer.print(" \"mostCalled\": [");
|
|
124
|
+
first = true;
|
|
125
|
+
for (int i = 0; i < Math.min(20, sorted.size()); i++) {
|
|
126
|
+
if (!first) writer.print(", ");
|
|
127
|
+
first = false;
|
|
128
|
+
Map.Entry<String, Integer> e = sorted.get(i);
|
|
129
|
+
writer.print("{\"name\": \"" + escapeJson(e.getKey()) + "\", \"count\": " + e.getValue() + "}");
|
|
130
|
+
}
|
|
131
|
+
writer.println("]");
|
|
132
|
+
|
|
133
|
+
writer.println(" }");
|
|
134
|
+
writer.println("}");
|
|
135
|
+
|
|
136
|
+
println("Exported call graph with " + callGraph.size() + " functions");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private String escapeJson(String s) {
|
|
141
|
+
if (s == null) return "";
|
|
142
|
+
return s.replace("\\", "\\\\")
|
|
143
|
+
.replace("\"", "\\\"")
|
|
144
|
+
.replace("\n", "\\n")
|
|
145
|
+
.replace("\r", "\\r")
|
|
146
|
+
.replace("\t", "\\t");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* ###
|
|
2
|
+
* Export decompiled C code for all functions
|
|
3
|
+
* @category Export
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import ghidra.app.decompiler.DecompInterface;
|
|
7
|
+
import ghidra.app.decompiler.DecompileOptions;
|
|
8
|
+
import ghidra.app.decompiler.DecompileResults;
|
|
9
|
+
import ghidra.app.script.GhidraScript;
|
|
10
|
+
import ghidra.program.model.listing.Function;
|
|
11
|
+
import ghidra.program.model.listing.FunctionIterator;
|
|
12
|
+
|
|
13
|
+
import java.io.File;
|
|
14
|
+
import java.io.FileWriter;
|
|
15
|
+
import java.io.PrintWriter;
|
|
16
|
+
|
|
17
|
+
public class ExportDecompiled extends GhidraScript {
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
public void run() throws Exception {
|
|
21
|
+
String outputDir = System.getenv("GHIDRA_OUTPUT_DIR");
|
|
22
|
+
if (outputDir == null || outputDir.isEmpty()) {
|
|
23
|
+
outputDir = ".";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
String programName = currentProgram.getName().replaceAll("[^a-zA-Z0-9._-]", "_");
|
|
27
|
+
File outputFile = new File(outputDir, programName + "_decompiled.c");
|
|
28
|
+
|
|
29
|
+
println("Decompiling all functions to: " + outputFile.getAbsolutePath());
|
|
30
|
+
|
|
31
|
+
DecompInterface decompiler = new DecompInterface();
|
|
32
|
+
DecompileOptions options = new DecompileOptions();
|
|
33
|
+
decompiler.setOptions(options);
|
|
34
|
+
|
|
35
|
+
if (!decompiler.openProgram(currentProgram)) {
|
|
36
|
+
printerr("Failed to initialize decompiler: " + decompiler.getLastMessage());
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
|
41
|
+
// Write header
|
|
42
|
+
writer.println("/*");
|
|
43
|
+
writer.println(" * Decompiled from: " + currentProgram.getName());
|
|
44
|
+
writer.println(" * Architecture: " + currentProgram.getLanguage().getProcessor());
|
|
45
|
+
writer.println(" * Compiler: " + currentProgram.getCompilerSpec().getCompilerSpecID());
|
|
46
|
+
writer.println(" */");
|
|
47
|
+
writer.println();
|
|
48
|
+
|
|
49
|
+
FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
|
|
50
|
+
int count = 0;
|
|
51
|
+
int failed = 0;
|
|
52
|
+
|
|
53
|
+
while (functions.hasNext() && !monitor.isCancelled()) {
|
|
54
|
+
Function func = functions.next();
|
|
55
|
+
|
|
56
|
+
// Skip external/thunk functions
|
|
57
|
+
if (func.isExternal() || func.isThunk()) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
monitor.setMessage("Decompiling: " + func.getName());
|
|
62
|
+
|
|
63
|
+
DecompileResults results = decompiler.decompileFunction(func, 30, monitor);
|
|
64
|
+
|
|
65
|
+
if (results.decompileCompleted()) {
|
|
66
|
+
String decompiledCode = results.getDecompiledFunction().getC();
|
|
67
|
+
writer.println("/* Function: " + func.getName() + " @ " + func.getEntryPoint() + " */");
|
|
68
|
+
writer.println(decompiledCode);
|
|
69
|
+
writer.println();
|
|
70
|
+
count++;
|
|
71
|
+
} else {
|
|
72
|
+
writer.println("/* FAILED TO DECOMPILE: " + func.getName() + " @ " + func.getEntryPoint() + " */");
|
|
73
|
+
writer.println("/* Error: " + results.getErrorMessage() + " */");
|
|
74
|
+
writer.println();
|
|
75
|
+
failed++;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
println("Decompiled " + count + " functions (" + failed + " failed)");
|
|
80
|
+
} finally {
|
|
81
|
+
decompiler.dispose();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* ###
|
|
2
|
+
* Export function list with addresses, signatures, and metadata as JSON
|
|
3
|
+
* @category Export
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import ghidra.app.script.GhidraScript;
|
|
7
|
+
import ghidra.program.model.listing.Function;
|
|
8
|
+
import ghidra.program.model.listing.FunctionIterator;
|
|
9
|
+
import ghidra.program.model.listing.Parameter;
|
|
10
|
+
import ghidra.program.model.symbol.SourceType;
|
|
11
|
+
|
|
12
|
+
import java.io.File;
|
|
13
|
+
import java.io.FileWriter;
|
|
14
|
+
import java.io.PrintWriter;
|
|
15
|
+
|
|
16
|
+
public class ExportFunctions extends GhidraScript {
|
|
17
|
+
|
|
18
|
+
@Override
|
|
19
|
+
public void run() throws Exception {
|
|
20
|
+
String outputDir = System.getenv("GHIDRA_OUTPUT_DIR");
|
|
21
|
+
if (outputDir == null || outputDir.isEmpty()) {
|
|
22
|
+
outputDir = ".";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
String programName = currentProgram.getName().replaceAll("[^a-zA-Z0-9._-]", "_");
|
|
26
|
+
File outputFile = new File(outputDir, programName + "_functions.json");
|
|
27
|
+
|
|
28
|
+
println("Exporting functions to: " + outputFile.getAbsolutePath());
|
|
29
|
+
|
|
30
|
+
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
|
31
|
+
writer.println("{");
|
|
32
|
+
writer.println(" \"program\": \"" + escapeJson(currentProgram.getName()) + "\",");
|
|
33
|
+
writer.println(" \"architecture\": \"" + currentProgram.getLanguage().getProcessor() + "\",");
|
|
34
|
+
writer.println(" \"functions\": [");
|
|
35
|
+
|
|
36
|
+
FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
|
|
37
|
+
boolean first = true;
|
|
38
|
+
int count = 0;
|
|
39
|
+
|
|
40
|
+
while (functions.hasNext() && !monitor.isCancelled()) {
|
|
41
|
+
Function func = functions.next();
|
|
42
|
+
|
|
43
|
+
if (!first) {
|
|
44
|
+
writer.println(",");
|
|
45
|
+
}
|
|
46
|
+
first = false;
|
|
47
|
+
|
|
48
|
+
writer.println(" {");
|
|
49
|
+
writer.println(" \"name\": \"" + escapeJson(func.getName()) + "\",");
|
|
50
|
+
writer.println(" \"address\": \"" + func.getEntryPoint() + "\",");
|
|
51
|
+
writer.println(" \"size\": " + func.getBody().getNumAddresses() + ",");
|
|
52
|
+
writer.println(" \"signature\": \"" + escapeJson(func.getPrototypeString(false, false)) + "\",");
|
|
53
|
+
writer.println(" \"returnType\": \"" + escapeJson(func.getReturnType().getDisplayName()) + "\",");
|
|
54
|
+
writer.println(" \"callingConvention\": \"" + escapeJson(func.getCallingConventionName()) + "\",");
|
|
55
|
+
writer.println(" \"isExternal\": " + func.isExternal() + ",");
|
|
56
|
+
writer.println(" \"isThunk\": " + func.isThunk() + ",");
|
|
57
|
+
writer.println(" \"hasVarArgs\": " + func.hasVarArgs() + ",");
|
|
58
|
+
writer.println(" \"sourceType\": \"" + func.getSymbol().getSource() + "\",");
|
|
59
|
+
|
|
60
|
+
// Parameters
|
|
61
|
+
writer.print(" \"parameters\": [");
|
|
62
|
+
Parameter[] params = func.getParameters();
|
|
63
|
+
for (int i = 0; i < params.length; i++) {
|
|
64
|
+
if (i > 0) writer.print(", ");
|
|
65
|
+
writer.print("{\"name\": \"" + escapeJson(params[i].getName()) + "\", ");
|
|
66
|
+
writer.print("\"type\": \"" + escapeJson(params[i].getDataType().getDisplayName()) + "\"}");
|
|
67
|
+
}
|
|
68
|
+
writer.println("],");
|
|
69
|
+
|
|
70
|
+
// Called functions
|
|
71
|
+
writer.print(" \"calls\": [");
|
|
72
|
+
java.util.Set<Function> called = func.getCalledFunctions(monitor);
|
|
73
|
+
int callIdx = 0;
|
|
74
|
+
for (Function calledFunc : called) {
|
|
75
|
+
if (callIdx >= 50) break; // Limit to 50 calls
|
|
76
|
+
if (callIdx > 0) writer.print(", ");
|
|
77
|
+
writer.print("\"" + escapeJson(calledFunc.getName()) + "\"");
|
|
78
|
+
callIdx++;
|
|
79
|
+
}
|
|
80
|
+
writer.println("],");
|
|
81
|
+
|
|
82
|
+
// Calling functions
|
|
83
|
+
writer.print(" \"calledBy\": [");
|
|
84
|
+
java.util.Set<Function> callers = func.getCallingFunctions(monitor);
|
|
85
|
+
int callerIdx = 0;
|
|
86
|
+
for (Function caller : callers) {
|
|
87
|
+
if (callerIdx >= 50) break; // Limit to 50 callers
|
|
88
|
+
if (callerIdx > 0) writer.print(", ");
|
|
89
|
+
writer.print("\"" + escapeJson(caller.getName()) + "\"");
|
|
90
|
+
callerIdx++;
|
|
91
|
+
}
|
|
92
|
+
writer.println("]");
|
|
93
|
+
|
|
94
|
+
writer.print(" }");
|
|
95
|
+
count++;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
writer.println();
|
|
99
|
+
writer.println(" ]");
|
|
100
|
+
writer.println("}");
|
|
101
|
+
|
|
102
|
+
println("Exported " + count + " functions");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private String escapeJson(String s) {
|
|
107
|
+
if (s == null) return "";
|
|
108
|
+
return s.replace("\\", "\\\\")
|
|
109
|
+
.replace("\"", "\\\"")
|
|
110
|
+
.replace("\n", "\\n")
|
|
111
|
+
.replace("\r", "\\r")
|
|
112
|
+
.replace("\t", "\\t");
|
|
113
|
+
}
|
|
114
|
+
}
|