JacoDB使用

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值