9

Androidマーケットプレイスで新しいライセンス(LVL)を使用したいと思っていますが、ストックAESObfuscatorでパフォーマンスの問題が発生しています。具体的には、コンストラクターがデバイスで実行されるまでに数秒かかります(エミュレーターでの純粋な苦痛)。このコードは、キャッシュされたライセンス応答をチェックするためにも実行する必要があるため、起動時にライセンスをチェックすることに深刻な影響を及ぼします。

LVLサンプルアプリを実行して、AESObfuscatorのコンストラクターのバーバリアンスタイルのプロファイリングを次に示します。

public AESObfuscator(byte[] salt, String applicationId, String deviceId) {
        Log.w("AESObfuscator", "constructor starting");
        try {
            Log.w("AESObfuscator", "1");
            SecretKeyFactory factory = SecretKeyFactory.getInstance(KEYGEN_ALGORITHM);
            Log.w("AESObfuscator", "2");
            KeySpec keySpec =
                new PBEKeySpec((applicationId + deviceId).toCharArray(), salt, 1024, 256);
            Log.w("AESObfuscator", "3");
            SecretKey tmp = factory.generateSecret(keySpec);
            Log.w("AESObfuscator", "4");
            SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
            Log.w("AESObfuscator", "5");
            mEncryptor = Cipher.getInstance(CIPHER_ALGORITHM);
            Log.w("AESObfuscator", "6");
            mEncryptor.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(IV));
            Log.w("AESObfuscator", "7");
            mDecryptor = Cipher.getInstance(CIPHER_ALGORITHM);
            Log.w("AESObfuscator", "8");
            mDecryptor.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(IV));
        } catch (GeneralSecurityException e) {
            // This can't happen on a compatible Android device.
            throw new RuntimeException("Invalid environment", e);
        }
        Log.w("AESObfuscator", "constructor done");
    }

Nexus Oneの出力:

09-28 09:29:02.799: INFO/System.out(12377): debugger has settled (1396)
09-28 09:29:02.988: WARN/AESObfuscator(12377): constructor starting
09-28 09:29:02.988: WARN/AESObfuscator(12377): 1
09-28 09:29:02.999: WARN/AESObfuscator(12377): 2
09-28 09:29:02.999: WARN/AESObfuscator(12377): 3
09-28 09:29:09.369: WARN/AESObfuscator(12377): 4
09-28 09:29:09.369: WARN/AESObfuscator(12377): 5
09-28 09:29:10.389: WARN/AESObfuscator(12377): 6
09-28 09:29:10.398: WARN/AESObfuscator(12377): 7
09-28 09:29:10.398: WARN/AESObfuscator(12377): 8
09-28 09:29:10.409: WARN/AESObfuscator(12377): constructor done
09-28 09:29:10.409: WARN/ActivityManager(83): Launch timeout has expired, giving up wake lock!
09-28 09:29:10.458: INFO/LicenseChecker(12377): Binding to licensing service.

7秒間のスラッシング(エミュレーターでは約20秒)。AsyncTaskでスピンオフすることはできますが、ライセンスを検証するまでアプリを実際に実行できないため、そこではあまり効果がありません。ユーザーがライセンスを確認するのを待っている間、私が得るのは、すてきな、かなり7秒のプログレスバーだけです。

何か案は?自分のライセンス応答をキャッシュするために、AESよりも単純なもので自分の難読化ツールをロールしますか?

4

5 に答える 5

6

広範囲にわたる検索といじくり回しの後、私の最善の回避策は、PBEKeySpecでPKCS#5コードを使用するのではなく、自分でAESキーを作成することであるように思われます。他の人がこの問題を投稿していないことに少し驚いています。

回避策は、一連の識別データ(デバイスID、IMEI、パッケージ名など)を文字列に結合することです。次に、その文字列のSHA-1ハッシュを取得して、24バイトのAESキーを20バイト取得します。確かに、PKCS#5ほどエントロピーはなく、4バイトのキーがわかっています。しかし、実際には、誰が暗号攻撃を開始するのでしょうか?それはまだかなり健全であり、それを強化する他の試みにもかかわらず、LVLにははるかに弱い攻撃ポイントがあります。

AES暗号の作成でさえコストがかかる(エミュレーターで2秒)操作のように思われるため、難読化と難読化解除の呼び出しで必要になるまで、暗号化メンバーと復号化メンバーの作成も延期します。アプリがキャッシュされたライセンス応答を使用している場合、暗号化機能は必要ありません。これにより、最も一般的な起動モードからかなりのサイクルが削減されます。

私の新しいコンストラクターは以下のとおりです。ソースファイル全体が必要な場合は、私に連絡してください。

   /**
    * @param initialNoise device/app identifier. Use as many sources as possible to
    *    create this unique identifier.
    */
   public PixieObfuscator(String initialNoise) {
        try {
            // Hash up the initial noise into something smaller:
            MessageDigest md = MessageDigest.getInstance(HASH_ALGORITHM);
            md.update(initialNoise.getBytes());
            byte[] hash = md.digest();

            // Allocate a buffer for our actual AES key:
            byte[] aesKEY = new byte[AES_KEY_LENGTH];   

            // Fill it with our lucky byte to take up whatever slack is not filled by hash:
            Arrays.fill(aesKEY, LUCKY_BYTE);

            // Copy in as much of the hash as we got (should be twenty bytes, take as much as we can):
            for (int i = 0; i < hash.length && i < aesKEY.length; i++)
                aesKEY[i] = hash[i];

            // Now make the damn AES key object:
              secret = new SecretKeySpec(aesKEY, "AES");
        }
        catch (GeneralSecurityException ex) {
            throw new RuntimeException("Exception in PixieObfuscator constructor, invalid environment");
        }
   }
