mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/GP-5763_emteere_NoReturnExternalsINT3--SQUASHED'
This commit is contained in:
commit
22b3524206
2 changed files with 113 additions and 20 deletions
|
@ -41,6 +41,7 @@ import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.app.tablechooser.*;
|
import ghidra.app.tablechooser.*;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.block.*;
|
import ghidra.program.model.block.*;
|
||||||
|
import ghidra.program.model.lang.Processor;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
|
@ -49,10 +50,15 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
|
||||||
|
|
||||||
IssueEntries entryList = null;
|
IssueEntries entryList = null;
|
||||||
|
|
||||||
|
private final static String X86_NAME = "x86";
|
||||||
|
boolean isX86;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() throws Exception {
|
public void run() throws Exception {
|
||||||
Program cp = currentProgram;
|
Program cp = currentProgram;
|
||||||
|
|
||||||
|
isX86 = checkForX86(cp);
|
||||||
|
|
||||||
TableChooserExecutor executor = createTableExecutor();
|
TableChooserExecutor executor = createTableExecutor();
|
||||||
|
|
||||||
if (this.isRunningHeadless()) {
|
if (this.isRunningHeadless()) {
|
||||||
|
@ -89,6 +95,11 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkForX86(Program cp) {
|
||||||
|
return cp.getLanguage().getProcessor().equals(
|
||||||
|
Processor.findOrPossiblyCreateProcessor(X86_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
private void configureTableColumns(TableChooserDialog dialog) {
|
private void configureTableColumns(TableChooserDialog dialog) {
|
||||||
StringColumnDisplay explanationColumn = new StringColumnDisplay() {
|
StringColumnDisplay explanationColumn = new StringColumnDisplay() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -373,10 +384,22 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
FunctionManager functionManager = currentProgram.getFunctionManager();
|
FunctionManager functionManager = currentProgram.getFunctionManager();
|
||||||
FunctionIterator functionIter = functionManager.getFunctions(true);
|
|
||||||
AddressSet set = new AddressSet();
|
AddressSet set = new AddressSet();
|
||||||
HashSet<Function> suspectNoReturnFunctions = new HashSet<Function>();
|
HashSet<Function> suspectNoReturnFunctions = new HashSet<Function>();
|
||||||
|
|
||||||
|
FunctionIterator functionIter = functionManager.getFunctions(true);
|
||||||
|
checkFunctions(cp, functionIter, noReturnEntries, set, suspectNoReturnFunctions);
|
||||||
|
|
||||||
|
FunctionIterator externalFunctionIter = functionManager.getExternalFunctions();
|
||||||
|
checkFunctions(cp, externalFunctionIter, noReturnEntries, set, suspectNoReturnFunctions);
|
||||||
|
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkFunctions(Program cp, FunctionIterator functionIter,
|
||||||
|
IssueEntries noReturnEntries, AddressSet set,
|
||||||
|
HashSet<Function> suspectNoReturnFunctions) throws CancelledException {
|
||||||
while (functionIter.hasNext()) {
|
while (functionIter.hasNext()) {
|
||||||
Function candidateNoReturnfunction = functionIter.next();
|
Function candidateNoReturnfunction = functionIter.next();
|
||||||
noReturnEntries.setMessage("Checking function: " + candidateNoReturnfunction.getName());
|
noReturnEntries.setMessage("Checking function: " + candidateNoReturnfunction.getName());
|
||||||
|
@ -428,8 +451,6 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
|
||||||
suspectNoReturnFunctions.add(candidateNoReturnfunction);
|
suspectNoReturnFunctions.add(candidateNoReturnfunction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return set;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean testCalledFunctionsNonReturning(Function candidateNonReturningFunction,
|
private boolean testCalledFunctionsNonReturning(Function candidateNonReturningFunction,
|
||||||
|
@ -462,8 +483,8 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
|
||||||
FunctionManager funcManager = currentProgram.getFunctionManager();
|
FunctionManager funcManager = currentProgram.getFunctionManager();
|
||||||
Listing listing = currentProgram.getListing();
|
Listing listing = currentProgram.getListing();
|
||||||
while (fallThru != null) {
|
while (fallThru != null) {
|
||||||
if (funcManager.getFunctionAt(fallThru) != null) {
|
Function fallThruFunction = funcManager.getFunctionAt(fallThru);
|
||||||
|
if (fallThruFunction != null) {
|
||||||
NoReturnLocations location = new NoReturnLocations(currentProgram,
|
NoReturnLocations location = new NoReturnLocations(currentProgram,
|
||||||
ref.getToAddress(), ref.getFromAddress(), "Function defined after call");
|
ref.getToAddress(), ref.getFromAddress(), "Function defined after call");
|
||||||
dialog.add(location);
|
dialog.add(location);
|
||||||
|
@ -490,11 +511,15 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
|
||||||
// or references. This is especially true if there is only one
|
// or references. This is especially true if there is only one
|
||||||
// example for a calling reference.
|
// example for a calling reference.
|
||||||
if (callingFunc != null) {
|
if (callingFunc != null) {
|
||||||
|
Address fromAddress = reference.getFromAddress();
|
||||||
Function function =
|
Function function =
|
||||||
funcManager.getFunctionContaining(reference.getFromAddress());
|
funcManager.getFunctionContaining(fromAddress);
|
||||||
if (callingFunc.equals(function)) {
|
// The reference must come from an address within this function
|
||||||
|
// before this function call (reference fromAddress)
|
||||||
|
// this should get rid of spurious data references from other functions
|
||||||
|
if ((fromAddress.compareTo(fallThru) < 0) && callingFunc.equals(function)) {
|
||||||
NoReturnLocations location = new NoReturnLocations(currentProgram,
|
NoReturnLocations location = new NoReturnLocations(currentProgram,
|
||||||
ref.getToAddress(), ref.getFromAddress(),
|
ref.getToAddress(), fromAddress,
|
||||||
"Data Reference from same function after call");
|
"Data Reference from same function after call");
|
||||||
dialog.add(location);
|
dialog.add(location);
|
||||||
return true;
|
return true;
|
||||||
|
@ -517,6 +542,15 @@ public class FixupNoReturnFunctionsScript extends GhidraScript {
|
||||||
dialog.add(location);
|
dialog.add(location);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (isX86) {
|
||||||
|
Instruction fallInstr = listing.getInstructionContaining(fallThru);
|
||||||
|
if (fallInstr != null && fallInstr.getMnemonicString().equals("INT3")) {
|
||||||
|
NoReturnLocations location = new NoReturnLocations(currentProgram,
|
||||||
|
ref.getToAddress(), ref.getFromAddress(), "INT3 interrupt after call");
|
||||||
|
dialog.add(location);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
fallThru = null;
|
fallThru = null;
|
||||||
if (block.getFlowType().isFallthrough()) {
|
if (block.getFlowType().isFallthrough()) {
|
||||||
CodeBlockReferenceIterator dests = block.getDestinations(monitor);
|
CodeBlockReferenceIterator dests = block.getDestinations(monitor);
|
||||||
|
|
|
@ -25,8 +25,7 @@ import ghidra.app.util.importer.MessageLog;
|
||||||
import ghidra.framework.options.Options;
|
import ghidra.framework.options.Options;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.block.*;
|
import ghidra.program.model.block.*;
|
||||||
import ghidra.program.model.lang.GhidraLanguagePropertyKeys;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.lang.Language;
|
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.pcode.PcodeOp;
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.pcode.Varnode;
|
import ghidra.program.model.pcode.Varnode;
|
||||||
|
@ -80,6 +79,9 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
|
||||||
private Address lastGetNextFuncAddress = null; // last addr used for getNextFunction()
|
private Address lastGetNextFuncAddress = null; // last addr used for getNextFunction()
|
||||||
private Address nextFunction = null; // last return nextFunction
|
private Address nextFunction = null; // last return nextFunction
|
||||||
|
|
||||||
|
private final static String X86_NAME = "x86";
|
||||||
|
boolean isX86;
|
||||||
|
|
||||||
public FindNoReturnFunctionsAnalyzer() {
|
public FindNoReturnFunctionsAnalyzer() {
|
||||||
this(NAME, DESCRIPTION, AnalyzerType.INSTRUCTION_ANALYZER);
|
this(NAME, DESCRIPTION, AnalyzerType.INSTRUCTION_ANALYZER);
|
||||||
}
|
}
|
||||||
|
@ -106,6 +108,8 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
|
||||||
this.reasonList = new ArrayList<>();
|
this.reasonList = new ArrayList<>();
|
||||||
lastGetNextFuncAddress = null;
|
lastGetNextFuncAddress = null;
|
||||||
|
|
||||||
|
isX86 = checkForX86(program);
|
||||||
|
|
||||||
monitor.setMessage("NoReturn - Finding non-returning functions");
|
monitor.setMessage("NoReturn - Finding non-returning functions");
|
||||||
|
|
||||||
AddressSet noReturnSet = new AddressSet();
|
AddressSet noReturnSet = new AddressSet();
|
||||||
|
@ -150,6 +154,11 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean checkForX86(Program cp) {
|
||||||
|
return cp.getLanguage().getProcessor().equals(
|
||||||
|
Processor.findOrPossiblyCreateProcessor(X86_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* repair any damaged locations
|
* repair any damaged locations
|
||||||
*
|
*
|
||||||
|
@ -358,7 +367,10 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// detected a calling issue, check other instructions calling the same place
|
// detected a calling issue, check other instructions calling the same place
|
||||||
Address[] flows = inst.getFlows();
|
Address[] flows = getAllFlows(inst);
|
||||||
|
if (flows == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (Address target : flows) {
|
for (Address target : flows) {
|
||||||
|
|
||||||
int count = 1;
|
int count = 1;
|
||||||
|
@ -558,6 +570,17 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// on x86 INT3 after a call indicates a non-returning call from alignment padding
|
||||||
|
if (isX86) {
|
||||||
|
Instruction fallInstr = listing.getInstructionContaining(fallThru);
|
||||||
|
if (fallInstr != null && fallInstr.getMnemonicString().equals("INT3")) {
|
||||||
|
NoReturnLocations location =
|
||||||
|
new NoReturnLocations(target, fallThru, "INT3 interrupt after call");
|
||||||
|
reasonList.add(location);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// get the next instruction in fallthru chain
|
// get the next instruction in fallthru chain
|
||||||
fallThru = null;
|
fallThru = null;
|
||||||
if (instr.getFlowType().isFallthrough()) {
|
if (instr.getFlowType().isFallthrough()) {
|
||||||
|
@ -567,6 +590,38 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all flows that have already been added to instruction.
|
||||||
|
* If there are none and this is an indirect, get the function at
|
||||||
|
* the end of the read.
|
||||||
|
* @param callInst
|
||||||
|
* @return all flows
|
||||||
|
*/
|
||||||
|
private Address[] getAllFlows(Instruction callInst) {
|
||||||
|
Address[] flows = callInst.getFlows();
|
||||||
|
if (flows != null && flows.length > 0) {
|
||||||
|
return flows;
|
||||||
|
}
|
||||||
|
FlowType flowType = callInst.getFlowType();
|
||||||
|
if (!flowType.isCall() || !flowType.isIndirect()) {
|
||||||
|
return flows;
|
||||||
|
}
|
||||||
|
// if haven't found any flows yet, check for a read of a location that refers
|
||||||
|
// to a function.
|
||||||
|
Reference[] referencesFrom = callInst.getReferencesFrom();
|
||||||
|
for (Reference reference : referencesFrom) {
|
||||||
|
if (reference.getReferenceType().isRead()) {
|
||||||
|
Function functionAt = program.getFunctionManager().getFunctionAt(reference.getToAddress());
|
||||||
|
if (functionAt != null) {
|
||||||
|
flows = new Address[1];
|
||||||
|
flows[0] = reference.getToAddress();
|
||||||
|
return flows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flows;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if fallThru address has inconsistent (data/call) references to it.
|
* Return true if fallThru address has inconsistent (data/call) references to it.
|
||||||
* Adds the reason for non-returning reason to no return locations list.
|
* Adds the reason for non-returning reason to no return locations list.
|
||||||
|
@ -585,8 +640,8 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
|
||||||
Reference reference = refIterTo.next();
|
Reference reference = refIterTo.next();
|
||||||
RefType refType = reference.getReferenceType();
|
RefType refType = reference.getReferenceType();
|
||||||
if (refType.isRead() || refType.isWrite()) {
|
if (refType.isRead() || refType.isWrite()) {
|
||||||
// look at function the reference is coming from
|
// Check function the reference is coming from
|
||||||
// is the function the same as the call is in
|
// is the same function as the call is in
|
||||||
// This is a better indicator of non-returning
|
// This is a better indicator of non-returning
|
||||||
// Random references from another function could be bad disassembly
|
// Random references from another function could be bad disassembly
|
||||||
// or references. This is especially true if there is only one
|
// or references. This is especially true if there is only one
|
||||||
|
@ -595,9 +650,13 @@ public class FindNoReturnFunctionsAnalyzer extends AbstractAnalyzer {
|
||||||
// TODO: if this is done before functions are created from calls
|
// TODO: if this is done before functions are created from calls
|
||||||
// then this check will do nothing
|
// then this check will do nothing
|
||||||
if (callingFunc != null) {
|
if (callingFunc != null) {
|
||||||
|
Address fromAddress = reference.getFromAddress();
|
||||||
Function function =
|
Function function =
|
||||||
funcManager.getFunctionContaining(reference.getFromAddress());
|
funcManager.getFunctionContaining(fromAddress);
|
||||||
if (callingFunc.equals(function)) {
|
// The reference must come from an address within this function
|
||||||
|
// before this the function call (reference fromAddress)
|
||||||
|
// this should get rid of considering spurious/bad data references from other functions
|
||||||
|
if ((fromAddress.compareTo(addr) < 0) && callingFunc.equals(function)) {
|
||||||
NoReturnLocations location =
|
NoReturnLocations location =
|
||||||
new NoReturnLocations(calledAddr, reference.getToAddress(),
|
new NoReturnLocations(calledAddr, reference.getToAddress(),
|
||||||
"Data Reference from same function after call");
|
"Data Reference from same function after call");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue