mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-25 05:32:14 +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/sbyte.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/switchind.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())
|
||||
data.opMarkCalculatedBool(callop);
|
||||
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);
|
||||
VarnodeData vdata;
|
||||
OpCode res = fc->assumedOutputExtension(addr,sz,vdata);
|
||||
|
@ -4658,6 +4658,7 @@ FuncCallSpecs::FuncCallSpecs(PcodeOp *call_op)
|
||||
isinputactive = false;
|
||||
isoutputactive = false;
|
||||
isbadjumptable = false;
|
||||
isstackoutputlock = false;
|
||||
}
|
||||
|
||||
void FuncCallSpecs::setFuncdata(Funcdata *f)
|
||||
|
@ -1479,9 +1479,11 @@ public:
|
||||
void updateOutputNoTypes(const vector<Varnode *> &triallist,TypeFactory *factory);
|
||||
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
|
||||
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
|
||||
int4 numParams(void) const { return store->getNumInputs(); } ///< Get the number of input parameters
|
||||
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
|
||||
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
|
||||
@ -1601,6 +1603,7 @@ class FuncCallSpecs : public FuncProto {
|
||||
bool isinputactive; ///< Are we actively trying to recover input 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 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 *buildParam(Funcdata &data,Varnode *vn,ProtoParameter *param,Varnode *stackref);
|
||||
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
|
||||
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
|
||||
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 *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)) {
|
||||
ParamActive *active = fc->getActiveInput();
|
||||
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
|
||||
int4 truncateAmount = transAddr.justifiedContain(size, truncAddr, vData.size, false);
|
||||
int4 diff = (int4)(truncAddr.getOffset() - transAddr.getOffset());
|
||||
truncAddr = addr + diff; // Convert truncated Address to caller's perspective
|
||||
int4 truncateAmount = addr.justifiedContain(size, truncAddr, vData.size, false);
|
||||
PcodeOp *op = fc->getOp();
|
||||
PcodeOp *subpieceOp = fd->newOp(2,op->getAddr());
|
||||
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,
|
||||
/// but the range is even bigger. We split it 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
|
||||
/// the pieces are concatenated to form a Varnode of the whole range.
|
||||
/// \param fc is the call site potentially returning a value
|
||||
/// \param addr is the starting address of the range
|
||||
/// \param size is the size of the range in bytes
|
||||
/// The potential \e return storage is an \b indirect \b creation at this stage, and the guarded range properly
|
||||
/// contains the return storage. We split the full range up into 2 or 3 Varnodes, and make each one via
|
||||
/// an INDIRECT. The pieces are concatenated to form a Varnode of the whole range.
|
||||
/// \param callOp is the call causing the indirection
|
||||
/// \param addr is the starting address of the full range
|
||||
/// \param size is the size of the full 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
|
||||
/// \return \b true if the INDIRECTs were created
|
||||
bool Heritage::guardCallOverlappingOutput(FuncCallSpecs *fc,const Address &addr,int4 size,vector<Varnode *> &write)
|
||||
|
||||
void Heritage::guardOutputOverlap(PcodeOp *callOp,const Address &addr,int4 size,const Address &retAddr, int4 retSize,
|
||||
vector<Varnode *> &write)
|
||||
{
|
||||
VarnodeData vData;
|
||||
|
||||
if (!fc->getBiggestContainedOutput(addr, size, vData))
|
||||
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);
|
||||
int4 sizeFront = (int4)(retAddr.getOffset() - addr.getOffset());
|
||||
int4 sizeBack = size - retSize - sizeFront;
|
||||
PcodeOp *indOp = fd->newIndirectCreation(callOp,retAddr,retSize,true);
|
||||
Varnode *vnCollect = indOp->getOut();
|
||||
PcodeOp *insertPoint = fc->getOp();
|
||||
PcodeOp *insertPoint = callOp;
|
||||
if (sizeFront != 0) {
|
||||
PcodeOp *indOpFront = fd->newIndirectCreation(indOp,addr,sizeFront,false);
|
||||
Varnode *newFront = indOpFront->getOut();
|
||||
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->opSetInput(concatFront,newFront,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);
|
||||
insertPoint = concatFront;
|
||||
}
|
||||
if (sizeBack != 0) {
|
||||
Address addrBack = truncAddr + vData.size;
|
||||
PcodeOp *indOpBack = fd->newIndirectCreation(fc->getOp(),addrBack,sizeBack,false);
|
||||
Address addrBack = retAddr + retSize;
|
||||
PcodeOp *indOpBack = fd->newIndirectCreation(callOp,addrBack,sizeBack,false);
|
||||
Varnode *newBack = indOpBack->getOut();
|
||||
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->opSetInput(concatBack,newBack,slotNew);
|
||||
fd->opSetInput(concatBack,vnCollect,1-slotNew);
|
||||
@ -1240,10 +1232,157 @@ bool Heritage::guardCallOverlappingOutput(FuncCallSpecs *fc,const Address &addr,
|
||||
}
|
||||
vnCollect->setActiveHeritage();
|
||||
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);
|
||||
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
|
||||
///
|
||||
/// 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();
|
||||
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;
|
||||
if (fc->isOutputActive()) {
|
||||
if (fc->isOutputActive() && tryregister) {
|
||||
ParamActive *active = fc->getActiveOutput();
|
||||
int4 outputCharacter = fc->characterizeAsOutput(addr, size);
|
||||
int4 outputCharacter = fc->characterizeAsOutput(transAddr, size);
|
||||
if (outputCharacter != ParamEntry::no_containment) {
|
||||
effecttype = EffectRecord::killedbycall; // A potential output is always killed by call
|
||||
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
|
||||
}
|
||||
else {
|
||||
if (active->whichTrial(addr,size)<0) { // If not already a trial
|
||||
active->registerTrial(addr,size);
|
||||
if (active->whichTrial(transAddr,size)<0) { // If not already a trial
|
||||
active->registerTrial(transAddr,size);
|
||||
possibleoutput = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fc->isInputActive()) {
|
||||
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
|
||||
else if (fc->isStackOutputLock() && tryregister) {
|
||||
int4 outputCharacter = fc->characterizeAsOutput(transAddr, size);
|
||||
if (outputCharacter != ParamEntry::no_containment) {
|
||||
effecttype = EffectRecord::unknown_effect;
|
||||
if (tryOutputStackGuard(fc, addr, transAddr, size, outputCharacter, write))
|
||||
effecttype = EffectRecord::unaffected; // Range is handled
|
||||
}
|
||||
Address transAddr(spc,off); // Address relative to callee's stack
|
||||
if (tryregister) {
|
||||
int4 inputCharacter = fc->characterizeAsInputParam(transAddr,size);
|
||||
if (inputCharacter == ParamEntry::contains_justified) { // Call could be using this range as an input parameter
|
||||
ParamActive *active = fc->getActiveInput();
|
||||
if (active->whichTrial(transAddr,size)<0) { // If not already a trial
|
||||
PcodeOp *op = fc->getOp();
|
||||
active->registerTrial(transAddr,size);
|
||||
Varnode *vn = fd->newVarnode(size,addr);
|
||||
vn->setActiveHeritage();
|
||||
fd->opInsertInput(op,vn,op->numInput());
|
||||
}
|
||||
}
|
||||
if (fc->isInputActive() && tryregister) {
|
||||
int4 inputCharacter = fc->characterizeAsInputParam(transAddr,size);
|
||||
if (inputCharacter == ParamEntry::contains_justified) { // Call could be using this range as an input parameter
|
||||
ParamActive *active = fc->getActiveInput();
|
||||
if (active->whichTrial(transAddr,size)<0) { // If not already a trial
|
||||
PcodeOp *op = fc->getOp();
|
||||
active->registerTrial(transAddr,size);
|
||||
Varnode *vn = fd->newVarnode(size,addr);
|
||||
vn->setActiveHeritage();
|
||||
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"
|
||||
if ((effecttype == EffectRecord::unknown_effect)||(effecttype == EffectRecord::return_address)) {
|
||||
|
@ -252,7 +252,13 @@ class Heritage {
|
||||
vector<Varnode *> &read,vector<Varnode *> &write,vector<Varnode *> &inputvars);
|
||||
void guardInput(const Address &addr,int4 size,vector<Varnode *> &input);
|
||||
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 guardStores(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 IfcMapaddress(),"map","address");
|
||||
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 IfcMapexternalref(),"map","externalref");
|
||||
status->registerCom(new IfcMaplabel(),"map","label");
|
||||
@ -598,6 +600,49 @@ void IfcMaphash::execute(istream &s)
|
||||
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
|
||||
/// \brief Create a new function: `map function <address> [<functionname>] [nocode]`
|
||||
///
|
||||
|
@ -163,6 +163,16 @@ public:
|
||||
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 {
|
||||
public:
|
||||
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.
|
||||
/// Any analysis removing specific ranges from the mapped set (via markNotMapped()) is cleared.
|
||||
void ScopeLocal::resetLocalWindow(void)
|
||||
@ -1081,8 +1101,10 @@ void ScopeLocal::restructureVarnode(bool aliasyes)
|
||||
fakeInputSymbols();
|
||||
|
||||
state.sortAlias();
|
||||
if (aliasyes)
|
||||
if (aliasyes) {
|
||||
markUnaliased(state.getAlias());
|
||||
checkUnaliasedReturn(state.getAlias());
|
||||
}
|
||||
if (!state.getAlias().empty() && state.getAlias()[0] == 0) // If a zero offset use of the stack pointer exists
|
||||
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 collectNameRecs(void); ///< Collect names of unlocked Symbols on the stack
|
||||
void annotateRawStackPtr(void); ///< Generate placeholder PTRSUB off of stack pointer
|
||||
void checkUnaliasedReturn(const vector<uintb> &alias); ///< Determine if return storage is mapped
|
||||
public:
|
||||
ScopeLocal(uint8 id,AddrSpace *spc,Funcdata *fd,Architecture *g); ///< Constructor
|
||||
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