// 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; } }