简介
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 descriptor
boolean Z
char C
byte B
short S
int I
float F
long J
double D
Object Ljava/lang/Object;
[I
[[Ljava/lang/Object;
类类型的描述符是该类的内部名称,前面是L,然后是分号。
数组类型的描述符是方括号,后跟数组元素类型的描述符。
方法描述符
void m(int i, float f) (IF)V
int m(Object o) (Ljava/lang/Object;)I
int[] m(int i, String s) (ILjava/lang/String;)[I
Object 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():访问类的尾部,只有当类访问结束时,才能调用该方法,同时必须调用该方法
二 FieldVisitor
可以和ClassVisitor进行相比查看,属于字段观察者
调用顺序
FieldVisitor
(
visitAnnotation |
visitTypeAnnotation |
visitAttribute
)*
visitEnd
三 MethodVisit
可以和ClassVisitor进行相比查看,属于方法观察者
调用顺序如下
[
visitCode
(
visitFrame |
visitXxxInsn |
visitLabel |
visitTryCatchBlock
)*
visitMaxs
]
visitEnd
(1)visitCode()调用一次
(2)中间的方法具体内容等根据()和[]选择调用次数
(3)visitEnd()结尾调用一次
核心组件
一 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() {
}
}
后期会分析一下GadgetInspector的改造和使用。他的流程处理,逆拓扑算法一类的太复杂了,但是对ASM还是有比较高的要求,有兴趣的话可以看一看,前段时间在学习codeql,但是发现codeql对jar包,依赖里面的还是没发进行分析,codeql更加倾向于一些基础漏洞的挖掘,或者是把源码脱下来进行分析。当然发现了也有师傅提出来可以对jar包中的sink进行自定义,但是痛点还是在于只能根据已有的漏洞去做分析。这样子的话如果我们对jar包漏洞触发点比较了解全局搜索调用处根据参数是否可控也能完成,所有稍微还是有点鸡肋,于是谋生了学习一下GadgetInspector的想法。有兴趣的同学可以看看,先知社区三梦师傅等几位师傅都还是有了比较详细的分析。
原文始发于微信公众号(e0m安全屋):ASM(二)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论