Use jclasslib to modify bytecode/source code

  Viewing the source code is very simple. Some commonly used IDEs such as idea and eclipse provide the function of viewing the source code of class files. Although there are some discrepancies with the source code (decomposition of syntactic sugar, etc.), the function implementation is consistent, and it is closer to the JVM than the source code. situation at runtime.
  Sometimes we need to modify the source code to meet the requirements of use. It is relatively simple to re-source the bytecode generated by java code. One way is to inherit and then rewrite the function to be modified. The other way is to directly create a class file with the same name , copy the decompiled source code into it, and after modification, replace the class file in the original jar package with the newly generated class file, but some bytecodes are generated by other languages, and the decompiled file does not meet the java compilation syntax , it cannot be compiled into a new class file. In this case, a common way is to directly modify the bytecode to achieve it.
  There are many articles about modifying bytecode on the Internet, but most of them modify the constant pool to output different values. This article uses jclasslib to directly modify the source code logic in the bytecode. For the convenience of demonstration, the demo of this article may be relatively simple, but this method It can be used in more complex classes, please leave a message to discuss if necessary.

1. Java source code

package com.zhanghao.test.jclasslib;

public class JclasslibTest {
    
    
    public static void main(String[] args) {
    
    
        int a = 1;
        int b = 2;
        printMin(a, b);
    }

    private static void printMin(int a, int b) {
    
    
        int min = a <= b ? a : b;
        System.out.println(min);
    }
}

Output the smaller value of the two parameters: 1
Goal: Make the printMin method output a larger value by modifying the bytecode: 2

2. The decompilation result of the class file

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.zhanghao.test.jclasslib;

public class JclasslibTest {
    
    
    public JclasslibTest() {
    
    
    }

    public static void main(String[] args) {
    
    
        int a = 1;
        int b = 2;
        printMin(a, b);
    }

    private static void printMin(int a, int b) {
    
    
        int min = a <= b ? a : b;
        System.out.println(min);
    }
}

  It can be seen that the generated class file only adds a default no-argument construction method compared to the java source code (because the demo is relatively simple, it does not reflect various strategies for compilation optimization)

3. Download and install jclasslib

https://github.com/ingokegel/jclasslib/releases

4. Use jclasslib to open the class file

insert image description here
The names of each part of the bytecode are already clear, so I won’t explain too much here. Look at the location we need to modify. Methods->printMin The
insert image description here
bytecode corresponding to the printMin method is 19 lines in total.
0: Put the first parameter on the stack (parameter a =1)
1: Push the second parameter onto the stack (parameter b=2)
2: Compare the size of the two int values ​​at the top of the stack, and jump to the bytecode instruction on line 9 when the result is greater than 0 (the judgment condition enters the corresponding logic block)
5: Push the first parameter onto the stack (choose a)
6: Unconditionally jump to the bytecode instruction on line 10
9: Push the second parameter onto the stack (choose b)
10: Put the int value at the top of the stack Store in the third local variable (storage selection result c)
11: Obtain the static domain of the specified class and push it to the top of the stack (obtain the instance and method to be run)
14: Push the third parameter onto the stack (put c
15: Call the instance method 18
: Return void from the current method.
For more bytecode instructions, please refer to the virtual machine bytecode instruction table
. Refer to the virtual machine bytecode instruction table. To achieve a larger value in the pringMin output parameter, just change the third command if_icmpgt to if_icmple.

mnemonic if_icmpgt if_icmple
Command meaning Compare the size of the two int values ​​at the top of the stack, and jump when the result is greater than 0 Compare the size of the two int values ​​at the top of the stack, and jump when the result is less than or equal to 0
bytecode 0xa3 0xa4
signed decimal number -93 -92

5. Modify the bytecode

package com.zhanghao.test.jclasslib;

import com.alibaba.fastjson.JSON;
import java.io.*;
import org.gjt.jclasslib.io.ClassFileWriter;
import org.gjt.jclasslib.structures.AttributeInfo;
import org.gjt.jclasslib.structures.ClassFile;
import org.gjt.jclasslib.structures.MethodInfo;
import org.gjt.jclasslib.structures.attributes.CodeAttribute;

public class JclasslibModify {
    
    
    public static void main(String[] args) throws Exception {
    
    
        String filePath = "/Users/zhanghao/Desktop/jclasslib/JclasslibTest.class";
        FileInputStream fis = new FileInputStream(filePath);

        DataInput di = new DataInputStream(fis);
        ClassFile cf = new ClassFile();
        cf.read(di);
        System.out.println(JSON.toJSONString(cf));
        MethodInfo[] methodInfos = cf.getMethods();
        MethodInfo methodInfo = methodInfos[2];
        AttributeInfo[] attributeInfos = methodInfo.getAttributes();
        CodeAttribute codeAttribute = (CodeAttribute) attributeInfos[0];
        byte[] bytes = codeAttribute.getCode();
        bytes[2] = -92;

        fis.close();
        File f = new File(filePath);
        ClassFileWriter.writeToFile(f, cf);
    }
}

To read a class file, you can view the class structure of ClassFile through debug or output json, modify the bytecode in the class structure and then rewrite the file.
Need to use jclasslib.jar, download link: jclasslib.jar

6. View the modified results

package com.zhanghao.test;

public class JclasslibTest {
    
    
    public JclasslibTest() {
    
    
    }

    public static void main(String[] args) {
    
    
        int a = 1;
        int b = 2;
        printMin(a, b);
    }

    private static void printMin(int a, int b) {
    
    
        int min = a > b ? a : b;
        System.out.println(min);
    }
}

insert image description here

This article just briefly explains the method of modifying the bytecode. When the bytecode needs to be modified in the actual project, the situation will be more complicated. Replace the corresponding bytecode block in the original bytecode, and other files also need to be modified accordingly, such as constant pool, etc.

Guess you like

Origin blog.csdn.net/qq_21033663/article/details/105928982
Recommended