Adjust tryCallReturnPull

This commit is contained in:
caheckman 2020-02-21 11:57:31 -05:00
parent f28c377e9b
commit 6beb631e39
2 changed files with 119 additions and 130 deletions

View file

@ -193,38 +193,6 @@ SubvariableFlow::ReplaceOp *SubvariableFlow::createOpDown(OpCode opc,int4 numpar
return rop;
}
/// \brief Convert a new INDIRECT op into the logically trimmed variant of the given original PcodeOp
///
/// This method assumes the original op was an \e indirect \e creation. The input and output
/// Varnode for the new INDIRECT are not provided by this method. It only provides the
/// \e indirect \e effect input and patches up any active parameter recovery process.
/// \param newop is the new INDIRECT op to convert
/// \param oldop is the original INDIRECT op
/// \param out is the subgraph output variable node of the new INDIRECT
void SubvariableFlow::patchIndirect(PcodeOp *newop,PcodeOp *oldop, ReplaceVarnode *out)
{
PcodeOp *indop = PcodeOp::getOpFromConst(oldop->getIn(1)->getAddr());
bool possibleout = !oldop->getIn(0)->isIndirectZero();
Varnode *outvn = getReplaceVarnode(out);
fd->opSetOutput(newop,outvn);
fd->opSetOpcode(newop, CPUI_INDIRECT);
fd->opSetInput(newop,fd->newConstant(outvn->getSize(),0),0);
fd->opSetInput(newop,fd->newVarnodeIop(indop),1);
fd->markIndirectCreation(newop,possibleout);
fd->opInsertBefore(newop, indop);
FuncCallSpecs *fc = fd->getCallSpecs(indop);
if (fc == (FuncCallSpecs *)0) return;
if (fc->isOutputActive()) {
ParamActive *active = fc->getActiveOutput();
int4 trial = active->whichTrial( out->vn->getAddr(), out->vn->getSize() );
if (trial < 0)
throw LowlevelError("Cannot trim output trial to subflow");
Address addr = getReplacementAddress(out);
active->shrink(trial,addr,flowsize);
}
}
/// \brief Determine if the given subgraph variable can act as a parameter to the given CALL op
///
/// We assume the variable flows as a parameter to the CALL. If the CALL doesn't lock the parameter
@ -248,8 +216,8 @@ bool SubvariableFlow::tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
if (fc->isInputLocked() && (!fc->isDotdotdot())) return false;
patchlist.push_back(PatchRecord());
patchlist.back().type = 2;
patchlist.back().pullop = op;
patchlist.back().type = PatchRecord::parameter_patch;
patchlist.back().patchOp = op;
patchlist.back().in1 = rvn;
patchlist.back().slot = slot;
pullcount += 1; // A true terminal modification
@ -294,8 +262,8 @@ bool SubvariableFlow::tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
else if (retvn->isConstant() && retop != op) {
// Trace won't revisit this RETURN, so we need to generate patch now
patchlist.push_back(PatchRecord());
patchlist.back().type = 2;
patchlist.back().pullop = retop;
patchlist.back().type = PatchRecord::parameter_patch;
patchlist.back().patchOp = retop;
patchlist.back().in1 = rep;
patchlist.back().slot = slot;
pullcount += 1;
@ -304,8 +272,8 @@ bool SubvariableFlow::tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
returnsTraversed = true;
}
patchlist.push_back(PatchRecord());
patchlist.back().type = 2;
patchlist.back().pullop = op;
patchlist.back().type = PatchRecord::parameter_patch;
patchlist.back().patchOp = op;
patchlist.back().in1 = rvn;
patchlist.back().slot = slot;
pullcount += 1; // A true terminal modification
@ -319,24 +287,25 @@ bool SubvariableFlow::tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot)
/// \param op is the given INDIRECT
/// \param rvn is the given subgraph variable acting as the output of the INDIRECT
/// \return \b true if we can successfully trim the value to its logical size
bool SubvariableFlow::tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn)
bool SubvariableFlow::tryCallReturnPush(PcodeOp *op,ReplaceVarnode *rvn)
{
if (!op->isIndirectCreation()) return false;
PcodeOp *indop = PcodeOp::getOpFromConst(op->getIn(1)->getAddr());
FuncCallSpecs *fc = fd->getCallSpecs(indop);
if (!aggressive) {
if ((rvn->vn->getConsume()&~rvn->mask)!=0) // If there's something outside the mask being consumed
return false; // Don't truncate
}
if ((rvn->mask & 1) == 0) return false; // Verify the logical value is the least significant part
if (bitsize < 8) return false; // Make sure logical value is at least a byte
FuncCallSpecs *fc = fd->getCallSpecs(op);
if (fc == (FuncCallSpecs *)0) return false;
if (fc->isOutputLocked()) return false;
if (fc->isOutputActive()) {
ParamActive *active = fc->getActiveOutput();
int4 trial = active->whichTrial( rvn->vn->getAddr(), rvn->vn->getSize() );
if (trial < 0) return false;
Address newaddr = getReplacementAddress(rvn);
if (!active->testShrink(trial,newaddr, flowsize ))
return false;
}
createOp(CPUI_INDIRECT,2,rvn);
if (fc->isOutputActive()) return false; // Don't trim while in the middle of figuring out return value
patchlist.push_front(PatchRecord()); // Push to the front of the patch list
patchlist.front().type = PatchRecord::returnpush_patch;
patchlist.front().patchOp = op;
patchlist.front().in1 = rvn;
// pullcount += 1; // This is a push NOT a pull
return true;
}
@ -354,8 +323,8 @@ bool SubvariableFlow::trySwitchPull(PcodeOp *op,ReplaceVarnode *rvn)
if ((rvn->vn->getConsume()&~rvn->mask)!=0) // If there's something outside the mask being consumed
return false; // we can't trim
patchlist.push_back(PatchRecord());
patchlist.back().type = 2;
patchlist.back().pullop = op;
patchlist.back().type = PatchRecord::parameter_patch;
patchlist.back().patchOp = op;
patchlist.back().in1 = rvn;
patchlist.back().slot = 0;
pullcount += 1; // A true terminal modification
@ -764,12 +733,10 @@ bool SubvariableFlow::traceBackward(ReplaceVarnode *rvn)
return true;
}
break;
case CPUI_INDIRECT:
// TODO: This assumes that the INDIRECT is CALL-based. Add STORE-based logic.
if (aggressive) {
if (tryCallReturnPull(op,rvn))
return true;
}
case CPUI_CALL:
case CPUI_CALLIND:
if (tryCallReturnPush(op,rvn))
return true;
break;
case CPUI_INT_EQUAL:
case CPUI_INT_NOTEQUAL:
@ -928,11 +895,10 @@ bool SubvariableFlow::traceBackwardSext(ReplaceVarnode *rvn)
if (rop->input.size()==1)
addConstant(rop,calc_mask(op->getIn(1)->getSize()),1,op->getIn(1)->getOffset()); // Preserve the shift amount
return true;
case CPUI_INDIRECT:
if (aggressive) {
if (tryCallReturnPull(op,rvn))
return true;
}
case CPUI_CALL:
case CPUI_CALLIND:
if (tryCallReturnPush(op,rvn))
return true;
break;
default:
break;
@ -1060,8 +1026,8 @@ void SubvariableFlow::addTerminalPatch(PcodeOp *pullop,ReplaceVarnode *rvn)
{
patchlist.push_back(PatchRecord());
patchlist.back().type = 0; // Ultimately gets converted to a COPY
patchlist.back().pullop = pullop; // Operation pulling the variable out
patchlist.back().type = PatchRecord::copy_patch; // Ultimately gets converted to a COPY
patchlist.back().patchOp = pullop; // Operation pulling the variable out
patchlist.back().in1 = rvn; // Point in container flow for pull
pullcount += 1; // a true terminal modification
}
@ -1078,8 +1044,8 @@ void SubvariableFlow::addTerminalPatchSameOp(PcodeOp *pullop,ReplaceVarnode *rvn
{
patchlist.push_back(PatchRecord());
patchlist.back().type = 2; // Keep the original op, just change input
patchlist.back().pullop = pullop; // Operation pulling the variable out
patchlist.back().type = PatchRecord::parameter_patch; // Keep the original op, just change input
patchlist.back().patchOp = pullop; // Operation pulling the variable out
patchlist.back().in1 = rvn; // Point in container flow for pull
patchlist.back().slot = slot;
pullcount += 1; // a true terminal modification
@ -1096,8 +1062,8 @@ void SubvariableFlow::addBooleanPatch(PcodeOp *pullop,ReplaceVarnode *rvn,int4 s
{
patchlist.push_back(PatchRecord());
patchlist.back().type = 2; // Make no change to the operator, just put in the new input
patchlist.back().pullop = pullop; // Operation pulling the variable out
patchlist.back().type = PatchRecord::parameter_patch; // Make no change to the operator, just put in the new input
patchlist.back().patchOp = pullop; // Operation pulling the variable out
patchlist.back().in1 = rvn; // Point in container flow for pull
patchlist.back().slot = slot;
// this is not a true modification
@ -1114,9 +1080,9 @@ void SubvariableFlow::addSuggestedPatch(ReplaceVarnode *rvn,PcodeOp *pushop,int4
{
patchlist.push_back(PatchRecord());
patchlist.back().type = 3;
patchlist.back().type = PatchRecord::extension_patch;
patchlist.back().in1 = rvn;
patchlist.back().pullop = pushop;
patchlist.back().patchOp = pushop;
if (sa == -1)
sa = leastsigbit_set(rvn->mask);
patchlist.back().slot = sa;
@ -1134,8 +1100,8 @@ void SubvariableFlow::addComparePatch(ReplaceVarnode *in1,ReplaceVarnode *in2,Pc
{
patchlist.push_back(PatchRecord());
patchlist.back().type = 1;
patchlist.back().pullop = op;
patchlist.back().type = PatchRecord::compare_patch;
patchlist.back().patchOp = op;
patchlist.back().in1 = in1;
patchlist.back().in2 = in2;
pullcount += 1;
@ -1315,27 +1281,39 @@ bool SubvariableFlow::doTrace(void)
void SubvariableFlow::doReplacement(void)
{
list<PatchRecord>::iterator piter;
list<ReplaceOp>::iterator iter;
// Do up front processing of the call return patches, which will be at the front of the list
for(piter=patchlist.begin();piter!=patchlist.end();++piter) {
if ((*piter).type != PatchRecord::returnpush_patch) break;
PcodeOp *pushOp = (*piter).patchOp;
Varnode *newVn = getReplaceVarnode((*piter).in1);
Varnode *oldVn = pushOp->getOut();
fd->opSetOutput(pushOp, newVn);
// Create placeholder defining op for old Varnode, until dead code cleans it up
PcodeOp *newZext = fd->newOp(1, pushOp->getAddr());
fd->opSetOpcode(newZext, CPUI_INT_ZEXT);
fd->opSetInput(newZext,newVn,0);
fd->opSetOutput(newZext,oldVn);
fd->opInsertAfter(newZext, pushOp);
}
// Define all the outputs first
for(iter=oplist.begin();iter!=oplist.end();++iter) {
PcodeOp *newop = fd->newOp((*iter).numparams,(*iter).op->getAddr());
(*iter).replacement = newop;
if ((*iter).opc == CPUI_INDIRECT) {
patchIndirect( newop, (*iter).op, (*iter).output );
}
else {
fd->opSetOpcode(newop,(*iter).opc);
ReplaceVarnode *rout = (*iter).output;
// if (rout != (ReplaceVarnode *)0) {
// if (rout->replacement == (Varnode *)0)
// rout->replacement = fd->newUniqueOut(flowsize,newop);
// else
// fd->opSetOutput(newop,rout->replacement);
// }
fd->opSetOutput(newop,getReplaceVarnode(rout));
fd->opInsertAfter(newop,(*iter).op);
}
fd->opSetOpcode(newop,(*iter).opc);
ReplaceVarnode *rout = (*iter).output;
// if (rout != (ReplaceVarnode *)0) {
// if (rout->replacement == (Varnode *)0)
// rout->replacement = fd->newUniqueOut(flowsize,newop);
// else
// fd->opSetOutput(newop,rout->replacement);
// }
fd->opSetOutput(newop,getReplaceVarnode(rout));
fd->opInsertAfter(newop,(*iter).op);
}
// Set all the inputs
@ -1347,51 +1325,55 @@ void SubvariableFlow::doReplacement(void)
// These are operations that carry flow from the small variable into an existing
// variable of the correct size
list<PatchRecord>::iterator piter;
for(piter=patchlist.begin();piter!=patchlist.end();++piter) {
PcodeOp *pullop = (*piter).pullop;
int4 type = (*piter).type;
if (type == 0) {
for(;piter!=patchlist.end();++piter) {
PcodeOp *pullop = (*piter).patchOp;
switch((*piter).type) {
case PatchRecord::copy_patch:
while(pullop->numInput() > 1)
fd->opRemoveInput(pullop,pullop->numInput()-1);
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),0);
fd->opSetOpcode(pullop,CPUI_COPY);
}
else if (type == 1) { // A comparison
break;
case PatchRecord::compare_patch:
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),0);
fd->opSetInput(pullop,getReplaceVarnode((*piter).in2),1);
}
else if (type == 2) { // A call parameter, return value, or switch variable
break;
case PatchRecord::parameter_patch:
fd->opSetInput(pullop,getReplaceVarnode((*piter).in1),(*piter).slot);
}
else if (type == 3) {
// These are operations that flow the small variable into a bigger variable but
// where all the remaining bits are zero
int4 sa = (*piter).slot;
vector<Varnode *> invec;
Varnode *inVn = getReplaceVarnode((*piter).in1);
int4 outSize = pullop->getOut()->getSize();
if (sa == 0) {
invec.push_back( inVn );
OpCode opc = (inVn->getSize() == outSize) ? CPUI_COPY : CPUI_INT_ZEXT;
fd->opSetOpcode( pullop, opc );
fd->opSetAllInput(pullop,invec);
}
else {
if (inVn->getSize() != outSize) {
PcodeOp *zextop = fd->newOp(1,pullop->getAddr());
fd->opSetOpcode( zextop, CPUI_INT_ZEXT );
Varnode *zextout = fd->newUniqueOut(outSize,zextop);
fd->opSetInput(zextop,inVn,0);
fd->opInsertBefore(zextop,pullop);
invec.push_back(zextout);
}
else
break;
case PatchRecord::extension_patch:
{
// These are operations that flow the small variable into a bigger variable but
// where all the remaining bits are zero
int4 sa = (*piter).slot;
vector<Varnode *> invec;
Varnode *inVn = getReplaceVarnode((*piter).in1);
int4 outSize = pullop->getOut()->getSize();
if (sa == 0) {
invec.push_back(inVn);
invec.push_back(fd->newConstant(4,sa));
fd->opSetAllInput(pullop,invec);
fd->opSetOpcode( pullop, CPUI_INT_LEFT);
OpCode opc = (inVn->getSize() == outSize) ? CPUI_COPY : CPUI_INT_ZEXT;
fd->opSetOpcode(pullop, opc);
fd->opSetAllInput(pullop, invec);
}
else {
if (inVn->getSize() != outSize) {
PcodeOp *zextop = fd->newOp(1, pullop->getAddr());
fd->opSetOpcode(zextop, CPUI_INT_ZEXT);
Varnode *zextout = fd->newUniqueOut(outSize, zextop);
fd->opSetInput(zextop, inVn, 0);
fd->opInsertBefore(zextop, pullop);
invec.push_back(zextout);
}
else
invec.push_back(inVn);
invec.push_back(fd->newConstant(4, sa));
fd->opSetAllInput(pullop, invec);
fd->opSetOpcode(pullop, CPUI_INT_LEFT);
}
break;
}
case PatchRecord::returnpush_patch:
break; // Shouldn't see these here, handled earlier
}
}
}

View file

@ -62,8 +62,16 @@ class SubvariableFlow {
/// \brief Operation with a new logical value as (part of) input, but output Varnode is unchanged
class PatchRecord {
friend class SubvariableFlow;
int4 type; ///< 0=COPY 1=compare 2=call/return/branchind 3=AND/SHIFT
PcodeOp *pullop; ///< Op being affected
/// The possible types of patches on ops being performed
enum patchtype {
copy_patch, ///< Turn op into a COPY of the logical value
compare_patch, ///< Turn compare op inputs into logical values
parameter_patch, ///< Convert a CALL/CALLIND/RETURN/BRANCHIND parameter into logical value
extension_patch, ///< Convert op into something that copies/extends logical value, adding zero bits
returnpush_patch ///< Convert a sub-function return to the logical value
};
patchtype type; ///< The type of \b this patch
PcodeOp *patchOp; ///< Op being affected
ReplaceVarnode *in1; ///< The logical variable input
ReplaceVarnode *in2; ///< (optional second parameter)
int4 slot; ///< slot being affected or other parameter
@ -87,10 +95,9 @@ class SubvariableFlow {
ReplaceVarnode *setReplacement(Varnode *vn,uintb mask,bool &inworklist);
ReplaceOp *createOp(OpCode opc,int4 numparam,ReplaceVarnode *outrvn);
ReplaceOp *createOpDown(OpCode opc,int4 numparam,PcodeOp *op,ReplaceVarnode *inrvn,int4 slot);
void patchIndirect(PcodeOp *newop,PcodeOp *oldop,ReplaceVarnode *out);
bool tryCallPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot);
bool tryReturnPull(PcodeOp *op,ReplaceVarnode *rvn,int4 slot);
bool tryCallReturnPull(PcodeOp *op,ReplaceVarnode *rvn);
bool tryCallReturnPush(PcodeOp *op,ReplaceVarnode *rvn);
bool trySwitchPull(PcodeOp *op,ReplaceVarnode *rvn);
bool traceForward(ReplaceVarnode *rvn); ///< Trace the logical data-flow forward for the given subgraph variable
bool traceBackward(ReplaceVarnode *rvn); ///< Trace the logical data-flow backward for the given subgraph variable