Androidプラグイン化の原理
5928 ワード
Activity起動プロセス:
1.startActivityの場合は最終的にAMSのstartActivityメソッドに進みます.
2.システムは、このActivityが合法かどうかを検証する情報の山をチェックします.
3.Activity ThreadのHandlerのhandleLaunchActivityにコールバックします.
4.ここでperformLaunchActivityメソッドを使用してActivityを作成し、一連のライフサイクルをコールバックする方法について説明します.
5.Activityを作成するとLoaderApkオブジェクトが作成され、そのオブジェクトのgetClassLoaderを使用してActivityが作成されます.
6.getClassLoader()メソッドを参照すると、PathClassLoaderが返され、BaseDexClassLoaderから継承されていることがわかります.
7.次に、BaseDexClassLoaderが作成時にDexPathListタイプのpathListオブジェクトを作成したことを確認します.
そしてfindClassでpathListを呼び出す.findClassの方法.
8.次にDexPathListクラスのfindClassを見てみると、彼の内部にはElement[]dexElementsのdex配列が維持されていることがわかります.
findClassの場合は配列から遍歴して検索されます.
プラグインの実装手順:
1.まず、DexClassloaderで独自のDexClassloaderオブジェクトを作成してプラグインapkをロードします.
2.ホストapkのClassLoaderのpathListオブジェクトとうちのClassloaderのpathListを取得します.最終ロード時にpathListが実行するためです.findClassメソッド.
3.次に、ホストpathListオブジェクトのElement[]と作成したClassloaderのElement[]を取得します.
4.dexファイルを追加するため、元の配列の長さが増加し、新しいElementタイプの配列を新規作成します.長さは新しいものと古いものです.
5.プラグインdexファイルとホストの元のdexファイルを新しい配列に入れてマージします.
6.我々の新しい配列をpathListオブジェクトに設定
7.エージェントシステムがActivityを起動する方法を起動し、起動するActivityを私たちのピットを占めるActivityに置き換えて、システムをだまして検査する目的を達成しました.
今、私たちはすでにこのamsのエージェントオブジェクトを手に入れました.今、私たちは自分のエージェントオブジェクトを作成して元のamsをブロックする方法が必要です.
次に、動的エージェントを使用して、上で取得したamsをエージェントします.
8.システムチェックが完了した後、再度エージェントブロックシステムがActivityを作成する方法で、元の私たちが交換したActivityを再び交換し、AndroidManifestに登録しない目的に達しました.
9.最後に私たちが前に書いたコードを呼び出すには、早ければ早いほどアプリケーションで呼び出すのもいいし、ActivityのattachBaseContextメソッドでもいいです.Demo:https://github.com/suyimin/Plugin
1.startActivityの場合は最終的にAMSのstartActivityメソッドに進みます.
2.システムは、このActivityが合法かどうかを検証する情報の山をチェックします.
3.Activity ThreadのHandlerのhandleLaunchActivityにコールバックします.
4.ここでperformLaunchActivityメソッドを使用してActivityを作成し、一連のライフサイクルをコールバックする方法について説明します.
5.Activityを作成するとLoaderApkオブジェクトが作成され、そのオブジェクトのgetClassLoaderを使用してActivityが作成されます.
6.getClassLoader()メソッドを参照すると、PathClassLoaderが返され、BaseDexClassLoaderから継承されていることがわかります.
7.次に、BaseDexClassLoaderが作成時にDexPathListタイプのpathListオブジェクトを作成したことを確認します.
そしてfindClassでpathListを呼び出す.findClassの方法.
8.次にDexPathListクラスのfindClassを見てみると、彼の内部にはElement[]dexElementsのdex配列が維持されていることがわかります.
findClassの場合は配列から遍歴して検索されます.
プラグインの実装手順:
1.まず、DexClassloaderで独自のDexClassloaderオブジェクトを作成してプラグインapkをロードします.
//dex
String cachePath = MainActivity.this.getCacheDir().getAbsolutePath();
// apk
String apkPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/chajian.apk";
// ClassLoader, DexClassLoader
DexClassLoader mClassLoader = new DexClassLoader(apkPath, cachePath,cachePath, getClassLoader());
2.ホストapkのClassLoaderのpathListオブジェクトとうちのClassloaderのpathListを取得します.最終ロード時にpathListが実行するためです.findClassメソッド.
// ClassLoader
PathClassLoader pathLoader = (PathClassLoader) MyApplication.getContext().getClassLoader();
// pathList
Object suZhuPathList = getPathList(pathLoader);
// pathList
Object chaJianPathList = getPathList(loader);
3.次に、ホストpathListオブジェクトのElement[]と作成したClassloaderのElement[]を取得します.
//
Object suzhuElements = getDexElements(suZhuPathList)
//
Object chajianElements = getDexElements(chaJianPathList)
4.dexファイルを追加するため、元の配列の長さが増加し、新しいElementタイプの配列を新規作成します.長さは新しいものと古いものです.
//
Class> localClass = suzhu.getClass().getComponentType();
//
int i = Array.getLength(suzhu);
//
int j = i + Array.getLength(chajian);
//
Object result = Array.newInstance(localClass, j);
5.プラグインdexファイルとホストの元のdexファイルを新しい配列に入れてマージします.
// dex
for (int k = 0; k < j; ++k) {
if (k < i) {
Array.set(result, k, Array.get(suzhu, k));
} else {
Array.set(result, k, Array.get(chajian, k - i));
}
}
6.我々の新しい配列をpathListオブジェクトに設定
setField(suZhuPathList, suZhuPathList.getClass(), "dexElements", result);
7.エージェントシステムがActivityを起動する方法を起動し、起動するActivityを私たちのピットを占めるActivityに置き換えて、システムをだまして検査する目的を達成しました.
// ActivityManagerNative
Class> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
// gDefault
Field gDefaultField = activityManagerNativeClass.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
// gDefault
Object gDefault = gDefaultField.get(null);
// gDefault android.util.Singleton ;
Class> singleton = Class.forName("android.util.Singleton");
// gDefault Singleton , Singleton AMS
Field mInstanceField = singleton.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//ams
Object rawIActivityManager = mInstanceField.get(gDefault);
今、私たちはすでにこのamsのエージェントオブジェクトを手に入れました.今、私たちは自分のエージェントオブジェクトを作成して元のamsをブロックする方法が必要です.
class IActivityManagerHandler implements InvocationHandler {
...
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
Log.e("Main","startActivity ");
// Intent
Intent raw;
int index = 0;
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
raw = (Intent) args[index];
// Intent
Intent newIntent = new Intent();
// Activity , " "
String stubPackage = MyApplication.getContext().getPackageName();
// Activity ZhanKengActivitiy
ComponentName componentName = new ComponentName(stubPackage, ZhanKengActivitiy.class.getName());
newIntent.setComponent(componentName);
// TargetActivity
newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT, raw);
// Intent, AMS
args[index] = newIntent;
Log.e("Main","startActivity hook ");
Log.e("Main","args[index] hook = " + args[index]);
return method.invoke(mBase, args);
}
return method.invoke(mBase, args);
}
}
次に、動的エージェントを使用して、上で取得したamsをエージェントします.
// , , , ,
// , ams IActivityManager
Class> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
// ,IActivityManagerHandler , demo
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class>[] { iActivityManagerInterface }, new IActivityManagerHandler(rawIActivityManager));
// singleton
mInstanceField.set(gDefault, proxy);
8.システムチェックが完了した後、再度エージェントブロックシステムがActivityを作成する方法で、元の私たちが交換したActivityを再び交換し、AndroidManifestに登録しない目的に達しました.
try {
//
Field intent = obj.getClass().getDeclaredField("intent");
intent.setAccessible(true);
Intent raw = (Intent) intent.get(obj);
Intent target = raw.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
raw.setComponent(target.getComponent());
Log.e("Main","target = " + target);
} catch (Exception e) {
throw new RuntimeException("hook launch activity failed", e);
}
9.最後に私たちが前に書いたコードを呼び出すには、早ければ早いほどアプリケーションで呼び出すのもいいし、ActivityのattachBaseContextメソッドでもいいです.Demo:https://github.com/suyimin/Plugin