soot基础 -- 从头开始创建一个类

2024-03-29 06:38

本文主要是介绍soot基础 -- 从头开始创建一个类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原文地址:点击打开链接

首先,我们需要创建一个类,为了创建一个类文件(class file),需要下面这些步骤

加载 Java.lang.Object 和库类(Library Classes)

     加载 java.lang.Object,它是java类层次的根。

    当我们通过扩展(extends)Soot的框架时,这步并非是必须的,因为在这种情况下,当用户代码被调用时类文件的加载就已经完成了。

[java] view plain copy
  1. Scene.v().loadClassAndSupport("java.lang.Object");     

     这行代码使得Soot去加载 java.lang.Object 类同时创建 java.lang.Object 中对应的 SootClass 对象,还有 SootMethod 和 SootFields 。当然,java.lang.Object 对象会引用其他对象。调用 loadClassAndSupport 会加载指定类的传递闭包(the transitive closure),故而为了加载 java.lang.Object 所需的其他所有类型也被加载了(译者注:明白点就是被加载的指定类所引用的类也会被加载)。

     这个过程称之为 resolution。

由于我们的 HelloWorld 程序将会使用到标准库里的类,我们必须解决这些:

[java] view plain copy
  1. Scene.v().loadClassAndSupport("java.lang.System");  

这一行代码引用了 Scene.v()。Scene是一个容器(译者注:这里指的是它的作用像容器),它包含了整个程序里所有的 SootClass ,并且提供了许多实用的方法。Scene是一个单例(singleton),通过调用 Scene.v()访问。

  注意:Soot要么从类文件加载类,要么从 .jimple输入文件中加载类。当从类文件加载类时,Soot会加载每个类文件的常量池中含有的类名(load all class names referred to in the constant pool of each class file);从 .jimple文件加载则只会加载必要的类型。

    创建一个新的 SootClass 对象

创建 HelloWorld对应的SootClass,并将它的超类设为 java.lang.Object

[java] view plain copy
  1. SootClass sClass=new SootClass("HelloWorld", Modifier.PUBLIC);  
这行代码给名为HelloWorld的公有类创建了一个SootClass对象。
[java] view plain copy
  1. sClass.setSuperclass(Scene.v().getSootClass("java.lang.Object"));  
该代码将新创建的类的超类设置为 java.lang.Object 对应的 SootClass 对象(译者注:Soot里使用SootClass代表java中的类)。注意Scene里 getSootClass 方法的使用。
[java] view plain copy
  1. Scene.v().addClass(sClass);  
这行代码把新建的 HelloWorld类添加到 Scene里,所有的类一旦它们被创建,都应该属于Scene。


向 SootClass 里添加方法

    为HelloWorld创建一个 main() 方法,该方法有一个空的方法体(empty body)。

注意到我们已经有一个 SootClass 了,我们需要往里面添加方法

[java] view plain copy
  1. SootMethod method = new SootMethod("main",                   
  2.     Arrays.asList(new Type[] {ArrayType.v(RefType.v("java.lang.String"), 1)}),  
  3.     VoidType.v(), Modifier.PUBLIC | Modifier.STATIC);  
我们创建了一个新的方法 main,它的访问类型是 public static,同时声明带有 java.lang.String对象的数组类型参数,同时返回 void。

因为 SootMethod 的构造方法需要一个List参数,我们调用Java里的方法 Arrays.asList 从使用new Type[] 动态生成的只含一个元素的数组中创建一个列表。我们通过调用 RefType来获取对应于 java.lang.String 类的类型(指Soot里对应的类型),通过ArrayType来创建对应于 java.lang.String 对象的一维 ArrayType。

类型:每一个SootClass代表一个Java对象,给定一个具有给定类型的对象,我们可以实例化类。type和class这两个概念密切相关但是不同,为了通过名字获得 java.lang.String 类的类型我们调用 RefType.v("java.lang.String")(译者注:结果为 java.lang.String)。给定一个SootClass对象 sc,我们可以调用 sc.getType()(译者注:对于例子中的sClass,其结果为 HelloWorld)来获得对应的类型。   

[java] view plain copy
  1. sClass.addMethod(method);  
这句代码将这个方法添加到对应的类中。