于 2010-09-29T17:03:03.240 に答える
3

私もそれを最適化しましたが、すべてを1つのクラスにまとめました。暗号を静的にしたので、一度作成するだけで、keygenアルゴリズムをSHA1ではなくMD5で128ビットに変更しました。LicenseCheckerCallbackは、3秒待つのではなく、0.5秒以内に起動するようになりました。

public class AESObfuscator implements Obfuscator {

private static final String KEYGEN_ALGORITHM = "PBEWithMD5And128BitAES-CBC-OpenSSL";
// Omitted all other the other unchanged variables

private static Cipher mEncryptor = null;
private static Cipher mDecryptor = null;

public AESObfuscator(byte[] salt, String applicationId, String deviceId) {

    if (null == mEncryptor || null == mDecryptor) {
        try {
            // Anything in here was unchanged
        } catch (GeneralSecurityException e) {
            // This can't happen on a compatible Android device.
            throw new RuntimeException("Invalid environment", e);
        }
    }
}
于 2010-11-12T14:33:31.437 に答える
2

難読化ツールを書き直すよりも、別のスレッドで実行する方が理にかなっています。確かに、クラッカーはその間あなたのアプリを使うことができます、しかしそれで何ですか?3秒では、有用なことを行うのに十分な時間ではありませんが、正当なユーザーがライセンスの承認を待つのは非常に長い時間です。

于 2011-11-11T16:11:41.140 に答える
0

私は同じ問題に遭遇しました。

私がしたことは、ライセンスの初期化を、以下を使用して可能な限り低いスレッド優先度で非同期タスクに入れることでした。

android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);

メソッドで

doInBackground

ただし、ライセンスが無効なダイアログを表示する場合、これはGUIスレッドで実行されます。

したがって、私のライセンスチェックは次のようになります。

public class LicenseHandler {

    private LicenseHandlerTask task;

public LicenseHandler(final Activity context) {
    super();
    task = new LicenseHandlerTask(context);
    task.execute();
}
/**
 * This will run the task with the lowest thread priority because the
 * AESObfuscator is very slow and will have effect on the performance of the
 * app.<br>
 * 
 */
private static class LicenseHandlerDelay extends
        AsyncTask<Void, Void, ImplLicenseHandler> {
    private final Activity context;

    public LicenseHandlerDelay(final Activity context) {
        this.context = context;
    }

    @Override
    protected ImplLicenseHandler doInBackground(final Void... params) {
        // set the lowest priority available for this task
          android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);

        ImplLicenseHandler checker = new ImplLicenseHandler(context);
        return checker;
    }

    @Override
    protected void onPostExecute(final ImplLicenseHandler result) {
                    checker.check();
    }

}

/**
 * cancels the background task for checking the license if it is running
 */
public void destroy() {
    try {
        if (null != task) {
            task.cancel(true);
            task = null;
        }
    } catch (Throwable e) {
        // regardless of errors
    }
}
}

LicenseHandlerの実装は次のようになります

public class ImplLicenseHandler {

    ...

    private Context mContext = null;
    private AndroidPitLicenseChecker mChecker = null;
    private LicenseCheckerCallback mLicenseCheckerCallback = null;

    public ImplLicencseHandler(Context context){
            this.mContext = context;
            final ServerManagedPolicy googleLicensePolicy = new LicensePolicy(
            mContext, new AESObfuscator(ImplLicenseHandler.SALT,mContext.getPackageName(), ImplLicenseHandler.DEVICE_ID));
            mChecker = new AndroidPitLicenseChecker(mContext,
            mContext.getPackageName(),
            ImplLicenseHandler.ANDROIDPIT_PUBLIC_KEY, googleLicensePolicy,
            ImplLicenseHandler.GOOGLE_LICENSE_PUBLIC_KEY);
            mLicenseCheckerCallback = new LicenseCheckerCallback();
     }

     public void check(){
            mContext.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                         mChecker.checkAccess(mLicenseCheckerCallback);
                    }
            });
     }

     ...

}

ただし、LicenseCheckerCallbackにGUI要素が表示されている場合は、次を使用してそのメソッドを実行する必要があります。

context.runOnUIThread(action);
于 2014-03-09T16:32:52.080 に答える
-3

これでうまくいきます

public class AESObfuscator implements Obfuscator {

private static final String KEYGEN_ALGORITHM = "PBEWithMD5And128BitAES-CBC-OpenSSL";
// Omitted all other the other unchanged variables

private static Cipher mEncryptor = null;
private static Cipher mDecryptor = null;

public AESObfuscator(byte[] salt, String applicationId, String deviceId) {

    if (null == mEncryptor || null == mDecryptor) {
        try {
            // Anything in here was unchanged
        } catch (GeneralSecurityException e) {
            // This can't happen on a compatible Android device.
            throw new RuntimeException("Invalid environment", e);
        }
    }
}
于 2011-04-13T01:35:13.893 に答える