mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
Merge remote-tracking branch 'origin/Ghidra_11.2'
This commit is contained in:
commit
8d4e33cc50
8 changed files with 198 additions and 103 deletions
|
@ -73,6 +73,8 @@ src/decompile/datatests/stackstring.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/statuscmp.xml||GHIDRA||||END|
|
src/decompile/datatests/statuscmp.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/switchhide.xml||GHIDRA||||END|
|
src/decompile/datatests/switchhide.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/switchind.xml||GHIDRA||||END|
|
src/decompile/datatests/switchind.xml||GHIDRA||||END|
|
||||||
|
src/decompile/datatests/switchloop.xml||GHIDRA||||END|
|
||||||
|
src/decompile/datatests/switchmulti.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/switchreturn.xml||GHIDRA||||END|
|
src/decompile/datatests/switchreturn.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/threedim.xml||GHIDRA||||END|
|
src/decompile/datatests/threedim.xml||GHIDRA||||END|
|
||||||
src/decompile/datatests/twodim.xml||GHIDRA||||END|
|
src/decompile/datatests/twodim.xml||GHIDRA||||END|
|
||||||
|
|
|
@ -4410,12 +4410,8 @@ int4 ActionSwitchNorm::apply(Funcdata &data)
|
||||||
for(int4 i=0;i<data.numJumpTables();++i) {
|
for(int4 i=0;i<data.numJumpTables();++i) {
|
||||||
JumpTable *jt = data.getJumpTable(i);
|
JumpTable *jt = data.getJumpTable(i);
|
||||||
if (!jt->isLabelled()) {
|
if (!jt->isLabelled()) {
|
||||||
if (jt->recoverLabels(&data)) { // Recover case statement labels
|
jt->matchModel(&data);
|
||||||
// If this returns true, the jumptable was not fully recovered during flow analysis
|
jt->recoverLabels(&data); // Recover case statement labels
|
||||||
// So we need to issue a restart
|
|
||||||
data.getOverride().insertMultistageJump(jt->getOpAddress());
|
|
||||||
data.setRestartPending(true);
|
|
||||||
}
|
|
||||||
jt->foldInNormalization(&data);
|
jt->foldInNormalization(&data);
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -781,7 +781,6 @@ void FlowInfo::generateOps(void)
|
||||||
if (hasInject())
|
if (hasInject())
|
||||||
injectPcode();
|
injectPcode();
|
||||||
do {
|
do {
|
||||||
bool collapsed_jumptable = false;
|
|
||||||
while(!tablelist.empty()) { // For each jumptable found
|
while(!tablelist.empty()) { // For each jumptable found
|
||||||
vector<JumpTable *> newTables;
|
vector<JumpTable *> newTables;
|
||||||
recoverJumpTables(newTables, notreached);
|
recoverJumpTables(newTables, notreached);
|
||||||
|
@ -793,16 +792,13 @@ void FlowInfo::generateOps(void)
|
||||||
int4 num = jt->numEntries();
|
int4 num = jt->numEntries();
|
||||||
for(int4 i=0;i<num;++i)
|
for(int4 i=0;i<num;++i)
|
||||||
newAddress(jt->getIndirectOp(),jt->getAddressByIndex(i));
|
newAddress(jt->getIndirectOp(),jt->getAddressByIndex(i));
|
||||||
if (jt->isPossibleMultistage())
|
|
||||||
collapsed_jumptable = true;
|
|
||||||
while(!addrlist.empty()) // Try to fill in as much more as possible
|
while(!addrlist.empty()) // Try to fill in as much more as possible
|
||||||
fallthru();
|
fallthru();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkContainedCall(); // Check for PIC constructions
|
checkContainedCall(); // Check for PIC constructions
|
||||||
if (collapsed_jumptable)
|
checkMultistageJumptables();
|
||||||
checkMultistageJumptables();
|
|
||||||
while(notreachcnt < notreached.size()) {
|
while(notreachcnt < notreached.size()) {
|
||||||
tablelist.push_back(notreached[notreachcnt]);
|
tablelist.push_back(notreached[notreachcnt]);
|
||||||
notreachcnt += 1;
|
notreachcnt += 1;
|
||||||
|
@ -1435,14 +1431,18 @@ void FlowInfo::recoverJumpTables(vector<JumpTable *> &newTables,vector<PcodeOp *
|
||||||
JumpTable::RecoveryMode mode;
|
JumpTable::RecoveryMode mode;
|
||||||
JumpTable *jt = data.recoverJumpTable(partial,op,this,mode); // Recover it
|
JumpTable *jt = data.recoverJumpTable(partial,op,this,mode); // Recover it
|
||||||
if (jt == (JumpTable *)0) { // Could not recover jumptable
|
if (jt == (JumpTable *)0) { // Could not recover jumptable
|
||||||
if ((mode == JumpTable::fail_noflow) && (tablelist.size() > 1) && (!isInArray(notreached,op))) {
|
if (!isFlowForInline()) // Unless this flow is being inlined for something else
|
||||||
// If the indirect op was not reachable with current flow AND there is more flow to generate,
|
|
||||||
// AND we haven't tried to recover this table before
|
|
||||||
notreached.push_back(op); // Save this op so we can try to recovery table again later
|
|
||||||
}
|
|
||||||
else if (!isFlowForInline()) // Unless this flow is being inlined for something else
|
|
||||||
truncateIndirectJump(op,mode); // Treat the indirect jump as a call
|
truncateIndirectJump(op,mode); // Treat the indirect jump as a call
|
||||||
}
|
}
|
||||||
|
else if (jt->isPartial()) {
|
||||||
|
if (tablelist.size() > 1 && !isInArray(notreached,op)) {
|
||||||
|
// If the recovery is incomplete with current flow AND there is more flow to generate,
|
||||||
|
// AND we haven't tried to recover this table before
|
||||||
|
notreached.push_back(op); // Save this op so we can try to recover the table again later
|
||||||
|
}
|
||||||
|
else
|
||||||
|
jt->markComplete(); // If we aren't revisiting, mark the table as complete
|
||||||
|
}
|
||||||
newTables.push_back(jt);
|
newTables.push_back(jt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -528,14 +528,11 @@ JumpTable::RecoveryMode Funcdata::stageJumpTable(Funcdata &partial,JumpTable *jt
|
||||||
try {
|
try {
|
||||||
jt->setLoadCollect(flow->doesJumpRecord());
|
jt->setLoadCollect(flow->doesJumpRecord());
|
||||||
jt->setIndirectOp(partop);
|
jt->setIndirectOp(partop);
|
||||||
if (jt->getStage()>0)
|
if (jt->isPartial())
|
||||||
jt->recoverMultistage(&partial);
|
jt->recoverMultistage(&partial);
|
||||||
else
|
else
|
||||||
jt->recoverAddresses(&partial); // Analyze partial to recover jumptable addresses
|
jt->recoverAddresses(&partial); // Analyze partial to recover jumptable addresses
|
||||||
}
|
}
|
||||||
catch(JumptableNotReachableError &err) { // Thrown by recoverAddresses
|
|
||||||
return JumpTable::fail_noflow;
|
|
||||||
}
|
|
||||||
catch(JumptableThunkError &err) { // Thrown by recoverAddresses
|
catch(JumptableThunkError &err) { // Thrown by recoverAddresses
|
||||||
return JumpTable::fail_thunk;
|
return JumpTable::fail_thunk;
|
||||||
}
|
}
|
||||||
|
@ -645,7 +642,7 @@ JumpTable *Funcdata::recoverJumpTable(Funcdata &partial,PcodeOp *op,FlowInfo *fl
|
||||||
jt = linkJumpTable(op); // Search for pre-existing jumptable
|
jt = linkJumpTable(op); // Search for pre-existing jumptable
|
||||||
if (jt != (JumpTable *)0) {
|
if (jt != (JumpTable *)0) {
|
||||||
if (!jt->isOverride()) {
|
if (!jt->isOverride()) {
|
||||||
if (jt->getStage() != 1)
|
if (!jt->isPartial())
|
||||||
return jt; // Previously calculated jumptable (NOT an override and NOT incomplete)
|
return jt; // Previously calculated jumptable (NOT an override and NOT incomplete)
|
||||||
}
|
}
|
||||||
mode = stageJumpTable(partial,jt,op,flow); // Recover based on override information
|
mode = stageJumpTable(partial,jt,op,flow); // Recover based on override information
|
||||||
|
|
|
@ -1049,7 +1049,7 @@ void JumpBasic::analyzeGuards(BlockBasic *bl,int4 pathout)
|
||||||
int4 i,j,indpath;
|
int4 i,j,indpath;
|
||||||
int4 maxbranch = 2; // Maximum number of CBRANCHs to consider
|
int4 maxbranch = 2; // Maximum number of CBRANCHs to consider
|
||||||
int4 maxpullback = 2;
|
int4 maxpullback = 2;
|
||||||
bool usenzmask = (jumptable->getStage() == 0);
|
bool usenzmask = !jumptable->isPartial();
|
||||||
|
|
||||||
selectguards.clear();
|
selectguards.clear();
|
||||||
BlockBasic *prevbl;
|
BlockBasic *prevbl;
|
||||||
|
@ -2205,6 +2205,33 @@ JumpModel *JumpAssisted::clone(JumpTable *jt) const
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JumpTable::saveModel(void)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (origmodel != (JumpModel *)0)
|
||||||
|
delete origmodel;
|
||||||
|
origmodel = jmodel;
|
||||||
|
jmodel = (JumpModel *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JumpTable::restoreSavedModel(void)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (jmodel != (JumpModel *)0)
|
||||||
|
delete jmodel;
|
||||||
|
jmodel = origmodel;
|
||||||
|
origmodel = (JumpModel *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JumpTable::clearSavedModel(void)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (origmodel != (JumpModel *)0) {
|
||||||
|
delete origmodel;
|
||||||
|
origmodel = (JumpModel *)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Try to recover each model in turn, until we find one that matches the specific BRANCHIND.
|
/// Try to recover each model in turn, until we find one that matches the specific BRANCHIND.
|
||||||
/// \param fd is the function containing the switch
|
/// \param fd is the function containing the switch
|
||||||
void JumpTable::recoverModel(Funcdata *fd)
|
void JumpTable::recoverModel(Funcdata *fd)
|
||||||
|
@ -2252,12 +2279,12 @@ void JumpTable::sanityCheck(Funcdata *fd,vector<int4> *loadcounts)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (jmodel->isOverride())
|
if (jmodel->isOverride())
|
||||||
return; // Don't perform sanity check on an override
|
return; // Don't perform sanity check on an override
|
||||||
uint4 sz = addresstable.size();
|
uint4 sz = addresstable.size();
|
||||||
|
|
||||||
if (!isReachable(indirect))
|
if (!isReachable(indirect))
|
||||||
throw JumptableNotReachableError("No legal flow");
|
partialTable = true; // If the jumptable is not reachable, mark as incomplete
|
||||||
if (addresstable.size() == 1) { // One entry is likely some kind of thunk
|
if (addresstable.size() == 1) { // One entry is likely some kind of thunk
|
||||||
bool isthunk = false;
|
bool isthunk = false;
|
||||||
uintb diff;
|
uintb diff;
|
||||||
Address addr = addresstable[0];
|
Address addr = addresstable[0];
|
||||||
|
@ -2346,7 +2373,7 @@ JumpTable::JumpTable(Architecture *g,Address ad)
|
||||||
maxaddsub = 1;
|
maxaddsub = 1;
|
||||||
maxleftright = 1;
|
maxleftright = 1;
|
||||||
maxext = 1;
|
maxext = 1;
|
||||||
recoverystage = 0;
|
partialTable = false;
|
||||||
collectloads = false;
|
collectloads = false;
|
||||||
defaultIsFolded = false;
|
defaultIsFolded = false;
|
||||||
}
|
}
|
||||||
|
@ -2367,7 +2394,7 @@ JumpTable::JumpTable(const JumpTable *op2)
|
||||||
maxaddsub = op2->maxaddsub;
|
maxaddsub = op2->maxaddsub;
|
||||||
maxleftright = op2->maxleftright;
|
maxleftright = op2->maxleftright;
|
||||||
maxext = op2->maxext;
|
maxext = op2->maxext;
|
||||||
recoverystage = op2->recoverystage;
|
partialTable = op2->partialTable;
|
||||||
collectloads = op2->collectloads;
|
collectloads = op2->collectloads;
|
||||||
defaultIsFolded = false;
|
defaultIsFolded = false;
|
||||||
// We just clone the addresses themselves
|
// We just clone the addresses themselves
|
||||||
|
@ -2587,8 +2614,8 @@ void JumpTable::recoverAddresses(Funcdata *fd)
|
||||||
}
|
}
|
||||||
if (jmodel->getTableSize() == 0) {
|
if (jmodel->getTableSize() == 0) {
|
||||||
ostringstream err;
|
ostringstream err;
|
||||||
err << "Impossible to reach jumptable at " << opaddress;
|
err << "Jumptable with 0 entries at " << opaddress;
|
||||||
throw JumptableNotReachableError(err.str());
|
throw LowlevelError(err.str());
|
||||||
}
|
}
|
||||||
// if (sz < 2)
|
// if (sz < 2)
|
||||||
// fd->warning("Jumptable has only one branch",opaddress);
|
// fd->warning("Jumptable has only one branch",opaddress);
|
||||||
|
@ -2609,10 +2636,7 @@ void JumpTable::recoverAddresses(Funcdata *fd)
|
||||||
void JumpTable::recoverMultistage(Funcdata *fd)
|
void JumpTable::recoverMultistage(Funcdata *fd)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (origmodel != (JumpModel *)0)
|
saveModel();
|
||||||
delete origmodel;
|
|
||||||
origmodel = jmodel;
|
|
||||||
jmodel = (JumpModel *)0;
|
|
||||||
|
|
||||||
vector<Address> oldaddresstable = addresstable;
|
vector<Address> oldaddresstable = addresstable;
|
||||||
addresstable.clear();
|
addresstable.clear();
|
||||||
|
@ -2621,36 +2645,25 @@ void JumpTable::recoverMultistage(Funcdata *fd)
|
||||||
recoverAddresses(fd);
|
recoverAddresses(fd);
|
||||||
}
|
}
|
||||||
catch(JumptableThunkError &err) {
|
catch(JumptableThunkError &err) {
|
||||||
if (jmodel != (JumpModel *)0)
|
restoreSavedModel();
|
||||||
delete jmodel;
|
|
||||||
jmodel = origmodel;
|
|
||||||
origmodel = (JumpModel *)0;
|
|
||||||
addresstable = oldaddresstable;
|
addresstable = oldaddresstable;
|
||||||
fd->warning("Second-stage recovery error",indirect->getAddr());
|
fd->warning("Second-stage recovery error",indirect->getAddr());
|
||||||
}
|
}
|
||||||
catch(LowlevelError &err) {
|
catch(LowlevelError &err) {
|
||||||
if (jmodel != (JumpModel *)0)
|
restoreSavedModel();
|
||||||
delete jmodel;
|
|
||||||
jmodel = origmodel;
|
|
||||||
origmodel = (JumpModel *)0;
|
|
||||||
addresstable = oldaddresstable;
|
addresstable = oldaddresstable;
|
||||||
fd->warning("Second-stage recovery error",indirect->getAddr());
|
fd->warning("Second-stage recovery error",indirect->getAddr());
|
||||||
}
|
}
|
||||||
recoverystage = 2;
|
partialTable = false;
|
||||||
if (origmodel != (JumpModel *)0) { // Keep the new model if it was created successfully
|
clearSavedModel(); // Keep the new model if it was created successfully
|
||||||
delete origmodel;
|
|
||||||
origmodel = (JumpModel *)0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is run assuming the address table has already been recovered, via recoverAddresses() in another
|
/// This assumes the address table has already been recovered, via recoverAddresses() in another
|
||||||
/// Funcdata instance. So recoverModel() needs to be rerun on the instance passed in here.
|
/// Funcdata instance. We rerun recoverModel() to match with the current Funcdata. If the recovered model
|
||||||
///
|
/// does not match the original address table size, we may be missing control-flow. In this case,
|
||||||
/// The unnormalized switch variable is recovered, and for each possible address table entry, the variable
|
/// if it looks like we have a \e multistage jumptable, we generate a multistage restart, otherwise
|
||||||
/// value that produces it is calculated and stored as the formal \e case label for the associated code block.
|
/// we generate a warning of the mismatch.
|
||||||
/// \param fd is the (final instance of the) function containing the switch
|
void JumpTable::matchModel(Funcdata *fd)
|
||||||
/// \return \b true if it looks like a multi-stage restart is needed.
|
|
||||||
bool JumpTable::recoverLabels(Funcdata *fd)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
if (!isRecovered())
|
if (!isRecovered())
|
||||||
|
@ -2658,24 +2671,33 @@ bool JumpTable::recoverLabels(Funcdata *fd)
|
||||||
|
|
||||||
// Unless the model is an override, move model (created on a flow copy) so we can create a current instance
|
// Unless the model is an override, move model (created on a flow copy) so we can create a current instance
|
||||||
if (jmodel != (JumpModel *)0) {
|
if (jmodel != (JumpModel *)0) {
|
||||||
if (origmodel != (JumpModel *)0)
|
if (!jmodel->isOverride())
|
||||||
delete origmodel;
|
saveModel();
|
||||||
if (!jmodel->isOverride()) {
|
else {
|
||||||
origmodel = jmodel;
|
clearSavedModel();
|
||||||
jmodel = (JumpModel *)0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fd->warning("Switch is manually overridden",opaddress);
|
fd->warning("Switch is manually overridden",opaddress);
|
||||||
}
|
|
||||||
|
|
||||||
bool multistagerestart = false;
|
|
||||||
recoverModel(fd); // Create a current instance of the model
|
|
||||||
if (jmodel != (JumpModel *)0) {
|
|
||||||
if (jmodel->getTableSize() != addresstable.size()) {
|
|
||||||
fd->warning("Could not find normalized switch variable to match jumptable",opaddress);
|
|
||||||
if ((addresstable.size()==1)&&(jmodel->getTableSize() > 1))
|
|
||||||
multistagerestart = true;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
recoverModel(fd); // Create a current instance of the model
|
||||||
|
if (jmodel != (JumpModel *)0 && jmodel->getTableSize() != addresstable.size()) {
|
||||||
|
if ((addresstable.size()==1)&&(jmodel->getTableSize() > 1)) {
|
||||||
|
// The jumptable was not fully recovered during flow analysis, try to issue a restart
|
||||||
|
fd->getOverride().insertMultistageJump(opaddress);
|
||||||
|
fd->setRestartPending(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fd->warning("Could not find normalized switch variable to match jumptable",opaddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The unnormalized switch variable is recovered, and for each possible address table entry, the variable
|
||||||
|
/// value that produces it is calculated and stored as the formal \e case label for the associated code block.
|
||||||
|
/// \param fd is the (final instance of the) function containing the switch
|
||||||
|
void JumpTable::recoverLabels(Funcdata *fd)
|
||||||
|
|
||||||
|
{
|
||||||
|
if (jmodel != (JumpModel *)0) {
|
||||||
if ((origmodel == (JumpModel *)0)||(origmodel->getTableSize()==0)) {
|
if ((origmodel == (JumpModel *)0)||(origmodel->getTableSize()==0)) {
|
||||||
jmodel->findUnnormalized(maxaddsub,maxleftright,maxext);
|
jmodel->findUnnormalized(maxaddsub,maxleftright,maxext);
|
||||||
jmodel->buildLabels(fd,addresstable,label,jmodel);
|
jmodel->buildLabels(fd,addresstable,label,jmodel);
|
||||||
|
@ -2692,11 +2714,7 @@ bool JumpTable::recoverLabels(Funcdata *fd)
|
||||||
trivialSwitchOver();
|
trivialSwitchOver();
|
||||||
jmodel->buildLabels(fd,addresstable,label,origmodel);
|
jmodel->buildLabels(fd,addresstable,label,origmodel);
|
||||||
}
|
}
|
||||||
if (origmodel != (JumpModel *)0) {
|
clearSavedModel();
|
||||||
delete origmodel;
|
|
||||||
origmodel = (JumpModel *)0;
|
|
||||||
}
|
|
||||||
return multistagerestart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear out any data that is specific to a Funcdata instance.
|
/// Clear out any data that is specific to a Funcdata instance.
|
||||||
|
@ -2704,10 +2722,7 @@ bool JumpTable::recoverLabels(Funcdata *fd)
|
||||||
void JumpTable::clear(void)
|
void JumpTable::clear(void)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (origmodel != (JumpModel *)0) {
|
clearSavedModel();
|
||||||
delete origmodel;
|
|
||||||
origmodel = (JumpModel *)0;
|
|
||||||
}
|
|
||||||
if (jmodel->isOverride())
|
if (jmodel->isOverride())
|
||||||
jmodel->clear();
|
jmodel->clear();
|
||||||
else {
|
else {
|
||||||
|
@ -2722,7 +2737,7 @@ void JumpTable::clear(void)
|
||||||
indirect = (PcodeOp *)0;
|
indirect = (PcodeOp *)0;
|
||||||
switchVarConsume = ~((uintb)0);
|
switchVarConsume = ~((uintb)0);
|
||||||
defaultBlock = -1;
|
defaultBlock = -1;
|
||||||
recoverystage = 0;
|
partialTable = false;
|
||||||
// -opaddress- -maxtablesize- -maxaddsub- -maxleftright- -maxext- -collectloads- are permanent
|
// -opaddress- -maxtablesize- -maxaddsub- -maxleftright- -maxext- -collectloads- are permanent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2816,11 +2831,11 @@ bool JumpTable::checkForMultistage(Funcdata *fd)
|
||||||
|
|
||||||
{
|
{
|
||||||
if (addresstable.size()!=1) return false;
|
if (addresstable.size()!=1) return false;
|
||||||
if (recoverystage != 0) return false;
|
if (partialTable) return false;
|
||||||
if (indirect == (PcodeOp *)0) return false;
|
if (indirect == (PcodeOp *)0) return false;
|
||||||
|
|
||||||
if (fd->getOverride().queryMultistageJumptable(indirect->getAddr())) {
|
if (fd->getOverride().queryMultistageJumptable(indirect->getAddr())) {
|
||||||
recoverystage = 1; // Mark that we need additional recovery
|
partialTable = true; // Mark that we need additional recovery
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -42,11 +42,6 @@ struct JumptableThunkError : public LowlevelError {
|
||||||
JumptableThunkError(const string &s) : LowlevelError(s) {} ///< Construct with an explanatory string
|
JumptableThunkError(const string &s) : LowlevelError(s) {} ///< Construct with an explanatory string
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \brief Exception thrown is there are no legal flows to a switch
|
|
||||||
struct JumptableNotReachableError : public LowlevelError {
|
|
||||||
JumptableNotReachableError(const string &s) : LowlevelError(s) {} ///< Constructor
|
|
||||||
};
|
|
||||||
|
|
||||||
/// \brief A description where and how data was loaded from memory
|
/// \brief A description where and how data was loaded from memory
|
||||||
///
|
///
|
||||||
/// This is a generic table description, giving the starting address
|
/// This is a generic table description, giving the starting address
|
||||||
|
@ -547,9 +542,8 @@ public:
|
||||||
success = 0, ///< JumpTable is fully recovered
|
success = 0, ///< JumpTable is fully recovered
|
||||||
fail_normal = 1, ///< Normal failure to recover
|
fail_normal = 1, ///< Normal failure to recover
|
||||||
fail_thunk = 2, ///< Likely \b thunk
|
fail_thunk = 2, ///< Likely \b thunk
|
||||||
fail_noflow = 3, ///< No legal flow to BRANCHIND
|
fail_return = 3, ///< Likely \b return operation
|
||||||
fail_return = 4, ///< Likely \b return operation
|
fail_callother = 4 ///< Address formed by CALLOTHER
|
||||||
fail_callother = 5 ///< Address formed by CALLOTHER
|
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
/// \brief An address table index and its corresponding out-edge
|
/// \brief An address table index and its corresponding out-edge
|
||||||
|
@ -575,9 +569,12 @@ private:
|
||||||
uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize
|
uint4 maxaddsub; ///< Maximum ADDs or SUBs to normalize
|
||||||
uint4 maxleftright; ///< Maximum shifts to normalize
|
uint4 maxleftright; ///< Maximum shifts to normalize
|
||||||
uint4 maxext; ///< Maximum extensions to normalize
|
uint4 maxext; ///< Maximum extensions to normalize
|
||||||
int4 recoverystage; ///< 0=no stages recovered, 1=additional stage needed, 2=complete
|
bool partialTable; ///< Set to \b true if \b this table is incomplete and needs additional recovery steps
|
||||||
bool collectloads; ///< Set to \b true if information about in-memory model data is/should be collected
|
bool collectloads; ///< Set to \b true if information about in-memory model data is/should be collected
|
||||||
bool defaultIsFolded; ///< The \e default block is the target of a folded CBRANCH (and cannot have a label)
|
bool defaultIsFolded; ///< The \e default block is the target of a folded CBRANCH (and cannot have a label)
|
||||||
|
void saveModel(void); ///< Save off current model (if any) and prepare for instantiating a new model
|
||||||
|
void restoreSavedModel(void); ///< Restore any saved model as the current model
|
||||||
|
void clearSavedModel(void); ///< Clear any saved model
|
||||||
void recoverModel(Funcdata *fd); ///< Attempt recovery of the jump-table model
|
void recoverModel(Funcdata *fd); ///< Attempt recovery of the jump-table model
|
||||||
void trivialSwitchOver(void); ///< Switch \b this table over to a trivial model
|
void trivialSwitchOver(void); ///< Switch \b this table over to a trivial model
|
||||||
void sanityCheck(Funcdata *fd,vector<int4> *loadpoints); ///< Perform sanity check on recovered address targets
|
void sanityCheck(Funcdata *fd,vector<int4> *loadpoints); ///< Perform sanity check on recovered address targets
|
||||||
|
@ -590,8 +587,8 @@ public:
|
||||||
bool isRecovered(void) const { return !addresstable.empty(); } ///< Return \b true if a model has been recovered
|
bool isRecovered(void) const { return !addresstable.empty(); } ///< Return \b true if a model has been recovered
|
||||||
bool isLabelled(void) const { return !label.empty(); } ///< Return \b true if \e case labels are computed
|
bool isLabelled(void) const { return !label.empty(); } ///< Return \b true if \e case labels are computed
|
||||||
bool isOverride(void) const; ///< Return \b true if \b this table was manually overridden
|
bool isOverride(void) const; ///< Return \b true if \b this table was manually overridden
|
||||||
bool isPossibleMultistage(void) const { return (addresstable.size()==1); } ///< Return \b true if this could be multi-staged
|
bool isPartial(void) const { return partialTable; } ///< Return \b true if \b this is a partial table needing more recovery
|
||||||
int4 getStage(void) const { return recoverystage; } ///< Return what stage of recovery this jump-table is in.
|
void markComplete(void) { partialTable = false; } ///< Mark whatever is recovered so far as the complete table
|
||||||
int4 numEntries(void) const { return addresstable.size(); } ///< Return the size of the address table for \b this jump-table
|
int4 numEntries(void) const { return addresstable.size(); } ///< Return the size of the address table for \b this jump-table
|
||||||
uintb getSwitchVarConsume(void) const { return switchVarConsume; } ///< Get bits of switch variable consumed by \b this table
|
uintb getSwitchVarConsume(void) const { return switchVarConsume; } ///< Get bits of switch variable consumed by \b this table
|
||||||
int4 getDefaultBlock(void) const { return defaultBlock; } ///< Get the out-edge corresponding to the \e default switch destination
|
int4 getDefaultBlock(void) const { return defaultBlock; } ///< Get the out-edge corresponding to the \e default switch destination
|
||||||
|
@ -616,7 +613,8 @@ public:
|
||||||
bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); } ///< Hide any guard code for \b this switch
|
bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); } ///< Hide any guard code for \b this switch
|
||||||
void recoverAddresses(Funcdata *fd); ///< Recover the raw jump-table addresses (the address table)
|
void recoverAddresses(Funcdata *fd); ///< Recover the raw jump-table addresses (the address table)
|
||||||
void recoverMultistage(Funcdata *fd); ///< Recover jump-table addresses keeping track of a possible previous stage
|
void recoverMultistage(Funcdata *fd); ///< Recover jump-table addresses keeping track of a possible previous stage
|
||||||
bool recoverLabels(Funcdata *fd); ///< Recover the case labels for \b this jump-table
|
void matchModel(Funcdata *fd); ///< Try to match JumpTable model to the existing function
|
||||||
|
void recoverLabels(Funcdata *fd); ///< Recover the case labels for \b this jump-table
|
||||||
bool checkForMultistage(Funcdata *fd); ///< Check if this jump-table requires an additional recovery stage
|
bool checkForMultistage(Funcdata *fd); ///< Check if this jump-table requires an additional recovery stage
|
||||||
void clear(void); ///< Clear instance specific data for \b this jump-table
|
void clear(void); ///< Clear instance specific data for \b this jump-table
|
||||||
void encode(Encoder &encoder) const; ///< Encode \b this jump-table as a \<jumptable> element
|
void encode(Encoder &encoder) const; ///< Encode \b this jump-table as a \<jumptable> element
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<decompilertest>
|
||||||
|
<binaryimage arch="x86:LE:64:default:gcc">
|
||||||
|
<!--
|
||||||
|
Switch variable also a loop variable with increments in cases
|
||||||
|
-->
|
||||||
|
<bytechunk space="ram" offset="0x100000" readonly="true">
|
||||||
|
f30f1efa4189f831c9488d35c0000000
|
||||||
|
41b908000000bf0a00000041ba050000
|
||||||
|
0041bb0300000083f90a0f849c000000
|
||||||
|
8d41ff83f808770a486304864801f03e
|
||||||
|
ffe0418d4001b901000000eb77418d40
|
||||||
|
02b902000000eb6c438d040083f80b41
|
||||||
|
0f4dcbeb5f418d4064b904000000eb54
|
||||||
|
4489c099f7ff4181f895000000410f4e
|
||||||
|
caeb41418d80e8030000b906000000eb
|
||||||
|
33418d8010270000b907000000eb2544
|
||||||
|
89c04489c9348789c283e20129d1eb14
|
||||||
|
418d4008b909000000eb09418d4010b9
|
||||||
|
0a0000004189c0e95bffffff4489c0c3
|
||||||
|
7dffffff88ffffff95ffffffa0ffffff
|
||||||
|
b3ffffffc1ffffffcfffffffe0ffffff
|
||||||
|
ebffffff
|
||||||
|
</bytechunk>
|
||||||
|
<symbol space="ram" offset="0x100000" name="switchloop"/>
|
||||||
|
</binaryimage>
|
||||||
|
<script>
|
||||||
|
<com>option readonly on</com>
|
||||||
|
<com>parse line extern uint4 switchloop(uint4 startval);</com>
|
||||||
|
<com>lo fu switchloop</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>quit</com>
|
||||||
|
</script>
|
||||||
|
<stringmatch name="Switch Loop #1" min="9" max="9">case .*:</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #2" min="1" max="1">startval = startval \+ 2;</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #3" min="1" max="1">startval = startval \* 2;</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #4" min="1" max="1">startval = startval \+ 100;</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #5" min="1" max="1">startval / 10;</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #6" min="1" max="1">startval = startval \+ 1000;</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #7" min="1" max="1">startval = startval \+ 10000;</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #8" min="1" max="1">startval = startval \^ 0x87;</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #9" min="1" max="1">startval = startval \+ 8;</stringmatch>
|
||||||
|
<stringmatch name="Switch Loop #10" min="1" max="1">startval = startval \+ 1;</stringmatch>
|
||||||
|
</decompilertest>
|
|
@ -0,0 +1,43 @@
|
||||||
|
<decompilertest>
|
||||||
|
<binaryimage arch="x86:LE:64:default:gcc">
|
||||||
|
<!--
|
||||||
|
Switch in a loop and also depending on a variable set to a constant locally and
|
||||||
|
modified indirectly.
|
||||||
|
-->
|
||||||
|
<bytechunk space="ram" offset="0x100000" readonly="true">
|
||||||
|
f30f1efa4883ec2848897c24084889e7
|
||||||
|
488974241048c704240000000048c744
|
||||||
|
241800000000e8d50f00004c8b14244c
|
||||||
|
8b5c241031c9488d3583000000bf6500
|
||||||
|
000041b9030000004d85d2745b4d8d04
|
||||||
|
0b4883f90677574863048e4801f03eff
|
||||||
|
e0498d480aeb39498d48f6eb33496bc8
|
||||||
|
07eb2d4c89c0489949f7f94889c1eb20
|
||||||
|
4c89c0489948f7ff4889d1eb134c89c1
|
||||||
|
4881f1ba0a0000eb074c89c14883c920
|
||||||
|
4883f9637ea2eb0d4983c8ffeb0749c7
|
||||||
|
c0feffffff4c89c04883c428c3
|
||||||
|
</bytechunk>
|
||||||
|
<bytechunk space="ram" offset="0x1000c0" readonly="true">
|
||||||
|
a1ffffffa7ffffffadffffffb3ffffff
|
||||||
|
c0ffffffcdffffffd9ffffff
|
||||||
|
</bytechunk>
|
||||||
|
<symbol space="ram" offset="0x100000" name="switchmulti"/>
|
||||||
|
</binaryimage>
|
||||||
|
<script>
|
||||||
|
<com>option readonly on</com>
|
||||||
|
<com>lo fu switchmulti</com>
|
||||||
|
<com>decompile</com>
|
||||||
|
<com>print C</com>
|
||||||
|
<com>quit</com>
|
||||||
|
</script>
|
||||||
|
<stringmatch name="Switch Multi #1" min="7" max="7">case .*:</stringmatch>
|
||||||
|
<stringmatch name="Switch Multi #2" min="1" max="1">uVar1 \+ 10;</stringmatch>
|
||||||
|
<stringmatch name="Switch Multi #3" min="1" max="1">uVar1 \- 10;</stringmatch>
|
||||||
|
<stringmatch name="Switch Multi #4" min="1" max="1">uVar1 \* 7;</stringmatch>
|
||||||
|
<stringmatch name="Switch Multi #5" min="1" max="1">uVar1 / 3;</stringmatch>
|
||||||
|
<stringmatch name="Switch Multi #6" min="1" max="1">uVar1 % 0x65;</stringmatch>
|
||||||
|
<stringmatch name="Switch Multi #7" min="1" max="1">uVar1 \^ 0xaba;</stringmatch>
|
||||||
|
<stringmatch name="Switch Multi #8" min="1" max="1">uVar1 \| 0x20;</stringmatch>
|
||||||
|
<stringmatch name="Switch Multi #9" min="1" max="1">return 0xfffffffffffffffe;</stringmatch>
|
||||||
|
</decompilertest>
|
Loading…
Add table
Add a link
Reference in a new issue