Merge remote-tracking branch 'origin/GP-2601_InjectOverride'

(Closes #1719, Closes #4591)
This commit is contained in:
Ryan Kurtz 2022-10-04 01:40:25 -04:00
commit 40f1a87ecf
10 changed files with 120 additions and 35 deletions

View file

@ -25,6 +25,7 @@ src/decompile/datatests/forloop_varused.xml||GHIDRA||||END|
src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END|
src/decompile/datatests/impliedfield.xml||GHIDRA||||END|
src/decompile/datatests/indproto.xml||GHIDRA||||END|
src/decompile/datatests/injectoverride.xml||GHIDRA||||END|
src/decompile/datatests/loopcomment.xml||GHIDRA||||END|
src/decompile/datatests/mixfloatint.xml||GHIDRA||||END|
src/decompile/datatests/modulo.xml||GHIDRA||||END|

View file

@ -323,7 +323,7 @@ PcodeOp *FlowInfo::xrefControlFlow(list<PcodeOp *>::const_iterator oiter,bool &s
--oiter; // Backup one op, to pickup halt
break;
case CPUI_CALLIND:
if (setupCallindSpecs(op,true,fc))
if (setupCallindSpecs(op,fc))
--oiter; // Backup one op, to pickup halt
break;
case CPUI_CALLOTHER:
@ -685,27 +685,20 @@ bool FlowInfo::setupCallSpecs(PcodeOp *op,FuncCallSpecs *fc)
/// The new FuncCallSpecs object is created and initialized based on
/// the CALLIND op at the site. Any overriding prototype or control-flow may be examined and applied.
/// \param op is the given CALLIND op
/// \param tryoverride is \b true is overrides should be applied for the call site
/// \param fc is non-NULL if \e injection is in progress and a cycle check needs to be made
/// \return \b true if it is discovered the sub-function never returns
bool FlowInfo::setupCallindSpecs(PcodeOp *op,bool tryoverride,FuncCallSpecs *fc)
bool FlowInfo::setupCallindSpecs(PcodeOp *op,FuncCallSpecs *fc)
{
FuncCallSpecs *res;
res = new FuncCallSpecs(op);
qlst.push_back(res);
if (tryoverride) {
data.getOverride().applyIndirect(data,*res);
data.getOverride().applyPrototype(data,*res);
}
data.getOverride().applyIndirect(data,*res);
if (fc != (FuncCallSpecs *)0 && fc->getEntryAddress() == res->getEntryAddress())
res->setAddress(Address()); // Cancel any indirect override
data.getOverride().applyPrototype(data,*res);
queryCall(*res);
if (fc != (FuncCallSpecs *)0) {
if (fc->getEntryAddress() == res->getEntryAddress()) {
res->cancelInjectId();
res->setAddress(Address()); // Cancel any indirect override
}
}
if (!res->getEntryAddress().isInvalid()) { // If we are overridden to a direct call
// Change indirect pcode call into a normal pcode call
@ -721,9 +714,9 @@ void FlowInfo::truncateIndirectJump(PcodeOp *op,int4 failuremode)
{
data.opSetOpcode(op,CPUI_CALLIND); // Turn jump into call
bool tryoverride = (failuremode == 2);
setupCallindSpecs(op,tryoverride,(FuncCallSpecs *)0);
data.getCallSpecs(op)->setBadJumpTable(true);
setupCallindSpecs(op,(FuncCallSpecs *)0);
if (failuremode != 2) // Unless the switch was a thunk mechanism
data.getCallSpecs(op)->setBadJumpTable(true); // Consider using special name for switch variable
// Create an artificial return
PcodeOp *truncop = artificialHalt(op->getAddr(),0);
@ -1031,7 +1024,7 @@ void FlowInfo::xrefInlinedBranch(PcodeOp *op)
if (op->code() == CPUI_CALL)
setupCallSpecs(op,(FuncCallSpecs *)0);
else if (op->code() == CPUI_CALLIND)
setupCallindSpecs(op,true,(FuncCallSpecs *)0);
setupCallindSpecs(op,(FuncCallSpecs *)0);
else if (op->code() == CPUI_BRANCHIND) {
JumpTable *jt = data.linkJumpTable(op);
if (jt == (JumpTable *)0)

View file

@ -125,7 +125,7 @@ private:
bool checkForFlowModification(FuncCallSpecs &fspecs);
void queryCall(FuncCallSpecs &fspecs); ///< Try to recover the Funcdata object corresponding to a given call
bool setupCallSpecs(PcodeOp *op,FuncCallSpecs *fc); ///< Set up the FuncCallSpecs object for a new call site
bool setupCallindSpecs(PcodeOp *op,bool tryoverride,FuncCallSpecs *fc);
bool setupCallindSpecs(PcodeOp *op,FuncCallSpecs *fc);
void xrefInlinedBranch(PcodeOp *op); ///< Check for control-flow in a new injected p-code op
void doInjection(InjectPayload *payload,InjectContext &icontext,PcodeOp *op,FuncCallSpecs *fc);
void injectUserOp(PcodeOp *op); ///< Perform \e injection for a given user-defined p-code op

View file

@ -3743,6 +3743,19 @@ void FuncProto::clearInput(void)
flags &= ~((uint4)voidinputlock); // If a void was locked in clear it
}
/// Set the id directly.
/// \param id is the new id
void FuncProto::setInjectId(int4 id)
{
if (id < 0)
cancelInjectId();
else {
injectid = id;
flags |= is_inline;
}
}
void FuncProto::cancelInjectId(void)
{
@ -5098,8 +5111,6 @@ void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd)
Varnode *vn = data.newVarnodeCallSpecs(this);
data.opSetInput(op,vn,0);
data.opSetOpcode(op,CPUI_CALL);
if (isOverride()) // If we are overridden at the call-site
return; // Don't use the discovered function prototype
data.getOverride().insertIndirectOverride(op->getAddr(),entryaddress);
@ -5108,14 +5119,17 @@ void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd)
vector<Varnode *> newinput;
Varnode *newoutput;
FuncProto &newproto( newfd->getFuncProto() );
if ((!newproto.isNoReturn())&&(!newproto.isInline())&&
lateRestriction(newproto,newinput,newoutput)) {
commitNewInputs(data,newinput);
commitNewOutputs(data,newoutput);
}
else {
data.setRestartPending(true);
if ((!newproto.isNoReturn())&&(!newproto.isInline())) {
if (isOverride()) // If we are overridden at the call-site
return; // Don't use the discovered function prototype
if (lateRestriction(newproto,newinput,newoutput)) {
commitNewInputs(data,newinput);
commitNewOutputs(data,newoutput);
return; // We have successfully updated the prototype, don't restart
}
}
data.setRestartPending(true);
}
/// \brief Force a more restrictive prototype on \b this call site

View file

@ -1428,6 +1428,7 @@ public:
void clearUnlockedInput(void); ///< Clear input parameters that have not been locked
void clearUnlockedOutput(void); ///< Clear the return value if it has not been locked
void clearInput(void); ///< Clear all input parameters regardless of lock
void setInjectId(int4 id); ///< Associate a given injection with \b this prototype
void cancelInjectId(void); ///< Turn-off any in-lining for this function
void resolveModel(ParamActive *active);

View file

@ -125,6 +125,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status)
status->registerCom(new IfcCallGraphList(),"callgraph","list");
status->registerCom(new IfcCallFixup(),"fixup","call");
status->registerCom(new IfcCallOtherFixup(),"fixup","callother");
status->registerCom(new IfcFixupApply(),"fixup","apply");
status->registerCom(new IfcVolatile(),"volatile");
status->registerCom(new IfcReadonly(),"readonly");
status->registerCom(new IfcPointerSetting(),"pointer","setting");
@ -2883,6 +2884,43 @@ void IfcCallOtherFixup::execute(istream &s)
*status->optr << "Successfully registered callotherfixup" << endl;
}
/// \class IfcFixupApply
/// \brief Apply a call-fixup to a particular function: `fixup apply <fixup> <function>`
///
/// The call-fixup and function are named from the command-line. If they both exist,
/// the fixup is set on the function's prototype.
void IfcFixupApply::execute(istream &s)
{
if (dcp->conf == (Architecture *)0)
throw IfaceExecutionError("No load image present");
string fixupName,funcName;
s >> ws;
if (s.eof())
throw IfaceParseError("Missing fixup name");
s >> fixupName >> ws;
if (s.eof())
throw IfaceParseError("Missing function name");
s >> funcName;
int4 injectid = dcp->conf->pcodeinjectlib->getPayloadId(InjectPayload::CALLFIXUP_TYPE, fixupName);
if (injectid < 0)
throw IfaceExecutionError("Unknown fixup: " + fixupName);
string basename;
Scope *funcscope = dcp->conf->symboltab->resolveScopeFromSymbolName(funcName,"::",basename,(Scope *)0);
if (funcscope == (Scope *)0)
throw IfaceExecutionError("Bad namespace: "+funcName);
Funcdata *fd = funcscope->queryFunction( basename ); // Is function already in database
if (fd == (Funcdata *)0)
throw IfaceExecutionError("Unknown function name: "+funcName);
fd->getFuncProto().setInjectId(injectid);
*status->optr << "Successfully applied callfixup" << endl;
}
/// \class IfcVolatile
/// \brief Mark a memory range as volatile: `volatile <address+size>`
///

View file

@ -531,6 +531,11 @@ public:
virtual void execute(istream &s);
};
class IfcFixupApply : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcCountPcode : public IfaceDecompCommand {
public:
virtual void execute(istream &s);

View file

@ -0,0 +1,32 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:windows">
<!--
Example of CALLIND collapsing to inject function with CALLIND.
Example of CALL injecting to CALLIND that then collapses.
-->
<bytechunk space="ram" offset="0x18000c000" readonly="true">
534883ec20ff1516000000c300000000
48b830c000800100000066e80200c300
c320c0008001000000
</bytechunk>
<symbol space="ram" offset="0x18000c000" name="callind_inject"/>
<symbol space="ram" offset="0x18000c010" name="inject_collapse"/>
<symbol space="ram" offset="0x18000c020" name="_guard_dispatch_icall"/>
<symbol space="ram" offset="0x18000c030" name="collapsed"/>
</binaryimage>
<script>
<com>option readonly on</com>
<com>fixup apply guard_dispatch_icall _guard_dispatch_icall</com>
<com>parse line extern void collapsed(int4);</com>
<com>lo fu callind_inject</com>
<com>override proto r0x18000c005 <![CDATA[ void func(int4,int4); ]]> </com>
<com>decompile</com>
<com>print C</com>
<com>lo fu inject_collapse</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Inject Override #1" min="1" max="1">\(\*in_RAX\)\(param_1,param_2\);</stringmatch>
<stringmatch name="Inject Override #2" min="1" max="1">collapsed\(param_1\);</stringmatch>
</decompilertest>

View file

@ -20,10 +20,10 @@ import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.symbol.*;
import ghidra.util.*;
@ -39,16 +39,17 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
if (tokenAtCursor == null) {
return null;
}
Address addr = tokenAtCursor.getMinAddress();
if (addr == null) {
return null;
}
Namespace overspace = HighFunction.findOverrideSpace(func);
if (overspace == null) {
return null;
}
PcodeOp op = OverridePrototypeAction.getCallOp(func.getProgram(), tokenAtCursor);
if (op == null) {
return null;
}
SymbolTable symtab = func.getProgram().getSymbolTable();
SymbolIterator iter = symtab.getSymbols(overspace);
SymbolIterator iter = symtab.getSymbolsAsIterator(op.getSeqnum().getTarget());
while (iter.hasNext()) {
Symbol sym = iter.next();
if (!sym.getName().startsWith("prt")) {
@ -57,7 +58,7 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
if (!(sym instanceof CodeSymbol)) {
continue;
}
if (!sym.getAddress().equals(addr)) {
if (!sym.getParentNamespace().equals(overspace)) {
continue;
}
return (CodeSymbol) sym;

View file

@ -47,7 +47,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
* @param tokenAtCursor is the point in the window the user has selected
* @return the PcodeOp or null
*/
private static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) {
public static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) {
if (tokenAtCursor == null) {
return null;
}