给方法添加代码

 一个方法如果不包含任何代码那么它是没有作用的。我们接在给main方法添加一些代码。为了添加代码,我们必须选择一个表示这些代码的中间表示(iintermediate representation)。


创建 JimpleBody

 在Soot里,为了把代码与方法联系起来,我们给SootMethod 附上(attach)一个 Body。每个 Body 都知道它对应于哪个 SootMethod,然而,一个SootMethod每次只能有一个活动的Body(active Body),可以通过 SootMethod.getActivityBody()来访问。不同类型的 Body 由不同中间表示来提供;Soot有 JimpleBody、ShimpleBody、BafBody和GrimpBody。

准确地说,一个Body有三个重要的特征:Local链、Trap链和Unit链。链(Chain)有点类似列表结构,插入和删除元素的复杂度为O(1)。Locals是Body里的局部变量(译者注:Jimple里面会把每个方法需要用到的变量在方法体的最开始全部定义好);Traps表明哪个Unit捕获了哪个异常;Units就是语句本身(译者注:在Jimple里,一个Unit就表示一条Jimple语句)。

注意: Unit既可以表示语句(比如在Jimple里)也可以表示指令(比如在Baf里)。

给 main方法创建一个Jimple Body,并添加局部变量(Locals)和指令

[java] view plain copy
  1. JimpleBody body = Jimple.v().newBody(method);  
  2. method.setActiveBody(body);  
我们调用 Jimple的单例对象(singleton object)获得一个新的JimpleBody,并将它关联我们的方法,同时将其设为这个方法的活动Body。

添加 Local 

[java] view plain copy
  1. Local arg = Jimple.v().newLocal("l0", ArrayType.v(RefType.v("java.lang.String"), 1));  
  2. body.getLocals().add(arg);  
我们创建一个新的Jimple里的Local并将它添加到我们的Body。


添加一个单元(Unit)

[java] view plain copy
  1. units.add(Jimple.v().newIdentityStmt(arg,   
  2.       Jimple.v().newParameterRef(ArrayType.v  
  3.         (RefType.v("java.lang.String"), 1), 0)));  
SootMethod 声明它有参数,但这些参数并没有绑定到 Body里的Locals。IdentityStmt做了这些工作,它把第一个参数的值付给了arg,arg的类型为String数组。
[java] view plain copy
  1. // insert "tmpRef.println("Hello world!")"  
  2. {  
  3.     SootMethod toCall = Scene.v().getMethod  
  4.       ("<java.io.PrintStream: void println(java.lang.String)>");  
  5.     units.add(Jimple.v().newInvokeStmt  
  6.         (Jimple.v().newVirtualInvokeExpr  
  7.            (tmpRef, toCall.makeRef(), StringConstant.v("Hello world!"))));  
  8. }  
我们获得签名为<java.io.PrintStream: void println(java.lang.String)>的方法(这个方法的名称为 println,属于 PrintStream,返回void类型,参数为 String。这足够唯一标识这个方法),同时我们通过传递 String常量“Hello World”来调用它。


写入 类文件(class file)

  为了将这个程序写入到.class文件。方法体必须由Jimple转换成Jasmin,然后再组装成字节码。组装成字节码通过 JasminOutputStream实现。

我们首先构造一个输出流,它需要一个Jasmin源,并输出 .class文件。我们可以人为指定文件名,或者让Soot来决定正确的文件名。这里我们使用后者

[java] view plain copy
  1. String fileName = SourceLocator.v().getFileNameFor(sClass, Options.output_format_class);  
  2. OutputStream streamOut = new JasminOutputStream(new FileOutputStream(fileName));  
  3. PrintWriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut));  
如果我们希望输出Jimple文件而不是.class文件,我们使用如下代码:
[java] view plain copy
  1. String fileName = SourceLocator.v().getFileNameFor(sClass, Options.output_format_jimple);  
  2. OutputStream streamOut = new FileOutputStream(fileName);  
  3. PrintWriter writerOut = new PrintWriter(new OutputStreamWriter(streamOut));  
  4. Printer.v().printTo(sClass, writerOut);  
  5. writerOut.flush();  
  6. streamOut.close();  
我们忽略 JasminOutputStream,调用Printer的printTo方法。

HelloWorld 类对应的Jimple代码如下:

