Files
tdkpin/original/tools/AuditCoverage.java
T
2026-08-22 15:52:52 +02:00

90 lines
4.5 KiB
Java

// Report executable-segment instruction and function-body coverage for the current program.
// @category TDKPIN
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.PrintWriter;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionIterator;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.mem.MemoryBlock;
public class AuditCoverage extends GhidraScript {
@Override
public void run() throws Exception {
String[] args = getScriptArgs();
if (args.length != 1) {
throw new IllegalArgumentException("usage: AuditCoverage.java OUTPUT_TSV");
}
AddressSet executable = new AddressSet();
AddressSet instructions = new AddressSet();
AddressSet definedData = new AddressSet();
AddressSet functionBodies = new AddressSet();
int internalFunctions = 0;
try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(args[0])))) {
out.println("record\tname_or_start\tend\tbytes\tdetail");
for (MemoryBlock block : currentProgram.getMemory().getBlocks()) {
String detail = String.format("r=%s,w=%s,x=%s,initialized=%s",
block.isRead(), block.isWrite(), block.isExecute(), block.isInitialized());
out.printf("block\t%s\t%s\t%d\t%s%n", block.getName(), block.getEnd(),
block.getSize(), detail);
if (block.isExecute()) {
executable.add(block.getStart(), block.getEnd());
}
}
InstructionIterator instructionIterator =
currentProgram.getListing().getInstructions(executable, true);
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
instructions.add(instruction.getMinAddress(), instruction.getMaxAddress());
}
var dataIterator = currentProgram.getListing().getDefinedData(executable, true);
while (dataIterator.hasNext()) {
var data = dataIterator.next();
definedData.add(data.getMinAddress(), data.getMaxAddress());
}
FunctionIterator functionIterator = currentProgram.getFunctionManager().getFunctions(true);
while (functionIterator.hasNext()) {
Function function = functionIterator.next();
MemoryBlock block = currentProgram.getMemory().getBlock(function.getEntryPoint());
if (!function.isExternal() && block != null && block.isExecute()) {
internalFunctions++;
functionBodies.add(function.getBody());
}
}
AddressSet explained = instructions.union(definedData);
AddressSet undefined = executable.subtract(explained);
out.printf("summary\texecutable-bytes\t\t%d\t%n", executable.getNumAddresses());
out.printf("summary\tdisassembled-bytes\t\t%d\t%.2f%% of executable%n",
instructions.getNumAddresses(), percent(instructions, executable));
out.printf("summary\tdefined-data-bytes\t\t%d\t%.2f%% of executable%n",
definedData.getNumAddresses(), percent(definedData, executable));
out.printf("summary\texplained-code-or-data-bytes\t\t%d\t%.2f%% of executable%n",
explained.getNumAddresses(), percent(explained, executable));
out.printf("summary\tfunction-body-bytes\t\t%d\t%.2f%% of executable%n",
functionBodies.intersect(executable).getNumAddresses(), percent(functionBodies, executable));
out.printf("summary\tinternal-functions\t\t%d\t%n", internalFunctions);
out.printf("summary\tundefined-executable-bytes\t\t%d\t%n", undefined.getNumAddresses());
for (var range : undefined) {
out.printf("undefined-range\t%s\t%s\t%d\t%n", range.getMinAddress(),
range.getMaxAddress(), range.getLength());
}
}
}
private double percent(AddressSetView numerator, AddressSetView denominator) {
long total = denominator.getNumAddresses();
return total == 0 ? 0.0 : 100.0 * numerator.intersect(denominator).getNumAddresses() / total;
}
}