GP-3613 Support for return storage on stack

This commit is contained in:
caheckman 2023-06-28 22:09:14 +00:00
parent 22e5c1a48b
commit e9b5be17c1
11 changed files with 345 additions and 62 deletions

View File

@ -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|

View File

@ -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);

View File

@ -4658,6 +4658,7 @@ FuncCallSpecs::FuncCallSpecs(PcodeOp *call_op)
isinputactive = false;
isoutputactive = false;
isbadjumptable = false;
isstackoutputlock = false;
}
void FuncCallSpecs::setFuncdata(Funcdata *f)

View File

@ -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

View File

@ -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)) {

View File

@ -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);

View File

@ -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]`
///

View File

@ -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);

View File

@ -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
}

View File

@ -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

View File

@ -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>