一 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()));
}
}
263

被折叠的 条评论
为什么被折叠?



