ASM(二)

admin 2022年4月9日01:35:01评论24 views字数 5659阅读18分51秒阅读模式

简介

https://mp.weixin.qq.com/s?__biz=Mzg5MjY2NTU4Mw==&mid=2247485274&idx=1&sn=4d6cd0c6a6624276f79ab57356444d26&chksm=c03becdaf74c65cc690f9180d3f79220b1fa8044b4887f24dc0988f8e56fcedb770cb05d86dc&token=872431672&lang=zh_CN#rd

ASM是一种通用Java字节码操作和分析框架。它可以用于修改现有的class文件或动态生成class文件。(能在类被加载到虚拟机之前动态修改class文件)

类文件往期发过了(第三个参考链接)只简单提一下没提到的地方,这里主要是更加具体的学习一下ASM。

类文件

类型描述符

类型描述符Java type  Type descriptorboolean      Zchar      Cbyte      Bshort      Sint        Ifloat      Flong      Jdouble      DObject    Ljava/lang/Object;int[]      [IObject[][]  [[Ljava/lang/Object;类类型的描述符是该类的内部名称,前面是L,然后是分号。数组类型的描述符是方括号,后跟数组元素类型的描述符。

方法描述符

void m(int i, float f)        (IF)Vint m(Object o)          (Ljava/lang/Object;)Iint[] m(int i, String s)    (ILjava/lang/String;)[IObject m(int[] i)        ([I)Ljava/lang/Object;解析如下,()里面代表传入的参数类型,后面代表返回值的类型

ASM

接口

一  ClassVisitor  

jdk.internal.org.objectweb.asm.ClassVisitor
ClassVisitor 类观察者


方法访问顺序

visit
[visitSource] [visitModule] [visitNestHost][visitOuterClass]
(visitAnnotation | visitTypeAnnotation|visitAttribute)*
(visitNestMember|visitInnerClass|visitField| visitMethod)*
visitEnd;
(1)visit(版本,修饰符,类名,泛型信息,继承的父类,实现的接口) 包含访问类的头部信息,必须调用
visit(int version, int access, String name, String signature, String superName, String[] interfaces)
(2)某节点或者模块调用,[]代表只能调用一次,()*代表可以根据顺序调用多次
(3)void visitEnd() 访问类的尾部,调用结束后必须调用

部分方法解析
visit() 访问类的头部
visitSource(): 访问类的源码
visitOuterClass():访问类的外部类
visitAnnotation():访问类的注解
visitTypeAnnotation():访问类的签名类型(某个泛型)的注解;
visitAttribute():访问类的非标准属性;
visitInnerClass():访问一个内部类的信息
visitField():访问一个类的域(字段)信息
visitMethod():访问类的方法,如果需要修改类方法信息,则可以重写此方法
isitEnd():访问类的尾部,只有当类访问结束时,才能调用该方法,同时必须调用该方法

ASM(二)

FieldVisitor

可以和ClassVisitor进行相比查看,属于字段观察者调用顺序FieldVisitor( visitAnnotation | visitTypeAnnotation | visitAttribute)*visitEnd

ASM(二)

三  MethodVisit

可以和ClassVisitor进行相比查看,属于方法观察者调用顺序如下[    visitCode    (        visitFrame |        visitXxxInsn |        visitLabel |        visitTryCatchBlock    )*    visitMaxs]visitEnd(1)visitCode()调用一次(2)中间的方法具体内容等根据()和[]选择调用次数(3)visitEnd()结尾调用一次

ASM(二)

核心组件

一 ClassReader

ClassReader ClassReader类解析作为字节数组给出的已编译类,并在作为参数传递给其accept方法的ClassVisitor实例上调用相应的visitXxx方法。可以将其视为事件产生器。

二 ClassWriter

ClassWriter
ClassWriter类是ClassVisitor抽象类的子类,该类直接以二进制形式构建编译的类。
它产生一个包含已编译类的字节数组作为输出,可以使用toByteArray方法进行检索。
可以将其视为事件消费者。
他的构造方法有两种
ClassWriter(int flags)//直接重新生成一个类
ClassWriter(ClassReader classReader, int flags) //基于一个类去修改
其中flage
0:需要手动修改偏移
ClassWriter.COMPUTE_MAXS :自动修改,手动触发
ClassWriter.COMPUTE_FRAMES:自动修改

ClassVisitor

ClassVisitor ClassVisitor类将其接收的所有方法委托给另一个ClassVisitor实例。可以将其视为事件过滤器。

使用

我们上面三个类都是抽象类,所以可以用继承去重载中间的相应方法。

获取一个类所有的信息(包含类,方法,字段等)

//被拜访类import java.io.Serializable;public class e0mljaTest implements Serializable {    public static  String s1 ="s1";    public static int i1 =2;    public e0mljaTest(){
} public e0mljaTest(String s){
} public e0mljaTest(String s,Integer i){
}
public String e0mlja(String s){ return s; }}
//实现类import org.objectweb.asm.*;import java.io.IOException;import java.io.Serializable;
public class ASMtest extends ClassVisitor implements Serializable { public ASMtest() { super(Opcodes.ASM4); } protected ASMtest(int api, ClassVisitor classVisitor) { super(api, classVisitor); }
@Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces){ System.out.println("版本 = " +version); System.out.println("修饰符 = " +access); System.out.println("类名 = " +name); System.out.println("泛型信息 = "+signature); System.out.println("父类=" +superName); if (interfaces !=null){ for (String interfaceName:interfaces) { System.out.println("接口="+interfaceName); } }
System.out.println("类描述信息完毕n"); } @Override public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value){ System.out.println("修饰符 = " +access); System.out.println("字段名 = " +name); System.out.println("泛型信息 = "+signature); System.out.println("描述信息=" +descriptor); System.out.println("字段值="+value); System.out.println("方法描述信息完毕n"); return null; } @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions){ System.out.println("修饰符 = " +access); System.out.println("方法名 = " +name); System.out.println("泛型信息 = "+signature); System.out.println("描述信息=" +descriptor);// System.out.println("异常="+exceptions.toString()); System.out.println("字段描述信息完毕n"); return null; }
public static void main(String[] args) throws IOException { ASMtest asMtest = new ASMtest(); ClassReader cr = new ClassReader("e0mljaTest"); cr.accept(asMtest, 0); }}

我们想要利用ASM的ClassVisitor,只需要实现一个类继承ClassVisitor,然后重写其中相应的方法即可。当我们需要读取相关信息,就新建一个ClassReader,调用他的accept方法

//结果版本 = 55修饰符 = 33类名 = e0mljaTest泛型信息 = null父类=java/lang/Object接口=java/io/Serializable类描述信息完毕
修饰符 = 9字段名 = s1泛型信息 = null描述信息=Ljava/lang/String;字段值=null方法描述信息完毕
修饰符 = 9字段名 = i1泛型信息 = null描述信息=I字段值=null方法描述信息完毕
修饰符 = 1方法名 = <init>泛型信息 = null描述信息=()V字段描述信息完毕
修饰符 = 1方法名 = <init>泛型信息 = null描述信息=(Ljava/lang/String;)V字段描述信息完毕
修饰符 = 1方法名 = <init>泛型信息 = null描述信息=(Ljava/lang/String;Ljava/lang/Integer;)V字段描述信息完毕
修饰符 = 1方法名 = e0mlja泛型信息 = null描述信息=(Ljava/lang/String;)Ljava/lang/String;字段描述信息完毕
修饰符 = 8方法名 = <clinit>泛型信息 = null描述信息=()V字段描述信息完毕
能够看出,描述信息基本代表着这个类,方法或者字段的类型或者格式

二 生成一个类

生成一个类需要调用ClassWriter,只需要将上面的内容装在进去就行。 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);        cw.visit(55, 33,                "e0mljaTest1", null, "java/lang/Object",                new String[] { "java/io/Serializable" });
cw.visitField(9, "s1", "Ljava/lang/String;", null,"123");
cw.visitMethod(1, "<init>", "()V", null, null); cw.visitEnd(); byte[] b = cw.toByteArray(); FileOutputStream fos = new FileOutputStream(new File("C:\Users\yzc\Desktop\代码学习\java 学习程序\ASM\src\main\java\e0mlja\MyClass.class")); fos.write(b); fos.close();//如下,我生成了一个类import java.io.Serializable;
public class e0mljaTest1 implements Serializable { public static String s1;
public e0mljaTest1() { }}

ASM(二)

后期会分析一下GadgetInspector的改造和使用。他的流程处理,逆拓扑算法一类的太复杂了,但是对ASM还是有比较高的要求,有兴趣的话可以看一看,前段时间在学习codeql,但是发现codeql对jar包,依赖里面的还是没发进行分析,codeql更加倾向于一些基础漏洞的挖掘,或者是把源码脱下来进行分析。当然发现了也有师傅提出来可以对jar包中的sink进行自定义,但是痛点还是在于只能根据已有的漏洞去做分析。这样子的话如果我们对jar包漏洞触发点比较了解全局搜索调用处根据参数是否可控也能完成,所有稍微还是有点鸡肋,于是谋生了学习一下GadgetInspector的想法。有兴趣的同学可以看看,先知社区三梦师傅等几位师傅都还是有了比较详细的分析。


原文始发于微信公众号(e0m安全屋):ASM(二)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月9日01:35:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ASM(二)https://cn-sec.com/archives/890409.html

发表评论

匿名网友 填写信息