decompile 0.1.0__tar.gz
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.
- decompile-0.1.0/Dockerfile +75 -0
- decompile-0.1.0/DumpAllDecompile.java +309 -0
- decompile-0.1.0/MANIFEST.in +9 -0
- decompile-0.1.0/PKG-INFO +195 -0
- decompile-0.1.0/README.md +176 -0
- decompile-0.1.0/decompile.egg-info/PKG-INFO +195 -0
- decompile-0.1.0/decompile.egg-info/SOURCES.txt +20 -0
- decompile-0.1.0/decompile.egg-info/dependency_links.txt +1 -0
- decompile-0.1.0/decompile.egg-info/entry_points.txt +2 -0
- decompile-0.1.0/decompile.egg-info/top_level.txt +1 -0
- decompile-0.1.0/decompile_tool/DumpAllDecompile.java +309 -0
- decompile-0.1.0/decompile_tool/__init__.py +3 -0
- decompile-0.1.0/decompile_tool/cli.py +824 -0
- decompile-0.1.0/decompile_tool/enhance_with_copilot +184 -0
- decompile-0.1.0/enhance_with_copilot +184 -0
- decompile-0.1.0/packaging/README.md +86 -0
- decompile-0.1.0/packaging/aur/.SRCINFO +14 -0
- decompile-0.1.0/packaging/aur/PKGBUILD +27 -0
- decompile-0.1.0/packaging/deb/build-deb.sh +38 -0
- decompile-0.1.0/packaging/deb/control +12 -0
- decompile-0.1.0/pyproject.toml +49 -0
- decompile-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
FROM kalilinux/kali-rolling
|
|
2
|
+
|
|
3
|
+
ENV DEBIAN_FRONTEND=noninteractive
|
|
4
|
+
ENV DOTNET_ROOT=/opt/dotnet
|
|
5
|
+
ENV PATH="/opt/dotnet:/usr/local/bin:${PATH}"
|
|
6
|
+
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|
7
|
+
ENV DOTNET_NOLOGO=1
|
|
8
|
+
|
|
9
|
+
ARG DOTNET_CHANNEL=10.0
|
|
10
|
+
ARG ILSPYCMD_VERSION=10.0.1.8346
|
|
11
|
+
|
|
12
|
+
RUN printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\n' \
|
|
13
|
+
> /etc/apt/apt.conf.d/80-retries
|
|
14
|
+
|
|
15
|
+
RUN printf 'deb http://kali.download/kali kali-rolling main contrib non-free non-free-firmware\n' \
|
|
16
|
+
> /etc/apt/sources.list
|
|
17
|
+
|
|
18
|
+
RUN apt-get clean && \
|
|
19
|
+
rm -rf /var/lib/apt/lists/* && \
|
|
20
|
+
apt-get update --fix-missing && \
|
|
21
|
+
apt-get install -y --fix-missing --no-install-recommends \
|
|
22
|
+
ghidra \
|
|
23
|
+
openjdk-21-jre-headless \
|
|
24
|
+
python3 \
|
|
25
|
+
jq \
|
|
26
|
+
jadx \
|
|
27
|
+
apktool \
|
|
28
|
+
file \
|
|
29
|
+
binutils \
|
|
30
|
+
gcc \
|
|
31
|
+
libc6-dev \
|
|
32
|
+
coreutils \
|
|
33
|
+
curl \
|
|
34
|
+
unzip \
|
|
35
|
+
zip \
|
|
36
|
+
ca-certificates && \
|
|
37
|
+
rm -rf /var/lib/apt/lists/*
|
|
38
|
+
|
|
39
|
+
RUN install -d -m 0755 /etc/apt/keyrings && \
|
|
40
|
+
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
41
|
+
-o /etc/apt/keyrings/githubcli-archive-keyring.gpg && \
|
|
42
|
+
chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg && \
|
|
43
|
+
printf 'deb [arch=%s signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\n' \
|
|
44
|
+
"$(dpkg --print-architecture)" > /etc/apt/sources.list.d/github-cli.list && \
|
|
45
|
+
apt-get update --fix-missing && \
|
|
46
|
+
apt-get install -y --fix-missing --no-install-recommends gh && \
|
|
47
|
+
rm -rf /var/lib/apt/lists/*
|
|
48
|
+
|
|
49
|
+
RUN apt-get update --fix-missing && \
|
|
50
|
+
apt-get install -y --fix-missing --no-install-recommends libicu-dev && \
|
|
51
|
+
rm -rf /var/lib/apt/lists/*
|
|
52
|
+
|
|
53
|
+
RUN curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh && \
|
|
54
|
+
chmod +x /tmp/dotnet-install.sh && \
|
|
55
|
+
/tmp/dotnet-install.sh --channel "$DOTNET_CHANNEL" --install-dir "$DOTNET_ROOT" && \
|
|
56
|
+
rm -f /tmp/dotnet-install.sh && \
|
|
57
|
+
DOTNET_CLI_HOME=/tmp/dotnet-home dotnet tool install --tool-path /usr/local/bin ilspycmd --version "$ILSPYCMD_VERSION" && \
|
|
58
|
+
rm -rf /tmp/dotnet-home
|
|
59
|
+
|
|
60
|
+
RUN mkdir -p /usr/local/share/decompile /tmp/decompile-home/.config && \
|
|
61
|
+
chmod 0777 /tmp/decompile-home /tmp/decompile-home/.config
|
|
62
|
+
|
|
63
|
+
COPY DumpAllDecompile.java /usr/local/share/decompile/DumpAllDecompile.java
|
|
64
|
+
COPY enhance_with_copilot /usr/local/bin/enhance_with_copilot
|
|
65
|
+
COPY decompile_tool /usr/local/bin/decompile_tool
|
|
66
|
+
COPY decompile /usr/local/bin/decompile
|
|
67
|
+
|
|
68
|
+
RUN chmod +x /usr/local/bin/decompile && \
|
|
69
|
+
chmod +x /usr/local/bin/enhance_with_copilot && \
|
|
70
|
+
chmod +x /usr/local/bin/decompile_tool/cli.py && \
|
|
71
|
+
chmod +x /usr/local/bin/decompile_tool/enhance_with_copilot
|
|
72
|
+
|
|
73
|
+
WORKDIR /reverse
|
|
74
|
+
|
|
75
|
+
ENTRYPOINT ["decompile"]
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import ghidra.app.decompiler.DecompInterface;
|
|
2
|
+
import ghidra.app.decompiler.DecompileOptions;
|
|
3
|
+
import ghidra.app.decompiler.DecompileResults;
|
|
4
|
+
import ghidra.app.script.GhidraScript;
|
|
5
|
+
import ghidra.program.model.listing.Function;
|
|
6
|
+
import ghidra.program.model.listing.FunctionIterator;
|
|
7
|
+
import ghidra.program.model.listing.FunctionManager;
|
|
8
|
+
import ghidra.program.model.listing.Instruction;
|
|
9
|
+
import ghidra.program.model.listing.InstructionIterator;
|
|
10
|
+
import ghidra.program.model.listing.Listing;
|
|
11
|
+
import ghidra.program.model.mem.Memory;
|
|
12
|
+
import ghidra.program.model.mem.MemoryBlock;
|
|
13
|
+
|
|
14
|
+
import java.io.BufferedWriter;
|
|
15
|
+
import java.io.File;
|
|
16
|
+
import java.io.FileWriter;
|
|
17
|
+
import java.io.IOException;
|
|
18
|
+
|
|
19
|
+
public class DumpAllDecompile extends GhidraScript {
|
|
20
|
+
private Listing listing;
|
|
21
|
+
private Memory memory;
|
|
22
|
+
|
|
23
|
+
@Override
|
|
24
|
+
public void run() throws Exception {
|
|
25
|
+
String[] args = getScriptArgs();
|
|
26
|
+
if (args.length < 1) {
|
|
27
|
+
throw new IllegalArgumentException("Usage: DumpAllDecompile.java <output-dir> [base-name] [timeout-seconds]");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
String outDir = args[0];
|
|
31
|
+
String baseName = args.length > 1 ? args[1] : currentProgram.getName();
|
|
32
|
+
int timeoutSeconds = parseTimeout(args);
|
|
33
|
+
|
|
34
|
+
listing = currentProgram.getListing();
|
|
35
|
+
memory = currentProgram.getMemory();
|
|
36
|
+
|
|
37
|
+
File outputDirectory = new File(outDir);
|
|
38
|
+
if (!outputDirectory.exists() && !outputDirectory.mkdirs()) {
|
|
39
|
+
throw new IOException("Could not create output directory: " + outDir);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
File pseudocodeFile = new File(outputDirectory, baseName + ".pseudocode.c");
|
|
43
|
+
File disassemblyFile = new File(outputDirectory, baseName + ".disassembly.asm");
|
|
44
|
+
File summaryFile = new File(outputDirectory, baseName + ".summary.txt");
|
|
45
|
+
|
|
46
|
+
DecompInterface decompiler = new DecompInterface();
|
|
47
|
+
DecompileOptions options = new DecompileOptions();
|
|
48
|
+
decompiler.setOptions(options);
|
|
49
|
+
decompiler.toggleCCode(true);
|
|
50
|
+
decompiler.toggleSyntaxTree(true);
|
|
51
|
+
decompiler.openProgram(currentProgram);
|
|
52
|
+
|
|
53
|
+
int extractedFunctions = 0;
|
|
54
|
+
int skippedExternalFunctions = 0;
|
|
55
|
+
int decompiledFunctions = 0;
|
|
56
|
+
int failedDecompiles = 0;
|
|
57
|
+
int disassembledInstructions = 0;
|
|
58
|
+
|
|
59
|
+
try (
|
|
60
|
+
BufferedWriter pseudoWriter = new BufferedWriter(new FileWriter(pseudocodeFile, false));
|
|
61
|
+
BufferedWriter asmWriter = new BufferedWriter(new FileWriter(disassemblyFile, false));
|
|
62
|
+
BufferedWriter summaryWriter = new BufferedWriter(new FileWriter(summaryFile, false))
|
|
63
|
+
) {
|
|
64
|
+
writeHeader(pseudoWriter, "GHIDRA PSEUDOCODE OUTPUT");
|
|
65
|
+
writeHeader(asmWriter, "GHIDRA DISASSEMBLY OUTPUT");
|
|
66
|
+
writeHeader(summaryWriter, "GHIDRA EXTRACTION SUMMARY");
|
|
67
|
+
|
|
68
|
+
FunctionManager functionManager = currentProgram.getFunctionManager();
|
|
69
|
+
FunctionIterator functions = functionManager.getFunctions(true);
|
|
70
|
+
while (functions.hasNext() && !monitor.isCancelled()) {
|
|
71
|
+
Function function = functions.next();
|
|
72
|
+
if (!shouldExtract(function)) {
|
|
73
|
+
skippedExternalFunctions++;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
extractedFunctions++;
|
|
78
|
+
|
|
79
|
+
writeFunctionHeader(pseudoWriter, function);
|
|
80
|
+
writeFunctionHeader(asmWriter, function);
|
|
81
|
+
|
|
82
|
+
DecompileStatus status = decompileFunction(decompiler, function, timeoutSeconds, pseudoWriter);
|
|
83
|
+
if (status == DecompileStatus.SUCCESS) {
|
|
84
|
+
decompiledFunctions++;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
failedDecompiles++;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
disassembledInstructions += writeDisassembly(function, asmWriter);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
writeTotals(pseudoWriter, extractedFunctions);
|
|
94
|
+
writeTotals(asmWriter, extractedFunctions);
|
|
95
|
+
writeSummary(
|
|
96
|
+
summaryWriter,
|
|
97
|
+
pseudocodeFile,
|
|
98
|
+
disassemblyFile,
|
|
99
|
+
extractedFunctions,
|
|
100
|
+
skippedExternalFunctions,
|
|
101
|
+
decompiledFunctions,
|
|
102
|
+
failedDecompiles,
|
|
103
|
+
disassembledInstructions
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
decompiler.dispose();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
println("[+] Pseudocode saved to: " + pseudocodeFile.getAbsolutePath());
|
|
111
|
+
println("[+] Disassembly saved to: " + disassemblyFile.getAbsolutePath());
|
|
112
|
+
println("[+] Summary saved to: " + summaryFile.getAbsolutePath());
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private int parseTimeout(String[] args) {
|
|
116
|
+
if (args.length < 3) {
|
|
117
|
+
return 120;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
return Integer.parseInt(args[2]);
|
|
121
|
+
}
|
|
122
|
+
catch (NumberFormatException ignored) {
|
|
123
|
+
return 120;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private void writeHeader(BufferedWriter writer, String title) throws IOException {
|
|
128
|
+
writer.write(title);
|
|
129
|
+
writer.newLine();
|
|
130
|
+
writer.write(repeat("=", 100));
|
|
131
|
+
writer.newLine();
|
|
132
|
+
writer.write("Program : " + currentProgram.getName());
|
|
133
|
+
writer.newLine();
|
|
134
|
+
writer.write("Language : " + currentProgram.getLanguageID());
|
|
135
|
+
writer.newLine();
|
|
136
|
+
writer.write("Compiler : " + currentProgram.getCompilerSpec().getCompilerSpecID());
|
|
137
|
+
writer.newLine();
|
|
138
|
+
writer.write("ImageBase: " + currentProgram.getImageBase());
|
|
139
|
+
writer.newLine();
|
|
140
|
+
writer.write(repeat("=", 100));
|
|
141
|
+
writer.newLine();
|
|
142
|
+
writer.newLine();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private void writeFunctionHeader(BufferedWriter writer, Function function) throws IOException {
|
|
146
|
+
writer.newLine();
|
|
147
|
+
writer.write(repeat("=", 100));
|
|
148
|
+
writer.newLine();
|
|
149
|
+
writer.write("FUNCTION: " + function.getName());
|
|
150
|
+
writer.newLine();
|
|
151
|
+
writer.write("ADDRESS : " + function.getEntryPoint());
|
|
152
|
+
writer.newLine();
|
|
153
|
+
writer.write(repeat("=", 100));
|
|
154
|
+
writer.newLine();
|
|
155
|
+
writer.newLine();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private DecompileStatus decompileFunction(
|
|
159
|
+
DecompInterface decompiler,
|
|
160
|
+
Function function,
|
|
161
|
+
int timeoutSeconds,
|
|
162
|
+
BufferedWriter writer
|
|
163
|
+
) throws IOException {
|
|
164
|
+
try {
|
|
165
|
+
DecompileResults results = decompiler.decompileFunction(function, timeoutSeconds, monitor);
|
|
166
|
+
if (results != null && results.decompileCompleted() && results.getDecompiledFunction() != null) {
|
|
167
|
+
writer.write(results.getDecompiledFunction().getC());
|
|
168
|
+
writer.newLine();
|
|
169
|
+
return DecompileStatus.SUCCESS;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
String reason = results != null ? results.getErrorMessage() : "unknown error";
|
|
173
|
+
writer.write("[!] Decompile failed: " + reason);
|
|
174
|
+
writer.newLine();
|
|
175
|
+
return DecompileStatus.FAILED;
|
|
176
|
+
}
|
|
177
|
+
catch (Exception e) {
|
|
178
|
+
writer.write("[!] Error while decompiling: " + e.getMessage());
|
|
179
|
+
writer.newLine();
|
|
180
|
+
return DecompileStatus.FAILED;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private boolean shouldExtract(Function function) {
|
|
185
|
+
if (function.isExternal()) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
MemoryBlock block = memory.getBlock(function.getEntryPoint());
|
|
190
|
+
if (block == null) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return !"EXTERNAL".equalsIgnoreCase(block.getName());
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private int writeDisassembly(Function function, BufferedWriter writer) throws IOException {
|
|
198
|
+
int instructionCount = 0;
|
|
199
|
+
try {
|
|
200
|
+
InstructionIterator instructions = listing.getInstructions(function.getBody(), true);
|
|
201
|
+
while (instructions.hasNext() && !monitor.isCancelled()) {
|
|
202
|
+
writer.write(formatInstruction(instructions.next()));
|
|
203
|
+
writer.newLine();
|
|
204
|
+
instructionCount++;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (instructionCount == 0) {
|
|
208
|
+
writer.write("[!] No instructions found for function body");
|
|
209
|
+
writer.newLine();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (Exception e) {
|
|
213
|
+
writer.write("[!] Error while disassembling: " + e.getMessage());
|
|
214
|
+
writer.newLine();
|
|
215
|
+
}
|
|
216
|
+
return instructionCount;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private String formatInstruction(Instruction instruction) {
|
|
220
|
+
String address = instruction.getAddress().toString();
|
|
221
|
+
String bytes = instructionBytes(instruction);
|
|
222
|
+
String text = instruction.toString();
|
|
223
|
+
if (bytes.length() > 0) {
|
|
224
|
+
return String.format("%-18s %-24s %s", address, bytes, text);
|
|
225
|
+
}
|
|
226
|
+
return String.format("%-18s %s", address, text);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private String instructionBytes(Instruction instruction) {
|
|
230
|
+
try {
|
|
231
|
+
byte[] bytes = instruction.getBytes();
|
|
232
|
+
StringBuilder builder = new StringBuilder();
|
|
233
|
+
for (int i = 0; i < bytes.length; i++) {
|
|
234
|
+
if (i > 0) {
|
|
235
|
+
builder.append(' ');
|
|
236
|
+
}
|
|
237
|
+
builder.append(String.format("%02x", bytes[i] & 0xff));
|
|
238
|
+
}
|
|
239
|
+
return builder.toString();
|
|
240
|
+
}
|
|
241
|
+
catch (Exception ignored) {
|
|
242
|
+
return "";
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private void writeTotals(BufferedWriter writer, int extractedFunctions) throws IOException {
|
|
247
|
+
writer.newLine();
|
|
248
|
+
writer.write(repeat("=", 100));
|
|
249
|
+
writer.newLine();
|
|
250
|
+
writer.write("EXTRACTED FUNCTIONS: " + extractedFunctions);
|
|
251
|
+
writer.newLine();
|
|
252
|
+
writer.write(repeat("=", 100));
|
|
253
|
+
writer.newLine();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private void writeSummary(
|
|
257
|
+
BufferedWriter writer,
|
|
258
|
+
File pseudocodeFile,
|
|
259
|
+
File disassemblyFile,
|
|
260
|
+
int extractedFunctions,
|
|
261
|
+
int skippedExternalFunctions,
|
|
262
|
+
int decompiledFunctions,
|
|
263
|
+
int failedDecompiles,
|
|
264
|
+
int disassembledInstructions
|
|
265
|
+
) throws IOException {
|
|
266
|
+
writer.write("Pseudocode file : " + pseudocodeFile.getAbsolutePath());
|
|
267
|
+
writer.newLine();
|
|
268
|
+
writer.write("Disassembly file : " + disassemblyFile.getAbsolutePath());
|
|
269
|
+
writer.newLine();
|
|
270
|
+
writer.write("Extracted functions : " + extractedFunctions);
|
|
271
|
+
writer.newLine();
|
|
272
|
+
writer.write("Skipped external funcs : " + skippedExternalFunctions);
|
|
273
|
+
writer.newLine();
|
|
274
|
+
writer.write("Decompiled functions : " + decompiledFunctions);
|
|
275
|
+
writer.newLine();
|
|
276
|
+
writer.write("Failed decompiles : " + failedDecompiles);
|
|
277
|
+
writer.newLine();
|
|
278
|
+
writer.write("Disassembled instructions: " + disassembledInstructions);
|
|
279
|
+
writer.newLine();
|
|
280
|
+
writer.write("Memory blocks");
|
|
281
|
+
writer.newLine();
|
|
282
|
+
|
|
283
|
+
for (MemoryBlock block : memory.getBlocks()) {
|
|
284
|
+
writer.write(String.format(
|
|
285
|
+
" %s %s-%s size=%d execute=%s write=%s",
|
|
286
|
+
block.getName(),
|
|
287
|
+
block.getStart(),
|
|
288
|
+
block.getEnd(),
|
|
289
|
+
block.getSize(),
|
|
290
|
+
block.isExecute(),
|
|
291
|
+
block.isWrite()
|
|
292
|
+
));
|
|
293
|
+
writer.newLine();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
private String repeat(String value, int count) {
|
|
298
|
+
StringBuilder builder = new StringBuilder();
|
|
299
|
+
for (int i = 0; i < count; i++) {
|
|
300
|
+
builder.append(value);
|
|
301
|
+
}
|
|
302
|
+
return builder.toString();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private enum DecompileStatus {
|
|
306
|
+
SUCCESS,
|
|
307
|
+
FAILED
|
|
308
|
+
}
|
|
309
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include Dockerfile
|
|
3
|
+
include DumpAllDecompile.java
|
|
4
|
+
include enhance_with_copilot
|
|
5
|
+
include decompile_tool/__init__.py
|
|
6
|
+
include decompile_tool/cli.py
|
|
7
|
+
include decompile_tool/DumpAllDecompile.java
|
|
8
|
+
include decompile_tool/enhance_with_copilot
|
|
9
|
+
recursive-include packaging *
|
decompile-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: decompile
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Docker-isolated static reverse engineering orchestrator
|
|
5
|
+
Author: Admin12121
|
|
6
|
+
Project-URL: Homepage, https://github.com/Admin12121/decompile
|
|
7
|
+
Project-URL: Repository, https://github.com/Admin12121/decompile
|
|
8
|
+
Project-URL: Issues, https://github.com/Admin12121/decompile/issues
|
|
9
|
+
Keywords: reverse-engineering,decompiler,ghidra,jadx,ilspy,docker,ctf
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Topic :: Security
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# decompile
|
|
21
|
+
|
|
22
|
+
`decompile` is a Docker-first static reverse-engineering CLI.
|
|
23
|
+
|
|
24
|
+
Install the small host command, run `decompile ./file`, and the heavy tools run inside the Docker image. The host does not need Ghidra, JADX, apktool, ILSpy, or binutils installed.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
pip install decompile
|
|
30
|
+
decompile --update
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Other package targets:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
yay -S decompile
|
|
37
|
+
sudo apt install ./decompile_0.1.0_all.deb
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Docker is required for the normal published workflow.
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
decompile ./crackme
|
|
46
|
+
decompile --no-ai ./crackme
|
|
47
|
+
decompile --image docker.io/admin12121/decompile:stable ./crackme
|
|
48
|
+
decompile --local ./crackme
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Default output goes to:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
./crackme.ghidra-out/
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
You can choose the output directory:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
decompile ./crackme ./out
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## What It Does
|
|
64
|
+
|
|
65
|
+
`decompile` detects the input format, chooses the matching static toolchain, and writes useful reverse-engineering output into one directory.
|
|
66
|
+
|
|
67
|
+
Supported routes:
|
|
68
|
+
|
|
69
|
+
| Input | Tooling | Output |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| ELF, PE, EXE, DLL, SYS, Mach-O | Ghidra headless, objdump, optional AI cleanup | ASM, pseudocode C, enhanced C, summary |
|
|
72
|
+
| APK, AAB, DEX | JADX, apktool | Java/Kotlin source, resources, summary |
|
|
73
|
+
| JAR, WAR, EAR, `.class` | JADX | Java source, summary |
|
|
74
|
+
| .NET EXE/DLL | ilspycmd | C# source, summary |
|
|
75
|
+
| IPA, `.app` bundle | IPA/app extraction plus native analysis | Native output and app metadata |
|
|
76
|
+
|
|
77
|
+
Native binary output:
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
<name>.disassembly.asm
|
|
81
|
+
<name>.pseudocode.c
|
|
82
|
+
<name>.enhanced.c
|
|
83
|
+
<name>.summary.txt
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Android, Java, and .NET output usually includes:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
source/
|
|
90
|
+
resources/
|
|
91
|
+
<name>.summary.txt
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Docker Model
|
|
95
|
+
|
|
96
|
+
Published installs use this image by default:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
docker.io/admin12121/decompile:stable
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The image is pulled only when it is missing locally. Normal runs reuse the local image and do not check the registry.
|
|
103
|
+
|
|
104
|
+
Update manually:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
decompile --update
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Use a custom image:
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
decompile --image ghcr.io/you/decompile:dev ./file
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Run host tools directly:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
decompile --local ./file
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Inside Docker:
|
|
123
|
+
|
|
124
|
+
- input is mounted read-only
|
|
125
|
+
- output is mounted read-write
|
|
126
|
+
- the container runs as your current UID/GID
|
|
127
|
+
- temporary projects and scratch files are removed
|
|
128
|
+
- `--no-ai` disables network access for the analysis container
|
|
129
|
+
|
|
130
|
+
## AI Enhancement
|
|
131
|
+
|
|
132
|
+
For native binaries, `enhanced.c` can be generated from pseudocode, disassembly, objdump context, and summary data.
|
|
133
|
+
|
|
134
|
+
Use this when you want cleaner function names, variables, and reconstructed C-like output:
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
decompile ./file
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Disable it for malware, private samples, offline work, or reproducible local-only output:
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
decompile --no-ai ./file
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
When AI is enabled, analysis context may be sent to GitHub Copilot through `gh`. Pass authentication with `GH_TOKEN`, `GITHUB_TOKEN`, or your local GitHub CLI config.
|
|
147
|
+
|
|
148
|
+
## Options
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
decompile <file-or-bundle> [output-dir]
|
|
152
|
+
decompile --no-ai <file-or-bundle> [output-dir]
|
|
153
|
+
decompile --update [--image <image>]
|
|
154
|
+
decompile --image <image> <file-or-bundle> [output-dir]
|
|
155
|
+
decompile --local <file-or-bundle> [output-dir]
|
|
156
|
+
decompile --type <native|apk|aab|dex|jar|class|dotnet|ipa|app-bundle> <file> [output-dir]
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Useful environment variables:
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
DECOMPILE_DOCKER_IMAGE override the Docker image
|
|
163
|
+
DECOMPILE_USE_DOCKER=0 run local host tools
|
|
164
|
+
DECOMPILE_NO_AI=1 skip AI enhancement
|
|
165
|
+
DECOMPILE_KEEP_DEBUG=1 keep objdump and prompt/debug files
|
|
166
|
+
GHIDRA_TIMEOUT=120 per-function decompile timeout
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Limits
|
|
170
|
+
|
|
171
|
+
This is static analysis only. It does not run the target, debug it, emulate it, unpack it, or bypass runtime protections.
|
|
172
|
+
|
|
173
|
+
Packed binaries, heavy obfuscation, anti-disassembly tricks, encrypted IPA files, and protected mobile apps can still produce weak or incomplete output.
|
|
174
|
+
|
|
175
|
+
Docker isolation reduces host writes, but it is not a malware sandbox. Do not execute unknown samples with this tool.
|
|
176
|
+
|
|
177
|
+
## Development
|
|
178
|
+
|
|
179
|
+
Build the Docker image:
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
docker build -t decompile:latest .
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Use the local image:
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
decompile --image decompile:latest ./sample
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Build Python release artifacts:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
python3 -m build
|
|
195
|
+
```
|