15

デプロイされた Java プログラムの文字列定数、つまりコンパイルされたファイル内の値を変更する必要があります.class。再起動することはできますが、簡単に再コンパイルすることはできません (ただし、この質問で答えが得られない場合は不便です)。これは可能ですか?

更新: 16 進エディターでファイルを見たところ、文字列を簡単に変更できるようです。それは機能しますか、つまり、ファイルの何らかの署名を無効にしないでしょうか? 古い文字列と新しい文字列はどちらも英数字で、必要に応じて同じ長さにすることができます。

更新 2: 修正しました。変更が必要な特定のクラスは非常に小さく、プロジェクトの新しいバージョンでは変更されていないため、それをコンパイルしてそこから新しいクラスを取得することができました。ただし、教育目的で、コンパイルを含まない回答にまだ興味があります。

4

5 に答える 5

8

このクラスのソースがある場合、私のアプローチは次のとおりです。

  • JAR ファイルを取得する
  • 単一クラスのソースを取得する
  • クラスパス上の JAR を使用してソースをコンパイルします (この方法では、他に何もコンパイルする必要はありません。JAR に既にバイナリが含まれていても問題ありません)。これには最新の Java バージョンを使用できます。-sourceと を使用してコンパイラをダウングレードするだけ-targetです。
  • jar uまたは Ant タスクを使用して、JAR 内のクラス ファイルを新しいものに置き換えます。

Ant タスクの例:

        <jar destfile="${jar}"
            compress="true" update="true" duplicate="preserve" index="true"
            manifest="tmp/META-INF/MANIFEST.MF"
        >
            <fileset dir="build/classes">
                <filter />
            </fileset>
            <zipfileset src="${origJar}">
                <exclude name="META-INF/*"/>
            </zipfileset>
        </jar>

ここでマニフェストも更新します。最初に新しいクラスを配置してから、元の JAR からすべてのファイルを追加します。duplicate="preserve"新しいコードが上書きされないようにします。

コードが署名されていない場合、新しい文字列の長さが古い文字列とまったく同じであれば、バイトを置き換えることもできます。Java はコードに対していくつかのチェックを行いますが、.class ファイルにはチェックサムがありません

長さを維持する必要があります。そうしないと、クラスローダーが混乱します。

于 2012-05-21T09:02:55.623 に答える
5

定数プール内の文字列(技術的にはUtf8アイテム)を変更するときに必要な追加データは、長さフィールド(データの前にある2バイトのビッグエンディアン)だけです。変更が必要な追加のチェックサムやオフセットはありません。

2つの注意点があります。

  • 文字列は他の場所で使用される場合があります。たとえば、「Code」はメソッドコード属性に使用されるため、変更するとファイルが破損します。
  • 文字列はModifiedUtf8形式で保存されます。したがって、基本プレーン外のnullバイトとUnicode文字は異なる方法でエンコードされます。長さフィールドは文字数ではなくバイト数であり、65535に制限されています。

これを頻繁に行う場合は、クラスファイルエディタツールを入手することをお勧めしますが、16進エディタは迅速な変更に役立ちます。

于 2012-06-16T01:02:16.137 に答える
3

多くのバイトコードエンジニアリングライブラリを使用して.classを変更できます。たとえば、javaassistを使用します。

ただし、静的なfinalメンバーを置き換えようとしている場合は、コンパイラーがこの定数を使用する場所にインライン化するため、目的の効果が得られない可能性があります。

javaassist.jarを使用したサンプルコード

//ConstantHolder.java

public class ConstantHolder {

 public static final String HELLO="hello";

 public static void main(String[] args) {
  System.out.println("Value:" + ConstantHolder.HELLO);
 }
}

//ModifyConstant.java

import java.io.IOException;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;

//ModifyConstant.java
public class ModifyConstant {
 public static void main(String[] args) {
  modifyConstant();
 }

 private static void modifyConstant() {
  ClassPool pool = ClassPool.getDefault();
  try {
   CtClass pt = pool.get("ConstantHolder");
   CtField field = pt.getField("HELLO");
   pt.removeField(field);
   CtField newField = CtField.make("public static final String HELLO=\"hell\";", pt);
   pt.addField(newField);
   pt.writeFile();
  } catch (NotFoundException e) {
   e.printStackTrace();System.exit(-1);
  } catch (CannotCompileException e) {
   e.printStackTrace();System.exit(-1);
  } catch (IOException e) {
   e.printStackTrace();System.exit(-1);
  }
 }  
}

この場合、プログラムはHELLOの値を「Hello」から「Hell」に正常に変更します。ただし、ConstantHolderクラスを実行すると、コンパイラによるインライン化のため、「Value:Hello」が出力されます。

それが役に立てば幸い。

于 2012-05-21T09:54:35.670 に答える