Skip to content

Commit a68274e

Browse files
authored
Merge pull request #14 from IntegralPilot/master
SimpleKextFuzzing: add ghidra export script
2 parents 01f6f3b + a4237bd commit a68274e

1 file changed

Lines changed: 142 additions & 0 deletions

File tree

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// Exports memory segments and symbols in the format expected by the
2+
// Project Zero AppleAVD fuzzer loader.
3+
// @category Fuzzing
4+
// @author IntegralPilot
5+
6+
import ghidra.app.script.GhidraScript;
7+
import ghidra.program.model.listing.Program;
8+
import ghidra.program.model.mem.Memory;
9+
import ghidra.program.model.mem.MemoryBlock;
10+
import ghidra.program.model.mem.MemoryAccessException;
11+
import ghidra.program.model.symbol.Symbol;
12+
import ghidra.program.model.symbol.SymbolIterator;
13+
import ghidra.program.model.symbol.SymbolTable;
14+
import ghidra.program.model.symbol.SourceType;
15+
16+
import java.io.File;
17+
import java.io.FileOutputStream;
18+
import java.io.ByteArrayOutputStream;
19+
import java.io.IOException;
20+
import java.nio.ByteBuffer;
21+
import java.nio.ByteOrder;
22+
import java.nio.charset.StandardCharsets;
23+
import java.util.Arrays;
24+
import java.util.Comparator;
25+
import java.util.ArrayList;
26+
import java.util.List;
27+
28+
public class SegExportGhidra extends GhidraScript {
29+
30+
@Override
31+
public void run() throws Exception {
32+
Program program = currentProgram;
33+
if (program == null) {
34+
printerr("This script must be run with a program open.");
35+
return;
36+
}
37+
38+
File outputFile = getOutputFile();
39+
if (outputFile == null) {
40+
println("Export cancelled.");
41+
return;
42+
}
43+
44+
ByteArrayOutputStream fileContentStream = new ByteArrayOutputStream();
45+
46+
exportMemoryAndSymbols(fileContentStream, program);
47+
48+
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
49+
fos.write(fileContentStream.toByteArray());
50+
}
51+
52+
println("\n[SUCCESS] Export complete: " + outputFile.getAbsolutePath());
53+
}
54+
55+
private File getOutputFile() throws Exception {
56+
String[] args = getScriptArgs();
57+
if (args.length > 0) {
58+
File f = new File(args[0]);
59+
println("[INFO] Headless mode: Using output file from arguments: " + f.getAbsolutePath());
60+
return f;
61+
}
62+
return askFile("Select Output File", "Export");
63+
}
64+
65+
private void writeLongLE(ByteArrayOutputStream stream, long value) throws IOException {
66+
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
67+
buffer.order(ByteOrder.LITTLE_ENDIAN);
68+
buffer.putLong(value);
69+
stream.write(buffer.array());
70+
}
71+
72+
private void exportMemoryAndSymbols(ByteArrayOutputStream stream, Program program) throws IOException {
73+
Memory memory = program.getMemory();
74+
75+
List<MemoryBlock> initializedBlocks = new ArrayList<>();
76+
for (MemoryBlock block : memory.getBlocks()) {
77+
if (block.isInitialized()) {
78+
initializedBlocks.add(block);
79+
}
80+
}
81+
MemoryBlock[] blocks = initializedBlocks.toArray(new MemoryBlock[0]);
82+
Arrays.sort(blocks, Comparator.comparing(MemoryBlock::getStart));
83+
84+
println("[INFO] Writing " + blocks.length + " segment headers (initialized only)...");
85+
writeLongLE(stream, blocks.length);
86+
87+
for (MemoryBlock block : blocks) {
88+
writeLongLE(stream, block.getStart().getOffset());
89+
writeLongLE(stream, block.getEnd().getOffset() + 1);
90+
long perms = (block.isRead() ? 4 : 0) | (block.isWrite() ? 2 : 0) | (block.isExecute() ? 1 : 0);
91+
writeLongLE(stream, perms);
92+
}
93+
println("[INFO] Segment headers written.");
94+
95+
println("[INFO] Writing segment data...");
96+
for (MemoryBlock block : blocks) {
97+
try {
98+
byte[] data = new byte[(int)block.getSize()];
99+
block.getBytes(block.getStart(), data);
100+
stream.write(data);
101+
} catch (MemoryAccessException e) {
102+
printerr("[ERROR] Failed to read bytes from memory block: " + block.getName() + " - " + e.getMessage());
103+
}
104+
}
105+
println("[INFO] Segment data written.");
106+
107+
println("[INFO] Preparing and writing symbol table...");
108+
SymbolTable symbolTable = program.getSymbolTable();
109+
SymbolIterator symbols = symbolTable.getAllSymbols(true);
110+
111+
List<byte[]> symbolPayloads = new ArrayList<>();
112+
long totalSymbolPayloadSize = 0;
113+
114+
while (symbols.hasNext() && !monitor.isCancelled()) {
115+
Symbol sym = symbols.next();
116+
117+
if (sym.getSource() == SourceType.DEFAULT) continue;
118+
119+
byte[] nameBytes = sym.getName().getBytes(StandardCharsets.UTF_8);
120+
long addr = sym.getAddress().getOffset();
121+
122+
ByteBuffer entryBuffer = ByteBuffer.allocate(8 + nameBytes.length + 1);
123+
entryBuffer.order(ByteOrder.LITTLE_ENDIAN);
124+
entryBuffer.putLong(addr);
125+
entryBuffer.put(nameBytes);
126+
entryBuffer.put((byte) 0x00);
127+
128+
byte[] finalEntry = entryBuffer.array();
129+
symbolPayloads.add(finalEntry);
130+
totalSymbolPayloadSize += finalEntry.length;
131+
}
132+
133+
writeLongLE(stream, symbolPayloads.size());
134+
writeLongLE(stream, totalSymbolPayloadSize);
135+
136+
for (byte[] payload : symbolPayloads) {
137+
stream.write(payload);
138+
}
139+
140+
println("[INFO] Exported " + symbolPayloads.size() + " symbols.");
141+
}
142+
}

0 commit comments

Comments
 (0)