一 jacodb基础
1创建数据库
JcDatabase database = JacoDB.async(jcSettings).get();
1.1创建选项JcSettings
JcSettings jcSettings = new JcSettings() .useJavaHomeRuntime() .persistent("./tmp/compilation-db/test.db"), .installFeatures(Usages.INSTANCE, InMemoryHierarchy.INSTANCE);
1.1.1设置java运行时库
这里设置成和被监测代码运行时相同的java版本。
useJavaHomeRuntime()
从JAVA_HOME环境变量加载运行时库。
useProcessJavaRuntime()
和当前运行程序使用相同的运行时库。
useJavaRuntime(runtime: File)
指定加载运行时库的位置。
如果三个都不设置,默认使用useProcessJavaRuntime()。
1.1.2设置数据库文件位置
.persistent("./tmp/compilation-db/test.db")
1.1.3加载特性
.installFeatures(Usages.INSTANCE, InMemoryHierarchy.INSTANCE)
Usages.INSTANCE
InMemoryHierarchy.INSTANCE
⚠️创建数据库后,jre库的信息会自动存储到数据库。
1.1.4 监听文件系统变化
.watchFileSystem()
jar包删除会有以下日志警告:
[DefaultDispatcher-worker-2 @coroutine#186] WARN org.jacodb.impl.fs.ByteCodeLocationsKt$logger$1 - D:\code\jacodb-test\target\jacodb-test-1.0-SNAPSHOT.jar doesn't exists. make sure there is no mistake
jar包变更会将新的jar包所有内容存储到数据库,旧数据不会清除。
class文件的删除和新增,数据库不会更新。
2加载被分析代码的bitcode信息
指定jar包位置或者class目录:
File commonsMath32 = new File("commons-math3-3.2.jar"); File commonsMath36 = new File("commons-math3-3.6.1.jar"); File buildDir = new File("my-project/build/classes/java/main"); database.asyncLoad(Arrays.asList(commonsMath32, commonsMath36, buildDir));
3 监听class文件变更(无效)
// This method just refreshes the libraries inside the database. If there are any changes in libs then // the database updates data with the new results. database.asyncLoad(Collections.singletonList(buildDir));
测试未生效。目录下的class文件变更,和jar包变更都不会更新数据库数据。
4 查询类信息
4.1 指定查询范围
JcClasspath classpath = database.asyncClasspath(List.of(buildDir)).get(); JcClassOrInterface jcClass = classpath.findClassOrNull("org.example.B");
4.2 获取类信息
// 方法数量(包括构造方法) System.out.println(jcClass.getDeclaredMethods().size()); // 注解数量 System.out.println(jcClass.getAnnotations().size()); // 构造函数数量 System.out.println(JcClasses.getConstructors(jcClass).size()); jcClass.getClasspath(); jcClass.asmNode(); jcClass.getInnerClasses(); jcClass.bytecode(); jcClass.getInterfaces(); jcClass.getLookup(); jcClass.getOuterClass(); jcClass.getOuterMethod(); jcClass.getSignature(); jcClass.getSimpleName(); jcClass.getSuperClass(); jcClass.getAccess(); jcClass.getDeclaration(); jcClass.getName(); jcClass.asmNode(); jcClass.extensionValue(""); jcClass.isAnnotation(); jcClass.isAnonymous(); jcClass.isInterface(); jcClass.isFinal(); jcClass.isAbstract(); jcClass.isPackagePrivate(); jcClass.isPrivate(); jcClass.isProtected(); jcClass.isPublic(); jcClass.isStatic(); jcClass.isSynthetic();
5 类型
JcClassType b = (JcClassType)classpath.findTypeOrNull("org.example.B"); JcType xType = b.getFields() .stream() .filter(it -> "x".equals(it.getName())) .findFirst().get().getFieldType(); JcClassType stringType = (JcClassType) classpath.findTypeOrNull("java.lang.String"); System.out.println(xType.equals(stringType)); // will print "true"
JcClassType JcType JcTypeField
6 method
JcMethod jcMethod = JcClasses.findDeclaredMethodOrNull(cString, "getName", "(I)Ljava/lang/String;");
关于第二个入参"desc"的值,形式为:“(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;” 括号内的是入参类型,后面是返回值类型。如果desc为null,则返回第一个相同函数名的函数,后续重载方法忽略。
Raw instruction
JcInstList<JcRawInst> rawInstructionList = jcMethod.getRawInstList(); for (JcRawInst instruction : rawInstructionList) { System.out.println(instruction + " (" + instruction.getClass().getSimpleName() + ")"); List<JcRawExpr> expressionList = instruction.getOperands(); for (JcRawExpr expression : expressionList) { System.out.println(expression.toString()); System.out.println(expression.getTypeName().getTypeName()); List<JcRawValue> valueList = expression.getOperands(); for (JcRawValue value :valueList) { System.out.println("valueType:"+value.getTypeName().getTypeName()); } } }
7 Instruction
JcInstList<JcInst> instructionList = jcMethod.getInstList(); for (JcInst instruction : instructionList) { System.out.println(instruction.toString() + " (" + instruction.getClass().getSimpleName() + ")"); if (instruction instanceof JcAssignInst) { JcAssignInst assignInst = (JcAssignInst)instruction; JcValue lValue = assignInst.getLhv(); JcExpr rValue = assignInst.getRhv(); System.out.println(" " + lValue.getType().getTypeName() + " (" + lValue.getClass().getSimpleName() + ")"); System.out.println(" " + rValue.getType().getTypeName() + " (" + rValue.getClass().getSimpleName() + ")"); } else { List<JcExpr> jcExprList = instruction.getOperands(); for ( JcExpr expression : jcExprList) { System.out.println(" " + expression.getType().getTypeName() + " (" + expression.getClass().getSimpleName() + ")"); } } }
8 control flow graph
JcGraph jcGraph = jcMethod.flowGraph(); JcInst jcInst = jcGraph.getEntry(); System.out.println(jcInst + " (" + jcInst.getClass().getSimpleName() + ")"); List<JcExpr> jcExprList = jcInst.getOperands(); for ( JcExpr expression : jcExprList) { System.out.println(" " + expression.getType().getTypeName() + " (" + expression.getClass().getSimpleName() + ")"); } JcBlockGraph jcBlockGraph = jcGraph.blockGraph();
graph可视化
我们还提供了用于可视化JcGraph和JcBlockGraph的API:
-
JcGraph.view(dotCmd: String, viewerCmd: String, viewCatchConnections: Boolean = false)
— 使用DOT生成SVG文件(dotCmd需要指定DOT可执行文件的路径),并使用viewerCmd程序(指定浏览器可执行文件)查看。viewCatchConnections标志定义是否在图中显示throw...catch连接。 -
JcBlockGraph.view(dotCmd: String, viewerCmd: String)
— 类似,但显示JcBlockGraph。未验证调用
Visitor API
9 hierarchy
HierarchyExtension ext = new HierarchyExtensionImpl(classpath); Sequence<JcClassOrInterface> classSeq = ext.findSubClasses("org.example.C",true,true); Iterator<JcClassOrInterface> iter = classSeq.iterator(); while (iter.hasNext()) { JcClassOrInterface jcClass = iter.next(); System.out.println(jcClass.getName()); } Sequence<JcMethod> methodSeq = ext.findOverrides(jcMethod,true); Iterator<JcMethod> iterM = methodSeq.iterator(); while (iterM.hasNext()) { JcMethod method = iterM.next(); System.out.println(method.getName()); }
findSubClasses未生效,无法找到子类
findOverrides未生效,未找到重写的方法。
10 Usages
SyncUsagesExtension usages = new SyncUsagesExtension(ext,classpath); Sequence<JcMethod> usageMethods = usages.findUsages(jcMethod); usages.findUsages(field, FieldUsageMode.READ) usages.findUsages(field, FieldUsageMode.WRITE)
11 测试代码
测试类-被检测
package org.example; public class SubC extends C{ @Override public String getName(int k) { return "alex"+k; } }
package org.example; public class C { public String getName(int orderNum) { int j = 0; int k = orderNum +j; if ( orderNum > j ) { k = orderNum + j; } while(orderNum < j) { orderNum ++; k ++; } return genName(k); } private String genName(int k) { return "alex"+k; } public String getName() { return "alex"; } }
package org.example; import jdk.jfr.Description; @Description("test annotation") public class B extends A<String> implements InterfaceA { private int i; private int j; public B(int i) { this.i = i; } public B(int i,int j) { this.i = i; this.j = j; } @Override public void meth1() { } }
package org.example; public class A<T> { T x = null; }
测试类
import kotlin.sequences.Sequence; import org.jacodb.api.JcClassOrInterface; import org.jacodb.api.JcClasspath; import org.jacodb.api.JcDatabase; import org.jacodb.api.JcMethod; import org.jacodb.api.cfg.*; import org.jacodb.api.ext.HierarchyExtension; import org.jacodb.api.ext.JcClasses; import org.jacodb.impl.JacoDB; import org.jacodb.impl.JcSettings; import org.jacodb.impl.features.InMemoryHierarchy; import org.jacodb.impl.features.Usages; import org.junit.Test; import java.io.File; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutionException; import org.jacodb.impl.features.HierarchyExtensionImpl; public class GraphTest { @Test public void GraphTest() throws ExecutionException, InterruptedException { // 创建一个DB JcSettings jcSettings = new JcSettings() .watchFileSystem() .persistent("./tmp/compilation-db/test.db") .installFeatures(Usages.INSTANCE, InMemoryHierarchy.INSTANCE);//Usages.INSTANCE, JcDatabase database = JacoDB.async(jcSettings).get();// persist data // 加载字节码信息 File buildDir = new File("target/classes"); database.asyncLoad(List.of(buildDir)); // class查找范围 JcClasspath classpath = database.asyncClasspath(List.of(buildDir)).get(); // find class JcClassOrInterface cString = classpath.findClassOrNull("org.example.C"); // find method assert cString != null; JcMethod jcMethod = JcClasses.findDeclaredMethodOrNull(cString, "getName", "(I)Ljava/lang/String;"); // 3-address bytecode assert jcMethod != null; /** * System.out.println("--------->Raw instruction<---------"); // raw instruction JcInstList<JcRawInst> rawInstructionList = jcMethod.getRawInstList(); for (JcRawInst instruction : rawInstructionList) { System.out.println(instruction + " (" + instruction.getClass().getSimpleName() + ")"); // List<JcRawExpr> expressionList = instruction.getOperands(); // for (JcRawExpr expression : expressionList) { // System.out.println(expression.toString()); // System.out.println(expression.getTypeName().getTypeName()); // List<JcRawValue> valueList = expression.getOperands(); // for (JcRawValue value :valueList) { // System.out.println("valueType:"+value.getTypeName().getTypeName()); // } // } } */ /** * */ System.out.println("--------->Instruction<---------"); // instruction JcInstList<JcInst> instructionList = jcMethod.getInstList(); for (JcInst instruction : instructionList) { System.out.println(instruction.toString() + " (" + instruction.getClass().getSimpleName() + ")"); if (instruction instanceof JcAssignInst) { JcAssignInst assignInst = (JcAssignInst)instruction; JcValue lValue = assignInst.getLhv(); JcExpr rValue = assignInst.getRhv(); System.out.println(" " + lValue.getType().getTypeName() + " (" + lValue.getClass().getSimpleName() + ")"); System.out.println(" " + rValue.getType().getTypeName() + " (" + rValue.getClass().getSimpleName() + ")"); } else { List<JcExpr> jcExprList = instruction.getOperands(); for ( JcExpr expression : jcExprList) { System.out.println(" " + expression.getType().getTypeName() + " (" + expression.getClass().getSimpleName() + ")"); } } } /** * // control flow graph System.out.println("--------->control flow graph<---------"); JcGraph jcGraph = jcMethod.flowGraph(); JcInst jcInst = jcGraph.getEntry(); System.out.println(jcInst + " (" + jcInst.getClass().getSimpleName() + ")"); List<JcExpr> jcExprList = jcInst.getOperands(); for ( JcExpr expression : jcExprList) { System.out.println(" " + expression.getType().getTypeName() + " (" + expression.getClass().getSimpleName() + ")"); } */ // hierarchy System.out.println("--------->hierarchy<---------"); HierarchyExtension ext = new HierarchyExtensionImpl(classpath); Sequence<JcClassOrInterface> classSeq = ext.findSubClasses("org.example.C",true,true); Iterator<JcClassOrInterface> iter = classSeq.iterator(); while (iter.hasNext()) { JcClassOrInterface jcClass = iter.next(); System.out.println(jcClass.getName()); } Sequence<JcMethod> methodSeq = ext.findOverrides(jcMethod,true); Iterator<JcMethod> iterM = methodSeq.iterator(); while (iterM.hasNext()) { JcMethod method = iterM.next(); System.out.println(method.getName()); } } }
二 jacodb静态数据流分析
1 功能调用
api方式调用
import org.jacodb.analysis.AnalysisMain; import org.jacodb.analysis.engine.IfdsUnitRunnerFactory; import org.jacodb.analysis.engine.UnitResolver; import org.jacodb.analysis.engine.VulnerabilityInstance; import org.jacodb.analysis.graph.ApplicationGraphFactory; import org.jacodb.analysis.library.UnitResolversLibrary; import org.jacodb.api.JcClassOrInterface; import org.jacodb.api.JcClasspath; import org.jacodb.api.JcDatabase; import org.jacodb.api.JcMethod; import org.jacodb.api.analysis.JcApplicationGraph; import org.jacodb.impl.JacoDB; import org.jacodb.impl.JcSettings; import org.jacodb.impl.features.InMemoryHierarchy; import org.jacodb.impl.features.Usages; import org.junit.Test; import java.io.*; import java.util.List; import java.util.concurrent.ExecutionException; import org.jacodb.analysis.library.RunnersLibrary; import org.jacodb.analysis.sarif.SarifReport; public class StaticDataFlow { @Test public void UnusedVariableTest() throws ExecutionException, InterruptedException, IOException { // 创建一个DB JcSettings jcSettings = new JcSettings() .installFeatures(Usages.INSTANCE, InMemoryHierarchy.INSTANCE);//Usages.INSTANCE, JcDatabase database = JacoDB.async(jcSettings).get();// persist data // 加载字节码信息 File buildDir = new File("target/classes"); database.asyncLoad(List.of(buildDir)); // class查找范围 JcClasspath classpath = database.asyncClasspath(List.of(buildDir)).get(); // find class JcClassOrInterface cString = classpath.findClassOrNull("org.example.C"); assert cString != null; List<JcMethod> methodsToAnalyze = cString.getDeclaredMethods(); JcApplicationGraph applicationGraph = ApplicationGraphFactory .asyncNewApplicationGraphForAnalysis(classpath, null) .get(); UnitResolver<?> resolver = UnitResolversLibrary.getMethodUnitResolver(); IfdsUnitRunnerFactory runner = RunnersLibrary.getUnusedVariableRunnerFactory(); List<VulnerabilityInstance> vulns = AnalysisMain.runAnalysis( applicationGraph, resolver, runner, methodsToAnalyze, Integer.MAX_VALUE ); SarifReport report = SarifReport.Companion.fromVulnerabilities(vulns,3); OutputStream outputStream = new FileOutputStream("./report.sarif.json"); report.encodeToStream(outputStream); outputStream.flush(); outputStream.close(); } }
graph
根据cp生成graph
UnitResolver
fun getByName(name: String): UnitResolver<*> { return when (name) { "method" -> MethodUnitResolver "class" -> getClassUnitResolver(false) "package" -> PackageUnitResolver "singleton" -> SingletonUnitResolver else -> error("Unknown unit resolver $name") } }
runner
val runner = when (analysis) { "NPE" -> newNpeRunnerFactory() "Unused" -> UnusedVariableRunnerFactory "SQL" -> newSqlInjectionRunnerFactory() else -> { logger.error { "Unknown analysis type: $analysis" } return@mapNotNull null } }
methods
-s指定从哪个类开始分析,配置文件配置分析类型和解析器。
val startClassesAsList = startClasses.split(";") val startJcClasses = ConcurrentHashMap.newKeySet<JcClassOrInterface>() cp.executeAsync(object : JcClassProcessingTask { override fun process(clazz: JcClassOrInterface) { if (startClassesAsList.any { clazz.name.startsWith(it) }) { startJcClasses.add(clazz) } } }).get() val startJcMethods = startJcClasses.flatMap { it.declaredMethods }.filter { !it.isPrivate }
cli方式调用
官方未发布cli的jar包,需要下载源码自己编译。
java -classpath ^ jacodb-cli-1.4.jar;^ .\libs\annotations-13.0.jar;^ .\libs\asm-9.5.jar;^ .\libs\asm-analysis-9.5.jar;^ .\libs\asm-commons-9.5.jar;^ .\libs\asm-tree-9.5.jar;^ .\libs\asm-util-9.5.jar;^ .\libs\checker-qual-3.12.0.jar;^ .\libs\commons-math3-3.2.jar;^ .\libs\error_prone_annotations-2.11.0.jar;^ .\libs\failureaccess-1.0.1.jar;^ .\libs\guava-31.1-jre.jar;^ .\libs\hamcrest-core-1.3.jar;^ .\libs\j2objc-annotations-1.3.jar;^ .\libs\jacodb-1.4-SNAPSHOT.jar;^ .\libs\jacodb-analysis-1.4-SNAPSHOT-test-fixtures.jar;^ .\libs\jacodb-analysis-1.4-SNAPSHOT.jar;^ .\libs\jacodb-analysis-1.4.2.jar;^ .\libs\jacodb-api-1.4-SNAPSHOT.jar;^ .\libs\jacodb-api-1.4.2.jar;^ .\libs\jacodb-approximations-1.4-SNAPSHOT.jar;^ .\libs\jacodb-approximations-1.4.2.jar;^ .\libs\jacodb-benchmarks-1.4-SNAPSHOT.jar;^ .\libs\jacodb-core-1.4-SNAPSHOT-test-fixtures.jar;^ .\libs\jacodb-core-1.4-SNAPSHOT.jar;^ .\libs\jacodb-core-1.4.2.jar;^ .\libs\jacodb-examples-1.4-SNAPSHOT.jar;^ .\libs\jacodb-taint-configuration-1.4-SNAPSHOT.jar;^ .\libs\jacodb-taint-configuration-1.4.2.jar;^ .\libs\javax.activation-api-1.2.0.jar;^ .\libs\jaxb-api-2.3.1.jar;^ .\libs\jdot-1.0.jar;^ .\libs\jooq-3.14.16.jar;^ .\libs\jsr305-3.0.2.jar;^ .\libs\junit-4.13.1.jar;^ .\libs\kotlin-logging-1.8.3.jar;^ .\libs\kotlin-reflect-1.7.21.jar;^ .\libs\kotlin-stdlib-1.7.21.jar;^ .\libs\kotlin-stdlib-common-1.3.72.jar;^ .\libs\kotlin-stdlib-common-1.7.21.jar;^ .\libs\kotlin-stdlib-jdk7-1.7.21.jar;^ .\libs\kotlin-stdlib-jdk8-1.7.21.jar;^ .\libs\kotlinx-benchmark-runtime-jvm-0.4.4.jar;^ .\libs\kotlinx-cli-jvm-0.3.5.jar;^ .\libs\kotlinx-collections-immutable-jvm-0.3.5.jar;^ .\libs\kotlinx-coroutines-core-1.6.4.jar;^ .\libs\kotlinx-coroutines-core-common-1.3.8.jar;^ .\libs\kotlinx-coroutines-core-jvm-1.6.4.jar;^ .\libs\kotlinx-coroutines-jdk8-1.6.4.jar;^ .\libs\kotlinx-coroutines-reactive-1.6.4.jar;^ .\libs\kotlinx-coroutines-reactor-1.6.4.jar;^ .\libs\kotlinx-metadata-jvm-0.5.0.jar;^ .\libs\kotlinx-serialization-cbor-jvm-1.4.1.jar;^ .\libs\kotlinx-serialization-core-jvm-1.4.1.jar;^ .\libs\kotlinx-serialization-json-jvm-1.4.1.jar;^ .\libs\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;^ .\libs\reactive-streams-1.0.2.jar;^ .\libs\slf4j-api-1.7.29.jar;^ .\libs\slf4j-simple-1.7.36.jar;^ .\libs\sqlite-jdbc-3.41.2.2.jar ^ org.jacodb.cli.MainKt -a config.json -s org.jacodb.testing.analysis.NpeExamples
config.json
{ "analyses": { "NPE": {}, "Unused": { "UnitResolver": "class" }, "SQL": {} } }
UnitResolver有四种,分别为:method、class、package、singleton。分析精度依次递增,消耗依次递增。
命令参数
--analysisConf, -a — 包含以 JSON 格式提供的分析配置的文件路径(详细描述见下文)。 --start, -s — 指定分析开始的类。 --classpath, -cp — JacoDB 分析所需的类路径。 [可选] --dbLocation, -l — 存储字节码数据的 SQLite 数据库的位置。如果未指定,将不会将数据存储在数据库中。 [可选] --output, -o — 用于存储分析报告的文件。默认为 report.json。
2 已实现功能
空指针异常(NPE)
未使用变量(Unused)
sql注入(SQL)
通用污点分析
具体功能参考源码,在自定义分析时可以参考上述实现,以及【Analysis模块类结构】可以快速理清自定义分析器的逻辑。
3 自定义分析
自定义分析器
定义了一个空的分析器,只写了分析器扩展的基本结构类。在TestForwardFunctions.java文件中处理发现问题。
RunnersLibraryExt.java
package org.jacodb.analysis.library; import org.jacodb.analysis.engine.BaseIfdsUnitRunnerFactory; import org.jacodb.analysis.library.analyzers.TestAnalyzerFactory; public class RunnersLibraryExt { public static BaseIfdsUnitRunnerFactory newTestRunnerFactory(int maxPathLength) { if (maxPathLength ==0) { maxPathLength = 5; } return new BaseIfdsUnitRunnerFactory(new TestAnalyzerFactory(maxPathLength)); } }
TestAnalyzer.java
package org.jacodb.analysis.library.analyzers; import org.jacodb.analysis.engine.AbstractAnalyzer; import org.jacodb.analysis.engine.FlowFunctionsSpace; import org.jacodb.api.analysis.JcApplicationGraph; import org.jetbrains.annotations.NotNull; public class TestAnalyzer extends AbstractAnalyzer { private final int maxPathLength; private final JcApplicationGraph graph; public TestAnalyzer(JcApplicationGraph graph, int maxPathLength) { super(graph); this.maxPathLength = maxPathLength; this.graph = graph; } @Override public boolean isMainAnalyzer() { return true; } @NotNull @Override public FlowFunctionsSpace getFlowFunctions() { return new TestForwardFunctions(graph.getClasspath(),maxPathLength); } }
TestAnalyzerFactory.java
package org.jacodb.analysis.library.analyzers; import org.jacodb.analysis.engine.Analyzer; import org.jacodb.analysis.engine.AnalyzerFactory; import org.jacodb.api.analysis.JcApplicationGraph; import org.jetbrains.annotations.NotNull; public final class TestAnalyzerFactory implements AnalyzerFactory{ private int maxPathLength; public TestAnalyzerFactory(int maxPathLength){ this.maxPathLength = maxPathLength; } @NotNull @Override public Analyzer newAnalyzer(@NotNull JcApplicationGraph graph) { return new TestAnalyzer(graph,maxPathLength); } }
TestForwardFunctions.java
package org.jacodb.analysis.library.analyzers;
import org.jacodb.analysis.engine.DomainFact;
import org.jacodb.analysis.engine.FlowFunctionInstance;
import org.jacodb.analysis.engine.ZEROFact;
import org.jacodb.api.JcClasspath;
import org.jacodb.api.JcMethod;
import org.jacodb.api.cfg.JcExpr;
import org.jacodb.api.cfg.JcInst;
import org.jacodb.api.cfg.JcValue;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class TestForwardFunctions extends AbstractTaintForwardFunctions {
private final int maxPathLength;
public TestForwardFunctions(JcClasspath cp, int maxPathLength) {
super(cp);
this.maxPathLength = maxPathLength;
}
@NotNull
@Override
public List<DomainFact> transmitDataFlow(
@NotNull JcExpr jcExpr,
@NotNull JcValue jcValue,
@NotNull JcInst jcInst,
@NotNull DomainFact domainFact,
boolean b) {
System.out.println("------------>transmitDataFlow<------------ ");
System.out.println("positionMethod: " + jcInst.getLocation().getMethod());
System.out.println("jcExpr: " + jcExpr);
System.out.println("jcValue: " + jcValue);
System.out.println("jcInst: " + jcInst);
System.out.println("domainFact: " + domainFact);
System.out.println("b: " + b);
System.out.println();
// 此处最少有一个fact,否则分析就会结束。就是ifds中的ZERO fact要贯穿整个过程。
List<DomainFact> factList = new ArrayList<>();
factList.add(domainFact);
return factList;
}
@NotNull
@Override
public List<DomainFact> transmitDataFlowAtNormalInst(
@NotNull JcInst jcInst,
@NotNull JcInst jcInst1,
@NotNull DomainFact domainFact) {
System.out.println("------------>transmitDataFlowAtNormalInst<------------ ");
System.out.println("positionMethod: " + jcInst.getLocation().getMethod());
System.out.println("jcInst: " + jcInst);
System.out.println("jcInst1: " + jcInst1);
System.out.println("domainFact: " + domainFact);
System.out.println();
// 此处最少有一个fact,否则分析就会结束
List<DomainFact> factList = new ArrayList<>();
factList.add(domainFact);
return factList;
}
/**
* 初始化facts
* @param startStatement
* @return
*/
@NotNull
@Override
public Collection<DomainFact> obtainPossibleStartFacts(@NotNull JcInst startStatement) {
System.out.println("------------>obtainPossibleStartFacts<------------ ");
System.out.println("positionMethod: " + startStatement.getLocation().getMethod());
System.out.println("obtainPossibleStartFacts: " + startStatement);
System.out.println();
List<DomainFact> factList = new ArrayList<>();
factList.add(ZEROFact.INSTANCE);
return factList;
}
/**
* call to return 边
* @param callStatement
* @param returnSite
* @return
*/
@NotNull
@Override
public FlowFunctionInstance obtainCallToReturnFlowFunction(@NotNull JcInst callStatement, @NotNull JcInst returnSite){
System.out.println("------------>obtainCallToReturnFlowFunction<------------ ");
System.out.println("positionMethod: " + callStatement.getLocation().getMethod());
System.out.println("callStatement: " + callStatement);
System.out.println("obtainCallToReturnFlowFunction: " + returnSite);
System.out.println();
return super.obtainCallToReturnFlowFunction(callStatement,returnSite);
}
/**
* call 边
* @param callStatement
* @param callee
* @return
*/
@NotNull
@Override
public FlowFunctionInstance obtainCallToStartFlowFunction(@NotNull JcInst callStatement, @NotNull JcMethod callee){
System.out.println("------------>obtainCallToReturnFlowFunction<------------ ");
System.out.println("positionMethod: " + callStatement.getLocation().getMethod());
System.out.println("callStatement: " + callStatement);
System.out.println("callee: " + callee);
System.out.println();
return super.obtainCallToStartFlowFunction(callStatement,callee);
}
/**
* return 边
* @param callStatement
* @param returnSite
* @param exitStatement
* @return
*/
@NotNull
@Override
public FlowFunctionInstance obtainExitToReturnSiteFlowFunction(JcInst callStatement, @NotNull JcInst returnSite, @NotNull JcInst exitStatement) {
System.out.println("------------>obtainExitToReturnSiteFlowFunction<------------ ");
System.out.println("positionMethod: " + callStatement.getLocation().getMethod());
System.out.println("callStatement: " + callStatement);
System.out.println("returnSite: " + returnSite);
System.out.println("exitStatement: " + exitStatement);
System.out.println();
return super.obtainExitToReturnSiteFlowFunction(callStatement,returnSite,exitStatement);
}
/**
* normal 边
* @param current
* @param next
* @return
*/
@NotNull
@Override
public FlowFunctionInstance obtainSequentFlowFunction(@NotNull JcInst current, @NotNull JcInst next) {
System.out.println("------------>obtainSequentFlowFunction<------------ ");
System.out.println("positionMethod: " + current.getLocation().getMethod());
System.out.println("current: " + current);
System.out.println("next: " + next);
System.out.println();
return super.obtainSequentFlowFunction(current,next);
}
}
调用自定义分析器
只有这句和之前调用不一致。
IfdsUnitRunnerFactory runner = RunnersLibraryExt.newTestRunnerFactory(6);
完整代码:
import org.jacodb.analysis.AnalysisMain; import org.jacodb.analysis.engine.IfdsUnitRunnerFactory; import org.jacodb.analysis.engine.UnitResolver; import org.jacodb.analysis.engine.VulnerabilityInstance; import org.jacodb.analysis.graph.ApplicationGraphFactory; import org.jacodb.analysis.library.RunnersLibraryExt; import org.jacodb.analysis.library.UnitResolversLibrary; import org.jacodb.api.JcClassOrInterface; import org.jacodb.api.JcClasspath; import org.jacodb.api.JcDatabase; import org.jacodb.api.JcMethod; import org.jacodb.api.analysis.JcApplicationGraph; import org.jacodb.impl.JacoDB; import org.jacodb.impl.JcSettings; import org.jacodb.impl.features.InMemoryHierarchy; import org.jacodb.impl.features.Usages; import org.junit.Test; import java.io.*; import java.util.List; import java.util.concurrent.ExecutionException; import org.jacodb.analysis.sarif.SarifReport; public class StaticDataFlow { @Test public void UnusedVariableTest() throws ExecutionException, InterruptedException, IOException { // 创建一个DB JcSettings jcSettings = new JcSettings() .installFeatures(Usages.INSTANCE, InMemoryHierarchy.INSTANCE);//Usages.INSTANCE, JcDatabase database = JacoDB.async(jcSettings).get();// persist data // 加载字节码信息 File buildDir = new File("target/classes"); database.asyncLoad(List.of(buildDir)); // class查找范围 JcClasspath classpath = database.asyncClasspath(List.of(buildDir)).get(); // find class JcClassOrInterface cString = classpath.findClassOrNull("org.example.C"); assert cString != null; List<JcMethod> methodsToAnalyze = cString.getDeclaredMethods(); JcApplicationGraph applicationGraph = ApplicationGraphFactory .asyncNewApplicationGraphForAnalysis(classpath, null) .get(); UnitResolver<?> resolver = UnitResolversLibrary.getMethodUnitResolver(); IfdsUnitRunnerFactory runner = RunnersLibraryExt.newTestRunnerFactory(6); List<VulnerabilityInstance> vulns = AnalysisMain.runAnalysis( applicationGraph, resolver, runner, methodsToAnalyze, Integer.MAX_VALUE ); SarifReport report = SarifReport.Companion.fromVulnerabilities(vulns,3); OutputStream outputStream = new FileOutputStream("./report.sarif.json"); report.encodeToStream(outputStream); outputStream.flush(); outputStream.close(); } }
三 Analysis模块类结构
四 代码片段
查找方法或者变量在哪些地方被调用。
Sequence<UsageFeatureResponse> sequence = Usages.INSTANCE.syncQuery(classpath,new UsageFeatureRequest(
method.getName(),
method.getDescription(),
null,
List.of(182),
new HashSet<>(List.of(className))
));
// 未被调用的方法
if (!(sequence instanceof BatchedSequence)) {
// 过滤一些私有的动态生成之类的方法
filterModifier(method,entryMethodList);
}
查找方法在哪些方法中被调用
SyncUsagesExtension usagesExtension = new SyncUsagesExtension(new HierarchyExtensionImpl(classpath),classpath);
Sequence<JcMethod> usages = usagesExtension.findUsages(method);
Iterator<JcMethod> iterator = usages.iterator();
方法修饰符
/**
* final
* constructor
* classInitializer
* static
* public
* native
* synchronized
* constructor
* abstract
* packagePrivate
* private
* final
* synthetic
* @param method 目标方法
* @param entryMethodList 入口方法集合
*/
public static void filterModifier(JcMethod method) {
if (method.isPublic()
&& !method.isNative()
&& !method.isSynthetic()
&& !method.isAbstract()
&& !method.isClassInitializer()
&& !method.isConstructor()) {
logger.info(String.format("--%s(%s)" , method.getName(),method.getDescription()));
}
}