Merge remote-tracking branch 'origin/GP-1312_PointerPush'

This commit is contained in:
Ryan Kurtz 2021-09-22 12:55:34 -04:00
commit 7cbaa4fd61
5 changed files with 377 additions and 91 deletions

View file

@ -11,8 +11,9 @@ src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxyg
src/decompile/cpp/Makefile||GHIDRA||||END|
src/decompile/datatests/convert.xml||GHIDRA||||END|
src/decompile/datatests/deadvolatile.xml||GHIDRA||||END|
src/decompile/datatests/elseif.xml||GHIDRA||||END|
src/decompile/datatests/deindirect.xml||GHIDRA||||END|
src/decompile/datatests/dupptr.xml||GHIDRA||||END|
src/decompile/datatests/elseif.xml||GHIDRA||||END|
src/decompile/datatests/floatprint.xml||GHIDRA||||END|
src/decompile/datatests/forloop1.xml||GHIDRA||||END|
src/decompile/datatests/forloop_loaditer.xml||GHIDRA||||END|

View file

@ -5174,6 +5174,7 @@ void ActionDatabase::universalAction(Architecture *conf)
actcleanup->addRule( new Rule2Comp2Sub("cleanup") );
actcleanup->addRule( new RuleSubRight("cleanup") );
actcleanup->addRule( new RulePtrsubCharConstant("cleanup") );
actcleanup->addRule( new RuleExtensionPush("cleanup") );
}
act->addAction( actcleanup );

View file

@ -6098,74 +6098,81 @@ void AddTreeState::buildTree(void)
data.opDestroy(baseOp);
}
/// \brief Verify that given PcodeOp occurs at the bottom of the CPUI_INT_ADD tree
///
/// The main RulePtrArith algorithm assumes that the pointer Varnode is at the bottom
/// of the expression tree that is adding an offset to the pointer. This routine
/// verifies this condition.
/// \param op is the given PcodeOp which is the putative last operation in the tree
/// \param slot is the slot of the pointer Varnode within the given PcodeOp
/// \return \b true if the pointer is at the bottom of the tree, \b false otherwise
bool RulePtrArith::verifyAddTreeBottom(PcodeOp *op,int4 slot)
{
Varnode *vn = op->getOut();
Varnode *ptrbase = op->getIn(slot);
list<PcodeOp *>::const_iterator iter=vn->beginDescend();
OpCode opc;
if (iter == vn->endDescend()) return false; // Don't bother if no descendants
PcodeOp *lowerop = *iter++;
opc = lowerop->code();
if (vn->isSpacebase()) // For the RESULT to be a spacebase pointer
if (iter!=vn->endDescend()) // It must have only 1 descendant
return false;
if (opc == CPUI_INT_ADD) // Check for lone descendant which is an ADD
if (iter==vn->endDescend())
return false; // this is not bottom of add tree
if (ptrbase->isSpacebase() && (ptrbase->isInput()||(ptrbase->isConstant())) &&
(op->getIn(1-slot)->isConstant())) {
// Look for ANY descendant which LOADs or STOREs off of vn
if ((opc==CPUI_LOAD)||(opc==CPUI_STORE)) {
if (lowerop->getIn(1) == vn)
return false;
}
while(iter!=vn->endDescend()) {
opc = (*iter)->code();
if ((opc==CPUI_LOAD)||(opc==CPUI_STORE)) {
if ((*iter)->getIn(1) == vn)
return false;
}
++iter;
}
}
return true;
}
/// \brief Test for other pointers in the ADD tree above the given op that might be a preferred base
///
/// This tests the condition of RulePushPtr, making sure that the given op isn't the lone descendant
/// of a pointer constructed by INT_ADD on another pointer (which would then be preferred).
/// This tests the condition of RulePushPtr on the node immediately above the given putative base pointer
/// \param op is the given op
/// \param slot is the input slot of the pointer
/// \param slot is the input slot of the putative base pointer
/// \return \b true if the indicated slot holds the preferred pointer
bool RulePtrArith::verifyPreferredPointer(PcodeOp *op,int4 slot)
{
Varnode *vn = op->getIn(slot);
// Check if RulePushPtr would apply here
if (op->getIn(1-slot)->getType()->getMetatype() != TYPE_PTR && vn->isWritten()) {
PcodeOp *preOp = vn->getDef();
if (preOp->code() == CPUI_INT_ADD) {
if (vn->loneDescend() == op) {
int ptrCount = 0;
if (preOp->getIn(0)->getType()->getMetatype() == TYPE_PTR) ptrCount += 1;
if (preOp->getIn(1)->getType()->getMetatype() == TYPE_PTR) ptrCount += 1;
if (ptrCount == 1)
return false; // RulePushPtr would apply, so we are not preferred
}
if (!vn->isWritten()) return true;
PcodeOp *preOp = vn->getDef();
if (preOp->code() != CPUI_INT_ADD) return true;
int preslot = 0;
if (preOp->getIn(preslot)->getType()->getMetatype() != TYPE_PTR) {
preslot = 1;
if (preOp->getIn(preslot)->getType()->getMetatype() != TYPE_PTR)
return true;
}
return (1 != evaluatePointerExpression(preOp, preslot)); // Does earlier varnode look like the base pointer
}
/// \brief Determine if the expression rooted at the given INT_ADD operation is ready for conversion
///
/// Converting an expression of INT_ADDs into PTRSUBs and PTRADDs requires that the base pointer
/// be at the root of the expression tree. This method evaluates whether given root has the base
/// pointer at the bottom. If not, a \e push transform needs to be performed before RulePtrArith can apply.
/// This method returns a command code:
/// - 0 if no action should be taken, the expression is not fully linked or should not be converted
/// - 1 if a \e push action should be taken, prior to conversion
/// - 2 if the pointer arithmetic conversion can proceed
/// \param op is the given INT_ADD
/// \param slot is the index of the pointer
/// \return the command code
int4 RulePtrArith::evaluatePointerExpression(PcodeOp *op,int4 slot)
{
int4 res = 1; // Assume we are going to push
int4 count = 0; // Count descendants
Varnode *ptrBase = op->getIn(slot);
if (ptrBase->isFree() && !ptrBase->isConstant())
return 0;
if (op->getIn(1 - slot)->getType()->getMetatype() == TYPE_PTR)
res = 2;
Varnode *outVn = op->getOut();
list<PcodeOp *>::const_iterator iter;
for(iter=outVn->beginDescend();iter!=outVn->endDescend();++iter) {
PcodeOp *decOp = *iter;
count += 1;
OpCode opc = decOp->code();
if (opc == CPUI_INT_ADD) {
Varnode *otherVn = decOp->getIn(1 - decOp->getSlot(outVn));
if (otherVn->isFree() && !otherVn->isConstant())
return 0; // No action if the data-flow isn't fully linked
if (otherVn->getType()->getMetatype() == TYPE_PTR)
res = 2; // Do not push in the presence of other pointers
}
else if ((opc == CPUI_LOAD || opc == CPUI_STORE) && decOp->getIn(1) == outVn) { // If use is as pointer for LOAD or STORE
if (ptrBase->isSpacebase() && (ptrBase->isInput()||(ptrBase->isConstant())) &&
(op->getIn(1-slot)->isConstant()))
return 0;
res = 2;
}
else { // Any other op besides ADD, do not push
res = 2;
}
}
return true;
if (count == 0)
return 0;
if (count > 1) {
if (outVn->isSpacebase())
return 0; // For the RESULT to be a spacebase pointer it must have only 1 descendent
// res = 2; // Uncommenting this line will not let pointers get pushed to multiple descendants
}
return res;
}
/// \class RulePtrArith
@ -6206,7 +6213,7 @@ int4 RulePtrArith::applyOp(PcodeOp *op,Funcdata &data)
if (ct->getMetatype() == TYPE_PTR) break;
}
if (slot == op->numInput()) return 0;
if (!verifyAddTreeBottom(op, slot)) return 0;
if (evaluatePointerExpression(op, slot) != 2) return 0;
if (!verifyPreferredPointer(op, slot)) return 0;
const TypePointer *tp = (const TypePointer *) ct;
@ -6298,6 +6305,82 @@ int4 RuleStructOffset0::applyOp(PcodeOp *op,Funcdata &data)
return 1;
}
/// \brief Build a duplicate of the given Varnode as an output of a PcodeOp, preserving the storage address if possible
///
/// If the Varnode is already a \e unique or is \e addrtied
/// \param vn is the given Varnode
/// \param op is the PcodeOp to which the duplicate should be an output
/// \param data is the function to add the duplicate to
/// \return the duplicate Varnode
Varnode *RulePushPtr::buildVarnodeOut(Varnode *vn,PcodeOp *op,Funcdata &data)
{
if (vn->isAddrTied() || vn->getSpace()->getType() == IPTR_INTERNAL)
return data.newUniqueOut(vn->getSize(), op);
return data.newVarnodeOut(vn->getSize(), vn->getAddr(), op);
}
/// \brief Generate list of PcodeOps that need to be duplicated as part of pushing the pointer
///
/// If the pointer INT_ADD is duplicated as part of the push, some of the operations building
/// the offset to the pointer may also need to be duplicated. Identify these and add them
/// to the result list.
/// \param reslist is the result list to be populated
/// \param vn is the offset Varnode being added to the pointer
void RulePushPtr::collectDuplicateNeeds(vector<PcodeOp *> &reslist,Varnode *vn)
{
for(;;) {
if (!vn->isWritten()) return;
if (vn->isAutoLive()) return;
if (vn->loneDescend() == (PcodeOp *)0) return; // Already has multiple descendants
PcodeOp *op = vn->getDef();
OpCode opc = op->code();
if (opc == CPUI_INT_ZEXT || opc == CPUI_INT_SEXT || opc == CPUI_INT_2COMP)
reslist.push_back(op);
else if (opc == CPUI_INT_MULT) {
if (op->getIn(1)->isConstant())
reslist.push_back(op);
}
else
return;
vn = op->getIn(0);
}
}
/// \brief Duplicate the given PcodeOp so that the outputs have only 1 descendant
///
/// Run through the descendants of the PcodeOp output and create a duplicate
/// of the PcodeOp right before the descendant. We assume the PcodeOp either has
/// a single input, or has 2 inputs where the second is a constant.
/// The (original) PcodeOp is destroyed.
/// \param op is the given PcodeOp to duplicate
/// \param data is function to build duplicates in
void RulePushPtr::duplicateNeed(PcodeOp *op,Funcdata &data)
{
Varnode *outVn = op->getOut();
Varnode *inVn = op->getIn(0);
int num = op->numInput();
OpCode opc = op->code();
list<PcodeOp *>::const_iterator iter = outVn->beginDescend();
do {
PcodeOp *decOp = *iter;
int4 slot = decOp->getSlot(outVn);
PcodeOp *newOp = data.newOp(num, op->getAddr()); // Duplicate op associated with original address
Varnode *newOut = buildVarnodeOut(outVn, newOp, data); // Result contained in original storage
newOut->updateType(outVn->getType(),false,false);
data.opSetOpcode(newOp, opc);
data.opSetInput(newOp, inVn, 0);
if (num > 1)
data.opSetInput(newOp, op->getIn(1), 1);
data.opSetInput(decOp, newOut, slot);
data.opInsertBefore(newOp, decOp);
iter = outVn->beginDescend();
} while(iter != outVn->endDescend());
data.opDestroy(op);
}
/// \class RulePushPtr
/// \brief Push a Varnode with known pointer data-type to the bottom of its additive expression
///
@ -6312,47 +6395,51 @@ void RulePushPtr::getOpList(vector<uint4> &oplist) const
int4 RulePushPtr::applyOp(PcodeOp *op,Funcdata &data)
{
int4 i,j;
PcodeOp *decop,*newop;
Varnode *vn;
int4 slot;
Varnode *vni = (Varnode *)0;
const Datatype *ct;
if (!data.isTypeRecoveryOn()) return 0;
for(i=0;i<op->numInput();++i) { // Search for pointer type
vni = op->getIn(i);
ct = vni->getType();
if (ct->getMetatype() == TYPE_PTR) break;
for(slot=0;slot<op->numInput();++slot) { // Search for pointer type
vni = op->getIn(slot);
if (vni->getType()->getMetatype() == TYPE_PTR) break;
}
if (i == op->numInput()) return 0;
if ((i==0)&&(op->getIn(1)->getType()->getMetatype() == TYPE_PTR)) return 0; // Prevent infinite loops
vn = op->getOut();
if ((decop=vn->loneDescend()) == (PcodeOp *)0) return 0;
if (decop->code() != CPUI_INT_ADD) return 0;
if (slot == op->numInput()) return 0;
j = decop->getSlot(vn);
if (decop->getIn(1-j)->getType()->getMetatype() == TYPE_PTR) return 0; // Prevent infinite loops
if (RulePtrArith::evaluatePointerExpression(op, slot) != 1) return 0;
Varnode *vn = op->getOut();
Varnode *vnadd2 = op->getIn(1-slot);
vector<PcodeOp *> duplicateList;
if (vn->loneDescend() == (PcodeOp *)0)
collectDuplicateNeeds(duplicateList, vnadd2);
Varnode *vnadd1 = decop->getIn(1-j);
Varnode *vnadd2 = op->getIn(1-i);
Varnode *newout;
for(;;) {
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
if (iter == vn->endDescend()) break;
PcodeOp *decop = *iter;
int4 j = decop->getSlot(vn);
// vni and vnadd2 are propagated, so they shouldn't be free
if (vnadd2->isFree() && (!vnadd2->isConstant())) return 0;
if (vni->isFree() && (!vni->isConstant())) return 0;
Varnode *vnadd1 = decop->getIn(1-j);
Varnode *newout;
newop = data.newOp(2,decop->getAddr());
data.opSetOpcode(newop,CPUI_INT_ADD);
newout = data.newUniqueOut(vnadd1->getSize(),newop);
// Create new INT_ADD for the intermediate result that didn't exist in original code.
// We don't associate it with the address of the original INT_ADD
// We don't preserve the Varnode address of the original INT_ADD
PcodeOp *newop = data.newOp(2,decop->getAddr()); // Use the later address
data.opSetOpcode(newop,CPUI_INT_ADD);
newout = data.newUniqueOut(vnadd1->getSize(),newop); // Use a temporary storage address
data.opSetInput(decop,vni,0);
data.opSetInput(decop,newout,1);
data.opSetInput(decop,vni,0);
data.opSetInput(decop,newout,1);
data.opSetInput(newop,vnadd1,0);
data.opSetInput(newop,vnadd2,1);
data.opSetInput(newop,vnadd1,0);
data.opSetInput(newop,vnadd2,1);
data.opInsertBefore(newop,decop);
data.opInsertBefore(newop,decop);
}
if (!vn->isAutoLive())
data.opDestroy(op);
for(int4 i=0;i<duplicateList.size();++i)
duplicateNeed(duplicateList[i], data);
return 1;
}
@ -6648,6 +6735,56 @@ int4 RulePtrsubCharConstant::applyOp(PcodeOp *op,Funcdata &data)
return 1;
}
/// \class RuleExtensionPush
/// \brief Duplicate CPUI_INT_ZEXT and CPUI_INT_SEXT operations if the result is used in multiple pointer calculations
///
/// By making the extension operation part of each pointer calculation (where it is usually an implied cast),
/// we can frequently eliminate an explicit variable that would just hold the extension.
void RuleExtensionPush::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_INT_ZEXT);
oplist.push_back(CPUI_INT_SEXT);
}
int4 RuleExtensionPush::applyOp(PcodeOp *op,Funcdata &data)
{
Varnode *inVn = op->getIn(0);
if (inVn->isConstant()) return 0;
if (inVn->isAddrForce()) return 0;
if (inVn->isAddrTied()) return 0;
Varnode *outVn = op->getOut();
if (outVn->isTypeLock() || outVn->isNameLock()) return 0;
if (outVn->isAddrForce() || outVn->isAddrTied()) return 0;
list<PcodeOp *>::const_iterator iter;
int4 addcount = 0; // Number of INT_ADD descendants
int4 ptrcount = 0; // Number of PTRADD descendants
for(iter=outVn->beginDescend();iter!=outVn->endDescend();++iter) {
PcodeOp *decOp = *iter;
OpCode opc = decOp->code();
if (opc == CPUI_PTRADD) {
// This extension will likely be hidden
ptrcount += 1;
}
else if (opc == CPUI_INT_ADD) {
PcodeOp *subOp = decOp->getOut()->loneDescend();
if (subOp == (PcodeOp *)0 || subOp->code() != CPUI_PTRADD)
return 0;
addcount += 1;
}
else {
return 0;
}
}
if ((addcount + ptrcount) <= 1) return 0;
if (addcount > 0) {
if (op->getIn(0)->loneDescend() != (PcodeOp *)0) return 0;
}
RulePushPtr::duplicateNeed(op, data); // Duplicate the extension to all result descendants
return 1;
}
/// \class RuleSubNormal
/// \brief Pull-back SUBPIECE through INT_RIGHT and INT_SRIGHT
///

View file

@ -1027,7 +1027,6 @@ public:
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RulePtrArith : public Rule {
static bool verifyAddTreeBottom(PcodeOp *op,int4 slot);
static bool verifyPreferredPointer(PcodeOp *op,int4 slot);
public:
RulePtrArith(const string &g) : Rule(g, 0, "ptrarith") {} ///< Constructor
@ -1037,6 +1036,7 @@ public:
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
static int4 evaluatePointerExpression(PcodeOp *op,int4 slot);
};
class RuleStructOffset0 : public Rule {
public:
@ -1049,6 +1049,8 @@ public:
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RulePushPtr : public Rule {
static Varnode *buildVarnodeOut(Varnode *vn,PcodeOp *op,Funcdata &data);
static void collectDuplicateNeeds(vector<PcodeOp *> &reslist,Varnode *vn);
public:
RulePushPtr(const string &g) : Rule(g, 0, "pushptr") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
@ -1057,6 +1059,7 @@ public:
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
static void duplicateNeed(PcodeOp *op,Funcdata &data);
};
class RulePtraddUndo : public Rule {
public:
@ -1136,6 +1139,17 @@ public:
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RuleExtensionPush : public Rule {
public:
RuleExtensionPush(const string &g) : Rule( g, 0, "extensionpush") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Rule *)0;
return new RuleExtensionPush(getGroup());
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RuleSubNormal : public Rule {
public:
RuleSubNormal(const string &g) : Rule( g, 0, "subnormal") {} ///< Constructor

View file

@ -0,0 +1,133 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Each function has multiple pointer calculations with shared intermediate results.
In all cases, the base pointer must be pushed past the intermediate result in
order to recover a natural expression.
-->
<bytechunk space="ram" offset="0x10063a" readonly="true">
554889e54889
7de88975e48b45e44898488d14c50000
0000488b45e84801d0488945f8488b45
f8488d5008488d057803000048890248
8b45f8488d5010488d056c0300004889
02905dc3
</bytechunk>
<bytechunk space="ram" offset="0x1006ba" readonly="true">
554889e54881
eca800000089bd5cffffff488d8560ff
ffff4889c7e8aaffffff488d8560ffff
ff8b955cffffff4863d248c1e2024801
d0488945f8488b45f84883c008488945
f0488b45f08b00c1f80383e00f89c248
8b45f0891090c9c3
</bytechunk>
<bytechunk space="ram" offset="0x100718" readonly="true">
554889e54883ec20
48897de88975e4488b45e84889c7e851
ffffff8b45e44898488d148500000000
488b45e84801d0488945f8488b45f848
83c008488945f0488b45f08b00c1f803
83e00f89c2488b45f0891090c9c3
</bytechunk>
<bytechunk space="ram" offset="0x10076e" readonly="true">
5548
89e54881ec7805000089bd8cfaffff48
8d8590faffff4889c7e8f6feffff488d
8590faffff4883c024488945f88b858c
faffff4898488945f0488b55f04889d0
48c1e0044801d048c1e0034889c2488b
45f84801d0488945e8488b45f04883c0
014889c24889d048c1e0044801d048c1
e0034889c2488b45f84801d0488945e0
488b45e88b008d5025488b45e0891090
c9c3
</bytechunk>
<bytechunk space="ram" offset="0x100802" readonly="true">
554889e548897dd88975d48b45d4
4898488d148500000000488b45d84801
d0488945f8488b45f84883c008488945
f0488b45f84883c048488945e8488b45
f0c70007000000488b45e8c7006f0000
00905dc3
</bytechunk>
<bytechunk space="ram" offset="0x100854" readonly="true">
554889e548897dd88975d48b
45d44863d04889d04801c04801d048c1
e0024889c2488b45d84801d04883c004
488945f8488b45f84883e880488945f0
488b45f84883c008488945e8488b45f0
c70014000000488b45e8c7001e000000
905dc3
</bytechunk>
<bytechunk space="ram" offset="0x1008b3" readonly="true">
554889e548897dd88975d4488b
45d84883c01c488945f8488b45f8488d
50088b45d4489848c1e00a4801d04889
45f0488b45f8488d50088b45d483c001
489848c1e00a4801d0488945e8488b45
e88b008d50fd488b45f08910905dc3
</bytechunk>
<bytechunk space="ram" offset="0x1009e4" readonly="true">
48656c6c6f00476f6f6462796500
</bytechunk>
<symbol space="ram" offset="0x10063a" name="argv_test"/>
<symbol space="ram" offset="0x100684" name="initstruct"/>
<symbol space="ram" offset="0x1006a4" name="inithierarchy"/>
<symbol space="ram" offset="0x1006af" name="initarrayarray"/>
<symbol space="ram" offset="0x1006ba" name="loadstore_fieldarray"/>
<symbol space="ram" offset="0x100718" name="loadstore_ptrfieldarray"/>
<symbol space="ram" offset="0x10076e" name="doublearray_stack"/>
<symbol space="ram" offset="0x100802" name="sameindex"/>
<symbol space="ram" offset="0x100854" name="sameindex_samefield"/>
<symbol space="ram" offset="0x1008b3" name="twodim_array"/>
</binaryimage>
<script>
<com>parse line struct arraystruct { int4 a; int4 b; int4 arr1[16]; int4 arr2[16]; };</com>
<com>parse line struct substruct { int4 c; int4 d; int4 e; };</com>
<com>parse line struct hierarchy { int4 a; int4 b; substruct arr1[10]; substruct arr2[10]; };</com>
<com>parse line struct arrayarray { int4 a; int4 b; int4 arr[8][256]; };</com>
<com>parse line extern void initstruct(arraystruct *ptr);</com>
<com>parse line extern void inithierarchy(hierarchy *ptr);</com>
<com>parse line extern void initarrayarray(arrayarray *ptr);</com>
<com>parse line extern void argv_test(char **argv,int4 a);</com>
<com>parse line extern void loadstore_fieldarray(int4 a);</com>
<com>parse line extern void loadstore_ptrfieldarray(arraystruct *ptr,int4 a);</com>
<com>parse line extern void doublearray_stack(int4 a);</com>
<com>parse line extern void sameindex(arraystruct *ptr,int4 a);</com>
<com>parse line extern void sameindex_samefield(hierarchy *ptr,int4 a);</com>
<com>parse line extern void twodim_array(arrayarray *ptr,int4 a);</com>
<com>lo fu argv_test</com>
<com>dec</com>
<com>print C</com>
<com>lo fu loadstore_fieldarray</com>
<com>dec</com>
<com>print C</com>
<com>lo fu loadstore_ptrfieldarray</com>
<com>dec</com>
<com>print C</com>
<com>lo fu doublearray_stack</com>
<com>dec</com>
<com>print C</com>
<com>lo fu sameindex</com>
<com>dec</com>
<com>print C</com>
<com>lo fu sameindex_samefield</com>
<com>dec</com>
<com>print C</com>
<com>lo fu twodim_array</com>
<com>dec</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Intermediate pointers #1" min="1" max="1">argv\[\(int8\)a \+ 1\] = "Hello";</stringmatch>
<stringmatch name="Intermediate pointers #2" min="1" max="1">argv\[\(int8\)a \+ 2\] = "Goodbye";</stringmatch>
<stringmatch name="Intermediate pointers #3" min="1" max="1">aStack168\.arr1\[a\] = aStack168\.arr1\[a\] &gt;&gt; 3 &amp; 0xf;</stringmatch>
<stringmatch name="Intermediate pointers #4" min="1" max="1">ptr-&gt;arr1\[a\] = ptr-&gt;arr1\[a\] &gt;&gt; 3 &amp; 0xf;</stringmatch>
<stringmatch name="Intermediate pointers #5" min="1" max="1">aaStack1400\[\(int8\)a \+ 1\]\.arr1\[7\] = aaStack1400\[a\]\.arr1\[7\] \+ 0x25;</stringmatch>
<stringmatch name="Intermediate pointers #6" min="1" max="1">ptr-&gt;arr1\[a\] = 7;</stringmatch>
<stringmatch name="Intermediate pointers #7" min="1" max="1">ptr-&gt;arr2\[a\] = 0x6f;</stringmatch>
<stringmatch name="Intermediate pointers #8" min="1" max="1">ptr-&gt;arr1\[a\]\.d = 0x1e;</stringmatch>
<stringmatch name="Intermediate pointers #9" min="1" max="1">ptr-&gt;arr2\[a\]\.d = 0x14;</stringmatch>
<stringmatch name="Intermediate pointers #10" min="1" max="1">ptr-&gt;arr\[a\]\[7\] = ptr-&gt;arr\[a \+ 1\]\[7\] \+ -3;</stringmatch>
</decompilertest>