jclasslib を使用してバイトコード/ソース コードを変更する

  ソースコードの閲覧は非常に簡単で、ideaやeclipseなどの一般的に使われている一部のIDEではクラスファイルのソースコードを閲覧する機能が提供されていますが、ソースコードとの齟齬(構文糖の分解など)はありますが、その機能は実装には一貫性があり、実行時の状況はソース コードよりも JVM に近くなります。
  場合によっては、使用要件を満たすためにソース コードを変更する必要があります。Java コードによって生成されたバイトコードを再ソースするのは比較的簡単です。1 つの方法は、変更する関数を継承して書き直すことです。もう 1 つの方法は、直接変更することです。同じ名前のクラス ファイルを作成し、そこに逆コンパイルされたソース コードをコピーし、変更後、元の jar パッケージ内のクラス ファイルを新しく生成されたクラス ファイルに置き換えます。ただし、一部のバイトコードは他の言語によって生成されており、逆コンパイルされたファイルはJava コンパイル構文を満たしていないため、新しいクラス ファイルにコンパイルできません。この場合、一般的な方法は、バイトコードを直接変更してこれを実現することです。
  インターネット上にはバイトコードの変更に関する記事が数多くありますが、そのほとんどは定数プールを変更して異なる値を出力するものです。この記事では jclasslib を使用してバイトコード内のソース コード ロジックを直接変更します。デモンストレーションの便宜上、この記事のデモは比較的単純かもしれませんが、この方法はより複雑なクラスでも使用できます。必要に応じて議論するメッセージを残してください。

1. Java ソースコード

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);
    }
}

2 つのパラメータのうち小さい方の値を出力します: 1
目標: バイトコードを変更して、printMin メソッドがより大きい値を出力するようにします: 2

2. クラスファイルの逆コンパイル結果

//
// 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);
    }
}

  Java ソース コードと比較すると、生成されたクラス ファイルにはデフォルトの引数なしの構築メソッドが追加されているだけであることがわかります (デモは比較的単純であるため、コンパイル最適化のためのさまざまな戦略は反映されていません)。

3. jclasslib をダウンロードしてインストールします。

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

4. jclasslib を使用してクラス ファイルを開きます

ここに画像の説明を挿入
バイトコードの各部分の名前は既に明らかなので、ここではあまり説明しませんが、変更する箇所を見てください Methods->printMin printMin メソッドに対応するバイトコードは合計 19 行です
ここに画像の説明を挿入
0
:最初のパラメータをスタックに置く (パラメータ a =1)
1: 2 番目のパラメータをスタックにプッシュする (パラメータ b=2)
2: スタックの先頭にある 2 つの int 値の大きさを比較し、結果が 0 より大きい場合、9 行目のバイトコード命令 (判定条件が対応する論理ブロックに入る)
5: 最初のパラメータをスタックにプッシュ (a を選択)
6: 無条件で 10 行目のバイトコード命令にジャンプ
9: プッシュ2 番目のパラメータをスタックに配置します (b を選択)
10: int 値をスタックの先頭に置きます 3 番目のローカル変数に格納します (ストレージ選択結果 c)
11: 指定されたクラスの静的ドメインを取得して先頭にプッシュしますスタックのスタック (実行するインスタンスとメソッドを取得します)
14: 3 番目のパラメータをスタックにプッシュします (c を置きます
) 15: インスタンス メソッドを呼び出します
: 現在のメソッドから void を返します。
その他のバイトコード命令については、仮想マシン バイトコード命令テーブル
。仮想マシン バイトコード命令テーブルを参照してください。pringMin 出力パラメータの値を大きくするには、3 番目のコマンド if_icmpgt を if_icmple に変更するだけです。

ニモニック if_icmpgt if_icmple
コマンドの意味 スタックの先頭にある 2 つの int 値の大きさを比較し、結果が 0 より大きい場合にジャンプします スタックの先頭にある 2 つの int 値の大きさを比較し、結果が 0 以下の場合にジャンプします
バイトコード 0xa3 0xa4
符号付き 10 進数 -93 -92

5. バイトコードを変更する

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);
    }
}

クラス ファイルを読み取るには、デバッグまたは出力 JSON を通じて ClassFile のクラス構造を表示し、クラス構造内のバイトコードを変更して、ファイルを書き換えます。
jclasslib.jar を使用する必要があります。ダウンロード リンク: jclasslib.jar

6. 変更された結果を表示する

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);
    }
}

ここに画像の説明を挿入

この記事では、バイトコードの変更方法を簡単に説明します。実際のプロジェクトでバイトコードを変更する必要がある場合、状況はさらに複雑になります。元のバイトコード内の対応するバイトコード ブロックを置き換えると、他のファイルもそれに応じて変更する必要があります。 、定数プールなど。

おすすめ

転載: blog.csdn.net/qq_21033663/article/details/105928982