Ding

记一次 .class 文件的修改

参考资料

背景交代

因为 Android Studio 中的 Android Icon Creator 插件生成的图片路径还是在 drawable 之下,和同事纠结了一下还是决定将所有图片放在 mipmap 下面,所以就产生了动插件 jar 包的想法。

ROUND 1 - BytecodeViewer 2.9.8

很方便很强大的一个工具

Plugins 选项中,可以查看所有 String 和 修改 String 的功能。

但是在看 loading view 转啊转,重新打开文件的时候,并没有修改成功,卒

ROUND 2 - javac, javap

我们都知道,源文件 .java 通过 javac 变成字节码文件 .class, 那。。有没有一个命令是可以直接恢复 .class 文件变成 .java 文件呢?(呵呵,想得美

么,然后就找到了 javap

javap 可以查看 .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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
C:\Users\HCol\Desktop>javap HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
public HelloWorld();
public static final void main(java.lang.String[]);
}
C:\Users\HCol\Desktop>javap -v HelloWorld.class
Classfile /C:/Users/HCol/Desktop/HelloWorld.class
Last modified 2017-3-31; size 427 bytes
MD5 checksum a185526841e54eac7ed70bfe00bc6840
Compiled from "HelloWorld.java"
public class HelloWorld
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // Hello, world!
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // HelloWorld
#6 = Class #22 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 HelloWorld.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 Hello, world!
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 HelloWorld
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
{
public HelloWorld();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static final void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello, world!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 6: 0
line 7: 8
}
SourceFile: "HelloWorld.java"
C:\Users\HCol\Desktop>

好了,然而这波哔并不能装,就看看就好了,卒

ROUND 3 - jclasslib

好,既然到这儿了,那么这次我们就一定能改成功了,事不过三嘛(笑死

  1. 找到我们想要修改的地方

    这里我要修改的是 “Hello,world!”, 我们把它改成 “HAHAHA!”

    因为 “Hello,world!” 是一个 String 而且是在 main 方法里面,所以我们只需要关注几个地方就好了:

    • 常量池(Constant Pool)
    • 方法名(Method#main)
      jclasslib
  2. 去常量池找到目标资源

    点击序号即可在常量池中定位到资源了,咦 String_info 好像是哦, 还可以点嘎, Utf8_info ,咦,完了…

    嗯, 最后那个 Utf8_info 就是我们想要修改的目标资源了,记住序号 18

  3. 修改

    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
    String filePath = "G:/HelloWorld.class";
    String targetStr = "Hello, world!";
    String replaceStr = "HAHAHA!";
    int targetStrIndex = 18;
    FileInputStream fInput = null;
    DataInput di = null;
    try {
    File mTargetFile = new File(filePath);
    fInput = new FileInputStream(mTargetFile.getAbsolutePath());
    di = new DataInputStream(fInput);
    ClassFile cf = new ClassFile();
    cf.read(di);
    CPInfo[] cps = cf.getConstantPool();
    for (int i = 0; i <= cps.length; i++) {
    if (i == targetStrIndex) {
    String mStr = cps[i].getVerbose();
    System.out.println("before: " + mStr);
    StringBuilder sb = new StringBuilder(mStr);
    sb.replace(0, targetStr.length(), replaceStr);
    mStr = sb.toString();
    System.out.println("after: " + mStr);
    ConstantUtf8Info mUtf8Info = (ConstantUtf8Info) cps[i];
    mUtf8Info.setString(mStr);
    cps[i] = mUtf8Info;
    }
    }
    cf.setConstantPool(cps);
    fInput.close();
    File f = new File(filePath);
    ClassFileWriter.writeToFile(f, cf);
    } catch (Exception e) {
    e.printStackTrace();
    }

modify_success

运行结果

run

你的认可是我最大的动力!