mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-3941 New boolean correlation test
This commit is contained in:
parent
ad532036ab
commit
8f3328856c
2 changed files with 137 additions and 323 deletions
|
@ -17,206 +17,7 @@
|
|||
|
||||
namespace ghidra {
|
||||
|
||||
ConditionMarker::ConditionMarker(void)
|
||||
|
||||
{
|
||||
initop = (PcodeOp *)0;
|
||||
basevn = (Varnode *)0;
|
||||
boolvn = (Varnode *)0;
|
||||
bool2vn = (Varnode *)0;
|
||||
bool3vn = (Varnode *)0;
|
||||
binaryop = (PcodeOp *)0;
|
||||
}
|
||||
|
||||
/// Any marks on Varnodes in the root expression are cleared
|
||||
ConditionMarker::~ConditionMarker(void)
|
||||
|
||||
{
|
||||
basevn->clearMark();
|
||||
if (boolvn != (Varnode *)0)
|
||||
boolvn->clearMark();
|
||||
if (bool2vn != (Varnode *)0)
|
||||
bool2vn->clearMark();
|
||||
if (bool3vn != (Varnode *)0)
|
||||
bool3vn->clearMark();
|
||||
if (binaryop != (PcodeOp *)0) {
|
||||
binaryop->getIn(0)->clearMark();
|
||||
binaryop->getIn(1)->clearMark();
|
||||
}
|
||||
}
|
||||
|
||||
/// Starting with the CBRANCH, the key Varnodes in the expression producing
|
||||
/// the boolean value are marked. BOOL_NEGATE operations are traversed, but
|
||||
/// otherwise only one level of operator is walked.
|
||||
/// \param op is the root CBRANCH operation
|
||||
void ConditionMarker::setupInitOp(PcodeOp *op)
|
||||
|
||||
{
|
||||
initop = op;
|
||||
basevn = op->getIn(1);
|
||||
Varnode *curvn = basevn;
|
||||
curvn->setMark();
|
||||
if (curvn->isWritten()) {
|
||||
PcodeOp *tmp = curvn->getDef();
|
||||
if (tmp->code() == CPUI_BOOL_NEGATE) {
|
||||
boolvn = tmp->getIn(0);
|
||||
curvn = boolvn;
|
||||
curvn->setMark();
|
||||
}
|
||||
}
|
||||
if (curvn->isWritten()) {
|
||||
PcodeOp *tmp = curvn->getDef();
|
||||
if (tmp->isBoolOutput()&&(tmp->getEvalType()==PcodeOp::binary)) {
|
||||
binaryop = tmp;
|
||||
Varnode *binvn = binaryop->getIn(0);
|
||||
if (!binvn->isConstant()) {
|
||||
if (binvn->isWritten()) {
|
||||
PcodeOp *negop = binvn->getDef();
|
||||
if (negop->code() == CPUI_BOOL_NEGATE) {
|
||||
if (!negop->getIn(0)->isConstant()) {
|
||||
bool2vn = negop->getIn(0);
|
||||
bool2vn->setMark();
|
||||
}
|
||||
}
|
||||
}
|
||||
binvn->setMark();
|
||||
}
|
||||
binvn = binaryop->getIn(1);
|
||||
if (!binvn->isConstant()) {
|
||||
if (binvn->isWritten()) {
|
||||
PcodeOp *negop = binvn->getDef();
|
||||
if (negop->code() == CPUI_BOOL_NEGATE) {
|
||||
if (!negop->getIn(0)->isConstant()) {
|
||||
bool3vn = negop->getIn(0);
|
||||
bool3vn->setMark();
|
||||
}
|
||||
}
|
||||
}
|
||||
binvn->setMark();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Walk the tree rooted at the given p-code op, looking for things marked in
|
||||
/// the tree rooted at \b initop. Trim everything but BOOL_NEGATE operations,
|
||||
/// one MULTIEQUAL, and one binary boolean operation. If there is a Varnode
|
||||
/// in common with the root expression, this is returned, and the tree traversal
|
||||
/// state holds the path from the boolean value to the common Varnode.
|
||||
/// \param op is the given CBRANCH op to compare
|
||||
/// \return the Varnode in common with the root expression or NULL
|
||||
Varnode *ConditionMarker::findMatch(PcodeOp *op)
|
||||
|
||||
{
|
||||
PcodeOp *curop;
|
||||
// FlowBlock *bl = op->getParent();
|
||||
state = 0;
|
||||
Varnode *curvn = op->getIn(1);
|
||||
multion = false;
|
||||
binon = false;
|
||||
|
||||
matchflip = op->isBooleanFlip();
|
||||
|
||||
for(;;) {
|
||||
if (curvn->isMark()) return curvn;
|
||||
bool popstate = true;
|
||||
if (curvn->isWritten()) {
|
||||
curop = curvn->getDef();
|
||||
if (curop->code() == CPUI_BOOL_NEGATE) {
|
||||
curvn = curop->getIn(0);
|
||||
if (!binon) // Only flip if we haven't seen binop yet, as binops get compared directly
|
||||
matchflip = !matchflip;
|
||||
popstate = false;
|
||||
}
|
||||
// else if (curop->code() == CPUI_MULTIEQUAL) {
|
||||
// if ((curop->getParent()==bl)&&(!multion)) {
|
||||
// opstate[state] = curop;
|
||||
// slotstate[state] = 0;
|
||||
// flipstate[state] = matchflip;
|
||||
// state += 1;
|
||||
// curvn = curop->Input(0);
|
||||
// multion = true;
|
||||
// popstate = false;
|
||||
// }
|
||||
// }
|
||||
else if (curop->isBoolOutput()&&(curop->getEvalType()==PcodeOp::binary)) {
|
||||
if (!binon) {
|
||||
opstate[state] = curop;
|
||||
slotstate[state] = 0;
|
||||
flipstate[state] = matchflip;
|
||||
state += 1;
|
||||
curvn = curop->getIn(0);
|
||||
binon = true;
|
||||
popstate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (popstate) {
|
||||
while(state > 0) {
|
||||
curop = opstate[state-1];
|
||||
matchflip = flipstate[state-1];
|
||||
slotstate[state-1] += 1;
|
||||
if (slotstate[state-1] < curop->numInput()) {
|
||||
curvn = curop->getIn(slotstate[state-1]);
|
||||
break;
|
||||
}
|
||||
state -= 1;
|
||||
if (opstate[state]->code() == CPUI_MULTIEQUAL)
|
||||
multion = false;
|
||||
else
|
||||
binon = false;
|
||||
}
|
||||
if (state==0) break;
|
||||
}
|
||||
}
|
||||
return (Varnode *)0;
|
||||
}
|
||||
|
||||
/// \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 ConditionMarker::varnodeSame(Varnode *a,Varnode *b)
|
||||
|
||||
{
|
||||
if (a == b) return true;
|
||||
if (a->isConstant() && b->isConstant())
|
||||
return (a->getOffset() == b->getOffset());
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Do the given boolean Varnodes always hold complementary values
|
||||
///
|
||||
/// Test if they are constants, 1 and 0, or if one is the direct BOOL_NEGATE of the other.
|
||||
/// \param a is the first Varnode to compare
|
||||
/// \param b is the second Varnode to compare
|
||||
/// \return \b true if the Varnodes (always) hold complementary values
|
||||
bool ConditionMarker::varnodeComplement(Varnode *a,Varnode *b)
|
||||
|
||||
{
|
||||
if (a->isConstant() && b->isConstant()) {
|
||||
uintb vala = a->getOffset();
|
||||
uintb valb = b->getOffset();
|
||||
if ((vala==0)&&(valb==1)) return true;
|
||||
if ((vala==1)&&(valb==0)) return true;
|
||||
return false;
|
||||
}
|
||||
PcodeOp *negop;
|
||||
if (a->isWritten()) {
|
||||
negop = a->getDef();
|
||||
if (negop->code() == CPUI_BOOL_NEGATE)
|
||||
if (negop->getIn(0) == b)
|
||||
return true;
|
||||
}
|
||||
if (b->isWritten()) {
|
||||
negop = b->getDef();
|
||||
if (negop->code() == CPUI_BOOL_NEGATE)
|
||||
if (negop->getIn(0) == a)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const int4 BooleanExpressionMatch::maxDepth = 1;
|
||||
|
||||
/// \brief Test if two operations with same opcode produce complementary boolean values
|
||||
///
|
||||
|
@ -225,7 +26,7 @@ bool ConditionMarker::varnodeComplement(Varnode *a,Varnode *b)
|
|||
/// \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 ConditionMarker::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op)
|
||||
bool BooleanExpressionMatch::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op)
|
||||
|
||||
{
|
||||
OpCode opcode = bin1op->code();
|
||||
|
@ -256,108 +57,138 @@ bool ConditionMarker::sameOpComplement(PcodeOp *bin1op,PcodeOp *bin2op)
|
|||
return false;
|
||||
}
|
||||
|
||||
/// \brief Check if given p-code ops are complements where one is an BOOL_AND and the other is an BOOL_OR
|
||||
/// \brief Do the given Varnodes hold the same value, possibly as constants
|
||||
///
|
||||
/// \param bin1op is the first PcodeOp
|
||||
/// \param bin2op is the second
|
||||
/// \return \b true if the p-code ops produce complementary values
|
||||
bool ConditionMarker::andOrComplement(PcodeOp *bin1op,PcodeOp *bin2op)
|
||||
/// \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 BooleanExpressionMatch::varnodeSame(Varnode *a,Varnode *b)
|
||||
|
||||
{
|
||||
if (bin1op->code() == CPUI_BOOL_AND) {
|
||||
if (bin2op->code() != CPUI_BOOL_OR) return false;
|
||||
}
|
||||
else if (bin1op->code() == CPUI_BOOL_OR) {
|
||||
if (bin2op->code() != CPUI_BOOL_AND) return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
// Reaching here, one is AND and one is OR
|
||||
if (varnodeComplement( bin1op->getIn(0), bin2op->getIn(0))) {
|
||||
if (varnodeComplement( bin1op->getIn(1), bin2op->getIn(1)))
|
||||
return true;
|
||||
}
|
||||
else if (varnodeComplement( bin1op->getIn(0), bin2op->getIn(1))) {
|
||||
if (varnodeComplement( bin1op->getIn(1), bin2op->getIn(0)))
|
||||
return true;
|
||||
}
|
||||
if (a == b) return true;
|
||||
if (a->isConstant() && b->isConstant())
|
||||
return (a->getOffset() == b->getOffset());
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Determine if the two boolean expressions always produce the same or complementary values
|
||||
/// \brief Determine if two boolean Varnodes hold related values
|
||||
///
|
||||
/// A common Varnode in the two expressions is given. If the boolean expressions are
|
||||
/// uncorrelated, \b false is returned, otherwise \b true is returned. If the expressions
|
||||
/// are correlated but always hold opposite values, the field \b matchflip is set to \b true.
|
||||
/// \param vn is the common Varnode
|
||||
/// \return \b true if the expressions are correlated
|
||||
bool ConditionMarker::finalJudgement(Varnode *vn)
|
||||
/// 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 BooleanExpressionMatch::evaluate(Varnode *vn1,Varnode *vn2,int4 depth)
|
||||
|
||||
{
|
||||
if (initop->isBooleanFlip())
|
||||
matchflip = !matchflip;
|
||||
if ((vn == basevn)&&(!binon)) // No binary operation involved
|
||||
return true;
|
||||
if (boolvn != (Varnode *)0)
|
||||
matchflip = !matchflip;
|
||||
if ((vn == boolvn)&&(!binon)) // Negations involved
|
||||
return true;
|
||||
if ((binaryop == (PcodeOp *)0)||(!binon))
|
||||
return false; // Conditions don't match
|
||||
|
||||
// Both conditions used binary op
|
||||
PcodeOp *binary2op = (PcodeOp *)0;
|
||||
for(int4 i=0;i<state;++i) { // Find the binary op
|
||||
binary2op = opstate[i];
|
||||
if (binary2op->isBoolOutput()) break;
|
||||
}
|
||||
// Check if the binary ops are exactly the same
|
||||
if (binaryop->code() == binary2op->code()) {
|
||||
if (varnodeSame(binaryop->getIn(0),binary2op->getIn(0)) &&
|
||||
varnodeSame(binaryop->getIn(1),binary2op->getIn(1)))
|
||||
return true;
|
||||
if (sameOpComplement(binaryop,binary2op)) {
|
||||
matchflip = !matchflip;
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// If not, check if the binary ops are complements of one another
|
||||
matchflip = !matchflip;
|
||||
if (andOrComplement(binaryop,binary2op))
|
||||
return true;
|
||||
int4 slot1 = 0;
|
||||
int4 slot2 = 0;
|
||||
bool reorder;
|
||||
if (binaryop->code() != get_booleanflip(binary2op->code(),reorder))
|
||||
return false;
|
||||
if (reorder) slot2 = 1;
|
||||
if (!varnodeSame(binaryop->getIn(slot1),binary2op->getIn(slot2)))
|
||||
return false;
|
||||
if (!varnodeSame(binaryop->getIn(1-slot1),binary2op->getIn(1-slot2)))
|
||||
return false;
|
||||
return true;
|
||||
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) {
|
||||
if (varnodeSame(op1->getIn(0),op2->getIn(0)) && varnodeSame(op1->getIn(1),op2->getIn(1)))
|
||||
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;
|
||||
}
|
||||
|
||||
bool ConditionMarker::verifyCondition(PcodeOp *op,PcodeOp *iop)
|
||||
bool BooleanExpressionMatch::verifyCondition(PcodeOp *op, PcodeOp *iop)
|
||||
|
||||
{
|
||||
setupInitOp(iop);
|
||||
Varnode *matchvn = findMatch(op);
|
||||
if (matchvn == (Varnode *)0) return false;
|
||||
if (!finalJudgement(matchvn)) return false;
|
||||
|
||||
// Make final determination of what MULTIEQUAL slot is used
|
||||
if (!multion)
|
||||
multislot = -1;
|
||||
else {
|
||||
for(int4 i=0;i<state;++i)
|
||||
if (opstate[i]->code()==CPUI_MULTIEQUAL) {
|
||||
multislot = slotstate[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
int4 res = evaluate(op->getIn(1), iop->getIn(1), maxDepth);
|
||||
if (res == uncorrelated)
|
||||
return false;
|
||||
matchflip = (res == complementary);
|
||||
if (op->isBooleanFlip())
|
||||
matchflip = !matchflip;
|
||||
if (iop->isBooleanFlip())
|
||||
matchflip = !matchflip;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -428,7 +259,7 @@ bool ConditionalExecution::verifySameCondition(void)
|
|||
if (init_cbranch == (PcodeOp *)0) return false;
|
||||
if (init_cbranch->code() != CPUI_CBRANCH) return false;
|
||||
|
||||
ConditionMarker tester;
|
||||
BooleanExpressionMatch tester;
|
||||
if (!tester.verifyCondition(cbranch,init_cbranch))
|
||||
return false;
|
||||
|
||||
|
@ -1057,7 +888,7 @@ int4 RuleOrPredicate::applyOp(PcodeOp *op,Funcdata &data)
|
|||
if (branch0.zeroBlock == branch1.zeroBlock) return 0; // zero sets must be along different paths
|
||||
}
|
||||
else { // Make sure cbranches have shared condition and the different zero sets have complementary paths
|
||||
ConditionMarker condmarker;
|
||||
BooleanExpressionMatch condmarker;
|
||||
if (!condmarker.verifyCondition(branch0.cbranch,branch1.cbranch)) return 0;
|
||||
if (condmarker.getMultiSlot() != -1) return 0;
|
||||
branch0.discoverPathIsTrue();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue