APKアンチクラックの4:Androidコード動的ローディング技術

6489 ワード

転載は出典を明記してください.http://www.blogjava.net/zh-weir/arch...29/362294.html
Androidの動的ローディング技術
Androidアプリケーションの開発は、一般的には、従来の開発方式とコードアーキテクチャが私たちの一般的なニーズを満たすことができます.しかし、いくつかの特殊な問題があります.私たちはさらに深く考えさせられます.私たちは考え込んでいるうちに、新しい技術の形を生み出すことができました.どのように独自のコントロールが可能なAndroidアプリケーションを開発しますか?eclipseのように、プラグインを動的にロードすることができます.どのようにAndroidアプリケーションにサーバー上の予測できないコードを実行させますか?Androidに暗号化を適用するにはどうすればいいですか?実行時だけ自動解読して、解読を防ぐことができますか?Java技術に詳しい友達は、クラスのキャリアを柔軟にロードして実行する必要があると認識するかもしれません.これはJavaではすでに成熟した技術ですが、Androidではほとんどの人がまだ知らないです.クラスローディング機構Dalvik仮想マシンは他のJava仮想マシンのように、プログラムを実行するには、まず対応するクラスをメモリにロードする必要があります.Java標準の仮想マシンでは、クラスローディングは、クラスファイルから読み取ることができます.他の形式のバイナリストリームでもいいです.そのため、私たちはよくこの点を利用して、プログラムの実行時に手動でクラスをロードして、コードの動的なロード実行の目的を達成します.しかし、Dalvik仮想マシンは結局標準的なJava仮想マシンではないので、クラスローディング機構には同じところがあります.私たちは差別しなければならない.例えば、標準Java仮想マシンを使う時、私達は常にカスタムでClass Loaderからクラスのキャリアを引き継いでいます.その後、defineClass法により、1つのバイナリストリームからクラスをロードする.しかし、これはAndroidでは通用しません.皆さんは回り道をする必要がありません.ソースコードを参照してください.私たちはAndroidのClass LoaderのdefineClass方法が具体的にはVMClass LoaderのdefineClassローカル静的な方法を呼び出すことです.このローカル方法は、「UnisportedOperation Exception」を投げる以外は、何もしていません.戻り値さえも空です.
コード:
static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args, JValue* pResult)

{



    Object* loader = (Object*) args[0];

    StringObject* nameObj = (StringObject*) args[1];

    const u1* data = (const u1*) args[2];

    int offset = args[3];

    int len = args[4];



    Object* pd = (Object*) args[5];

    char* name = NULL;



    name = dvmCreateCstrFromString(nameObj);

    LOGE("ERROR: defineClass(%p, %s, %p, %d, %d, %p)
", loader, name, data, offset, len, pd); dvmThrowException("Ljava/lang/UnsupportedOperationException;", "can't load this type of class file"); free(name); RETURN_VOID(); }
Dalvik仮想マシンクラスのローディングメカニズムは、Dalvik仮想マシンでは、Class Loaderが有効ではない場合、私たちはどうやって動的ローディングクラスを実現しますか?Androidは私達のためにClass Loaderから派生した二つの種類です.DexClass LoaderとPathClass Loader.特に説明したいのは、PathClass Loaderの一部に注釈されたコードです.
コード:
/**//* --this doesn't work in current version of Dalvik--



    if (data != null) {



        System.out.println("--- Found class " + name + " in zip[" + i + "] '" + mZips[i].getName() + "'");



        int dotIndex = name.lastIndexOf('.');

        if (dotIndex != -1) {



            String packageName = name.substring(0, dotIndex);



            synchronized (this) {



                Package packageObj = getPackage(packageName);



                if (packageObj == null) {

                    definePackage(packageName, null, null, null, null, null, null, null);

                }

            }

        }



        return defineClass(name, data, 0, data.length);

    }

*/

これはもう一方で、defineClass関数がDalvik仮想マシンで去勢されたことを証明しました.この二つのクラスのキャリアは、Class Loaderから継承されており、本質的にはClass Loaderを搭載したfindClass方法である.loadClassを実行する時、Class Loaderの部分のソースコードを参照できます.
コード:
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {



    Class<?> clazz = findLoadedClass(className);



    if (clazz == null) {



        try {

            clazz = parent.loadClass(className, false);

        } catch (ClassNotFoundException e) {

            // Don't want to see this.

        }



        if (clazz == null) {

            clazz = findClass(className);

        }

    }



    return clazz;

}

したがって、DexClass LoaderとPathClass Loaderは、両親の委任モデルに合致するクラスのキャリアに属します.つまり、一つのクラスをロードする前に、自分と自分以上の種類のキャリアがすでにロードされているかどうかをチェックします.既に読み込まれている場合は、そのまま戻します.重複しないようにします.DexClass LoaderとPathClass Loaderは実はDexFileというクラスを通じてクラスローディングを実現しています.ちなみにここで必要なのは、Dalvik仮想マシンが識別するのは、classファイルではなく、dexファイルです.したがって、私たちがクラスにロードするファイルはdexファイルだけでなく、dexファイルが含まれているappkまたは.jarファイルもあります.DexFileが直接クラスをローディングできるなら、なぜクラスリーダーのサブクラスを使うべきですか?DexFileはクラスをロードする時、具体的にメンバーの方法のloadClassあるいはloadClass BinaryNameを呼び出します.このうち、loadClass BinaryNameは、パケット名を含むクラス名の中の”.”を”/”に変換する必要があります.私達はloadClassコードを見れば分かります.
コード:
public Class loadClass(String name, ClassLoader loader) {



        String slashName = name.replace('.', '/');

        return loadClassBinaryName(slashName, loader);

}

このコードの前にコメントがあります.要点の一部を切り取ると、If you are not carling this from a class loader、this is is most likely not going to do what you want.Use{@link Class(Stering)instead.これは私達がClassを使う必要がある理由です.どのようにしてClass Loaderでこの方法を呼び出したかを検証するかどうかについては、私は研究していません.興味があれば続けて深く入ることができます.細かいところがありますが、みんなが気づきにくいかもしれません.PathClass Loaderは構造関数new DexFileによってDexFileオブジェクトを生成する.DexClass Loaderは、その静的方法loadDex(path,outpath,0)によってDexFileオブジェクトを得る.この2つの違いは、DexClass Loaderが書き換え可能なoutpathパスを提供して、リリースするために使用する必要があることである.appパケットまたは.jarパケット中のdexファイル.言い換えれば、PathClass Loaderはzipパッケージからdexを自発的に解放できないので、直接Dexフォーマットファイルを操作したり、インストールされているappkだけをサポートします.DexClass Loaderは.appk、jar、および.dexファイルをサポートし、指定されたoutpathパスでdexファイルをリリースします.また、PathClass Loaderはクラスをロードする時にDexFileのloadClass BinaryNameを呼び出すが、DexClass LoaderはloadClassを呼び出す.したがって、PathClass Loaderを使用する場合はクラス全体名を「/」に置き換えます.実際にこの部分を操作するのは比較的に簡単で、だから私は余計なことを言いません.ただ簡単に言ってください.使用可能なツールはすべて通常のものです.javac、dx、eclipseなど.その中でdxツールは、classファイルの経路が一致しない可能性があるので、指定するのが一番いいです.クラスをロードした後、通常はJava反射機構によってこのクラスを使用することができます.しかし、このような効率は相対的に高くないです.また、いつも反射コードを使っています.より良い方法はインターフェースを定義し、このインターフェースを容器の端に書き込みます.ロードされるクラスは、このinterfaceから継承され、ClassのnewInstance法によってオブジェクトを生成することができるように、空の構造関数としてパラメータがあります.オブジェクトを強制的にinterfaceオブジェクトに変換することで、直接メンバーメソッドを呼び出すことができます.コードの暗号化については、最初にdexファイルを暗号化し、JNIを通じてNative層に復号コードを書き込むことを想定しています.復号した後、直接にバイナリストリームを送り、defineClassを通じてクラスをメモリにロードします.今もこのようにしてもいいですが、defineClassを直接使うことができないので、ファイルパスをdalvik仮想マシンのカーネルに伝えなければなりません.だから、復号後のファイルはディスクに書く必要があり、解読されるリスクが増えました.Dalvik仮想マシンのカーネルはdexファイルからクラスをロードするだけでは柔軟ではなく、非常に深いカーネルを研究していないので、Dalvik仮想マシン自体がサポートされていないかそれともAndroidかは確認できません.移植時に去勢されました.DalvikまたはAndroidオープンソースプロジェクトはいずれもrawデータ定義クラスをサポートできる方向に努力していると信じています.文書でGoogleを見ることができます.Jar or APK file with「classis.dex」.AndroidのDalvikソースからもRawDexFileの姿が見られます.RawDexFileが出る前に、私たちはこのような危険がある暗号化方式しか使えません.リリースされたdexファイルのパスと権限管理に注意してください.また、クラスをロードした後、他の目的でない限り、一時的な復号ファイルはすぐに削除されます.転載は出典を明記してください.http://www.blogjava.net/zh-weir/archive/2011/10/29/362294.html