843 lines
26 KiB
Scala
843 lines
26 KiB
Scala
package analysis
|
|
|
|
import com.microsoft.z3._
|
|
import com.typesafe.scalalogging.StrictLogging
|
|
import constraints.Z3Constraint
|
|
import dex.DexType.{PrimitiveType, ObjectType, VoidType}
|
|
import dex.{DexClass, DexMethod, DexType}
|
|
import org.jf.dexlib2.dexbacked.reference.{
|
|
DexBackedStringReference,
|
|
DexBackedTypeReference
|
|
}
|
|
import org.jf.dexlib2.iface.instruction.Instruction
|
|
import org.jf.dexlib2.iface.instruction.formats._
|
|
import org.jf.dexlib2.iface.reference.MethodReference
|
|
import utils.Utils
|
|
|
|
object InstructionAnalyzer extends StrictLogging {
|
|
|
|
/**
|
|
* Do nothing. All registers at pp+ approximates the registers at pp.
|
|
*/
|
|
def nop(i: Instruction, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
Seq(
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(r <= rPlus: _*),
|
|
s"R($pp) <= R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def const4(i: Instruction11n, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
val rApprox = r.<=(i.getRegisterA)(rPlus)
|
|
Seq(
|
|
Z3Constraint(
|
|
env.domains.primitiveDomain.approx(
|
|
env.domains.primitiveDomain.toElement(i.getWideLiteral.toInt),
|
|
rPlus.numbers(i.getRegisterA)
|
|
),
|
|
s"${i.getWideLiteral} <= R(${pp +})(${i.getRegisterA})",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(rApprox: _*),
|
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def constString(i: Instruction21c,
|
|
pp: PP,
|
|
env: Environment): Seq[Z3Constraint] = {
|
|
constString(
|
|
i.getRegisterA,
|
|
i.getReference.asInstanceOf[DexBackedStringReference].getString,
|
|
pp,
|
|
env
|
|
)
|
|
}
|
|
|
|
def constStringJumbo(i: Instruction31c,
|
|
pp: PP,
|
|
env: Environment): Seq[Z3Constraint] = {
|
|
constString(
|
|
i.getRegisterA,
|
|
i.getReference.asInstanceOf[DexBackedStringReference].getString,
|
|
pp,
|
|
env
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Make the register A approximates str.
|
|
* Constant strings are treated like instances of Ljava/lang/String;
|
|
*/
|
|
private def constString(registerA: Int,
|
|
str: String,
|
|
pp: PP,
|
|
env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
Seq(
|
|
Z3Constraint(
|
|
env.domains.referenceDomain.approx(
|
|
env.domains.referenceDomain.toElement(pp.toString),
|
|
rPlus.references(registerA)
|
|
),
|
|
s"($pp) <= R(${pp +})($registerA)",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(r.<=(registerA)(rPlus): _*),
|
|
s"R($pp) <={$registerA} R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def iput(i: Instruction22c, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
val rApprox = r.<=(rPlus)
|
|
val fieldRef = i.getReference.toString
|
|
val cName = Utils.classPrefix(fieldRef)
|
|
val fieldName = Utils.removeClassPrefix(fieldRef)
|
|
val fieldType = DexType(fieldName.substring(fieldName.indexOf(":") + 1))
|
|
val classes = classAndSubclasses(env)(cName)
|
|
val obj: FuncDecl = env.H.obj()
|
|
val objApp = env.ctx.mkApp(obj, env.ctx.mkString(pp.toString))
|
|
val accessor: FuncDecl = fieldType match {
|
|
case PrimitiveType() =>
|
|
env.z3Obj.primAccessor
|
|
}
|
|
val objFields =
|
|
env.ctx.mkApp(accessor, objApp).asInstanceOf[ArrayExpr]
|
|
val constraints: Seq[Z3Constraint] = classes.flatMap(
|
|
c =>
|
|
env.preAnalysisResult.objectCreation
|
|
.filter(p => p._2 == c.name)
|
|
.map(objPP => {
|
|
Z3Constraint(
|
|
env.ctx.mkImplies(
|
|
env.ctx.mkAnd(
|
|
env.domains.referenceDomain.approx(
|
|
env.domains.referenceDomain.toElement(objPP._1.toString),
|
|
r.references(i.getRegisterB)
|
|
)
|
|
),
|
|
fieldType match {
|
|
case PrimitiveType() =>
|
|
env.domains.primitiveDomain.approx(
|
|
r.numbers(i.getRegisterA),
|
|
env.ctx
|
|
.mkSelect(objFields, env.ctx.mkString(fieldName))
|
|
.asInstanceOf[env.domains.primitiveDomain.Element]
|
|
)
|
|
}
|
|
),
|
|
s"${objPP._1.toString} <= R($pp)(${i.getRegisterB}) => " +
|
|
s"R($pp)(${i.getRegisterA}) <= H($objPP).$fieldName",
|
|
pp
|
|
)
|
|
})
|
|
)
|
|
constraints ++ Seq(
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(rApprox: _*),
|
|
s"R($pp) <= R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def sgetObject(i: Instruction21c,
|
|
pp: PP,
|
|
env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
val sFieldRef = i.getReference.toString
|
|
val cName = Utils.classPrefix(sFieldRef)
|
|
val sFieldName = Utils.removeClassPrefix(sFieldRef)
|
|
val sFieldType = DexType(sFieldName.substring(sFieldName.indexOf(":") + 1))
|
|
val dexClass = env.app.classes.find(c => c.name == cName)
|
|
val (sFieldVal, sFieldTaint): (Expr, Expr) = dexClass match {
|
|
case Some(c) =>
|
|
val f = c.fields.find(dexField => dexField.name == sFieldName).get
|
|
env.SH(c, f)
|
|
case None =>
|
|
// Unknown class, use top as value.
|
|
sFieldType match {
|
|
case PrimitiveType() =>
|
|
(env.domains.primitiveDomain.top, env.domains.taintDomain.top)
|
|
case ObjectType(_) =>
|
|
(env.domains.referenceDomain.top, env.domains.taintDomain.top)
|
|
}
|
|
}
|
|
Seq(
|
|
Z3Constraint(
|
|
sFieldType match {
|
|
case PrimitiveType() =>
|
|
env.domains.primitiveDomain.approx(
|
|
sFieldVal.asInstanceOf[env.domains.primitiveDomain.Element],
|
|
rPlus.numbers(i.getRegisterA)
|
|
)
|
|
case ObjectType(_) =>
|
|
env.domains.referenceDomain.approx(
|
|
sFieldVal.asInstanceOf[env.domains.referenceDomain.Element],
|
|
rPlus.references(i.getRegisterA)
|
|
)
|
|
},
|
|
s"SH($cName,$sFieldName) <= R(${pp +})(${i.getRegisterA})",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.domains.taintDomain.approx(
|
|
sFieldTaint.asInstanceOf[env.domains.taintDomain.Element],
|
|
rPlus.taints(i.getRegisterA)
|
|
),
|
|
s"Taint R($pp)(${i.getRegisterA}) if SH($cName,$sFieldName) is tainted",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(r.<=(i.getRegisterA)(rPlus): _*),
|
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def sputObject(i: Instruction21c,
|
|
pp: PP,
|
|
env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
val sFieldRef = i.getReference.toString
|
|
val cName = Utils.classPrefix(sFieldRef)
|
|
val sFieldName = Utils.removeClassPrefix(sFieldRef)
|
|
val sFieldType = DexType(sFieldName.substring(sFieldName.indexOf(":") + 1))
|
|
val dexClass = env.app.classes.find(c => c.name == cName)
|
|
val (sFieldVal, sFieldTaint): (Expr, Expr) = dexClass match {
|
|
case Some(c) =>
|
|
val f = c.fields.find(dexField => dexField.name == sFieldName).get
|
|
env.SH(c, f)
|
|
case None =>
|
|
// Unknown class, use top as value.
|
|
sFieldType match {
|
|
case PrimitiveType() =>
|
|
(env.domains.primitiveDomain.top, env.domains.taintDomain.top)
|
|
case ObjectType(_) =>
|
|
(env.domains.referenceDomain.top, env.domains.taintDomain.top)
|
|
}
|
|
}
|
|
Seq(
|
|
Z3Constraint(
|
|
sFieldType match {
|
|
case PrimitiveType() =>
|
|
env.domains.primitiveDomain.approx(
|
|
r.numbers(i.getRegisterA),
|
|
sFieldVal.asInstanceOf[env.domains.primitiveDomain.Element]
|
|
)
|
|
case ObjectType(_) =>
|
|
env.domains.referenceDomain.approx(
|
|
r.references(i.getRegisterA),
|
|
sFieldVal.asInstanceOf[env.domains.referenceDomain.Element]
|
|
)
|
|
},
|
|
s"R($pp)(${i.getRegisterA}) <= SH($cName,$sFieldName)",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.domains.taintDomain.approx(
|
|
r.taints(i.getRegisterA),
|
|
sFieldTaint.asInstanceOf[env.domains.taintDomain.Element]
|
|
),
|
|
s"Taint SH($cName,$sFieldName) if R($pp)(${i.getRegisterA}) is tainted",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(r <= rPlus: _*),
|
|
s"R($pp) <= R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def move(i: Instruction12x, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
val rApprox = r.<=(i.getRegisterA)(rPlus)
|
|
Seq(
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
r.<=(i.getRegisterB, i.getRegisterA)(rPlus): _*
|
|
),
|
|
s"R($pp)(${i.getRegisterB}) <= R(${pp +})(${i.getRegisterA})",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(rApprox: _*),
|
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def moveResult(i: Instruction11x,
|
|
pp: PP,
|
|
env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
val rApprox = r.<=(i.getRegisterA)(rPlus)
|
|
Seq(
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
r.<=(env.R.retIdx, i.getRegisterA)(rPlus): _*
|
|
),
|
|
s"R($pp)(ret) <= R(${pp +})(${i.getRegisterA})",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(rApprox: _*),
|
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def goto(i: Instruction10t, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
|
val ppGoto = pp.copy(pc = pp.pc + i.getCodeOffset)
|
|
Seq(
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(env.R(pp) <= env.R(ppGoto): _*),
|
|
s"$pp: R($pp) <= R($ppGoto)",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def retVoid(i: Instruction10x,
|
|
pp: PP,
|
|
env: Environment): Seq[Z3Constraint] = {
|
|
Seq()
|
|
}
|
|
|
|
def ret(i: Instruction11x, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val mResult = env.M(pp.c, pp.m).get
|
|
pp.m.returnType match {
|
|
case PrimitiveType() =>
|
|
Seq(
|
|
Z3Constraint(
|
|
env.domains.primitiveDomain.approx(
|
|
r.numbers(i.getRegisterA),
|
|
mResult.asInstanceOf[env.domains.primitiveDomain.Element]
|
|
),
|
|
s"$pp: R($pp)(${i.getRegisterA}) <= M(${pp.c.name}, ${pp.m.name})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
def ifEq(i: Instruction22t, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val ppGoto = pp.copy(pc = pp.pc + i.getCodeOffset)
|
|
Seq(
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(r <= env.R(ppGoto): _*),
|
|
s"$pp: R($pp) <= R(${pp +})",
|
|
pp
|
|
),
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(r <= env.R(pp +): _*),
|
|
s"$pp: R($pp) <= R($ppGoto)",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def newInstance(i: Instruction21c,
|
|
pp: PP,
|
|
env: Environment): Seq[Z3Constraint] = {
|
|
val r = env.R(pp)
|
|
val objType = i.getReference.asInstanceOf[DexBackedTypeReference].getType
|
|
val obj: FuncDecl = env.H.obj()
|
|
val objApp = env.ctx.mkApp(obj, env.ctx.mkString(pp.toString))
|
|
val objName =
|
|
env.ctx.mkApp(env.z3Obj.nameAccessor, objApp).asInstanceOf[SeqExpr]
|
|
val primFields =
|
|
env.ctx.mkApp(env.z3Obj.primAccessor, objApp).asInstanceOf[ArrayExpr]
|
|
val fieldsInitialization: List[Z3Constraint] =
|
|
env.app.classes.find(c => c.name == objType) match {
|
|
case Some(dexClass) =>
|
|
dexClass.fields
|
|
.map(dexField =>
|
|
dexField.dexType match {
|
|
case PrimitiveType() =>
|
|
Z3Constraint(
|
|
env.domains.primitiveDomain.approx(
|
|
env.domains.primitiveDomain.toElement(0),
|
|
env.ctx
|
|
.mkSelect(primFields, env.ctx.mkString(dexField.name))
|
|
.asInstanceOf[env.domains.primitiveDomain.Element]
|
|
),
|
|
s"0 <= H($pp).${dexField.name}",
|
|
pp
|
|
)
|
|
case ObjectType(_) =>
|
|
Z3Constraint(
|
|
env.ctx.mkTrue(),
|
|
"true",
|
|
pp
|
|
)
|
|
})
|
|
.toList
|
|
case None => List()
|
|
}
|
|
val instanceClass = Z3Constraint(
|
|
env.ctx.mkEq(objName, env.ctx.mkString(objType)),
|
|
s"H($pp) = {'$objType',(f->_)^*}",
|
|
pp
|
|
)
|
|
val rPlus = env.R(pp +)
|
|
// Register A of pp+ contains the object pointer (pp).
|
|
val storeReference =
|
|
Z3Constraint(
|
|
env.domains.referenceDomain.approx(
|
|
env.domains.referenceDomain.toElement(pp.toString),
|
|
rPlus.references(i.getRegisterA)
|
|
),
|
|
s"($pp) <= R(${pp +})(${i.getRegisterA})",
|
|
pp
|
|
)
|
|
val regApprox = Z3Constraint(
|
|
env.ctx.mkAnd(r.<=(i.getRegisterA)(rPlus): _*),
|
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
|
pp
|
|
)
|
|
Seq(
|
|
instanceClass,
|
|
storeReference,
|
|
regApprox
|
|
) ++ fieldsInitialization
|
|
}
|
|
|
|
private def approxParam(env: Environment)(
|
|
pp: PP,
|
|
register: Int,
|
|
dexClass: DexClass,
|
|
dexMethod: DexMethod,
|
|
paramIdx: Int
|
|
): Z3Constraint = {
|
|
val r = env.R(pp)
|
|
val ppMethod = PP(dexClass, dexMethod, 0)
|
|
val rMethod = env.R(ppMethod)
|
|
val methodParamIdx = dexMethod.localRegistersCount + paramIdx
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
r.<=(register, methodParamIdx)(rMethod): _*
|
|
),
|
|
s"R($pp)($register) <= R($ppMethod)($methodParamIdx)",
|
|
pp
|
|
)
|
|
}
|
|
|
|
private def approxParameters(env: Environment)(
|
|
i: Instruction35c,
|
|
pp: PP,
|
|
dexClass: DexClass,
|
|
dexMethod: DexMethod): List[Z3Constraint] = {
|
|
var paramApprox: List[Z3Constraint] = List()
|
|
val regCount = i.getRegisterCount
|
|
if (regCount > 1) {
|
|
paramApprox = approxParam(env)(pp, i.getRegisterD, dexClass, dexMethod, 0) :: paramApprox
|
|
if (regCount > 2) {
|
|
paramApprox = approxParam(env)(
|
|
pp,
|
|
i.getRegisterE,
|
|
dexClass,
|
|
dexMethod,
|
|
1
|
|
) :: paramApprox
|
|
if (regCount > 3) {
|
|
paramApprox = approxParam(env)(
|
|
pp,
|
|
i.getRegisterF,
|
|
dexClass,
|
|
dexMethod,
|
|
2
|
|
) :: paramApprox
|
|
if (regCount > 4) {
|
|
paramApprox = approxParam(env)(
|
|
pp,
|
|
i.getRegisterG,
|
|
dexClass,
|
|
dexMethod,
|
|
3
|
|
) :: paramApprox
|
|
}
|
|
}
|
|
}
|
|
}
|
|
paramApprox.reverse
|
|
}
|
|
|
|
private def checkParamLeak(env: Environment)(pp: PP,
|
|
register: Int): Z3Constraint = {
|
|
val r = env.R(pp)
|
|
Z3Constraint(
|
|
env.ctx.mkNot(
|
|
env.domains.taintDomain.approx(
|
|
env.domains.taintDomain.top,
|
|
r.taints(register)
|
|
)
|
|
),
|
|
s"Check R($pp)($register) LEAK",
|
|
pp
|
|
)
|
|
}
|
|
|
|
private def checkParametersLeak(env: Environment)(
|
|
i: Instruction35c,
|
|
pp: PP
|
|
): List[Z3Constraint] = {
|
|
var paramsLeak: List[Z3Constraint] = List()
|
|
val regCount = i.getRegisterCount
|
|
if (regCount > 1) {
|
|
paramsLeak = checkParamLeak(env)(pp, i.getRegisterD) :: paramsLeak
|
|
if (regCount > 2) {
|
|
paramsLeak = checkParamLeak(env)(pp, i.getRegisterE) :: paramsLeak
|
|
if (regCount > 3) {
|
|
paramsLeak = checkParamLeak(env)(pp, i.getRegisterF) :: paramsLeak
|
|
if (regCount > 4) {
|
|
paramsLeak = checkParamLeak(env)(pp, i.getRegisterG) :: paramsLeak
|
|
}
|
|
}
|
|
}
|
|
}
|
|
paramsLeak.reverse
|
|
}
|
|
|
|
private def taintRegisterBasedOnParam(env: Environment)(
|
|
pp: PP,
|
|
register: Int,
|
|
paramRegister: Int): Z3Constraint = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
Z3Constraint(
|
|
env.domains.taintDomain.approx(
|
|
r.taints(paramRegister),
|
|
rPlus.taints(register)
|
|
),
|
|
s"Taint R(${pp +})($register) if R($pp)($paramRegister) is tainted",
|
|
pp
|
|
)
|
|
}
|
|
|
|
private def taintRegisterBasedOnParameters(env: Environment)(
|
|
i: Instruction35c,
|
|
pp: PP,
|
|
register: Int
|
|
): List[Z3Constraint] = {
|
|
var registerTaint: List[Z3Constraint] = List()
|
|
val paramsCount = i.getRegisterCount
|
|
if (paramsCount > 1) {
|
|
registerTaint = taintRegisterBasedOnParam(env)(
|
|
pp,
|
|
register,
|
|
i.getRegisterD) :: registerTaint
|
|
if (paramsCount > 2) {
|
|
registerTaint = taintRegisterBasedOnParam(env)(
|
|
pp,
|
|
register,
|
|
i.getRegisterE) :: registerTaint
|
|
if (paramsCount > 3) {
|
|
registerTaint = taintRegisterBasedOnParam(env)(
|
|
pp,
|
|
register,
|
|
i.getRegisterF) :: registerTaint
|
|
if (paramsCount > 4) {
|
|
registerTaint = taintRegisterBasedOnParam(env)(
|
|
pp,
|
|
register,
|
|
i.getRegisterG) :: registerTaint
|
|
}
|
|
}
|
|
}
|
|
}
|
|
registerTaint.reverse
|
|
}
|
|
|
|
private def taintResBasedOnParam(
|
|
env: Environment)(pp: PP, register: Int): Z3Constraint = {
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
Z3Constraint(
|
|
env.domains.taintDomain.approx(
|
|
r.taints(register),
|
|
rPlus.taints(env.R.retIdx)
|
|
),
|
|
s"Taint R(${pp +})(ret) if R($pp)($register) is tainted",
|
|
pp
|
|
)
|
|
}
|
|
|
|
private def taintResultBasedOnParameters(env: Environment)(
|
|
i: Instruction35c,
|
|
pp: PP
|
|
): List[Z3Constraint] = {
|
|
var resultTaint: List[Z3Constraint] = List()
|
|
val paramsCount = i.getRegisterCount
|
|
if (paramsCount > 1) {
|
|
resultTaint = taintResBasedOnParam(env)(pp, i.getRegisterD) :: resultTaint
|
|
if (paramsCount > 2) {
|
|
resultTaint = taintResBasedOnParam(env)(pp, i.getRegisterE) :: resultTaint
|
|
if (paramsCount > 3) {
|
|
resultTaint = taintResBasedOnParam(env)(pp, i.getRegisterF) :: resultTaint
|
|
if (paramsCount > 4) {
|
|
resultTaint = taintResBasedOnParam(env)(pp, i.getRegisterG) :: resultTaint
|
|
}
|
|
}
|
|
}
|
|
}
|
|
resultTaint.reverse
|
|
}
|
|
|
|
def invokeDirect(
|
|
i: Instruction35c,
|
|
pp: PP,
|
|
env: Environment,
|
|
addMethodToAnalyze: (DexClass, DexMethod) => Unit): Seq[Z3Constraint] = {
|
|
val mRef = i.getReference.asInstanceOf[MethodReference]
|
|
// RegisterD: parameter
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
val constraints: Seq[Z3Constraint] =
|
|
env.app.classes.find(c => c.name == mRef.getDefiningClass) match {
|
|
case Some(dexClass) =>
|
|
val method =
|
|
dexClass.methods
|
|
.find(m => m.name == Utils.removeClassPrefix(mRef.toString))
|
|
.get
|
|
addMethodToAnalyze(dexClass, method)
|
|
val ppMethod = PP(dexClass, method, 0)
|
|
val thisApprox = Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
r.<=(
|
|
i.getRegisterC,
|
|
method.registersCount
|
|
)(env.R(ppMethod)): _*
|
|
),
|
|
s"R($pp)(${i.getRegisterC}) <= R($ppMethod)(${method.registersCount})",
|
|
pp
|
|
)
|
|
val paramApprox = approxParameters(env)(i, pp, dexClass, method)
|
|
val returnApprox: Z3Constraint =
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
env
|
|
.M(dexClass, method)
|
|
.map(mResult => {
|
|
method.returnType match {
|
|
case PrimitiveType() =>
|
|
env.domains.primitiveDomain.approx(
|
|
mResult.asInstanceOf[env.domains.primitiveDomain.Element],
|
|
rPlus
|
|
.numbers(env.R.retIdx)
|
|
)
|
|
}
|
|
})
|
|
.getOrElse(env.ctx.mkTrue())
|
|
),
|
|
s"M(${dexClass.name},${method.name}) <= R(${pp +})(ret)",
|
|
pp
|
|
)
|
|
(thisApprox :: paramApprox) ++ Seq(returnApprox)
|
|
case None =>
|
|
taintRegisterBasedOnParameters(env)(i, pp, i.getRegisterC) ++
|
|
taintResultBasedOnParameters(env)(i, pp)
|
|
}
|
|
constraints ++ Seq(
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
r.<=(env.R.retIdx)(rPlus): _*
|
|
),
|
|
s"R($pp) <={ret} R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
}
|
|
|
|
def invokeVirtual(
|
|
i: Instruction35c,
|
|
pp: PP,
|
|
env: Environment,
|
|
addMethodToAnalyze: (DexClass, DexMethod) => Unit): Seq[Z3Constraint] = {
|
|
val mRef = i.getReference.asInstanceOf[MethodReference]
|
|
val r = env.R(pp)
|
|
val rPlus = env.R(pp +)
|
|
val cName = Utils.classPrefix(mRef.toString)
|
|
val mName = Utils.removeClassPrefix(mRef.toString)
|
|
val impls: Map[DexClass, DexClass] =
|
|
env.Dispatch(mName, mRef.getDefiningClass)
|
|
val constraints: Seq[Z3Constraint] =
|
|
if (impls.isEmpty) {
|
|
val returnApprox: Z3Constraint =
|
|
Z3Constraint(
|
|
DexType(mName.substring(mName.indexOf(")") + 1)) match {
|
|
case PrimitiveType() =>
|
|
env.domains.primitiveDomain.approx(
|
|
env.domains.primitiveDomain.top,
|
|
rPlus
|
|
.numbers(env.R.retIdx)
|
|
.asInstanceOf[env.domains.primitiveDomain.Element]
|
|
)
|
|
case ObjectType(_) =>
|
|
env.domains.referenceDomain.approx(
|
|
env.domains.referenceDomain.top,
|
|
rPlus
|
|
.references(env.R.retIdx)
|
|
.asInstanceOf[env.domains.referenceDomain.Element]
|
|
)
|
|
case VoidType() =>
|
|
env.ctx.mkTrue()
|
|
},
|
|
s"T <= R(${pp +})(ret)",
|
|
pp
|
|
)
|
|
Seq(returnApprox) ++ taintResultBasedOnParameters(env)(i, pp)
|
|
} else
|
|
impls
|
|
.flatMap(impl => {
|
|
val dexClass = impl._2
|
|
env.preAnalysisResult.objectCreation
|
|
.filter(p => p._2 == impl._1.name)
|
|
.map(objPP => {
|
|
val method = dexClass.methods
|
|
.find(m => m.name == Utils.removeClassPrefix(mRef.toString))
|
|
.get
|
|
addMethodToAnalyze(dexClass, method)
|
|
val ppMethod = PP(dexClass, method, 0)
|
|
val thisApprox = Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
r.<=(
|
|
i.getRegisterC,
|
|
method.registersCount
|
|
)(env.R(ppMethod)): _*
|
|
),
|
|
s"R($pp)(${i.getRegisterC}) <= R($ppMethod)(${method.registersCount})",
|
|
pp
|
|
)
|
|
val paramApprox = approxParameters(env)(i, pp, dexClass, method)
|
|
val returnApprox: Z3Constraint =
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
env
|
|
.M(dexClass, method)
|
|
.map(mResult => {
|
|
method.returnType match {
|
|
case PrimitiveType() =>
|
|
env.domains.primitiveDomain.approx(
|
|
mResult.asInstanceOf[env.domains.primitiveDomain.Element],
|
|
rPlus
|
|
.numbers(env.R.retIdx)
|
|
)
|
|
}
|
|
})
|
|
.getOrElse(env.ctx.mkTrue())
|
|
),
|
|
s"M(${dexClass.name},${method.name}) <= R(${pp +})(ret)",
|
|
pp
|
|
)
|
|
Z3Constraint(
|
|
env.ctx.mkImplies(
|
|
env.ctx.mkAnd(
|
|
env.domains.referenceDomain.approx(
|
|
env.domains.referenceDomain.toElement(
|
|
objPP._1.toString),
|
|
r.references(i.getRegisterC)
|
|
)
|
|
),
|
|
env.ctx.mkAnd(
|
|
env.ctx.mkAnd(
|
|
paramApprox.map(c => c.boolExpr): _*
|
|
),
|
|
env.ctx.mkAnd(
|
|
thisApprox.boolExpr
|
|
),
|
|
returnApprox.boolExpr
|
|
)
|
|
),
|
|
s"${objPP._1.toString} <= R($pp)(${i.getRegisterC}) => " +
|
|
(if (paramApprox.isEmpty) ""
|
|
else
|
|
paramApprox
|
|
.map(c => c.description)
|
|
.mkString(" ∧ ") + " ∧ ") + thisApprox.description +
|
|
" ∧ " + returnApprox.description,
|
|
pp
|
|
)
|
|
})
|
|
})
|
|
.toList
|
|
var taintConstraints = List[Z3Constraint]()
|
|
if (env.app.sourcesSinks.isSource(cName, mName)) {
|
|
taintConstraints = Z3Constraint(
|
|
env.domains.taintDomain.approx(
|
|
env.domains.taintDomain.top,
|
|
rPlus.taints(env.R.retIdx)
|
|
),
|
|
s"Taint R(${pp +})(ret)",
|
|
pp
|
|
) :: taintConstraints
|
|
}
|
|
if (env.app.sourcesSinks.isSink(cName, mName)) {
|
|
taintConstraints = taintConstraints ++ (
|
|
Z3Constraint(
|
|
env.ctx.mkNot(
|
|
env.domains.taintDomain.approx(
|
|
env.domains.taintDomain.top,
|
|
r.taints(i.getRegisterC)
|
|
)
|
|
),
|
|
s"Check R($pp)(${i.getRegisterC}) LEAK",
|
|
pp
|
|
) :: checkParametersLeak(env)(i, pp)
|
|
)
|
|
}
|
|
constraints ++ taintConstraints ++ Seq(
|
|
Z3Constraint(
|
|
env.ctx.mkAnd(
|
|
r.<=(env.R.retIdx)(rPlus): _*
|
|
),
|
|
s"R($pp) <={ret} R(${pp +})",
|
|
pp
|
|
)
|
|
)
|
|
|
|
}
|
|
|
|
private def classAndSubclasses(env: Environment)(
|
|
definingClass: String): List[DexClass] = {
|
|
env.app.classes.find(c => c.name == definingClass) match {
|
|
case Some(dexClass) => dexClass :: subclasses(env)(dexClass)
|
|
case None => List()
|
|
}
|
|
}
|
|
|
|
private def subclasses(env: Environment)(
|
|
dexClass: DexClass): List[DexClass] = {
|
|
env.app.classes
|
|
.filter(c => c.superClass == dexClass.name)
|
|
.flatMap(c => subclasses(env)(c))
|
|
.toList
|
|
}
|
|
|
|
}
|