ghidra/Ghidra/Features/Base/ghidra_scripts/CompareAnalysisScript.java

410 lines
15 KiB
Java

/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Script to compare analysis between current and chosen program.
//@category Analysis
import java.util.Iterator;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.*;
public class CompareAnalysisScript extends GhidraScript {
@Override
public void run() throws Exception {
if (currentAddress == null) {
println("No Location.");
return;
}
Program otherProgram = askProgram("Choose a program to compare to");
if (otherProgram == null) {
return;
}
println("\n\n****** COMPARING FUNCTIONS:\n");
compareFunctions(otherProgram);
println("\n\n****** COMPARING STRINGS:\n");
compareStrings(otherProgram);
println("\n\n****** PERCENT ANALYZED COMPARE SUMMARY:\n");
reportPercentDisassembled(currentProgram);
reportPercentDisassembled(otherProgram);
println("\n\n****** COMPARING SWITCH TABLES:\n");
compareSwitchTables(otherProgram);
println("\n\n****** COMPARING NON-RETURNING FUNCTIONS:\n");
compareNoReturns(otherProgram);
println("\n\n****** COMPARING ERRORS:\n");
compareErrors(otherProgram);
}
void compareFunctions(Program otherProgram) {
FunctionManager functionManager = otherProgram.getFunctionManager();
String currentProgramName = currentProgram.getDomainFile().getName();
String otherProgramName = otherProgram.getDomainFile().getName();
Listing listing = currentProgram.getListing();
int numMissingFuncs = 0;
int numFuncsInCurrentProg = 0;
println("Iterating through functions in " + currentProgramName);
FunctionIterator currentFunctions =
listing.getFunctions(currentProgram.getMinAddress(), true);
while (currentFunctions.hasNext() && !monitor.isCancelled()) {
Function func = currentFunctions.next();
numFuncsInCurrentProg++;
Address funcAddress = func.getBody().getMinAddress();
Function otherFunction = functionManager.getFunctionAt(funcAddress);
if (otherFunction == null) {
numMissingFuncs++;
println(numMissingFuncs + ": Missing function in " + otherProgramName + " at " +
funcAddress.toString());
}
}
println("Iterating through functions in " + otherProgramName);
FunctionManager currentFunctionManager = currentProgram.getFunctionManager();
int numMissingFuncs2 = 0;
int numFuncsInOtherProg = 0;
FunctionIterator otherFunctions =
otherProgram.getListing().getFunctions(otherProgram.getMinAddress(), true);
while (otherFunctions.hasNext() && !monitor.isCancelled()) {
Function otherfunc = otherFunctions.next();
numFuncsInOtherProg++;
Address funcAddress = otherfunc.getBody().getMinAddress();
Function func = currentFunctionManager.getFunctionAt(funcAddress);
if (func == null) {
numMissingFuncs2++;
println(numMissingFuncs2 + ": Missing function in " + currentProgramName + " at " +
funcAddress.toString());
}
}
println("\n\n****** FUNCTION COMPARE SUMMARY:\n");
println("There are " + numFuncsInCurrentProg + " functions in " + currentProgramName +
" and " + numFuncsInOtherProg + " functions in " + otherProgramName);
println("There are " + numMissingFuncs + " functions missing in " + otherProgramName +
" that are in " + currentProgramName);
println("There are " + numMissingFuncs2 + " functions missing in " + currentProgramName +
" that are in " + otherProgramName);
return;
}
void reportPercentDisassembled(Program prog) throws MemoryAccessException {
// find all the sections of memory marked as executable
String programName = prog.getDomainFile().getName();
AddressSetView execMemSet = prog.getMemory().getExecuteSet();
/*
int myExecSetLen = 0;
MemoryBlock[] blocks = prog.getMemory().getBlocks();
for (int i = 0; i < blocks.length; i++) {
if (blocks[i].isExecute()) {
myExecSetLen += blocks[i].getSize();
}
}*/
// tally up all the bytes that have been marked as instructions
// (probably faster ways to do this, but it works)
long numPossibleDis = execMemSet.getNumAddresses();
InstructionIterator instIter = prog.getListing().getInstructions(execMemSet, true);
int instCount = 0;
while (instIter.hasNext()) {
Instruction inst = instIter.next();
instCount += inst.getBytes().length;
}
DataIterator dataIter = prog.getListing().getData(execMemSet, true);
int dataCount = 0;
while (dataIter.hasNext()) {
Data data = dataIter.next();
if (data.isDefined()) {
dataCount += data.getBytes().length;
}
}
// dump the info
int total = instCount + dataCount;
if (numPossibleDis != 0) {
float coverage = (float) total / (float) numPossibleDis;
// Msg.info(this,"execSetLen = " + numPossibleDis);
// Msg.info(this,"MyExecSetLen = " + myExecSetLen);
// Msg.info(this,"NumInsts = " + instCount);
// Msg.info(this,"numData = " + dataCount);
//
// Msg.info(this,"totalDis = " + total);
float percentage = coverage * 100;
println(programName + ": " + percentage + "% disassembled.");
}
return;
}
void compareStrings(Program otherProgram) {
Listing listing = currentProgram.getListing();
Listing otherListing = otherProgram.getListing();
String currentProgramName = currentProgram.getDomainFile().getName();
String otherProgramName = otherProgram.getDomainFile().getName();
int numMissingStrings = 0;
int numStringsInCurrentProg = 0;
println("Iterating through strings in " + currentProgramName);
DataIterator definedDataIter = listing.getDefinedData(currentProgram.getMinAddress(), true);
while (definedDataIter.hasNext() && !monitor.isCancelled()) {
Data currentProgData = definedDataIter.next();
if (isString(currentProgData.getMnemonicString())) {
Address stringAddress = currentProgData.getAddress();
numStringsInCurrentProg++;
Data otherProgData = otherListing.getDataAt(stringAddress);
if (otherProgData == null || !isString(otherProgData.getMnemonicString())) {
numMissingStrings++;
println(numMissingStrings + ": Missing string in " + otherProgramName +
" at " + stringAddress.toString());
}
}
}
println("Iterating through strings in " + otherProgramName);
DataIterator otherDataIter =
otherListing.getDefinedData(otherProgram.getMinAddress(), true);
int numMissingStrings2 = 0;
int numStringsInOtherProg = 0;
while (otherDataIter.hasNext() && !monitor.isCancelled()) {
Data otherString = otherDataIter.next();
if (isString(otherString.getMnemonicString())) {
numStringsInOtherProg++;
Address otherStringAddress = otherString.getAddress();
Data currentData = listing.getDataAt(otherStringAddress);
if (currentData == null || !isString(currentData.getMnemonicString())) {
numMissingStrings2++;
println(numMissingStrings2 + ": Missing string in " + currentProgramName +
" at " + otherStringAddress.toString());
}
}
}
println("\n\n******STRING COMPARE SUMMARY:\n");
println("There are " + numStringsInCurrentProg + " strings in " + currentProgramName +
" and " + numStringsInOtherProg + " strings in " + otherProgramName);
println("There are " + numMissingStrings + " strings missing in " + otherProgramName +
" that are in " + currentProgramName);
println("There are " + numMissingStrings2 + " strings missing in " + currentProgramName +
" that are in " + otherProgramName);
return;
}
void compareSwitchTables(Program otherProgram) {
String currentProgramName = currentProgram.getDomainFile().getName();
String otherProgramName = otherProgram.getDomainFile().getName();
SymbolTable currentSymbolTable = currentProgram.getSymbolTable();
SymbolTable otherSymbolTable = otherProgram.getSymbolTable();
int numMissingSwitches = 0;
int numSwitchesInCurrentProg = 0;
println("Iterating through switch tables in " + currentProgramName);
SymbolIterator currentSymIter = currentSymbolTable.getSymbolIterator("switchdataD_*", true);
while (currentSymIter.hasNext() && !monitor.isCancelled()) {
Symbol currentSym = currentSymIter.next();
Address switchAddress = currentSym.getAddress();
numSwitchesInCurrentProg++;
Symbol otherSyms[] = otherSymbolTable.getSymbols(switchAddress);
if (!isSwitch(otherSyms, "switchdataD_")) {
numMissingSwitches++;
println(numMissingSwitches + ": Missing switch table in " + otherProgramName +
" at " + switchAddress.toString());
}
}
println("Iterating through switch tables in " + otherProgramName);
SymbolIterator otherSymIter = otherSymbolTable.getSymbolIterator("switchdataD_*", true);
int numMissingSwitches2 = 0;
int numSwitchesInOtherProg = 0;
while (otherSymIter.hasNext() && !monitor.isCancelled()) {
Symbol otherSym = otherSymIter.next();
Address otherSwitchAddress = otherSym.getAddress();
numSwitchesInOtherProg++;
Symbol currentSyms[] = currentSymbolTable.getSymbols(otherSwitchAddress);
if (!isSwitch(currentSyms, "switchdataD_")) {
numMissingSwitches2++;
println(numMissingSwitches2 + ": Missing switch table in " + currentProgramName +
" at " + otherSwitchAddress.toString());
}
}
println("\n\n******SWITCH TABLE COMPARE SUMMARY:\n");
println(
"There are " + numSwitchesInCurrentProg + " switch tables in " + currentProgramName +
" and " + numSwitchesInOtherProg + " switch table in " + otherProgramName);
println("There are " + numMissingSwitches + " switch tables missing in " +
otherProgramName + " that are in " + currentProgramName);
println("There are " + numMissingSwitches2 + " switch tables missing in " +
currentProgramName + " that are in " + otherProgramName);
return;
}
void compareNoReturns(Program otherProgram) {
FunctionManager functionManager = otherProgram.getFunctionManager();
String currentProgramName = currentProgram.getDomainFile().getName();
String otherProgramName = otherProgram.getDomainFile().getName();
Listing listing = currentProgram.getListing();
int numMissingNonReturningFuncs = 0;
int numNonReturningFuncsInCurrentProg = 0;
println("Iterating through non-returning functions in " + currentProgramName);
FunctionIterator currentFunctions =
listing.getFunctions(currentProgram.getMinAddress(), true);
while (currentFunctions.hasNext() && !monitor.isCancelled()) {
Function func = currentFunctions.next();
if (func.hasNoReturn()) {
numNonReturningFuncsInCurrentProg++;
Address funcAddress = func.getBody().getMinAddress();
Function otherFunction = functionManager.getFunctionAt(funcAddress);
if (otherFunction == null || !otherFunction.hasNoReturn()) {
numMissingNonReturningFuncs++;
println(numMissingNonReturningFuncs +
": Missing function or function is not marked as non-returning in " +
otherProgramName + " at " + funcAddress.toString());
}
}
}
println("Iterating through non-returning functions in " + otherProgramName);
FunctionManager currentFunctionManager = currentProgram.getFunctionManager();
int numMissingNonReturningFuncs2 = 0;
int numNonReturningFuncsInOtherProg = 0;
FunctionIterator otherFunctions =
otherProgram.getListing().getFunctions(otherProgram.getMinAddress(), true);
while (otherFunctions.hasNext() && !monitor.isCancelled()) {
Function otherfunc = otherFunctions.next();
if (otherfunc.hasNoReturn()) {
numNonReturningFuncsInOtherProg++;
Address funcAddress = otherfunc.getBody().getMinAddress();
Function func = currentFunctionManager.getFunctionAt(funcAddress);
if (func == null || !func.hasNoReturn()) {
numMissingNonReturningFuncs2++;
println(numMissingNonReturningFuncs2 +
": Missing function or function is not marked as non-returning in " +
currentProgramName + " at " + funcAddress.toString());
}
}
}
println("\n\n****** NON-RETURNING FUNCTION COMPARE SUMMARY:\n");
println("There are " + numNonReturningFuncsInCurrentProg + " non-returning functions in " +
currentProgramName + " and " + numNonReturningFuncsInOtherProg +
" non-returning functions in " + otherProgramName);
println(
"There are " + numMissingNonReturningFuncs + " non-returning functions missing in " +
otherProgramName + " that are in " + currentProgramName);
println(
"There are " + numMissingNonReturningFuncs2 + " non-returning functions missing in " +
currentProgramName + " that are in " + otherProgramName);
return;
}
void compareErrors(Program otherProgram) {
BookmarkManager currentBookmarkManager = currentProgram.getBookmarkManager();
BookmarkManager otherBookmarkManager = otherProgram.getBookmarkManager();
String currentProgramName = currentProgram.getDomainFile().getName();
String otherProgramName = otherProgram.getDomainFile().getName();
int numMissingErrors = 0;
int numErrorsInCurrentProg = 0;
println("Iterating through errors in " + currentProgramName);
Iterator<Bookmark> currentErrorIterator =
currentBookmarkManager.getBookmarksIterator("Error");
while (currentErrorIterator.hasNext() && !monitor.isCancelled()) {
Bookmark error = currentErrorIterator.next();
numErrorsInCurrentProg++;
Address errorAddress = error.getAddress();
Bookmark[] otherErrors = otherBookmarkManager.getBookmarks(errorAddress, "Error");
if (otherErrors.length == 0) {
numMissingErrors++;
println(numMissingErrors + ": No error in " + otherProgramName + " at " +
errorAddress.toString());
}
}
println("Iterating through errors in " + otherProgramName);
int numMissingErrors2 = 0;
int numErrorsInOtherProg = 0;
Iterator<Bookmark> otherErrorIterator = otherBookmarkManager.getBookmarksIterator("Error");
while (otherErrorIterator.hasNext() && !monitor.isCancelled()) {
Bookmark otherError = otherErrorIterator.next();
numErrorsInOtherProg++;
Address otherErrorAddress = otherError.getAddress();
Bookmark[] currentErrors =
currentBookmarkManager.getBookmarks(otherErrorAddress, "Error");
if (currentErrors.length == 0) {
numMissingErrors2++;
println(numMissingErrors2 + ": No error in " + currentProgramName + " at " +
otherErrorAddress.toString());
}
}
println("\n\n****** ERROR COMPARE SUMMARY:\n");
println("There are " + numErrorsInCurrentProg + " errors in " + currentProgramName +
" and " + numErrorsInOtherProg + " errors in " + otherProgramName);
println("There are " + numMissingErrors + " errors not in " + otherProgramName +
" that are in " + currentProgramName);
println("There are " + numMissingErrors2 + " errors not in " + currentProgramName +
" that are in " + otherProgramName);
return;
}
boolean isString(String mnemonic) {
if (mnemonic.equals(new String("ds")) || mnemonic.equals(new String("unicode")) ||
mnemonic.equals(new String("p_unicode")) || mnemonic.equals(new String("p_string")) ||
mnemonic.equals(new String("p_string255")) || mnemonic.equals(new String("mbcs"))) {
return true;
}
return false;
}
boolean isSwitch(Symbol[] syms, String name) {
for (Symbol sym : syms) {
if (sym.getName().startsWith(name)) {
return true;
}
}
return false;
}
}