Skip to content

Commit

Permalink
Add transformer to deal with method chaining
Browse files Browse the repository at this point in the history
  • Loading branch information
smeyer198 committed Jan 2, 2024
1 parent 4b9eed1 commit 6048654
Show file tree
Hide file tree
Showing 11 changed files with 276 additions and 43 deletions.
1 change: 0 additions & 1 deletion CryptoAnalysis/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,6 @@
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bctls-jdk18on -->
<dependency>
Expand Down
20 changes: 18 additions & 2 deletions CryptoAnalysis/src/main/java/crypto/HeadlessCryptoScanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import crypto.exceptions.CryptoAnalysisException;
import crypto.exceptions.CryptoAnalysisParserException;
import crypto.preanalysis.ExceptionAwareTransformer;
import crypto.preanalysis.IdentityTransformer;
import crypto.preanalysis.SeedFactory;
import crypto.preanalysis.SeedGenerationTransformer;
import crypto.providerdetection.ProviderDetection;
import crypto.reporting.CSVReporter;
import crypto.reporting.CSVSummaryReporter;
Expand Down Expand Up @@ -118,6 +120,7 @@ public void exec() {
if(isPreAnalysis()){
try {
initializeSootWithEntryPointAllReachable(false);
setupTransformer();
} catch (CryptoAnalysisException e) {
LOGGER.error("Error happened when executing HeadlessCryptoScanner.", e);
}
Expand All @@ -129,10 +132,10 @@ public void exec() {
LOGGER.info("Using call graph algorithm {}", callGraphAlgorithm());
try {
initializeSootWithEntryPointAllReachable(true);
setupTransformer();
} catch (CryptoAnalysisException e) {
LOGGER.error("Error happened when executing HeadlessCryptoScanner.", e);
}
ExceptionAwareTransformer.setup(rules);
LOGGER.info("Analysis soot setup done in {} ", stopwatch);
analyse();
LOGGER.info("Analysis finished in {}", stopwatch);
Expand All @@ -142,6 +145,13 @@ public void exec() {
public boolean hasSeeds(){
return hasSeeds;
}

private void setupTransformer() {
SeedGenerationTransformer.setup(rules);
IdentityTransformer.setup();
ExceptionAwareTransformer.setup(rules);
PackManager.v().runBodyPacks();
}

private void checkIfUsesObject() {
final SeedFactory seedFactory = new SeedFactory(HeadlessCryptoScanner.rules);
Expand Down Expand Up @@ -326,6 +336,12 @@ private void initializeSootWithEntryPointAllReachable(boolean wholeProgram) thro
Options.v().set_no_bodies_for_excluded(true);
Options.v().set_allow_phantom_refs(true);
Options.v().set_keep_line_number(true);

Options.v().setPhaseOption("jb", "use-original-names:true");

// enabled to deal with method chaining
Options.v().setPhaseOption("jb.lp", "enabled:true");

// JAVA 8
if(getJavaVersion() < 9)
{
Expand Down Expand Up @@ -370,7 +386,7 @@ private List<String> getIncludeList() {
includeList.add("java.lang.String");
includeList.add("java.lang.StringCoding");
includeList.add("java.lang.StringIndexOutOfBoundsException");
return includeList;
return new LinkedList<>();
}

private List<String> getExcludeList() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,22 @@
package crypto.analysis;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import boomerang.callgraph.ObservableICFG;
import boomerang.debugger.Debugger;
import boomerang.jimple.AllocVal;
import boomerang.jimple.Statement;
import boomerang.jimple.Val;
import boomerang.results.ForwardBoomerangResults;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.google.common.collect.Table.Cell;

import boomerang.callgraph.ObservableICFG;
import boomerang.debugger.Debugger;
import boomerang.jimple.AllocVal;
import boomerang.jimple.Statement;
import boomerang.jimple.Val;
import boomerang.results.ForwardBoomerangResults;
import crypto.analysis.errors.ForbiddenPredicateError;
import crypto.analysis.errors.IncompleteOperationError;
import crypto.analysis.errors.RequiredPredicateError;
import crypto.analysis.errors.TypestateError;
import crypto.constraints.ConstraintSolver;
import crypto.constraints.EvaluableConstraint;
import crypto.extractparameter.CallSiteWithExtractedValue;
import crypto.extractparameter.CallSiteWithParamIndex;
import crypto.extractparameter.ExtractParameterAnalysis;
import crypto.extractparameter.ExtractedValue;
Expand Down Expand Up @@ -67,6 +53,16 @@
import typestate.finiteautomata.ITransition;
import typestate.finiteautomata.State;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

public class AnalysisSeedWithSpecification extends IAnalysisSeed {

private final ClassSpecification spec;
Expand Down Expand Up @@ -118,9 +114,10 @@ public String toString() {
public void execute() {
cryptoScanner.getAnalysisListener().seedStarted(this);
runTypestateAnalysis();
if (results == null)
if (results == null) {
// Timeout occured.
return;
}
allCallsOnObject = results.getInvokedMethodOnInstance();
runExtractParameterAnalysis();
checkInternalConstraints();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package crypto.preanalysis;

import soot.Body;
import soot.BodyTransformer;
import soot.PackManager;
import soot.PhaseOptions;
import soot.Transform;
import soot.UnitPatchingChain;
import soot.jimple.AssignStmt;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.internal.JInvokeStmt;
import soot.jimple.internal.JimpleLocal;

import java.util.Map;

/**
* This transformer removes redundant jimple body statements that are introduced by
* the jb.lp phase. These statements include identity statements as v = v and
* v = v.method(). Without this transformer, Boomerang is not able to instantiate
* correct queries.
*/
public class IdentityTransformer extends BodyTransformer {

public static void setup() {
String phaseName = "jtp.itr";
PackManager.v().getPack("jtp").remove(phaseName);
PackManager.v().getPack("jtp").add(new Transform(phaseName, new IdentityTransformer()));
PhaseOptions.v().setPhaseOption(phaseName, "on");
}

@Override
protected void internalTransform(Body body, String phaseName, Map<String, String> options) {
if (body.getMethod().getDeclaringClass().getName().startsWith("java.")) {
return;
}
if (!body.getMethod().getDeclaringClass().isApplicationClass()) {
return;
}

UnitPatchingChain units = body.getUnits();
units.snapshotIterator().forEachRemaining(unit -> {
if (!(unit instanceof AssignStmt)) {
return;
}

AssignStmt assignStmt = (AssignStmt) unit;

// Identity statements for variable v: v = v
if (assignStmt.getLeftOp().equals(assignStmt.getRightOp())) {
units.remove(unit);
}

if (!(assignStmt.getLeftOp() instanceof JimpleLocal)) {
return;
}

if (!(assignStmt.getRightOp() instanceof InstanceInvokeExpr)) {
return;
}

// Replace statement of the form: v = v.method() with v.method()
JimpleLocal leftSide = (JimpleLocal) assignStmt.getLeftOp();
InstanceInvokeExpr invokeExpr = (InstanceInvokeExpr) assignStmt.getRightOp();
if (leftSide.equals(invokeExpr.getBase())) {
units.swapWith(unit, new JInvokeStmt(invokeExpr));
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package crypto.preanalysis;

import crypto.rules.CrySLMethod;
import crypto.rules.CrySLRule;
import crypto.rules.TransitionEdge;
import soot.Body;
import soot.BodyTransformer;
import soot.PackManager;
import soot.PhaseOptions;
import soot.SootClass;
import soot.SootMethod;
import soot.Transform;
import soot.Type;
import soot.UnitPatchingChain;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.internal.JAssignStmt;
import soot.jimple.internal.JimpleLocal;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
* This transformer extends the jimple bodies, if CryptoAnalysis would not
* be able to generate seeds for some objects. Minimal programs as
* 'Cipher c = Cipher.getInstance("AES");' are transformed to jimple code
* of the form 'staticinvoke Cipher.getInstance([...])'. This transformer
* introduces a new JimpleLocal 'Cipher0' and transforms the statement to
* Cipher0 = staticinvoke Cipher.getInstance([...]). Without this transformation,
* CryptoAnalysis would not be able to generate a seed for c and would not check
* for incorrect usages.
*/
public class SeedGenerationTransformer extends BodyTransformer {

public static void setup(Collection<CrySLRule> rules) {
final String phaseName = "jtp.sgtr";
PackManager.v().getPack("jtp").remove(phaseName);
PackManager.v().getPack("jtp").add(new Transform(phaseName, new SeedGenerationTransformer(rules)));
PhaseOptions.v().setPhaseOption(phaseName, "on");
}

private final Collection<CrySLRule> rules;
private int counter;

public SeedGenerationTransformer(Collection<CrySLRule> rules) {
this.rules = rules;
this.counter = 0;
}

@Override
protected void internalTransform(Body body, String phaseName, Map<String, String> options) {
if (body.getMethod().getDeclaringClass().getName().startsWith("java.")) {
return;
}
if (!body.getMethod().getDeclaringClass().isApplicationClass()) {
return;
}

UnitPatchingChain units = body.getUnits();
units.snapshotIterator().forEachRemaining(unit -> {
if (!(unit instanceof InvokeStmt)) {
return;
}

InvokeStmt invokeStmt = (InvokeStmt) unit;
if (!(invokeStmt.getInvokeExpr() instanceof StaticInvokeExpr)) {
return;
}

SootClass declaringClass = invokeStmt.getInvokeExpr().getMethod().getDeclaringClass();
CrySLRule rule = getRuleForClass(declaringClass);

if (rule == null) {
return;
}

if (isSeedGeneratingStatement(invokeStmt.getInvokeExpr(), rule)) {
// Create new Variable
JimpleLocal leftSide = new JimpleLocal(declaringClass.getShortName() + counter, declaringClass.getType());
body.getLocals().add(leftSide);
counter++;

units.swapWith(unit, new JAssignStmt(leftSide, invokeStmt.getInvokeExpr()));
}
});
}

private CrySLRule getRuleForClass(SootClass sootClass) {
for (CrySLRule rule : rules) {
if (rule.getClassName().equals(sootClass.getName())) {
return rule;
}
}
return null;
}

private boolean isSeedGeneratingStatement(InvokeExpr invokeExpr, CrySLRule rule) {
Collection<TransitionEdge> initialTransitions = rule.getUsagePattern().getInitialTransitions();
SootMethod method = invokeExpr.getMethod();

for (TransitionEdge transition : initialTransitions) {
Collection<CrySLMethod> labels = transition.getLabel();

for (CrySLMethod label : labels) {
if (doMethodsMatch(method, rule.getClassName(), label)) {
return true;
}
}
}

return false;
}

private boolean doMethodsMatch(SootMethod sootMethod, String ruleClassName, CrySLMethod crySLMethod) {
String declaringSootMethodClass = sootMethod.getDeclaringClass().getName();
if (!ruleClassName.equals(declaringSootMethodClass)) {
return false;
}

if (!sootMethod.getName().equals(crySLMethod.getShortMethodName())) {
return false;
}

if (!doParametersMatch(sootMethod.getParameterTypes(), crySLMethod.getParameters())) {
return false;
}

return true;
}

private boolean doParametersMatch(List<Type> parameterTypes, List<Entry<String, String>> parameterLabels) {
if (parameterTypes.size() != parameterLabels.size()) {
return false;
}

for (int i = 0; i < parameterTypes.size(); i++) {
if (parameterLabels.get(i).getValue().equals("AnyType")) {
continue;
}

Type parameterType = parameterTypes.get(i);
// Soot does not track generic types, so we are required to remove <...> from the parameter
String parameterLabelType = parameterLabels.get(i).getValue().replaceAll("[<].*?[>]", "");

if (!parameterType.toString().equals(parameterLabelType)) {
return false;
}
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void testBCDigestExamples() {
setErrorsCount("<fabric_api_archieve.Crypto: byte[] sign(byte[],byte[])>", HardCodedError.class, 1);
setErrorsCount("<fabric_api_archieve.Crypto: byte[] sign(byte[],byte[])>", RequiredPredicateError.class, 1);

setErrorsCount("<pluotsorbet.BouncyCastleSHA256: void TestSHA256DigestOne()>", TypestateError.class, 2);
setErrorsCount("<pluotsorbet.BouncyCastleSHA256: void TestSHA256DigestOne()>", TypestateError.class, 1);
setErrorsCount("<pluotsorbet.BouncyCastleSHA256: void testSHA256DigestTwo()>", TypestateError.class, 3);

scanner.exec();
Expand Down
Loading

0 comments on commit 6048654

Please sign in to comment.