[java] view plain copy
  1. public class HelloWorld extends java.lang.Object  
  2. {  
  3.   
  4.     public static void main(java.lang.String[])  
  5.     {  
  6.         java.lang.String l0;  
  7.         java.io.PrintStream tempRef;  
  8.   
  9.         l0 := @parameter0: java.lang.String[];  
  10.   
  11.         tempRef = <java.lang.System: java.io.PrintStream out>;  
  12.   
  13.         virtualinvoke tempRef.<java.io.PrintStream: void println(java.lang.String)>("Hello World!");  
  14.   
  15.         return;  
  16.     }  
  17. }  

上述例子完整代码如下:

    import java.io.FileOutputStream;  import java.io.IOException;  import java.io.OutputStream;  import java.io.OutputStreamWriter;  import java.io.PrintWriter;  import java.util.Arrays;  import soot.ArrayType;  import soot.Local;  import soot.Modifier;  import soot.Printer;  import soot.RefType;  import soot.Scene;  import soot.SootClass;  import soot.SootMethod;  import soot.SourceLocator;  import soot.Type;  import soot.Unit;  import soot.VoidType;  import soot.jimple.Jimple;  import soot.jimple.JimpleBody;  import soot.jimple.StringConstant;  import soot.options.Options;  import soot.util.Chain;  public class CreateHelloWorldInJimple {  public static void main(String[] args) throws IOException {  SootMethod method;  SootClass sClass=new SootClass("HelloWorld",Modifier.PUBLIC);  //Scene代表一个容器,包含了一个程序里所有的SootClass  Scene.v().loadClassAndSupport("java.lang.Object");  Scene.v().loadClassAndSupport("java.lang.System");  sClass.setSuperclass(Scene.v().getSootClass("java.lang.Object"));  Scene.v().addClass(sClass);  //add method  //RefType代表引用类型  method=new SootMethod("main",Arrays.asList(new Type[]{ArrayType.v(RefType.v("java.lang.String"),1)}),VoidType.v(),Modifier.PUBLIC|Modifier.STATIC);  sClass.addMethod(method);  //add code to method  JimpleBody body=Jimple.v().newBody(method);//attach the body to method  method.setActiveBody(body);  Local arg=Jimple.v().newLocal("l0",RefType.v("java.lang.String"));  //Local arg=Jimple.v().newLocal("l0",ArrayType.v(RefType.v("java.lang.String"),1));  body.getLocals().add(arg);  //add unit,Units are the statements themselves  Chain<Unit> units=body.getUnits();  Unit u=Jimple.v().newIdentityStmt(arg,Jimple.v().newParameterRef(ArrayType.v(RefType.v("java.lang.String"),1),0));  units.add(u);  Local tmpRef=Jimple.v().newLocal("tempRef",RefType.v(Scene.v().getSootClass("java.io.PrintStream")));  //Local tmpRef=Jimple.v().newLocal("tempRef",RefType.v("java.io.PrintStream"));  body.getLocals().add(tmpRef);  units.add(Jimple.v().newAssignStmt(tmpRef,Jimple.v().newStaticFieldRef(Scene.v().getField("<java.lang.System: java.io.PrintStream out>").makeRef())));  {  SootMethod toCallMethod=Scene.v().getMethod("<java.io.PrintStream: void println(java.lang.String)>");  units.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(tmpRef,toCallMethod.makeRef(),StringConstant.v("Hello World!"))));  }  //insert "return"  units.add(Jimple.v().newReturnVoidStmt());  //write to class file  String fileName=SourceLocator.v().getFileNameFor(sClass, Options.output_format_jimple);  OutputStream streamOut=new FileOutputStream(fileName);  PrintWriter writerOut=new PrintWriter(new OutputStreamWriter(streamOut));  //covert from Jimple to Jasmin  /* JasminClass jasminClass=new JasminClass(sClass); jasminClass.print(writerOut); writerOut.flush(); streamOut.close(); */  //output jimple source  Printer.v().printTo(sClass, writerOut);  writerOut.flush();  streamOut.close();  //System.out.println(RefType.v("java.lang.String").toString());  //System.out.println(RefType.v("java.lang.String").getClassName());   //System.out.println(method.getActiveBody());  //System.out.println(sClass.getType());  }  }  


转自:

原作者CSDN文章

这篇关于soot基础 -- 从头开始创建一个类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/857881

相关文章

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou