String Encryption

文字列を予め暗号化しておき,実行時,暗号化された文字列が参照される直 前に復号する難読化手法です.

Properties

NameDefault ValueDescription
provideremptyJCE プロバイダを指定する.空文字の場合はデフォルトのプロバイダを用いる.初期値は空文字列.
algorithmDES/56文字列を暗号化するときに用いるアルゴリズムと鍵長を「アルゴリズム名/鍵長」のフォーマットで指定する.それぞれ用いることができるのは JCE プロバイダが提供するもののみ.
keyempty暗号化するときに用いるの鍵.空文字列の場合は難読化時に乱数でキーを生成する.初期値は空文字列.

Algorithm

  • 文字列を operand stack に積んでいる部分を探す
  • 積んでいる文字列(LDC が指す Constant_String が参照している Constant_Utf8) を暗号化する.
  • LDC の直後に復号用のメソッド呼び出しルーチンを追加する
  • クラス全体に以上の処理を行った後,クラスに文字列を復号するメソッドを追加する.

Example

文字列暗号難読化を施した例を以下に示します.難読化前のソースコード中 には World や Hello などの文字列を見つけることができますが,難読化後の ソースコードからは何が出力されるのかは全くわかりません.

ここで示した難読化後のプログラムはバイトコード表現をそのままソースコー ドに直しているだけなので,__decrypt メソッド内の try-catch 節は実際に はありません.

public class HelloWorld{
    public String greeting(){
        return greeting("World");
    }

    public String greeting(String name){
        if(name == null){
            return greeting();
        }
        return new String(new StringBuffer("Hello ").append(name));
    }

    public static void main(String[] args){
        String name = null;
        if(args.length > 0){
            name = args[0];
        }
        System.out.println(new HelloWorld().greeting(name));
    }
}
public class HelloWorld{

    public String greeting(){
        return greeting(__decrypt("380aeeae1b7701ce"));
    }

    public String greeting(String name){
        if(name == null){
            return greeting();
        }
        return new String((new StringBuffer(__decrypt("b710eaebac3c751c"))).append(name));
    }

    public static void main(String[] args){
        String name = null;
        if(args.length > 0){
            name = args[0];
        }
        System.out.println(new HelloWorld().greeting(name));
    }

    private static String __decrypt(String name){
        try{
            SecretKeySpec skeySpec = new SecretKeySpec(
                new byte[] {
                    (byte)2,   (byte)179, (byte)59,  (byte)253, 
                    (byte)181, (byte)55,  (byte)185, (byte)196,
                 },
                 "DES"
             );
            Cipher cipher = Cipher.getInstance("DES", "SunJCE");
            cipher.init(2, skeySpec);
    
            byte[] nameData = new byte[name.length() / 2];
            for(int i = 0; i < nameData.length; i++)
                nameData[i] = (byte)Integer.parseInt(name.substring(i * 2, i * 2 + 2), 16);
    
            return new String(cipher.doFinal(nameData));
        } catch(Exception e){
        }
        return null;
    }
}

Related Publications