ASM 之 ClassRemapper 使用

ClassRemapper 用于修改Class名称的修改。

代码示例

一下代码中的 Class 名称为 MyClass,如果把 MyClass 修改 YourClass,修改Class 只是单单修改Class名称是不够的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.example.remap;

public class MyClass {

public void callA(){
System.out.println("callA");
this.callB();
}

public void callB(){
System.out.println("callB");
}

}

字节码分析

在方法callA 中 调用callB的时候 INVOKEVIRTUAL MyClass.callB () : void
,也调用了MyClass,所以在设计到MyClass的地方都需要修改,使用ClassRemapper 可以非常方便的修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// class version 52.0 (52)
// access flags 0x21
public class MyClass {

// compiled from: MyClass.java

// access flags 0x1
public <init>() : void
L0
LINENUMBER 3 L0
ALOAD 0: this
INVOKESPECIAL Object.<init> () : void
RETURN
L1
LOCALVARIABLE this MyClass L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1

// access flags 0x1
public callA() : void
L0
LINENUMBER 6 L0
GETSTATIC System.out : PrintStream
LDC "callA"
INVOKEVIRTUAL PrintStream.println (String) : void
L1
LINENUMBER 7 L1
ALOAD 0: this
INVOKEVIRTUAL MyClass.callB () : void
L2
LINENUMBER 8 L2
RETURN
L3
LOCALVARIABLE this MyClass L0 L3 0
MAXSTACK = 2
MAXLOCALS = 1

// access flags 0x1
public callB() : void
L0
LINENUMBER 11 L0
GETSTATIC System.out : PrintStream
LDC "callB"
INVOKEVIRTUAL PrintStream.println (String) : void
L1
LINENUMBER 12 L1
RETURN
L2
LOCALVARIABLE this MyClass L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
}

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.example.remap;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.SimpleRemapper;

public class ClassRemapperTest {

public static void main(String[] args) throws Exception{
ClassReader classReader = new ClassReader("com.example.remap.MyClass");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
//修改Class名称
SimpleRemapper remapper = new SimpleRemapper("com/example/remap/MyClass","com/example/remap/YourClass");

ClassRemapper mapper = new ClassRemapper(classWriter, remapper);

classReader.accept(mapper ,ClassReader.EXPAND_FRAMES);

byte[] classFile = classWriter.toByteArray();
//使用反射调用callA方法
Class c= new ByteClassLoader().define("com.example.remap.YourClass", classFile);
c.getMethod("callA",new Class[]{}).invoke( c.newInstance() );


}
static class ByteClassLoader extends ClassLoader {
public Class define(String name, byte[] body) {
return defineClass(name, body, 0, body.length);
}
}
}

修改后的Class 字节码分析

所以设计到Class名称的地方全部修改了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// class version 52.0 (52)
// access flags 0x21
public class com/example/remap/YourClass {

// compiled from: MyClass.java

// access flags 0x1
public <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lcom/example/remap/YourClass; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1

// access flags 0x1
public callA()V
L0
LINENUMBER 6 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "callA"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 7 L1
ALOAD 0
INVOKEVIRTUAL com/example/remap/YourClass.callB ()V
L2
LINENUMBER 8 L2
RETURN
L3
LOCALVARIABLE this Lcom/example/remap/YourClass; L0 L3 0
MAXSTACK = 2
MAXLOCALS = 1

// access flags 0x1
public callB()V
L0
LINENUMBER 11 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "callB"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 12 L1
RETURN
L2
LOCALVARIABLE this Lcom/example/remap/YourClass; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
}

执行结果

1
2
callA
callB