Soot学习笔记(2)

本文是Soot学习笔记的第二部分,主要介绍如何利用Soot检查类文件并通过对类文件的修改进行程序剖析。目标包括理解如何使用Soot检查Java类文件以及如何通过插入代码来剖析程序中的静态调用次数。以TestInvoke类为例,展示了MainDriver包装类如何改变TestInvoke.class并在第6行和第50行插入静态调用计数代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过soot来分析一个java classfile 对于Sable官网的介绍的文档,我们将使用soot flow analysis framework

学习这个例子的目地在于:

1. how to inspect a class file by using Soot, and   // 怎么样去用soot去检测一个class文件
2. how to profile a program by instrumenting the class file. //通过instrumenting class file to 剖析你的程序

任务:计算我们到底进行了多少次静态调用

1.Testinvoke 类 ,该类是我们要进行分析的类的sourcecode。

class TestInvoke {

	private static int calls = 0;

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			foo();
		}
		System.out.println("I made " + calls + " static calls");
	}

	private static void foo() {
		calls++;
		bar();
	}

	private static void bar() {
		calls++;
	}

}

为了计算我们到底进行了多少次静态调用,我们需要一个辅助类MyCounter

/* The counter class */
public class MyCounter {
	/* the counter, initialize to zero */
	private static int c = 0;

	/**
	 * increases the counter by
	 * 
	 * <pre>
	 * howmany
	 * </pre>
	 * 
	 * @param howmany
	 *            , the increment of the counter.
	 */
	public static synchronized void increase(int howmany) {
		c += howmany;
	}

	/**
	 * reports the counter content.
	 */
	public static synchronized void report() {
		System.err.println("counter : " + c);
	}
}

接下来我们要创建一个wrapper class(封装类)通过加入一个phrase 进入soot来完成我们的任务:剖析TestInvoke需要进行多少staticInvoke?然后在再执行运行soot(通过soot.Main.main()).

wrapper class: MainDriver:

import soot.*;
/* import necessary soot packages */

public class MainDriver {
	
	/**
	 * 这里放入: TestInvoke.java
	 * @param args
	 */
    public static void main(String[] args) {
    	
    	 /* check the arguments */
        if (args.length == 0){
            System.err.println("Usage: java MainDriver [options] classname");
            System.exit(0);
        }
        
        /* add a phase to transformer pack by call Pack.add */ 
        Pack jtp = PackManager.v().getPack("jtp");
        
        jtp.add(new Transform("jtp.instrumanter", 
                new InvokeStaticInstrumenter()));
        /* Give control to Soot to process all options,
         * InvokeStaticInstrumenter.internalTransform will get called.
         */
        soot.Main.main(args);
    }
}
 我们要如何去统计TestInvoke中staticInvoke调用了多少次呢,我们就要在它每次staticInvoke之前先放入一个类似计数器的语句来统计staticInvoke的运行次数,这个任务就需要InvokeStaticInstrumanters

import soot.*;
import soot.jimple.*;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.toolkits.graph.UnitGraph;
import soot.util.*;
import java.util.*;

/**
 * InvokeStaticInstrumenter inserts count instructions before INVOKESTATIC
 * bytecode in a program. The instrumented program will report how many static
 * invocations happen in a run.
 * 
 * Goal: Insert counter instruction before static invocation instruction. Report
 * counters before program’s normal exit point. Approach: 1. Create a counter
 * class which has a counter field, and a reporting method. 2. Take each method
 * body, go through each instruction, and insert count instructions before
 * INVOKESTATIC. 3. Make a call of reporting method of the counter class.
 * 
 * Things to learn from this example: 1. How to use Soot to examine a Java
 * class. 2. How to insert profiling instructions in a class.
 * InvokeStaticInstrumenter extends the abstract class BodyTransformer, and
 * implements
 * 
 * <pre>
 * internalTransform
 * </pre>
 * 
 * method.
 */
public class InvokeStaticInstrumenter extends BodyTransformer {
	
	/* some internal fields */
	static SootClass counterClass;
	static SootMethod increaseCounter, reportCounter;
	
   //在soot中注册我们的辅助类
	static {
		counterClass = Scene.v().loadClassAndSupport("MyCounter");
		increaseCounter = counterClass.getMethod("void increase(int)");
		reportCounter = counterClass.getMethod("void report()");
	}

	/** internalTransform goes through a method body and inserts
	 * counter instructions before an INVOKESTATIC instruction
	 */
	@Override
	protected void internalTransform(Body body, String arg1, Map arg2) {
		
		 // 得到该方法
		SootMethod method = body.getMethod();
		
		// 调试
		System.out.println("instrumenting method : " + method.getSignature());
		System.out.println("MethodName: " + method.getName());
		
		// 得到该方法的UnitChain
		Chain units = body.getUnits();
		
		
		//当遍历它的时候,改变它的语句链
		Iterator stmtIt = units.snapshotIterator();

		
		 // 遍历每一条语句
		while (stmtIt.hasNext()) {
			
			// 得到statement
			Stmt stmt = (Stmt) stmtIt.next();
			if (!stmt.containsInvokeExpr()) {
				continue;
			}
			// take out the invoke expression
			InvokeExpr expr = (InvokeExpr) stmt.getInvokeExpr();
			
			// 跳过 non-static invocations
			if (!(expr instanceof StaticInvokeExpr)) {
				continue;
			}
			
			// now we reach the real instruction
			// call Chain.insertBefore() to insert instructions
			//插入一条Expr,调用我们构造的辅助类的方法,参数为1(显然)
			InvokeExpr incExpr = Jimple.v().newStaticInvokeExpr(
					increaseCounter.makeRef(), IntConstant.v(1));
			Stmt incStmt = Jimple.v().newInvokeStmt(incExpr);
			
			// 3. insert new statement into the chain
		    //we are mutating the unit chain)
			
			units.insertBefore(incStmt, stmt);
		}
		
		
		//最后,我们要插入一条语句来输出最后的结果
		
		//判断当前是不是main方法
		String signature = method.getSubSignature();
		boolean isMain = signature.equals("void main(java.lang.String[])");
		if (isMain) {
			stmtIt = units.snapshotIterator();
			while (stmtIt.hasNext()) {
				Stmt stmt = (Stmt) stmtIt.next();
				
				 // check if the instruction is a return with/without value
				if ((stmt instanceof ReturnStmt)
						|| (stmt instanceof ReturnVoidStmt)) {
					
					// 2. then, make a invoke statement
					InvokeExpr reportExpr = Jimple.v().newStaticInvokeExpr(
							reportCounter.makeRef());
					
					// 3. insert new statement into the chain
					Stmt reportStmt = Jimple.v().newInvokeStmt(reportExpr);
					units.insertBefore(reportStmt, stmt);
				}
			}
		}

	}



}

下面是

改变后的TestInvoke.class改变后的截图:


   6  invokestatic MyCounter.increase(int) : void [36]

  50  invokestatic MyCounter.report() : void [17]

可以看到 6,50行的变化~我们插入了我们需要的expr



                
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值