`
envy2002
  • 浏览: 151717 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

ASM 使用的一个范例。

 
阅读更多

 

现在开始研究Junit,试图通过Junit来改善自己的编程,看了一两天,觉得自己懂了,开始上马。

 

同事写了一个byte 数组转 json的组件,我的目标是通过撰写 Juit的测试用例来测试这些组件是否OK。

 

一上来就遇到了一个不小的麻烦,我的测试主要是来比较两个“对象”是否相等,给定了一个byte数组,组件返回给我了一个

 

json对象,我也自己模拟了一个json对象,我需要用JSONObject.equals来判断两个对象是否相等,很遗憾,同时用的是

 

org.json,但不是json-lib中的 net.sf.json中的JSONObject,json-lib中的JSONObject是有equals方法的,可以递归地

 

比较到最底层。我用的是jar包,我修改不了org.json.JSONObject的源代码,我后来想用 toString来比较,但是org.json.

 

JSONObject反编译出来的源码是:

 

 

    public JSONObject()
    {
        map = new HashMap();
    }

 

 

 

 

  private Map map;

 

 

 它里面的数据结构是hashmap, 所以就可能和我要求的不一样,

 

 例如 加入我要求 {"aa":"11","bb":"22"},

 

  它 toString 给我的结果可能是 {"bb":"22","aa":"11"}

 

所以,这个toString方法是不能间接证明它们 not equlas的。现在如果想想,如果能让

 

 

   public JSONObject()
    {
        map = new LinkedHashMap();
    }
那该多好啊,转化组件是有配置的,配置是有顺序的,所以我自己知道顺序,那么这样toString就可以做到比较两者之间
是否相等。
																				
	

   该 如何让这个jar里面的类的hashmap-->linkedhashmap呢,我在网上找到了ASM。

 

 

  废话不多说了,就是看看如何做吧。我们那这个问题当模型的话,那么我们只需要修改类的构造方法就是了。

 

但是,我需要提一点的是,网上很多教程没有说,类的值域好像是无法修改的,只能先删除,后添加一个新的,比如如果类

 

里面有个 private String name-->你希望name是int型,那么先删掉name,后价格 string 型的name.至于有没有修改

 

的方法,我也不确定。我以网上这个例子来当范本吧。

 

http://victorzhzh.iteye.com/blog/882699

 

 

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class Person {
	public int name ;
	public Map address ;

}

 

 

我们的目标是

 

 

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public class Person {
	public String name ;
	public Map address ;
	
	public Person()
	{
		address=new LinkedHashMap();
	}
}

 

 

 

怎么做呢?

 

import java.util.LinkedHashMap;
import java.util.Map;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.CheckClassAdapter;



public class Transform extends CheckClassAdapter {

	public Transform(ClassVisitor cv) {
		super(cv);
	}

	@Override
	public void visitEnd() {  //这个方法只会执行一次,在这里我们可以构造我们需要的东西。
		
		cv.visitField(Opcodes.ACC_PUBLIC, "name", Type.getDescriptor(int.class),
				null, null);
        //新增加一个方法
        MethodVisitor mv= cv.visitMethod(Opcodes.ACC_PUBLIC,
                "<init>",
                "()V",
                null,
                null);
        
        
        
        
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Objec", "<init>", "()V");
        
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitInsn(Opcodes.ICONST_2);
        mv.visitFieldInsn(Opcodes.PUTFIELD, "Person", "name", "I");
        
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitTypeInsn(Opcodes.NEW, "java/util/LinkedHashMap");  
        mv.visitInsn(Opcodes.DUP); 
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/LinkedHashMap", "<init>", "()V");
        mv.visitFieldInsn(Opcodes.PUTFIELD, "Person", "address", " java/util/Map"); 
//        mv.visitVarInsn(Opcodes.ASTORE, 0);

        mv.visitInsn(Opcodes.RETURN);

       // mv.visitMaxs(0, 0);
        mv.visitEnd();
        
	}
	
    public FieldVisitor visitField(int access, String name, String desc,  
            String signature, Object value) {  
    	
       // cv.visitField(Opcodes.ACC_PUBLIC, "age", Type.getDescriptor(int.class),  
       //         null, null);  
    	if("name".equals(name)) //值域变量名称为name的时候
    		return null;          //为null表示去掉
        return super.visitField(access, name, desc, signature, value); //不为null,保留 
    }  
    
    @Override
    public MethodVisitor visitMethod(
        int access,
        String name,
        String desc,
        String signature,
        String[] exceptions)
    {
        if ( "<init>".equals(name)) { //init为初始化方法
            return null; //为空表示删除
           }
 
         //其他的方法保留
           return super.visitMethod(access, name, desc, signature, exceptions);
        

    }
	

}
 

 

 

 

 

public class GeneratorClassLoader extends ClassLoader {

	@SuppressWarnings("rawtypes")
	public Class defineClassFromClassFile(String className, byte[] classFile)
			throws ClassFormatError {
		return defineClass(className, classFile, 0, classFile.length);
	}
}

 

 

 

 

import java.io.FileOutputStream;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.CheckClassAdapter;

public class TransformTest {
	
	public void addAge() throws Exception {
//   输入需要改造的类,Person
		ClassReader classReader = new ClassReader(
				"Person");
//       写改造好的类,怎么改,谁改,classAdapter
		ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
		CheckClassAdapter classAdapter = new Transform(classWriter);

		classReader.accept(classAdapter, ClassReader.SKIP_DEBUG);
              //生成的,改造好的类的二级制文件。
		byte[] classFile = classWriter.toByteArray();

         //直接把这些二进制文件,通过类加载器,加载到虚拟机中去,为了检验,我们需要存到文件中去
//		GeneratorClassLoader classLoader = new GeneratorClassLoader();
//		Class clazz = classLoader.defineClassFromClassFile(
//				"Person", classFile);
//		Object obj = clazz.newInstance();

		//System.out.println(clazz.getDeclaredField("name").get(obj));//----(1)
		//System.out.println(clazz.getDeclaredField("age").get(obj));//----(2)
		//存到e盘log目录下的Example.class文件中,我们可以用反编译工具来查看,我们修改的
              是否正确。
	       FileOutputStream fos = new FileOutputStream("e:\\logs\\Example.class");
	       fos.write(classFile);
	       fos.close();
	}
	
	  public static void main(String[] args) throws Exception
	  {
		  new TransformTest().addAge();
	  }
}

 

 

我们重点考察的是Transform的visitEnd方法,这个方法完成了我们队类的改写。

 

  public FieldVisitor visitField(int access, String name, String desc,  

            String signature, Object value)

 

和     public MethodVisitor visitMethod(

        int access,

        String name,

        String desc,

        String signature,

        String[] exceptions)

 

这两个方法,分别表示构建值域和函数,如果为空,就表示没有这个值域或者函数了。这个应该会遍历那个最原始的类。

 

比方说,我们最初的Person类里面有2个值域,5个方法,那么就会执行两次visitField,5次visitMethod方法。

 

所以我们遇到想去掉的方法,或者值域就Ok了。

 

visitEnd这个方法只会执行一次,所以,我们在这里加上我们想要的东西。

 

		cv.visitField(Opcodes.ACC_PUBLIC, "name", Type.getDescriptor(int.class),
				null, null); //name改变为int 型
        //新增加一个方法,构造方法
        MethodVisitor mv= cv.visitMethod(Opcodes.ACC_PUBLIC,
                "<init>",
                "()V",
                null,
                null);
        
        
       
        //为什么加这段,以后再说
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Objec", "<init>", "()V");
        
       //让int型的name=2
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitInsn(Opcodes.ICONST_2);
        mv.visitFieldInsn(Opcodes.PUTFIELD, "Person", "name", "I");
        
      //让 address=new LinkedHashMap();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitTypeInsn(Opcodes.NEW, "java/util/LinkedHashMap");  
        mv.visitInsn(Opcodes.DUP); 
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/LinkedHashMap", "<init>", "()V");
        mv.visitFieldInsn(Opcodes.PUTFIELD, "Person", "address", " java/util/Map"); 
//        mv.visitVarInsn(Opcodes.ASTORE, 0);

        mv.visitInsn(Opcodes.RETURN);

       // mv.visitMaxs(0, 0);
        mv.visitEnd();
 

 

这样,我们就完成了改造的任务,为什么这样改,怎样改。

 

首先我们需要分析一下这个过程。我们需要工具,一个是asm的eclipse插件,这个插件负责把类的二进制结构显示出来,

 

名字叫 

bytecode-outline

 

在这里 2.4.0版本的 http://forge.ow2.org/project/download.php?group_id=23&file_id=17193

 

还有一个windows xp上的反编译工具。

 

我们先用eclipse写出,我们需要的类,然后用 bytecode-outline观察,然后再用asm api构造出这个想要的类,然后保存到文件中,然后用 jd-gui反编译,看看是不是我们想要的类。

 

例如,我们需要这样,这是我们的目标代码

 

public class Person {
	public int name ;
	public String  address ;
//	private Map map;
//	
	public Person()
	{
		name=7;
		address="福田";
	}
}
 

bytecode-outline看到的是:

 

// class version 50.0 (50)
// access flags 0x21
public class Person {

  // compiled from: Person.java

  // access flags 0x1
  public I name

  // access flags 0x1
  public Ljava/lang/String; address

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 10 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init>()V
   L1
    LINENUMBER 12 L1
    ALOAD 0
    BIPUSH 7
    PUTFIELD Person.name : I
   L2
    LINENUMBER 13 L2
    ALOAD 0
    LDC "\u798f\u7530"
    PUTFIELD Person.address : Ljava/lang/String;
   L3
    LINENUMBER 14 L3
    RETURN
   L4
    LOCALVARIABLE this LPerson; L0 L4 0
    MAXSTACK = 2
    MAXLOCALS = 1
}

 

那么我们的构建代码将是这样:

 

	public void visitEnd() {
		
		cv.visitField(Opcodes.ACC_PUBLIC, "name", Type.getDescriptor(int.class),
				null, null);
        //新增加一个方法
        MethodVisitor mv= cv.visitMethod(Opcodes.ACC_PUBLIC,
                "<init>",
                "()V",
                null,
                null);
        
        
        
        
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Objec", "<init>", "()V");
        
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitVarInsn(Opcodes.BIPUSH, 7);
        //mv.visitInsn(Opcodes.ICONST_2);
        mv.visitFieldInsn(Opcodes.PUTFIELD, "Person", "name", "I");
        
       mv.visitVarInsn(Opcodes.ALOAD, 0);
       mv.visitLdcInsn("\u798f\u7530");
       mv.visitFieldInsn(Opcodes.PUTFIELD, "Person", "address", "Ljava/lang/String"); 
        
        
//        mv.visitVarInsn(Opcodes.ALOAD, 0);
//        mv.visitTypeInsn(Opcodes.NEW, "java/util/LinkedHashMap");  
//        mv.visitInsn(Opcodes.DUP); 
//        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/LinkedHashMap", "<init>", "()V");
//        mv.visitFieldInsn(Opcodes.PUTFIELD, "Person", "address", " java/util/Map"); 
//        mv.visitVarInsn(Opcodes.ASTORE, 0);

        mv.visitInsn(Opcodes.RETURN);

       // mv.visitMaxs(0, 0);
        mv.visitEnd();
        
	}

 

蓝色对蓝色,红色对红色。呵呵,然后我们用 jd-gui反编译一下example.class会发现 

public class Person
{
  public String address;
  public int name = 7;

  public Person()
  {
    super();
    this.address = "福田";
  }
}

 

 

这样,我们就找到了一种比较好的途径来构建我们需要的类,因为java类的字节码还是比较难认的,这种方式就能照葫芦画瓢,

 

以比较小的代价完成我们的目标,如果你要想深入,那么就需要精通虚拟机的类的结构了。

 

掌握了这个方法,我们就可以在没有源代码的情况下,通过反编译工具来构建我们需要的类了,快试试吧。其实我也不怎么懂这个ASM框架的,就是调试调试,基本了解了其流程,利用“工具”吧,逆向工程来学习。

分享到:
评论

相关推荐

    asm330lhh-STdC嵌入式程序示例

    在本文中,我们将深入探讨"asm330lhh-STdC嵌入式程序示例",这是一个基于ASM330LHH传感器的嵌入式系统应用实例。ASM330LHH是一款高性能的三轴加速度计和三轴陀螺仪,常用于运动检测、导航系统以及物联网(IoT)设备。...

    简单ASM示例

    总的来说,"简单ASM示例"是一个很好的起点,通过实践这些例子,你可以逐步掌握ASM的基本语法和编程思想。同时,建议配合相关的教材或在线资源,深入学习汇编语言与计算机体系结构的关系,以便更好地理解和利用这种...

    【ProToolkit范例之五】输出Asm的架构

    它可以是机械设备、汽车部件或任何其他工程组件,其中每个零件都有独立的特征和属性,但它们在Asm中协同工作,形成一个整体。 Pro/Toolkit为开发者提供了访问Asm数据的接口,包括但不限于以下功能: 1. 创建Asm:...

    ASM4.0 中文教程与多种字节码编程实战案例

    内容包含ASM4.0中文手册,以及四种ASM常见的字节码操作应用范例,包含最新版本的ASM9.2的jar包,包含asm-9.2.jar,asm-commons-9.2.jar,asm-util-9.2.jar。 学习文章地址 ...

    c语言中嵌入asm汇编代码示例

    然而,需要注意的是,`scanf`函数中的`&y`表示取`y`的地址,这是因为`scanf`函数需要一个变量的地址来存储输入的值。 通过这个简单的示例,我们可以看到C语言与汇编语言的结合如何使得程序能够更精细地控制底层硬件...

    hkWin7asm 采用win32汇编写的模仿windows7 win+上下左右控制窗口大小效果(附源代码)

    hkWin7asm 采用win32汇编写的模仿windows7 win+上下左右控制顶层窗口大小效果,另外加了个win+回车窗口自适应居中效果 压缩包内含有编译完成的本件及源代码 编译环境使用masm32,需要先下载masm32才可以进行编译操作

    BJ8M603A-应用范例(C&ASM)V1.1_BJ8M603A_

    汇编语言则更接近底层硬件,可以直接控制单片机的每一个硬件资源,虽然编写起来较为繁琐,但在需要高效运行或者精确控制硬件时不可或缺。压缩包中的C语言示例可能包括了基本的I/O操作、中断服务例程、定时器设置等,...

    labview与单片机进行串口通信的VI范例方法

    总之,这个范例是一个很好的学习资源,它演示了如何在LabVIEW和单片机之间建立可靠的串口通信。通过深入理解并实践这个例子,开发者可以更好地掌握两者之间的数据交互,为未来的嵌入式系统设计打下坚实基础。

    ASM.rar_asm汇编示例

    汇编语言范例,读者可以参考编程,喜爱汇编的朋友可以下来看看

    哈希表类_汇编版(HashMap_ASM) 支持自定义数据值

    1) 添加 取值时可提供一个逻辑参考参数,保存取值状态结果。2) 添加 支持自定义数据值, 以及使用范例(用法比较另类)。0.4版(2018.11.22)。1) 修复 由于WIN10下,文本比较SSE4.2会产生奔溃,屏蔽掉文本比较SSE4.2。...

    八核心MCU 之FPPA 80Cxx原厂范例程序

    “FPPA 80Cxx”是这款MCU的具体型号,FPPA可能是制造商的缩写或者产品系列名,而80Cxx可能是该系列中的一个具体产品代号,通常这类编号会包含关于芯片性能和功能的一些信息。“范例程序”则意味着这是用于演示和学习...

    一个温控的例子带汇编一个温控的例子带汇编

    标题和描述中提到的"一个温控的例子带汇编"是一个关于温度控制系统的实例,其中涉及到使用汇编语言编程和可能的硬件设计。这个系统可能是基于微控制器的,通过读取温度传感器的数据来调节环境温度。汇编语言是低级...

    EM78P153 C语言唤醒范例

    EM78P153 C语言唤醒范例 #include "EM78x153xx.h" #define ENWDT 1 #define DSWDT 0 #define WATCHDOG ENWDT void Pin_Wake_up(void) { _asm { mov a,@0x0e contw } WDTCR=0x3f; //disable wdt _asm { ...

    PIC10F200 Example program_pic10f200范例_PIC10f200_PIC10F200通讯_10F2

    标题中的“PIC10F200 Example program”和描述中的“pic10f200范例”指的是基于Microchip公司的微控制器PIC10F200的一个示例程序。PIC10F200是一款低功耗、高性能的8位微控制器,常用于小型嵌入式系统设计。这个示例...

    C51及汇编语言范例程序

    《C51及汇编语言范例程序》是学习微控制器编程的重要参考资料,它涵盖了多个实际应用项目,如数字多用仪表、红外遥控系统、简易电子琴和带农历的电子万年历。这些项目不仅展示了C51语言和汇编语言的结合使用,还提供...

    80X86汇编学习辅助工具

    1. "范例.asm":这是一个示例汇编源代码文件,初学者可以通过阅读和分析这个文件来了解80X86汇编的基本语法和结构。它可能包含了基本的指令,如数据定义、过程调用、控制流程指令等。 2. "ASM_Tool.exe":这是80X86...

    VB与s7200通讯例子……读取温度值的程序范例

    根据提供的文件信息,本文将详细解析“VB与S7-200通讯例子——读取温度值的程序范例”中的关键...同时,对于初学者来说,这也是一个很好的学习案例,能够帮助他们理解如何通过编程实现工业自动化系统的数据采集和处理。

    LINUX2.6.26.6内核下的第一个LED驱动程序测试成功!

    ### Linux 2.6.26.6 内核下的 LED 驱动程序分析 ...通过上述分析,我们可以看出,对于初学者来说,这个驱动程序是一个很好的学习范例,不仅可以了解到 Linux 内核的基本结构,还可以掌握字符设备驱动的编写方法。

    asm-best-practises:阿里云服务网格的最佳实践

    ASM最佳实践范例 交通转移 交通管理 流量解码 安全的 观察 网格化 http_hybrid_demo http_workload_blue_green_demo vm_tracing_demo

    文档使用说明21.doc

    USB20C可能是一个USB接口控制器,用于构建嵌入式系统中的USB通信模块。用户需要了解如何利用这个设备来控制USB接口,并且程序主要是主机端的实现,意味着它负责发起USB通信。 4. **安装步骤**:用户需要将源代码...

Global site tag (gtag.js) - Google Analytics