/* ### * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "expression.hh" namespace ghidra { /// \brief Return \b true if the alternate path looks more valid than the main path. /// /// Two different paths from a common Varnode each terminate at a CALL, CALLIND, or RETURN. /// Evaluate which path most likely represents actual parameter/return value passing, /// based on traversal information about each path. /// \param vn is the Varnode terminating the \e alternate path /// \param flags indicates traversals for both paths /// \return \b true if the alternate path is preferred bool TraverseNode::isAlternatePathValid(const Varnode *vn,uint4 flags) { if ((flags & (indirect | indirectalt)) == indirect) // If main path traversed an INDIRECT but the alternate did not return true; // Main path traversed INDIRECT, alternate did not if ((flags & (indirect | indirectalt)) == indirectalt) return false; // Alternate path traversed INDIRECT, main did not if ((flags & actionalt) != 0) return true; // Alternate path traversed a dedicated COPY if (vn->loneDescend() == (PcodeOp*)0) return false; const PcodeOp *op = vn->getDef(); if (op == (PcodeOp*)0) return true; while(op->isIncidentalCopy() && op->code() == CPUI_COPY) { // Skip any incidental COPY vn = op->getIn(0); if (vn->loneDescend() == (PcodeOp *)0) return false; op = vn->getDef(); if (op == (PcodeOp *)0) return true; } return !op->isMarker(); // MULTIEQUAL or INDIRECT indicates multiple values } /// \brief Test if two operations with same opcode produce complementary boolean values /// /// This only tests for cases where the opcode is INT_LESS or INT_SLESS and one of the /// inputs is constant. /// \param bin1op is the first p-code op to compare /// \param bin2op is the second p-code op to compare /// \return \b true if the two operations always produce complementary values bool BooleanMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op) { OpCode opcode = bin1op->code(); if ((opcode == CPUI_INT_SLESS)||(opcode==CPUI_INT_LESS)) { // Basically we test for the scenario like: x < 9 8 < x int4 constslot = 0; if (bin1op->getIn(1)->isConstant()) constslot = 1; if (!bin1op->getIn(constslot)->isConstant()) return false; if (!bin2op->getIn(1-constslot)->isConstant()) return false; if (!varnodeSame(bin1op->getIn(1-constslot),bin2op->getIn(constslot))) return false; uintb val1 = bin1op->getIn(constslot)->getOffset(); uintb val2 = bin2op->getIn(1-constslot)->getOffset(); if (constslot!=0) { uintb tmp = val2; val2 = val1; val1 = tmp; } if (val1 + 1 != val2) return false; if ((val2 == 0)&&(opcode==CPUI_INT_LESS)) return false; // Corner case for unsigned if (opcode==CPUI_INT_SLESS) { // Corner case for signed int4 sz = bin1op->getIn(constslot)->getSize(); if (signbit_negative(val2,sz) && (!signbit_negative(val1,sz))) return false; } return true; } return false; } /// \brief Do the given Varnodes hold the same value, possibly as constants /// /// \param a is the first Varnode to compare /// \param b is the second Varnode /// \return \b true if the Varnodes (always) hold the same value bool BooleanMatch::varnodeSame(Varnode *a,Varnode *b) { if (a == b) return true; if (a->isConstant() && b->isConstant()) return (a->getOffset() == b->getOffset()); return false; } /// \brief Determine if two boolean Varnodes hold related values /// /// The values may be the \e same, or opposite of each other (\e complementary). /// Otherwise the values are \e uncorrelated. The trees constructing each Varnode /// are examined up to a maximum \b depth. If this is exceeded \e uncorrelated is returned. /// \param vn1 is the first boolean Varnode /// \param vn2 is the second boolean Varnode /// \param depth is the maximum depth to traverse in the evaluation /// \return the correlation class int4 BooleanMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth) { if (vn1 == vn2) return same; PcodeOp *op1,*op2; OpCode opc1,opc2; if (vn1->isWritten()) { op1 = vn1->getDef(); opc1 = op1->code(); if (opc1 == CPUI_BOOL_NEGATE) { int res = evaluate(op1->getIn(0),vn2,depth); if (res == same) // Flip same <-> complementary result res = complementary; else if (res == complementary) res = same; return res; } } else { op1 = (PcodeOp *)0; // Don't give up before checking if op2 is BOOL_NEGATE opc1 = CPUI_MAX; } if (vn2->isWritten()) { op2 = vn2->getDef(); opc2 = op2->code(); if (opc2 == CPUI_BOOL_NEGATE) { int4 res = evaluate(vn1,op2->getIn(0),depth); if (res == same) // Flip same <-> complementary result res = complementary; else if (res == complementary) res = same; return res; } } else return uncorrelated; if (op1 == (PcodeOp *)0) return uncorrelated; if (!op1->isBoolOutput() || !op2->isBoolOutput()) return uncorrelated; if (depth != 0 && (opc1 == CPUI_BOOL_AND || opc1 == CPUI_BOOL_OR || opc1 == CPUI_BOOL_XOR)) { if (opc2 == CPUI_BOOL_AND || opc2 == CPUI_BOOL_OR || opc2 == CPUI_BOOL_XOR) { if (opc1 == opc2 || (opc1 == CPUI_BOOL_AND && opc2 == CPUI_BOOL_OR) || (opc1 == CPUI_BOOL_OR && opc2 == CPUI_BOOL_AND)) { int4 pair1 = evaluate(op1->getIn(0),op2->getIn(0),depth-1); int4 pair2; if (pair1 == uncorrelated) { pair1 = evaluate(op1->getIn(0),op2->getIn(1),depth-1); // Try other possible pairing (commutative op) if (pair1 == uncorrelated) return uncorrelated; pair2 = evaluate(op1->getIn(1),op2->getIn(0),depth-1); } else { pair2 = evaluate(op1->getIn(1),op2->getIn(1),depth-1); } if (pair2 == uncorrelated) return uncorrelated; if (opc1 == opc2) { if (pair1 == same && pair2 == same) return same; else if (opc1 == CPUI_BOOL_XOR) { if (pair1 == complementary && pair2 == complementary) return same; return complementary; } } else { // Must be CPUI_BOOL_AND and CPUI_BOOL_OR if (pair1 == complementary && pair2 == complementary) return complementary; // De Morgan's Law } } } } else { // Two boolean output ops, compare them directly if (opc1 == opc2) { bool sameOp = true; int numInputs = op1->numInput(); for (int i = 0; i < numInputs; ++i){ if (!varnodeSame(op1->getIn(i),op2->getIn(i))){ sameOp = false; break; } } if (sameOp){ return same; } if (sameOpComplement(op1,op2)) { return complementary; } return uncorrelated; } // Check if the binary ops are complements of one another int4 slot1 = 0; int4 slot2 = 0; bool reorder; if (opc1 != get_booleanflip(opc2,reorder)) return uncorrelated; if (reorder) slot2 = 1; if (!varnodeSame(op1->getIn(slot1),op2->getIn(slot2))) return uncorrelated; if (!varnodeSame(op1->getIn(1-slot1),op2->getIn(1-slot2))) return uncorrelated; return complementary; } return uncorrelated; } const int4 BooleanExpressionMatch::maxDepth = 1; bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop) { int4 res = BooleanMatch::evaluate(op->getIn(1), iop->getIn(1), maxDepth); if (res == BooleanMatch::uncorrelated) return false; matchflip = (res == BooleanMatch::complementary); if (op->isBooleanFlip()) matchflip = !matchflip; if (iop->isBooleanFlip()) matchflip = !matchflip; return true; } /// Assuming root->getOut() is the root of an expression formed with the /// CPUI_INT_ADD op, collect all the Varnode \e terms of the expression. void TermOrder::collect(void) { Varnode *curvn; PcodeOp *curop; PcodeOp *subop,*multop; vector opstack; // Depth first traversal path vector multstack; opstack.push_back(root); multstack.push_back((PcodeOp *)0); while(!opstack.empty()) { curop = opstack.back(); multop = multstack.back(); opstack.pop_back(); multstack.pop_back(); for(int4 i=0;inumInput();++i) { curvn = curop->getIn(i); // curvn is a node of the subtree IF if (!curvn->isWritten()) { // curvn is not defined by another operation terms.emplace_back(curop,i,multop); continue; } if (curvn->loneDescend() == (PcodeOp *)0) { // curvn has more then one use terms.emplace_back(curop,i,multop); continue; } subop = curvn->getDef(); if (subop->code() != CPUI_INT_ADD) { // or if curvn is defined with some other type of op if ((subop->code()==CPUI_INT_MULT)&&(subop->getIn(1)->isConstant())) { PcodeOp *addop = subop->getIn(0)->getDef(); if ((addop!=(PcodeOp *)0)&&(addop->code()==CPUI_INT_ADD)) { if (addop->getOut()->loneDescend()!=(PcodeOp *)0) { opstack.push_back(addop); multstack.push_back(subop); continue; } } } terms.emplace_back(curop,i,multop); continue; } opstack.push_back(subop); multstack.push_back(multop); } } } void TermOrder::sortTerms(void) { sorter.reserve(terms.size()); for(vector::iterator iter=terms.begin();iter!=terms.end();++iter) sorter.push_back( &(*iter) ); sort(sorter.begin(),sorter.end(),additiveCompare); } /// The value \b true is returned if it can be proven that two terms add the same value to their /// respective expressions. /// \param op2 is the other term to compare with \b this /// \return \b true if the terms are equivalent bool AddExpression::Term::isEquivalent(const Term &op2) const { if (coeff != op2.coeff) return false; return functionalEquality(vn,op2.vn); } /// The value \b true is returned if it can be proven that the expressions always produce the same value. /// \param op2 is the other expression to compare with \b this /// \return \b true if the expressions are equivalent bool AddExpression::isEquivalent(const AddExpression &op2) const { if (constval != op2.constval) return false; if (numTerms != op2.numTerms) return false; if (numTerms == 1) { if (terms[0].isEquivalent(op2.terms[0])) return true; } else if (numTerms == 2) { if (terms[0].isEquivalent(op2.terms[0]) && terms[1].isEquivalent(op2.terms[1])) return true; if (terms[0].isEquivalent(op2.terms[1]) && terms[1].isEquivalent(op2.terms[0])) return true; } return false; } /// Recursively collect terms, up to the given depth. INT_ADD either contributes to the constant sum, or /// it is recursively walked. Term coefficients are collected from INT_MULT with a constant. /// \param vn is the root of the (sub)expression /// \param coeff is the current multiplicative coefficient of the subexpression /// \param depth is the current depth void AddExpression::gather(Varnode *vn,uintb coeff,int4 depth) { if (vn->isConstant()) { constval = constval + coeff * vn->getOffset(); constval &= calc_mask(vn->getSize()); return; } if (vn->isWritten()) { PcodeOp *op = vn->getDef(); if (op->code() == CPUI_INT_ADD) { if (!op->getIn(1)->isConstant()) depth -= 1; if (depth >= 0) { gather(op->getIn(0),coeff,depth); gather(op->getIn(1),coeff,depth); return; } } else if (op->code() == CPUI_INT_MULT) { if (op->getIn(1)->isConstant()) { coeff = coeff * op->getIn(1)->getOffset(); coeff &= calc_mask(vn->getSize()); gather(op->getIn(0),coeff,depth); return; } } } add(vn,coeff); return; } /// Gather up to two non-constant additive terms, given two root Varnodes that are being subtracted. /// \param a is the first root /// \param b is the second root being subtracted from the first void AddExpression::gatherTwoTermsSubtract(Varnode *a,Varnode *b) { int4 depth = (a->isConstant() || b->isConstant()) ? 1 : 0; gather(a,(uintb)1,depth); gather(b,calc_mask(b->getSize()),depth); } /// Gather up to two non-constant additive terms, given two root Varnodes being added /// \param a is the first root /// \param b is the second root being added to the first void AddExpression::gatherTwoTermsAdd(Varnode *a,Varnode *b) { int4 depth = (a->isConstant() || b->isConstant()) ? 1 : 0; gather(a,(uintb)1,depth); gather(b,(uintb)1,depth); } /// Gather up to two non-constant additive terms in the expression at the given root. /// \param root is the root Varnode void AddExpression::gatherTwoTermsRoot(Varnode *root) { gather(root,(uintb)1,1); } /// \brief Perform basic comparison of two given Varnodes /// /// Return /// - 0 if \b vn1 and \b vn2 must hold same value /// - -1 if they definitely don't hold same value /// - 1 if the same value depends on ops writing to \b vn1 and \b vn2 /// \param vn1 is the first Varnode to compare /// \param vn2 is the second /// \return a code -1, 0, or 1 static int4 functionalEqualityLevel0(Varnode *vn1,Varnode *vn2) { if (vn1==vn2) return 0; if (vn1->getSize() != vn2->getSize()) return -1; if (vn1->isConstant()) { if (vn2->isConstant()) { return (vn1->getOffset() == vn2->getOffset()) ? 0 : -1; } return -1; } if (vn2->isConstant()) return -1; if (vn1->isWritten() && vn2->isWritten()) return 1; return -1; } /// \brief Try to determine if \b vn1 and \b vn2 contain the same value /// /// Return: /// - -1, if they do \b not, or if it can't be immediately verified /// - 0, if they \b do hold the same value /// - >0, if the result is contingent on additional varnode pairs having the same value /// In the last case, the varnode pairs are returned as (res1[i],res2[i]), /// where the return value is the number of pairs. /// \param vn1 is the first Varnode to compare /// \param vn2 is the second Varnode /// \param res1 is a reference to the first returned Varnode /// \param res2 is a reference to the second returned Varnode /// \return the result of the comparison int4 functionalEqualityLevel(Varnode *vn1,Varnode *vn2,Varnode **res1,Varnode **res2) { int4 testval = functionalEqualityLevel0(vn1,vn2); if (testval != 1) return testval; PcodeOp *op1 = vn1->getDef(); PcodeOp *op2 = vn2->getDef(); OpCode opc = op1->code(); if (opc != op2->code()) return -1; int4 num = op1->numInput(); if (num != op2->numInput()) return -1; if (op1->isMarker()) return -1; if (op2->isCall()) return -1; if (opc == CPUI_LOAD) { // FIXME: We assume two loads produce the same // result if the address is the same and the loads // occur in the same instruction if (op1->getAddr() != op2->getAddr()) return -1; } if (num >= 3) { if (opc != CPUI_PTRADD) return -1; // If this is a PTRADD if (op1->getIn(2)->getOffset() != op2->getIn(2)->getOffset()) return -1; // Make sure the elsize constant is equal num = 2; // Otherwise treat as having 2 inputs } for(int4 i=0;igetIn(i); res2[i] = op2->getIn(i); } testval = functionalEqualityLevel0(res1[0],res2[0]); if (testval == 0) { // A match locks in this comparison ordering if (num==1) return 0; testval = functionalEqualityLevel0(res1[1],res2[1]); if (testval==0) return 0; if (testval < 0) return -1; res1[0] = res1[1]; // Match is contingent on second pair res2[0] = res2[1]; return 1; } if (num == 1) return testval; int4 testval2 = functionalEqualityLevel0(res1[1],res2[1]); if (testval2 == 0) { // A match locks in this comparison ordering return testval; } int4 unmatchsize; if ((testval==1)&&(testval2==1)) unmatchsize = 2; else unmatchsize = -1; if (!op1->isCommutative()) return unmatchsize; // unmatchsize must be 2 or -1 here on a commutative operator, // try flipping int4 comm1 = functionalEqualityLevel0(res1[0],res2[1]); int4 comm2 = functionalEqualityLevel0(res1[1],res2[0]); if ((comm1==0) && (comm2==0)) return 0; if ((comm1<0)||(comm2<0)) return unmatchsize; if (comm1==0) { // AND (comm2==1) res1[0] = res1[1]; // Left over unmatch is res1[1] and res2[0] return 1; } if (comm2==0) { // AND (comm1==1) res2[0] = res2[1]; // Left over unmatch is res1[0] and res2[1] return 1; } // If we reach here (comm1==1) AND (comm2==1) if (unmatchsize == 2) // If the original ordering wasn't impossible return 2; // Prefer the original ordering Varnode *tmpvn = res2[0]; // Otherwise swap the ordering res2[0] = res2[1]; res2[1] = tmpvn; return 2; } /// \brief Determine if two Varnodes hold the same value /// /// Only return \b true if it can be immediately determined they are equivalent /// \param vn1 is the first Varnode /// \param vn2 is the second Varnode /// \return true if they are provably equal bool functionalEquality(Varnode *vn1,Varnode *vn2) { Varnode *buf1[2]; Varnode *buf2[2]; return (functionalEqualityLevel(vn1,vn2,buf1,buf2)==0); } /// \brief Return true if vn1 and vn2 are verifiably different values /// /// This is actually a rather speculative test /// \param vn1 is the first Varnode to compare /// \param vn2 is the second Varnode /// \param depth is the maximum level to recurse while testing /// \return \b true if they are different bool functionalDifference(Varnode *vn1,Varnode *vn2,int4 depth) { PcodeOp *op1,*op2; int4 i,num; if (vn1 == vn2) return false; if ((!vn1->isWritten())||(!vn2->isWritten())) { if (vn1->isConstant() && vn2->isConstant()) return !(vn1->getAddr()==vn2->getAddr()); if (vn1->isInput()&&vn2->isInput()) return false; // Might be the same if (vn1->isFree()||vn2->isFree()) return false; // Might be the same return true; } op1 = vn1->getDef(); op2 = vn2->getDef(); if (op1->code() != op2->code()) return true; num = op1->numInput(); if (num != op2->numInput()) return true; if (depth==0) return true; // Different as far as we can tell depth -= 1; for(i=0;igetIn(i),op2->getIn(i),depth)) return true; return false; } } // End namespace ghidra