mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-09-13 21:56:19 +00:00
Merge remote-tracking branch 'origin/GP-2601_InjectOverride'
(Closes #1719, Closes #4591)
This commit is contained in:
commit
40f1a87ecf
|
@ -25,6 +25,7 @@ src/decompile/datatests/forloop_varused.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END|
|
src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/impliedfield.xml||GHIDRA||||END|
|
src/decompile/datatests/impliedfield.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/indproto.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/loopcomment.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/mixfloatint.xml||GHIDRA||||END|
|
src/decompile/datatests/mixfloatint.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/modulo.xml||GHIDRA||||END|
|
src/decompile/datatests/modulo.xml||GHIDRA||||END|
|
||||||
|
|
|
@ -323,7 +323,7 @@ PcodeOp *FlowInfo::xrefControlFlow(list<PcodeOp *>::const_iterator oiter,bool &s
|
||||||
--oiter; // Backup one op, to pickup halt
|
--oiter; // Backup one op, to pickup halt
|
||||||
break;
|
break;
|
||||||
case CPUI_CALLIND:
|
case CPUI_CALLIND:
|
||||||
if (setupCallindSpecs(op,true,fc))
|
if (setupCallindSpecs(op,fc))
|
||||||
--oiter; // Backup one op, to pickup halt
|
--oiter; // Backup one op, to pickup halt
|
||||||
break;
|
break;
|
||||||
case CPUI_CALLOTHER:
|
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 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.
|
/// 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 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
|
/// \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
|
/// \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;
|
FuncCallSpecs *res;
|
||||||
res = new FuncCallSpecs(op);
|
res = new FuncCallSpecs(op);
|
||||||
qlst.push_back(res);
|
qlst.push_back(res);
|
||||||
|
|
||||||
if (tryoverride) {
|
|
||||||
data.getOverride().applyIndirect(data,*res);
|
data.getOverride().applyIndirect(data,*res);
|
||||||
data.getOverride().applyPrototype(data,*res);
|
if (fc != (FuncCallSpecs *)0 && fc->getEntryAddress() == res->getEntryAddress())
|
||||||
}
|
|
||||||
queryCall(*res);
|
|
||||||
if (fc != (FuncCallSpecs *)0) {
|
|
||||||
if (fc->getEntryAddress() == res->getEntryAddress()) {
|
|
||||||
res->cancelInjectId();
|
|
||||||
res->setAddress(Address()); // Cancel any indirect override
|
res->setAddress(Address()); // Cancel any indirect override
|
||||||
}
|
data.getOverride().applyPrototype(data,*res);
|
||||||
}
|
queryCall(*res);
|
||||||
|
|
||||||
if (!res->getEntryAddress().isInvalid()) { // If we are overridden to a direct call
|
if (!res->getEntryAddress().isInvalid()) { // If we are overridden to a direct call
|
||||||
// Change indirect pcode call into a normal pcode 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
|
data.opSetOpcode(op,CPUI_CALLIND); // Turn jump into call
|
||||||
bool tryoverride = (failuremode == 2);
|
setupCallindSpecs(op,(FuncCallSpecs *)0);
|
||||||
setupCallindSpecs(op,tryoverride,(FuncCallSpecs *)0);
|
if (failuremode != 2) // Unless the switch was a thunk mechanism
|
||||||
data.getCallSpecs(op)->setBadJumpTable(true);
|
data.getCallSpecs(op)->setBadJumpTable(true); // Consider using special name for switch variable
|
||||||
|
|
||||||
// Create an artificial return
|
// Create an artificial return
|
||||||
PcodeOp *truncop = artificialHalt(op->getAddr(),0);
|
PcodeOp *truncop = artificialHalt(op->getAddr(),0);
|
||||||
|
@ -1031,7 +1024,7 @@ void FlowInfo::xrefInlinedBranch(PcodeOp *op)
|
||||||
if (op->code() == CPUI_CALL)
|
if (op->code() == CPUI_CALL)
|
||||||
setupCallSpecs(op,(FuncCallSpecs *)0);
|
setupCallSpecs(op,(FuncCallSpecs *)0);
|
||||||
else if (op->code() == CPUI_CALLIND)
|
else if (op->code() == CPUI_CALLIND)
|
||||||
setupCallindSpecs(op,true,(FuncCallSpecs *)0);
|
setupCallindSpecs(op,(FuncCallSpecs *)0);
|
||||||
else if (op->code() == CPUI_BRANCHIND) {
|
else if (op->code() == CPUI_BRANCHIND) {
|
||||||
JumpTable *jt = data.linkJumpTable(op);
|
JumpTable *jt = data.linkJumpTable(op);
|
||||||
if (jt == (JumpTable *)0)
|
if (jt == (JumpTable *)0)
|
||||||
|
|
|
@ -125,7 +125,7 @@ private:
|
||||||
bool checkForFlowModification(FuncCallSpecs &fspecs);
|
bool checkForFlowModification(FuncCallSpecs &fspecs);
|
||||||
void queryCall(FuncCallSpecs &fspecs); ///< Try to recover the Funcdata object corresponding to a given call
|
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 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 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 doInjection(InjectPayload *payload,InjectContext &icontext,PcodeOp *op,FuncCallSpecs *fc);
|
||||||
void injectUserOp(PcodeOp *op); ///< Perform \e injection for a given user-defined p-code op
|
void injectUserOp(PcodeOp *op); ///< Perform \e injection for a given user-defined p-code op
|
||||||
|
|
|
@ -3743,6 +3743,19 @@ void FuncProto::clearInput(void)
|
||||||
flags &= ~((uint4)voidinputlock); // If a void was locked in clear it
|
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)
|
void FuncProto::cancelInjectId(void)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -5098,8 +5111,6 @@ void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd)
|
||||||
Varnode *vn = data.newVarnodeCallSpecs(this);
|
Varnode *vn = data.newVarnodeCallSpecs(this);
|
||||||
data.opSetInput(op,vn,0);
|
data.opSetInput(op,vn,0);
|
||||||
data.opSetOpcode(op,CPUI_CALL);
|
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);
|
data.getOverride().insertIndirectOverride(op->getAddr(),entryaddress);
|
||||||
|
|
||||||
|
@ -5108,14 +5119,17 @@ void FuncCallSpecs::deindirect(Funcdata &data,Funcdata *newfd)
|
||||||
vector<Varnode *> newinput;
|
vector<Varnode *> newinput;
|
||||||
Varnode *newoutput;
|
Varnode *newoutput;
|
||||||
FuncProto &newproto( newfd->getFuncProto() );
|
FuncProto &newproto( newfd->getFuncProto() );
|
||||||
if ((!newproto.isNoReturn())&&(!newproto.isInline())&&
|
if ((!newproto.isNoReturn())&&(!newproto.isInline())) {
|
||||||
lateRestriction(newproto,newinput,newoutput)) {
|
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);
|
commitNewInputs(data,newinput);
|
||||||
commitNewOutputs(data,newoutput);
|
commitNewOutputs(data,newoutput);
|
||||||
|
return; // We have successfully updated the prototype, don't restart
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
data.setRestartPending(true);
|
data.setRestartPending(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Force a more restrictive prototype on \b this call site
|
/// \brief Force a more restrictive prototype on \b this call site
|
||||||
|
|
|
@ -1428,6 +1428,7 @@ public:
|
||||||
void clearUnlockedInput(void); ///< Clear input parameters that have not been locked
|
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 clearUnlockedOutput(void); ///< Clear the return value if it has not been locked
|
||||||
void clearInput(void); ///< Clear all input parameters regardless of lock
|
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 cancelInjectId(void); ///< Turn-off any in-lining for this function
|
||||||
|
|
||||||
void resolveModel(ParamActive *active);
|
void resolveModel(ParamActive *active);
|
||||||
|
|
|
@ -125,6 +125,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status)
|
||||||
status->registerCom(new IfcCallGraphList(),"callgraph","list");
|
status->registerCom(new IfcCallGraphList(),"callgraph","list");
|
||||||
status->registerCom(new IfcCallFixup(),"fixup","call");
|
status->registerCom(new IfcCallFixup(),"fixup","call");
|
||||||
status->registerCom(new IfcCallOtherFixup(),"fixup","callother");
|
status->registerCom(new IfcCallOtherFixup(),"fixup","callother");
|
||||||
|
status->registerCom(new IfcFixupApply(),"fixup","apply");
|
||||||
status->registerCom(new IfcVolatile(),"volatile");
|
status->registerCom(new IfcVolatile(),"volatile");
|
||||||
status->registerCom(new IfcReadonly(),"readonly");
|
status->registerCom(new IfcReadonly(),"readonly");
|
||||||
status->registerCom(new IfcPointerSetting(),"pointer","setting");
|
status->registerCom(new IfcPointerSetting(),"pointer","setting");
|
||||||
|
@ -2883,6 +2884,43 @@ void IfcCallOtherFixup::execute(istream &s)
|
||||||
*status->optr << "Successfully registered callotherfixup" << endl;
|
*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
|
/// \class IfcVolatile
|
||||||
/// \brief Mark a memory range as volatile: `volatile <address+size>`
|
/// \brief Mark a memory range as volatile: `volatile <address+size>`
|
||||||
///
|
///
|
||||||
|
|
|
@ -531,6 +531,11 @@ public:
|
||||||
virtual void execute(istream &s);
|
virtual void execute(istream &s);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IfcFixupApply : public IfaceDecompCommand {
|
||||||
|
public:
|
||||||
|
virtual void execute(istream &s);
|
||||||
|
};
|
||||||
|
|
||||||
class IfcCountPcode : public IfaceDecompCommand {
|
class IfcCountPcode : public IfaceDecompCommand {
|
||||||
public:
|
public:
|
||||||
virtual void execute(istream &s);
|
virtual void execute(istream &s);
|
||||||
|
|
|
@ -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>
|
|
@ -20,10 +20,10 @@ import ghidra.app.decompiler.ClangToken;
|
||||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||||
import ghidra.app.util.HelpTopics;
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.program.database.symbol.CodeSymbol;
|
import ghidra.program.database.symbol.CodeSymbol;
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.listing.Function;
|
import ghidra.program.model.listing.Function;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.pcode.HighFunction;
|
import ghidra.program.model.pcode.HighFunction;
|
||||||
|
import ghidra.program.model.pcode.PcodeOp;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
|
|
||||||
|
@ -39,16 +39,17 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
|
||||||
if (tokenAtCursor == null) {
|
if (tokenAtCursor == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Address addr = tokenAtCursor.getMinAddress();
|
|
||||||
if (addr == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Namespace overspace = HighFunction.findOverrideSpace(func);
|
Namespace overspace = HighFunction.findOverrideSpace(func);
|
||||||
if (overspace == null) {
|
if (overspace == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
PcodeOp op = OverridePrototypeAction.getCallOp(func.getProgram(), tokenAtCursor);
|
||||||
|
if (op == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
SymbolTable symtab = func.getProgram().getSymbolTable();
|
SymbolTable symtab = func.getProgram().getSymbolTable();
|
||||||
SymbolIterator iter = symtab.getSymbols(overspace);
|
SymbolIterator iter = symtab.getSymbolsAsIterator(op.getSeqnum().getTarget());
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
Symbol sym = iter.next();
|
Symbol sym = iter.next();
|
||||||
if (!sym.getName().startsWith("prt")) {
|
if (!sym.getName().startsWith("prt")) {
|
||||||
|
@ -57,7 +58,7 @@ public class DeletePrototypeOverrideAction extends AbstractDecompilerAction {
|
||||||
if (!(sym instanceof CodeSymbol)) {
|
if (!(sym instanceof CodeSymbol)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!sym.getAddress().equals(addr)) {
|
if (!sym.getParentNamespace().equals(overspace)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return (CodeSymbol) sym;
|
return (CodeSymbol) sym;
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class OverridePrototypeAction extends AbstractDecompilerAction {
|
||||||
* @param tokenAtCursor is the point in the window the user has selected
|
* @param tokenAtCursor is the point in the window the user has selected
|
||||||
* @return the PcodeOp or null
|
* @return the PcodeOp or null
|
||||||
*/
|
*/
|
||||||
private static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) {
|
public static PcodeOp getCallOp(Program program, ClangToken tokenAtCursor) {
|
||||||
if (tokenAtCursor == null) {
|
if (tokenAtCursor == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue