Day 9 HomeWrok-ClassHierarchyAnalysis

前言

利用CHA完成调用图的构造

处理Java中四种调用:

  • invokestatic
  • invokespecial
  • invokeinterface
  • invokevirtual

一些准备

  • 目录设置

这次我们是要进行过程间分析,所以有些细节的作用就显现出来了。

我之前喜欢将工作目录直接设置到要测试的代码目录上,比如:

Options.v().set_process_dir(Arrays.asList("target/classes/com/DeadCodeDetection/TestDC"));

之前确实不会有什么问题,而且直接不包括我们的分析代码,比较方便。但对于这次来说就不一样了,假如我们还这么设置:

Options.v().set_process_dir(Arrays.asList("target/classes/com/CHA/TestCHA"));

对我们要分析的代码

package com.CHA.TestCHA;

public class TestCode{}

class A {

    static void main() {
        A.foo();
    }

    static void foo() {
        A a = new A();
        a.bar();
    }

    void bar() {
        C c = new C();
        c.bar();
    }
}

class B extends A {
    void bar() { }
}

class C extends A {

    void bar() {
        if (Math.random() > 0.5) {
            A.foo();
        }
    }

    void m() {  }
}

生成的Jimple如下(不是全称类名):
在这里插入图片描述

但内部有关调用时函数签名却是:

在这里插入图片描述

包含了类的全名,所以从生成的Jimple结果来看,在分析中会找不到对应的方法,debug一下确实如此:

在这里插入图片描述

这是初始化时,因为工作目录的设置得到的方法签名,到后面利用Unit分析时:
在这里插入图片描述

就不会进入if了,所以稍微注意下就行,我就直接这么安排目录:

在这里插入图片描述

init中这么写即可:

Options.v().set_process_dir(Arrays.asList("target/classes/"));
//不分析的目录
List<String> excluded = new LinkedList<>();
excluded.add("com.*");
Options.v().set_exclude(excluded);
Options.v().set_no_bodies_for_excluded(true);

在这里插入图片描述

  • 分析阶段

需要开启全局模式(可能是因为要进行过程间分析?)

PackManager.v().getPack("wjtp").add(new Transform("wjtp.cg_CHA", new CHATransformer()));
PackManager.v().getPack("wjtp").apply();
  • 明确需要的类
    • CHATransformer
    • CallKind:java中的枚举类,其实就是用来判断是哪种调用
    • CallEdge:代表调用边
    • CallGraphBuilderJimpleCallGraph:Builder利用后者提供的各种操作,实现算法分析

过程分析

JimpleCallGraph初始化,将方法和它所有的units关系保存下来,方便后面建立调用边时寻找。那么思路就是对于要分析的类的方法,如果不为空,就将其保存在一个Map中:

在这里插入图片描述

那么就可以将其传入到我们的CallGraphBuilder中进行分析了,回顾一下课上的算法:
在这里插入图片描述

那我们就准备好WL,RM,按照简单,方法的entry都从main开始,所以JimpleCallGraph中提供一个操作:
在这里插入图片描述

剩下差不多一样的过程,对照着算法来写,缺什么补什么就行,不再写的那么麻烦了,但还是希望写代码前能考虑考虑,BuildCallGraph如下:

在这里插入图片描述

cg的一些操作如下:

private Set<SootMethod> reachableMethods = new HashSet<>();

private Map<SootMethod, Set<CallEdge>> caller2callee = new HashMap<>();//caller ---> callees

public Collection<Unit> getCallSiteIn(SootMethod method) {//一个方法中所有的callsites
    List<Unit> callSites = new LinkedList<>();
    if (method.hasActiveBody()) {
        Body body = method.getActiveBody();
        for (Unit unit : body.getUnits()) {
            Stmt stmt = (Stmt) unit;
            if (stmt.containsInvokeExpr()) {
                callSites.add(stmt);
            }
        }
    }
    return callSites;
}

public boolean addEdge(Unit callsite, SootMethod callee, CallKind callKind){
    CallEdge callEdge = new CallEdge(callKind, callsite, callee);

    SootMethod caller = unit2Owner.get(callsite);
    Set<CallEdge> callees = caller2callee.computeIfAbsent(caller, k -> new HashSet<>());
    callees.add(callEdge);

    return ret;
}

public boolean contains(SootMethod method) {
    return reachableMethods.contains(method);
}

public void addRM(SootMethod method) {
    reachableMethods.add(method);
}

对于Resolve来说,也是这样的

在这里插入图片描述

接下来就是Dispatch
在这里插入图片描述

剩下的就是些其他类的补充了

CallKind

public enum CallKind {

    INTERFACE("invokeinterface"),
    VIRTUAL("invokevirtual"),
    SPECIAL("invokespecial"),
    STATIC("invokestatic");

    private String inst;

    CallKind(String inst) {
        this.inst = inst;
    }

    public static CallKind getCallKind(Unit unit) throws IllegalArgumentException {//返回给定Unit的调用类型
        InvokeExpr invoke = ((Stmt) unit).getInvokeExpr();
        if (invoke instanceof InterfaceInvokeExpr) {
            return INTERFACE;
        }
        if (invoke instanceof VirtualInvokeExpr) {
            return VIRTUAL;
        }
        if (invoke instanceof SpecialInvokeExpr) {
            return SPECIAL;
        }
        if (invoke instanceof StaticInvokeExpr) {
            return STATIC;
        }
        throw new IllegalArgumentException(invoke.toString());
    }

    @Override
    public String toString() {
        return inst;
    }

}

CallEdge

public class CallEdge {

    private CallKind callKind;

    private Unit callSite;

    private SootMethod callee;

    public CallEdge(CallKind callKind, Unit callSite, SootMethod callee) {
        this.callKind = callKind;
        this.callSite = callSite;
        this.callee = callee;
    }

    public CallKind getCallKind() {
        return callKind;
    }

    public Unit getCallSite() {
        return callSite;
    }

    public SootMethod getCallee() {
        return callee;
    }

    @Override
    public String toString() {
        StringBuilder buff = new StringBuilder();
        buff.append("@").append(callSite.getTag(LineNumberTag.IDENTIFIER))
                .append(": ").append(callSite)
                .append(" -> ").append(callee.getSignature());
        return buff.toString();
    }

}

然后我们再Transformer中添加如下测试即可:

public class CHATransformer extends SceneTransformer {
    @Override
    protected void internalTransform(String s, Map<String, String> map) {
        CHACallGraphBuilder cgBuilder = CHACallGraphBuilder.v();
        JimpleCallGraph cg = new JimpleCallGraph();
        cgBuilder.buildCallGraph(cg);

        for (SootClass clazz : Scene.v().getApplicationClasses()) {
            for (SootMethod method : clazz.getMethods()) {
                StringBuilder buff = new StringBuilder();
                // basic information
                buff.append(method.getSignature())
                        .append(": \n")
                        .append("\t ").append(cg.contains(method) ? "Reachable" : "Unreachable")
                        .append("\n");

                // call edge
                Set<CallEdge> edgeSet = cg.getCallOutOf(method);
                for (CallEdge callEdge : edgeSet) {
                    buff.append("\t ").append(callEdge).append("\n");
                }
                buff.append("\n");
                System.out.println(buff);
            }
        }
    }
}

在这里插入图片描述
在这里插入图片描述

结果

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值