mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 13:42:06 +00:00
GP-3613 Support for return storage on stack
This commit is contained in:
parent
22e5c1a48b
commit
e9b5be17c1
@ -55,6 +55,7 @@ src/decompile/datatests/readvolatile.xml||GHIDRA||||END|
|
|||||||
src/decompile/datatests/retstruct.xml||GHIDRA||||END|
|
src/decompile/datatests/retstruct.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/sbyte.xml||GHIDRA||||END|
|
src/decompile/datatests/sbyte.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/skipnext2.xml||GHIDRA||||END|
|
src/decompile/datatests/skipnext2.xml||GHIDRA||||END|
|
||||||
|
src/decompile/datatests/stackreturn.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/statuscmp.xml||GHIDRA||||END|
|
src/decompile/datatests/statuscmp.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/switchind.xml||GHIDRA||||END|
|
src/decompile/datatests/switchind.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/threedim.xml||GHIDRA||||END|
|
src/decompile/datatests/threedim.xml||GHIDRA||||END|
|
||||||
|
@ -1501,6 +1501,11 @@ void ActionFuncLink::funcLinkOutput(FuncCallSpecs *fc,Funcdata &data)
|
|||||||
if (sz == 1 && outtype->getMetatype() == TYPE_BOOL && data.isTypeRecoveryOn())
|
if (sz == 1 && outtype->getMetatype() == TYPE_BOOL && data.isTypeRecoveryOn())
|
||||||
data.opMarkCalculatedBool(callop);
|
data.opMarkCalculatedBool(callop);
|
||||||
Address addr = outparam->getAddress();
|
Address addr = outparam->getAddress();
|
||||||
|
if (addr.getSpace()->getType() == IPTR_SPACEBASE) {
|
||||||
|
// Delay creating output Varnode until heritage of the stack, when we know relative value of the stack pointer
|
||||||
|
fc->setStackOutputLock(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
data.newVarnodeOut(sz,addr,callop);
|
data.newVarnodeOut(sz,addr,callop);
|
||||||
VarnodeData vdata;
|
VarnodeData vdata;
|
||||||
OpCode res = fc->assumedOutputExtension(addr,sz,vdata);
|
OpCode res = fc->assumedOutputExtension(addr,sz,vdata);
|
||||||
|
@ -4658,6 +4658,7 @@ FuncCallSpecs::FuncCallSpecs(PcodeOp *call_op)
|
|||||||
isinputactive = false;
|
isinputactive = false;
|
||||||
isoutputactive = false;
|
isoutputactive = false;
|
||||||
isbadjumptable = false;
|
isbadjumptable = false;
|
||||||
|
isstackoutputlock = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FuncCallSpecs::setFuncdata(Funcdata *f)
|
void FuncCallSpecs::setFuncdata(Funcdata *f)
|
||||||
|
@ -1479,9 +1479,11 @@ public:
|
|||||||
void updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFactory *factory);
|
void updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFactory *factory);
|
||||||
void updateAllTypes(const vector<string> &namelist,const vector<Datatype *> &typelist,bool dtdtdt);
|
void updateAllTypes(const vector<string> &namelist,const vector<Datatype *> &typelist,bool dtdtdt);
|
||||||
ProtoParameter *getParam(int4 i) const { return store->getInput(i); } ///< Get the i-th input parameter
|
ProtoParameter *getParam(int4 i) const { return store->getInput(i); } ///< Get the i-th input parameter
|
||||||
|
void setParam(int4 i,const string &name,const ParameterPieces &piece) { store->setInput(i, name, piece); } ///< Set parameter storage directly
|
||||||
void removeParam(int4 i) { store->clearInput(i); } ///< Remove the i-th input parameter
|
void removeParam(int4 i) { store->clearInput(i); } ///< Remove the i-th input parameter
|
||||||
int4 numParams(void) const { return store->getNumInputs(); } ///< Get the number of input parameters
|
int4 numParams(void) const { return store->getNumInputs(); } ///< Get the number of input parameters
|
||||||
ProtoParameter *getOutput(void) const { return store->getOutput(); } ///< Get the return value
|
ProtoParameter *getOutput(void) const { return store->getOutput(); } ///< Get the return value
|
||||||
|
void setOutput(const ParameterPieces &piece) { store->setOutput(piece); } ///< Set return value storage directly
|
||||||
Datatype *getOutputType(void) const { return store->getOutput()->getType(); } ///< Get the return value data-type
|
Datatype *getOutputType(void) const { return store->getOutput()->getType(); } ///< Get the return value data-type
|
||||||
const RangeList &getLocalRange(void) const { return model->getLocalRange(); } ///< Get the range of potential local stack variables
|
const RangeList &getLocalRange(void) const { return model->getLocalRange(); } ///< Get the range of potential local stack variables
|
||||||
const RangeList &getParamRange(void) const { return model->getParamRange(); } ///< Get the range of potential stack parameters
|
const RangeList &getParamRange(void) const { return model->getParamRange(); } ///< Get the range of potential stack parameters
|
||||||
@ -1601,6 +1603,7 @@ class FuncCallSpecs : public FuncProto {
|
|||||||
bool isinputactive; ///< Are we actively trying to recover input parameters
|
bool isinputactive; ///< Are we actively trying to recover input parameters
|
||||||
bool isoutputactive; ///< Are we actively trying to recover output parameters
|
bool isoutputactive; ///< Are we actively trying to recover output parameters
|
||||||
bool isbadjumptable; ///< Was the call originally a jump-table we couldn't recover
|
bool isbadjumptable; ///< Was the call originally a jump-table we couldn't recover
|
||||||
|
bool isstackoutputlock; ///< Do we have a locked output on the stack
|
||||||
Varnode *getSpacebaseRelative(void) const; ///< Get the active stack-pointer Varnode at \b this call site
|
Varnode *getSpacebaseRelative(void) const; ///< Get the active stack-pointer Varnode at \b this call site
|
||||||
Varnode *buildParam(Funcdata &data,Varnode *vn,ProtoParameter *param,Varnode *stackref);
|
Varnode *buildParam(Funcdata &data,Varnode *vn,ProtoParameter *param,Varnode *stackref);
|
||||||
int4 transferLockedInputParam(ProtoParameter *param);
|
int4 transferLockedInputParam(ProtoParameter *param);
|
||||||
@ -1642,6 +1645,8 @@ public:
|
|||||||
bool isOutputActive(void) const { return isoutputactive; } ///< Return \b true if return value recovery analysis is active
|
bool isOutputActive(void) const { return isoutputactive; } ///< Return \b true if return value recovery analysis is active
|
||||||
void setBadJumpTable(bool val) { isbadjumptable = val; } ///< Toggle whether \b call site looked like an indirect jump
|
void setBadJumpTable(bool val) { isbadjumptable = val; } ///< Toggle whether \b call site looked like an indirect jump
|
||||||
bool isBadJumpTable(void) const { return isbadjumptable; } ///< Return \b true if \b this call site looked like an indirect jump
|
bool isBadJumpTable(void) const { return isbadjumptable; } ///< Return \b true if \b this call site looked like an indirect jump
|
||||||
|
void setStackOutputLock(bool val) { isstackoutputlock = val; } ///< Toggle whether output is locked and on the stack
|
||||||
|
bool isStackOutputLock(void) const { return isstackoutputlock; } ///< Return \b true if return value is locked and on the stack
|
||||||
ParamActive *getActiveInput(void) { return &activeinput; } ///< Get the analysis object for input parameter recovery
|
ParamActive *getActiveInput(void) { return &activeinput; } ///< Get the analysis object for input parameter recovery
|
||||||
ParamActive *getActiveOutput(void) { return &activeoutput; } ///< Get the analysis object for return value recovery
|
ParamActive *getActiveOutput(void) { return &activeoutput; } ///< Get the analysis object for return value recovery
|
||||||
|
|
||||||
|
@ -1168,10 +1168,10 @@ void Heritage::guardCallOverlappingInput(FuncCallSpecs *fc,const Address &addr,c
|
|||||||
if (fc->getBiggestContainedInputParam(transAddr, size, vData)) {
|
if (fc->getBiggestContainedInputParam(transAddr, size, vData)) {
|
||||||
ParamActive *active = fc->getActiveInput();
|
ParamActive *active = fc->getActiveInput();
|
||||||
Address truncAddr(vData.space,vData.offset);
|
Address truncAddr(vData.space,vData.offset);
|
||||||
|
int4 diff = (int4)(truncAddr.getOffset() - transAddr.getOffset());
|
||||||
|
truncAddr = addr + diff; // Convert truncated Address to caller's perspective
|
||||||
if (active->whichTrial(truncAddr, size) < 0) { // If not already a trial
|
if (active->whichTrial(truncAddr, size) < 0) { // If not already a trial
|
||||||
int4 truncateAmount = transAddr.justifiedContain(size, truncAddr, vData.size, false);
|
int4 truncateAmount = addr.justifiedContain(size, truncAddr, vData.size, false);
|
||||||
int4 diff = (int4)(truncAddr.getOffset() - transAddr.getOffset());
|
|
||||||
truncAddr = addr + diff; // Convert truncated Address to caller's perspective
|
|
||||||
PcodeOp *op = fc->getOp();
|
PcodeOp *op = fc->getOp();
|
||||||
PcodeOp *subpieceOp = fd->newOp(2,op->getAddr());
|
PcodeOp *subpieceOp = fd->newOp(2,op->getAddr());
|
||||||
fd->opSetOpcode(subpieceOp, CPUI_SUBPIECE);
|
fd->opSetOpcode(subpieceOp, CPUI_SUBPIECE);
|
||||||
@ -1187,51 +1187,43 @@ void Heritage::guardCallOverlappingInput(FuncCallSpecs *fc,const Address &addr,c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Guard an address range that is larger than the possible output storage
|
/// \brief Insert \e created INDIRECT ops, to guard the output of a call
|
||||||
///
|
///
|
||||||
/// A potential return value should look like an \b indirect \b creation at this stage,
|
/// The potential \e return storage is an \b indirect \b creation at this stage, and the guarded range properly
|
||||||
/// but the range is even bigger. We split it up into 2 or 3 Varnodes, and make each one via
|
/// contains the return storage. We split the full range up into 2 or 3 Varnodes, and make each one via
|
||||||
/// an INDIRECT. The piece corresponding to the potential return value is registered, and all
|
/// an INDIRECT. The pieces are concatenated to form a Varnode of the whole range.
|
||||||
/// the pieces are concatenated to form a Varnode of the whole range.
|
/// \param callOp is the call causing the indirection
|
||||||
/// \param fc is the call site potentially returning a value
|
/// \param addr is the starting address of the full range
|
||||||
/// \param addr is the starting address of the range
|
/// \param size is the size of the full range in bytes
|
||||||
/// \param size is the size of the range in bytes
|
/// \param retAddr is the starting address of the \e return storage
|
||||||
|
/// \param retSize is the size of the \e return storage in bytes
|
||||||
/// \param write is the set of new written Varnodes
|
/// \param write is the set of new written Varnodes
|
||||||
/// \return \b true if the INDIRECTs were created
|
void Heritage::guardOutputOverlap(PcodeOp *callOp,const Address &addr,int4 size,const Address &retAddr, int4 retSize,
|
||||||
bool Heritage::guardCallOverlappingOutput(FuncCallSpecs *fc,const Address &addr,int4 size,vector<Varnode *> &write)
|
vector<Varnode *> &write)
|
||||||
|
|
||||||
{
|
{
|
||||||
VarnodeData vData;
|
int4 sizeFront = (int4)(retAddr.getOffset() - addr.getOffset());
|
||||||
|
int4 sizeBack = size - retSize - sizeFront;
|
||||||
if (!fc->getBiggestContainedOutput(addr, size, vData))
|
PcodeOp *indOp = fd->newIndirectCreation(callOp,retAddr,retSize,true);
|
||||||
return false;
|
|
||||||
ParamActive *active = fc->getActiveOutput();
|
|
||||||
Address truncAddr(vData.space,vData.offset);
|
|
||||||
if (active->whichTrial(truncAddr, size) >= 0)
|
|
||||||
return false; // Trial already exists
|
|
||||||
int4 sizeFront = (int4)(vData.offset - addr.getOffset());
|
|
||||||
int4 sizeBack = size - vData.size - sizeFront;
|
|
||||||
PcodeOp *indOp = fd->newIndirectCreation(fc->getOp(),truncAddr,vData.size,true);
|
|
||||||
Varnode *vnCollect = indOp->getOut();
|
Varnode *vnCollect = indOp->getOut();
|
||||||
PcodeOp *insertPoint = fc->getOp();
|
PcodeOp *insertPoint = callOp;
|
||||||
if (sizeFront != 0) {
|
if (sizeFront != 0) {
|
||||||
PcodeOp *indOpFront = fd->newIndirectCreation(indOp,addr,sizeFront,false);
|
PcodeOp *indOpFront = fd->newIndirectCreation(indOp,addr,sizeFront,false);
|
||||||
Varnode *newFront = indOpFront->getOut();
|
Varnode *newFront = indOpFront->getOut();
|
||||||
PcodeOp *concatFront = fd->newOp(2,indOp->getAddr());
|
PcodeOp *concatFront = fd->newOp(2,indOp->getAddr());
|
||||||
int4 slotNew = vData.space->isBigEndian() ? 0 : 1;
|
int4 slotNew = retAddr.isBigEndian() ? 0 : 1;
|
||||||
fd->opSetOpcode(concatFront, CPUI_PIECE);
|
fd->opSetOpcode(concatFront, CPUI_PIECE);
|
||||||
fd->opSetInput(concatFront,newFront,slotNew);
|
fd->opSetInput(concatFront,newFront,slotNew);
|
||||||
fd->opSetInput(concatFront,vnCollect,1-slotNew);
|
fd->opSetInput(concatFront,vnCollect,1-slotNew);
|
||||||
vnCollect = fd->newVarnodeOut(sizeFront+vData.size,addr,concatFront);
|
vnCollect = fd->newVarnodeOut(sizeFront+retSize,addr,concatFront);
|
||||||
fd->opInsertAfter(concatFront, insertPoint);
|
fd->opInsertAfter(concatFront, insertPoint);
|
||||||
insertPoint = concatFront;
|
insertPoint = concatFront;
|
||||||
}
|
}
|
||||||
if (sizeBack != 0) {
|
if (sizeBack != 0) {
|
||||||
Address addrBack = truncAddr + vData.size;
|
Address addrBack = retAddr + retSize;
|
||||||
PcodeOp *indOpBack = fd->newIndirectCreation(fc->getOp(),addrBack,sizeBack,false);
|
PcodeOp *indOpBack = fd->newIndirectCreation(callOp,addrBack,sizeBack,false);
|
||||||
Varnode *newBack = indOpBack->getOut();
|
Varnode *newBack = indOpBack->getOut();
|
||||||
PcodeOp *concatBack = fd->newOp(2,indOp->getAddr());
|
PcodeOp *concatBack = fd->newOp(2,indOp->getAddr());
|
||||||
int4 slotNew = vData.space->isBigEndian() ? 1 : 0;
|
int4 slotNew = retAddr.isBigEndian() ? 1 : 0;
|
||||||
fd->opSetOpcode(concatBack, CPUI_PIECE);
|
fd->opSetOpcode(concatBack, CPUI_PIECE);
|
||||||
fd->opSetInput(concatBack,newBack,slotNew);
|
fd->opSetInput(concatBack,newBack,slotNew);
|
||||||
fd->opSetInput(concatBack,vnCollect,1-slotNew);
|
fd->opSetInput(concatBack,vnCollect,1-slotNew);
|
||||||
@ -1240,10 +1232,157 @@ bool Heritage::guardCallOverlappingOutput(FuncCallSpecs *fc,const Address &addr,
|
|||||||
}
|
}
|
||||||
vnCollect->setActiveHeritage();
|
vnCollect->setActiveHeritage();
|
||||||
write.push_back(vnCollect);
|
write.push_back(vnCollect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Try to guard an address range that is larger than the possible output storage
|
||||||
|
///
|
||||||
|
/// \param fc is the call site potentially returning a value
|
||||||
|
/// \param addr is the starting address of the range
|
||||||
|
/// \param addr is the starting address of the range relative to the callee
|
||||||
|
/// \param size is the size of the range in bytes
|
||||||
|
/// \param write is the set of new written Varnodes
|
||||||
|
/// \return \b true if the INDIRECTs were created
|
||||||
|
bool Heritage::tryOutputOverlapGuard(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size,
|
||||||
|
vector<Varnode *> &write)
|
||||||
|
|
||||||
|
{
|
||||||
|
VarnodeData vData;
|
||||||
|
|
||||||
|
if (!fc->getBiggestContainedOutput(transAddr, size, vData))
|
||||||
|
return false;
|
||||||
|
ParamActive *active = fc->getActiveOutput();
|
||||||
|
Address truncAddr(vData.space,vData.offset);
|
||||||
|
int4 diff = (int4)(truncAddr.getOffset() - transAddr.getOffset());
|
||||||
|
truncAddr = addr + diff; // Convert truncated Address to caller's perspective
|
||||||
|
if (active->whichTrial(truncAddr, size) >= 0)
|
||||||
|
return false; // Trial already exists
|
||||||
|
guardOutputOverlap(fc->getOp(),addr,size,truncAddr,vData.size,write);
|
||||||
active->registerTrial(truncAddr, vData.size);
|
active->registerTrial(truncAddr, vData.size);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Guard a stack range that properly contains the return value storage for a call
|
||||||
|
///
|
||||||
|
/// The full range is assumed to have a related value \e before the call. The pieces on either side
|
||||||
|
/// of the return value storage, or extracted via SUBPIECE, they flow through the call via INDIRECT, then
|
||||||
|
/// they rejoin together with the return storage via one or more PIECE operations.
|
||||||
|
/// \param callOp is the call being guarded
|
||||||
|
/// \param addr is the starting address of the stack range
|
||||||
|
/// \param size is the number of bytes in the range
|
||||||
|
/// \param retAddr is the starting address of the return storage
|
||||||
|
/// \param retSize is the number of bytes of return storage
|
||||||
|
/// \param write is the list of written Varnodes in the range (may be updated)
|
||||||
|
void Heritage::guardOutputOverlapStack(PcodeOp *callOp,const Address &addr,int4 size,
|
||||||
|
const Address &retAddr,int4 retSize,vector<Varnode *> &write)
|
||||||
|
{
|
||||||
|
int4 sizeFront = (int4)(retAddr.getOffset() - addr.getOffset());
|
||||||
|
int4 sizeBack = size - retSize - sizeFront;
|
||||||
|
PcodeOp *insertPoint = callOp;
|
||||||
|
Varnode *vnCollect = callOp->getOut();
|
||||||
|
if (vnCollect == (Varnode *)0)
|
||||||
|
vnCollect = fd->newVarnodeOut(retSize,retAddr,callOp);
|
||||||
|
if (sizeFront != 0) {
|
||||||
|
Varnode *newInput = fd->newVarnode(size,addr);
|
||||||
|
newInput->setActiveHeritage();
|
||||||
|
PcodeOp *subPiece = fd->newOp(2, callOp->getAddr());
|
||||||
|
fd->opSetOpcode(subPiece, CPUI_SUBPIECE);
|
||||||
|
int4 truncateAmount = addr.justifiedContain(size, addr, sizeFront, false);
|
||||||
|
fd->opSetInput(subPiece,fd->newConstant(4,truncateAmount),1);
|
||||||
|
fd->opSetInput(subPiece,newInput,0);
|
||||||
|
PcodeOp *indOpFront = fd->newIndirectOp(callOp,addr,sizeFront,0);
|
||||||
|
fd->opSetOutput(subPiece,indOpFront->getIn(0));
|
||||||
|
fd->opInsertBefore(subPiece,callOp);
|
||||||
|
Varnode *newFront = indOpFront->getOut();
|
||||||
|
PcodeOp *concatFront = fd->newOp(2,callOp->getAddr());
|
||||||
|
int4 slotNew = retAddr.isBigEndian() ? 0 : 1;
|
||||||
|
fd->opSetOpcode(concatFront, CPUI_PIECE);
|
||||||
|
fd->opSetInput(concatFront,newFront,slotNew);
|
||||||
|
fd->opSetInput(concatFront,vnCollect,1-slotNew);
|
||||||
|
vnCollect = fd->newVarnodeOut(sizeFront+retSize,addr,concatFront);
|
||||||
|
fd->opInsertAfter(concatFront, insertPoint);
|
||||||
|
insertPoint = concatFront;
|
||||||
|
}
|
||||||
|
if (sizeBack != 0) {
|
||||||
|
Varnode *newInput = fd->newVarnode(size,addr);
|
||||||
|
newInput->setActiveHeritage();
|
||||||
|
Address addrBack = retAddr + retSize;
|
||||||
|
PcodeOp *subPiece = fd->newOp(2, callOp->getAddr());
|
||||||
|
fd->opSetOpcode(subPiece, CPUI_SUBPIECE);
|
||||||
|
int4 truncateAmount = addr.justifiedContain(size, addrBack, sizeBack, false);
|
||||||
|
fd->opSetInput(subPiece,fd->newConstant(4,truncateAmount),1);
|
||||||
|
fd->opSetInput(subPiece,newInput,0);
|
||||||
|
PcodeOp *indOpBack = fd->newIndirectOp(callOp,addrBack,sizeBack,0);
|
||||||
|
fd->opSetOutput(subPiece,indOpBack->getIn(0));
|
||||||
|
fd->opInsertBefore(subPiece,callOp);
|
||||||
|
Varnode *newBack = indOpBack->getOut();
|
||||||
|
PcodeOp *concatBack = fd->newOp(2,callOp->getAddr());
|
||||||
|
int4 slotNew = retAddr.isBigEndian() ? 1 : 0;
|
||||||
|
fd->opSetOpcode(concatBack, CPUI_PIECE);
|
||||||
|
fd->opSetInput(concatBack,newBack,slotNew);
|
||||||
|
fd->opSetInput(concatBack,vnCollect,1-slotNew);
|
||||||
|
vnCollect = fd->newVarnodeOut(size,addr,concatBack);
|
||||||
|
fd->opInsertAfter(concatBack, insertPoint);
|
||||||
|
}
|
||||||
|
vnCollect->setActiveHeritage();
|
||||||
|
write.push_back(vnCollect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Attempt to guard a stack range against a call that returns a value overlapping that range
|
||||||
|
///
|
||||||
|
/// The call is assumed to have a locked output that is stored on the stack. This method creates the
|
||||||
|
/// actual output Varnode of the call op (creation has been deliberately delayed until now by ActionFuncLink).
|
||||||
|
/// If the return storage contains the guard range, a guard range Varnode is pulled off the return via SUBPIECE.
|
||||||
|
/// If the guard range contains the return storage, a final guard range Varnode is created by piecing together
|
||||||
|
/// the return storage with other stack varnodes that flow via INDIRECT through the call.
|
||||||
|
/// \param fc is the call being guarded
|
||||||
|
/// \param addr is the starting address of the range being guarded (relative to the caller's stack pointer)
|
||||||
|
/// \param transAddr is the starting address of the range (relative to the callee's stack pointer)
|
||||||
|
/// \param size is the number of bytes in the range
|
||||||
|
/// \param outputCharacter indicates the type of containment between the guarded range and the return storage
|
||||||
|
/// \param write is the list of written Varnodes in the range (may be updated)
|
||||||
|
/// \return \b true if the range was successfully guarded
|
||||||
|
bool Heritage::tryOutputStackGuard(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size,
|
||||||
|
int4 outputCharacter,vector<Varnode *> &write)
|
||||||
|
{
|
||||||
|
PcodeOp *callOp = fc->getOp();
|
||||||
|
if (outputCharacter == ParamEntry::contained_by) {
|
||||||
|
VarnodeData vData;
|
||||||
|
|
||||||
|
if (!fc->getBiggestContainedOutput(transAddr, size, vData))
|
||||||
|
return false;
|
||||||
|
Address truncAddr(vData.space,vData.offset);
|
||||||
|
int4 diff = (int4)(truncAddr.getOffset() - transAddr.getOffset());
|
||||||
|
truncAddr = addr + diff; // Convert truncated Address to caller's perspective
|
||||||
|
guardOutputOverlapStack(callOp,addr,size,truncAddr,vData.size,write);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Reaching here, output exists and contains the heritage range
|
||||||
|
Address retAddr = fc->getOutput()->getAddress();
|
||||||
|
int4 diff = (int4)(addr.getOffset() - transAddr.getOffset());
|
||||||
|
retAddr = retAddr + diff; // Translate output address to caller perspective
|
||||||
|
int4 retSize = fc->getOutput()->getSize();
|
||||||
|
Varnode *outvn = callOp->getOut();
|
||||||
|
Varnode *vnFinal = (Varnode *)0;
|
||||||
|
if (outvn == (Varnode *)0) {
|
||||||
|
outvn = fd->newVarnodeOut(retSize, retAddr, callOp);
|
||||||
|
vnFinal = outvn;
|
||||||
|
}
|
||||||
|
if (size < retSize) {
|
||||||
|
PcodeOp *subPiece = fd->newOp(2, callOp->getAddr());
|
||||||
|
fd->opSetOpcode(subPiece, CPUI_SUBPIECE);
|
||||||
|
int4 truncateAmount = retAddr.justifiedContain(retSize, addr, size, false);
|
||||||
|
fd->opSetInput(subPiece,fd->newConstant(4,truncateAmount),1);
|
||||||
|
fd->opSetInput(subPiece,outvn,0);
|
||||||
|
vnFinal = fd->newVarnodeOut(size, addr, subPiece);
|
||||||
|
fd->opInsertAfter(subPiece, callOp);
|
||||||
|
}
|
||||||
|
if (vnFinal != (Varnode *)0) {
|
||||||
|
vnFinal->setActiveHeritage();
|
||||||
|
write.push_back(vnFinal);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Guard CALL/CALLIND ops in preparation for renaming algorithm
|
/// \brief Guard CALL/CALLIND ops in preparation for renaming algorithm
|
||||||
///
|
///
|
||||||
/// For the given address range, we decide what the data-flow effect is
|
/// For the given address range, we decide what the data-flow effect is
|
||||||
@ -1268,51 +1407,57 @@ void Heritage::guardCalls(uint4 fl,const Address &addr,int4 size,vector<Varnode
|
|||||||
Varnode *vn = fc->getOp()->getOut();
|
Varnode *vn = fc->getOp()->getOut();
|
||||||
if ((vn->getAddr()==addr)&&(vn->getSize()==size)) continue;
|
if ((vn->getAddr()==addr)&&(vn->getSize()==size)) continue;
|
||||||
}
|
}
|
||||||
effecttype = fc->hasEffectTranslate(addr,size);
|
AddrSpace *spc = addr.getSpace();
|
||||||
|
uintb off = addr.getOffset();
|
||||||
|
bool tryregister = true;
|
||||||
|
if (spc->getType() == IPTR_SPACEBASE) {
|
||||||
|
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
|
||||||
|
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
|
||||||
|
else
|
||||||
|
tryregister = false; // Do not attempt to register this stack loc as a trial
|
||||||
|
}
|
||||||
|
Address transAddr(spc,off); // Address relative to callee's stack
|
||||||
|
effecttype = fc->hasEffect(transAddr,size);
|
||||||
bool possibleoutput = false;
|
bool possibleoutput = false;
|
||||||
if (fc->isOutputActive()) {
|
if (fc->isOutputActive() && tryregister) {
|
||||||
ParamActive *active = fc->getActiveOutput();
|
ParamActive *active = fc->getActiveOutput();
|
||||||
int4 outputCharacter = fc->characterizeAsOutput(addr, size);
|
int4 outputCharacter = fc->characterizeAsOutput(transAddr, size);
|
||||||
if (outputCharacter != ParamEntry::no_containment) {
|
if (outputCharacter != ParamEntry::no_containment) {
|
||||||
effecttype = EffectRecord::killedbycall; // A potential output is always killed by call
|
effecttype = EffectRecord::killedbycall; // A potential output is always killed by call
|
||||||
if (outputCharacter == ParamEntry::contained_by) {
|
if (outputCharacter == ParamEntry::contained_by) {
|
||||||
if (guardCallOverlappingOutput(fc, addr, size, write))
|
if (tryOutputOverlapGuard(fc, addr, transAddr, size, write))
|
||||||
effecttype = EffectRecord::unaffected; // Range is handled, don't do additional guarding
|
effecttype = EffectRecord::unaffected; // Range is handled, don't do additional guarding
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (active->whichTrial(addr,size)<0) { // If not already a trial
|
if (active->whichTrial(transAddr,size)<0) { // If not already a trial
|
||||||
active->registerTrial(addr,size);
|
active->registerTrial(transAddr,size);
|
||||||
possibleoutput = true;
|
possibleoutput = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fc->isInputActive()) {
|
else if (fc->isStackOutputLock() && tryregister) {
|
||||||
AddrSpace *spc = addr.getSpace();
|
int4 outputCharacter = fc->characterizeAsOutput(transAddr, size);
|
||||||
uintb off = addr.getOffset();
|
if (outputCharacter != ParamEntry::no_containment) {
|
||||||
bool tryregister = true;
|
effecttype = EffectRecord::unknown_effect;
|
||||||
if (spc->getType() == IPTR_SPACEBASE) {
|
if (tryOutputStackGuard(fc, addr, transAddr, size, outputCharacter, write))
|
||||||
if (fc->getSpacebaseOffset() != FuncCallSpecs::offset_unknown)
|
effecttype = EffectRecord::unaffected; // Range is handled
|
||||||
off = spc->wrapOffset(off - fc->getSpacebaseOffset());
|
|
||||||
else
|
|
||||||
tryregister = false; // Do not attempt to register this stack loc as a trial
|
|
||||||
}
|
}
|
||||||
Address transAddr(spc,off); // Address relative to callee's stack
|
}
|
||||||
if (tryregister) {
|
if (fc->isInputActive() && tryregister) {
|
||||||
int4 inputCharacter = fc->characterizeAsInputParam(transAddr,size);
|
int4 inputCharacter = fc->characterizeAsInputParam(transAddr,size);
|
||||||
if (inputCharacter == ParamEntry::contains_justified) { // Call could be using this range as an input parameter
|
if (inputCharacter == ParamEntry::contains_justified) { // Call could be using this range as an input parameter
|
||||||
ParamActive *active = fc->getActiveInput();
|
ParamActive *active = fc->getActiveInput();
|
||||||
if (active->whichTrial(transAddr,size)<0) { // If not already a trial
|
if (active->whichTrial(transAddr,size)<0) { // If not already a trial
|
||||||
PcodeOp *op = fc->getOp();
|
PcodeOp *op = fc->getOp();
|
||||||
active->registerTrial(transAddr,size);
|
active->registerTrial(transAddr,size);
|
||||||
Varnode *vn = fd->newVarnode(size,addr);
|
Varnode *vn = fd->newVarnode(size,addr);
|
||||||
vn->setActiveHeritage();
|
vn->setActiveHeritage();
|
||||||
fd->opInsertInput(op,vn,op->numInput());
|
fd->opInsertInput(op,vn,op->numInput());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (inputCharacter == ParamEntry::contained_by) // Call may be using part of this range as an input parameter
|
|
||||||
guardCallOverlappingInput(fc, addr, transAddr, size);
|
|
||||||
}
|
}
|
||||||
|
else if (inputCharacter == ParamEntry::contained_by) // Call may be using part of this range as an input parameter
|
||||||
|
guardCallOverlappingInput(fc, addr, transAddr, size);
|
||||||
}
|
}
|
||||||
// We do not guard the call if the effect is "unaffected" or "reload"
|
// We do not guard the call if the effect is "unaffected" or "reload"
|
||||||
if ((effecttype == EffectRecord::unknown_effect)||(effecttype == EffectRecord::return_address)) {
|
if ((effecttype == EffectRecord::unknown_effect)||(effecttype == EffectRecord::return_address)) {
|
||||||
|
@ -252,7 +252,13 @@ class Heritage {
|
|||||||
vector<Varnode *> &read,vector<Varnode *> &write,vector<Varnode *> &inputvars);
|
vector<Varnode *> &read,vector<Varnode *> &write,vector<Varnode *> &inputvars);
|
||||||
void guardInput(const Address &addr,int4 size,vector<Varnode *> &input);
|
void guardInput(const Address &addr,int4 size,vector<Varnode *> &input);
|
||||||
void guardCallOverlappingInput(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size);
|
void guardCallOverlappingInput(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size);
|
||||||
bool guardCallOverlappingOutput(FuncCallSpecs *fc,const Address &addr,int4 size,vector<Varnode *> &write);
|
void guardOutputOverlap(PcodeOp *callOp,const Address &addr,int4 size,const Address &retAddr,int4 retSize,
|
||||||
|
vector<Varnode *> &write);
|
||||||
|
bool tryOutputOverlapGuard(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size,
|
||||||
|
vector<Varnode *> &write);
|
||||||
|
bool tryOutputStackGuard(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size,
|
||||||
|
int4 outputCharacter,vector<Varnode *> &write);
|
||||||
|
void guardOutputOverlapStack(PcodeOp *callOp,const Address &addr,int4 size,const Address &retAddr,int4 retSize,vector<Varnode *> &write);
|
||||||
void guardCalls(uint4 fl,const Address &addr,int4 size,vector<Varnode *> &write);
|
void guardCalls(uint4 fl,const Address &addr,int4 size,vector<Varnode *> &write);
|
||||||
void guardStores(const Address &addr,int4 size,vector<Varnode *> &write);
|
void guardStores(const Address &addr,int4 size,vector<Varnode *> &write);
|
||||||
void guardLoads(uint4 fl,const Address &addr,int4 size,vector<Varnode *> &write);
|
void guardLoads(uint4 fl,const Address &addr,int4 size,vector<Varnode *> &write);
|
||||||
|
@ -55,6 +55,8 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status)
|
|||||||
status->registerCom(new IfcCleararch(),"clear","architecture");
|
status->registerCom(new IfcCleararch(),"clear","architecture");
|
||||||
status->registerCom(new IfcMapaddress(),"map","address");
|
status->registerCom(new IfcMapaddress(),"map","address");
|
||||||
status->registerCom(new IfcMaphash(),"map","hash");
|
status->registerCom(new IfcMaphash(),"map","hash");
|
||||||
|
status->registerCom(new IfcMapParam(),"map","param");
|
||||||
|
status->registerCom(new IfcMapReturn(),"map","return");
|
||||||
status->registerCom(new IfcMapfunction(),"map","function");
|
status->registerCom(new IfcMapfunction(),"map","function");
|
||||||
status->registerCom(new IfcMapexternalref(),"map","externalref");
|
status->registerCom(new IfcMapexternalref(),"map","externalref");
|
||||||
status->registerCom(new IfcMaplabel(),"map","label");
|
status->registerCom(new IfcMaplabel(),"map","label");
|
||||||
@ -598,6 +600,49 @@ void IfcMaphash::execute(istream &s)
|
|||||||
sym->getScope()->setAttribute(sym,Varnode::namelock|Varnode::typelock);
|
sym->getScope()->setAttribute(sym,Varnode::namelock|Varnode::typelock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \class IfcMapParam
|
||||||
|
/// \brief Map a parameter symbol for the current function: `map param #i <address> <typedeclaration>`
|
||||||
|
///
|
||||||
|
/// The position of the parameter in the input list is parsed as an integer, starting at 0.
|
||||||
|
/// The address range used for parameter is explicitly set. The data-type and name of the parameter
|
||||||
|
/// are parsed from the type declaration. The parameter is treated as name and type locked.
|
||||||
|
void IfcMapParam::execute(istream &s)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (dcp->fd == (Funcdata *)0)
|
||||||
|
throw IfaceExecutionError("No function loaded");
|
||||||
|
int4 i;
|
||||||
|
string name;
|
||||||
|
int4 size;
|
||||||
|
ParameterPieces piece;
|
||||||
|
s >> dec >> i; // Position of the parameter
|
||||||
|
piece.addr = parse_machaddr(s,size,*dcp->conf->types); // Starting address of parameter
|
||||||
|
piece.type = parse_type(s,name,dcp->conf);
|
||||||
|
piece.flags = ParameterPieces::typelock | ParameterPieces::namelock;
|
||||||
|
|
||||||
|
dcp->fd->getFuncProto().setParam(i, name, piece);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \class IfcMapReturn
|
||||||
|
/// \brief Map the return storage for the current function: `map return <address> <typedeclaration>`
|
||||||
|
///
|
||||||
|
/// The address range used for return storage is explicitly set, and the return value is set to the
|
||||||
|
/// parsed data-type. The function's output is considered locked.
|
||||||
|
void IfcMapReturn::execute(istream &s)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (dcp->fd == (Funcdata *)0)
|
||||||
|
throw IfaceExecutionError("No function loaded");
|
||||||
|
string name;
|
||||||
|
int4 size;
|
||||||
|
ParameterPieces piece;
|
||||||
|
piece.addr = parse_machaddr(s,size,*dcp->conf->types); // Starting address of return storage
|
||||||
|
piece.type = parse_type(s,name,dcp->conf);
|
||||||
|
piece.flags = ParameterPieces::typelock;
|
||||||
|
|
||||||
|
dcp->fd->getFuncProto().setOutput(piece);
|
||||||
|
}
|
||||||
|
|
||||||
/// \class IfcMapfunction
|
/// \class IfcMapfunction
|
||||||
/// \brief Create a new function: `map function <address> [<functionname>] [nocode]`
|
/// \brief Create a new function: `map function <address> [<functionname>] [nocode]`
|
||||||
///
|
///
|
||||||
|
@ -163,6 +163,16 @@ public:
|
|||||||
virtual void execute(istream &s);
|
virtual void execute(istream &s);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IfcMapParam : public IfaceDecompCommand {
|
||||||
|
public:
|
||||||
|
virtual void execute(istream &s);
|
||||||
|
};
|
||||||
|
|
||||||
|
class IfcMapReturn : public IfaceDecompCommand {
|
||||||
|
public:
|
||||||
|
virtual void execute(istream &s);
|
||||||
|
};
|
||||||
|
|
||||||
class IfcMapfunction : public IfaceDecompCommand {
|
class IfcMapfunction : public IfaceDecompCommand {
|
||||||
public:
|
public:
|
||||||
virtual void execute(istream &s);
|
virtual void execute(istream &s);
|
||||||
|
@ -344,6 +344,26 @@ void ScopeLocal::annotateRawStackPtr(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the return value is passed back in a location whose address space holds \b this scope's variables,
|
||||||
|
/// assume the return value is unmapped, unless there is a specific alias into the location.
|
||||||
|
/// Mark the range as unmapped.
|
||||||
|
/// \param is the sorted list of alias offsets into the space
|
||||||
|
void ScopeLocal::checkUnaliasedReturn(const vector<uintb> &alias)
|
||||||
|
|
||||||
|
{
|
||||||
|
PcodeOp *retOp = fd->getFirstReturnOp();
|
||||||
|
if (retOp == (PcodeOp *)0 || retOp->numInput() < 2) return;
|
||||||
|
Varnode *vn = retOp->getIn(1);
|
||||||
|
if (vn->getSpace() != space) return;
|
||||||
|
if (!vn->isMapped()) return;
|
||||||
|
vector<uintb>::const_iterator iter = lower_bound(alias.begin(),alias.end(),vn->getOffset());
|
||||||
|
if (iter != alias.end()) {
|
||||||
|
// Alias is greater than or equal to vn offset
|
||||||
|
if (*iter <= (vn->getOffset() + vn->getSize() - 1)) return; // Alias into return storage, don't continue
|
||||||
|
}
|
||||||
|
markNotMapped(space, vn->getOffset(), vn->getSize(), false);
|
||||||
|
}
|
||||||
|
|
||||||
/// This resets the discovery process for new local variables mapped to the scope's address space.
|
/// This resets the discovery process for new local variables mapped to the scope's address space.
|
||||||
/// Any analysis removing specific ranges from the mapped set (via markNotMapped()) is cleared.
|
/// Any analysis removing specific ranges from the mapped set (via markNotMapped()) is cleared.
|
||||||
void ScopeLocal::resetLocalWindow(void)
|
void ScopeLocal::resetLocalWindow(void)
|
||||||
@ -1081,8 +1101,10 @@ void ScopeLocal::restructureVarnode(bool aliasyes)
|
|||||||
fakeInputSymbols();
|
fakeInputSymbols();
|
||||||
|
|
||||||
state.sortAlias();
|
state.sortAlias();
|
||||||
if (aliasyes)
|
if (aliasyes) {
|
||||||
markUnaliased(state.getAlias());
|
markUnaliased(state.getAlias());
|
||||||
|
checkUnaliasedReturn(state.getAlias());
|
||||||
|
}
|
||||||
if (!state.getAlias().empty() && state.getAlias()[0] == 0) // If a zero offset use of the stack pointer exists
|
if (!state.getAlias().empty() && state.getAlias()[0] == 0) // If a zero offset use of the stack pointer exists
|
||||||
annotateRawStackPtr(); // Add a special placeholder PTRSUB
|
annotateRawStackPtr(); // Add a special placeholder PTRSUB
|
||||||
}
|
}
|
||||||
|
@ -218,6 +218,7 @@ class ScopeLocal : public ScopeInternal {
|
|||||||
void addRecommendName(Symbol *sym); ///< Convert the given symbol to a name recommendation
|
void addRecommendName(Symbol *sym); ///< Convert the given symbol to a name recommendation
|
||||||
void collectNameRecs(void); ///< Collect names of unlocked Symbols on the stack
|
void collectNameRecs(void); ///< Collect names of unlocked Symbols on the stack
|
||||||
void annotateRawStackPtr(void); ///< Generate placeholder PTRSUB off of stack pointer
|
void annotateRawStackPtr(void); ///< Generate placeholder PTRSUB off of stack pointer
|
||||||
|
void checkUnaliasedReturn(const vector<uintb> &alias); ///< Determine if return storage is mapped
|
||||||
public:
|
public:
|
||||||
ScopeLocal(uint8 id,AddrSpace *spc,Funcdata *fd,Architecture *g); ///< Constructor
|
ScopeLocal(uint8 id,AddrSpace *spc,Funcdata *fd,Architecture *g); ///< Constructor
|
||||||
virtual ~ScopeLocal(void) {} ///< Destructor
|
virtual ~ScopeLocal(void) {} ///< Destructor
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
<decompilertest>
|
||||||
|
<binaryimage arch="x86:LE:64:default:gcc">
|
||||||
|
<!--
|
||||||
|
Examples of functions returning values in callee relative stack locations
|
||||||
|
-->
|
||||||
|
<bytechunk space="ram" offset="0x100020" readonly="true">
|
||||||
|
4883ec18bf6400000066e8f300488b44
|
||||||
|
24086748a340011000bf01000000be14
|
||||||
|
00000066e8da008b4424089067a35001
|
||||||
|
100090bf02000000be1e00000066e8c1
|
||||||
|
00488b4424086748a358011000c3
|
||||||
|
</bytechunk>
|
||||||
|
<bytechunk space="ram" offset="0x100120" readonly="true">
|
||||||
|
c3c3c3
|
||||||
|
</bytechunk>
|
||||||
|
<symbol space="ram" offset="0x100020" name="stackreturn"/>
|
||||||
|
<symbol space="ram" offset="0x100120" name="perfect"/>
|
||||||
|
<symbol space="ram" offset="0x100121" name="small"/>
|
||||||
|
<symbol space="ram" offset="0x100122" name="big"/>
|
||||||
|
</binaryimage>
|
||||||
|
<script>
|
||||||
|
<com>map addr r0x100140 int8 perf_ret</com>
|
||||||
|
<com>map addr r0x100150 int4 small_ret</com>
|
||||||
|
<com>map addr r0x100158 int8 big_ret</com>
|
||||||
|
<com>lo fu perfect</com>
|
||||||
|
<com>map return s0x10 int8</com>
|
||||||
|
<com>lo fu small</com>
|
||||||
|
<com>map return s0x10 int8</com>
|
||||||
|
<com>lo fu big</com>
|
||||||
|
<com>map return s0x12 int2</com>
|
||||||
|
<com>lo fu stackreturn</com>
|
||||||
|
<com>map addr s0xfffffffffffffff0 int8 local</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>quit</com>
|
||||||
|
</script>
|
||||||
|
<stringmatch name="Stack Return #1" min="1" max="1">local = perfect\(100\);</stringmatch>
|
||||||
|
<stringmatch name="Stack Return #2" min="1" max="1">perf_ret = local;</stringmatch>
|
||||||
|
<stringmatch name="Stack Return #3" min="1" max="1">local = small\(1,0x14\);</stringmatch>
|
||||||
|
<stringmatch name="Stack Return #4" min="1" max="1">small_ret = \(int4\)local;</stringmatch>
|
||||||
|
<stringmatch name="Stack Return #5" min="1" max="1">local\._2_2_ = big\(2,0x1e\);</stringmatch>
|
||||||
|
</decompilertest>
|
Loading…
Reference in New Issue
Block a user