Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android加殼原理分析

Android加殼原理分析

編輯:關於Android編程

0x00

閱讀本文前,建議讀者首先閱讀Android加殼原理,參考文章Android中的Apk的加固(加殼)原理解析和實現。如果沒有看過這篇文章,本文理解起來比較困難。

0x01

下面我們來分析脫殼代碼為什麼要這樣寫,核心脫殼代碼在ProxyApplication類裡面,首先執行成員方法attachBaseContext,然後執行成員方法onCreate。

那麼attachBaseContext是什麼時候被執行的呢,為什麼先於onCreate執行呢?那就需要看Android的源碼了,我們選用的是Android2.3源碼。

我們首先看一張圖,這張圖表述了從桌面啟動一個應用Activity的啟動過程。

\

圖 1

其中當執行到ApplicationThread.bindApplication時,會向ActivityThreadl類的Handler對象mH發送消息。

 

        public final void bindApplication(String processName,
                ApplicationInfo appInfo, List providers,
                ComponentName instrumentationName, String profileFile,
                Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
                int debugMode, boolean isRestrictedBackupMode, Configuration config,
                Map services) {

            if (services != null) {
                // Setup the service cache in the ServiceManager
                ServiceManager.initServiceCache(services);
            }

            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.profileFile = profileFile;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.debugMode = debugMode;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.config = config;
            queueOrSendMessage(H.BIND_APPLICATION, data);
        }
代碼位於frameworks\base\core\java\android\app\ActivityThread.java。

 

 

queueOrSendMessage向ActivityThreadl類的Handler對象mH發送消息。

 

private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {
        synchronized (this) {
            if (DEBUG_MESSAGES) Slog.v(
                TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
                + ": " + arg1 + " / " + obj);
            Message msg = Message.obtain();
            msg.what = what;
            msg.obj = obj;
            msg.arg1 = arg1;
            msg.arg2 = arg2;
            mH.sendMessage(msg);
        }
    }
代碼位於frameworks\base\core\java\android\app\ActivityThread.java。

 

 

handler處理BIND_APPLICATION的流程如下。

 private final class H extends Handler {
		......
        }
        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);
            switch (msg.what) {
			......
                case BIND_APPLICATION:
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    break;
			......
        }

    }
代碼位於frameworks\base\core\java\android\app\ActivityThread.java。

 

 

繼續看handleBindApplication,其中data就是ApplicationThread.bindApplication生成的AppBindData對象。

 

    private final void handleBindApplication(AppBindData data) {
	mBoundApplication = data;
	......
        data.info = getPackageInfoNoCheck(data.appInfo);
	......
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;
	......

        try {
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                    "Unable to create application " + app.getClass().getName()
                    + ": " + e.toString(), e);
            }
        }
    }
代碼位於frameworks\base\core\java\android\app\ActivityThread.java。

 

首先把data賦值給了AppBindData對象mBoundApplication,然後通過getPackageInfoNoCheck得到的LoadedApk對象復制給data.info,之後調用data.info.makeApplication生成Application對象,我們下面來分析下data.info.makeApplication這個方法。

 

 

    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            ContextImpl appContext = new ContextImpl();
            appContext.init(this, null, mActivityThread);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

		......
        
        return app;
    }
代碼位於frameworks\base\core\java\android\app\LoadedApk.java。

 

首先通過mApplicationInfo.className獲取application的名字,在本例中是ProxyApplication。然後通過getClassLoader獲取了ClassLoader對象,那麼我先來分析下getClassLoader的實現。

 

    public ClassLoader getClassLoader() {
        synchronized (this) {
            if (mClassLoader != null) {
                return mClassLoader;
            }
		......
    }
我們姑且認為ClassLoader對象mClassLoader不為空,返回LoadedApk對象的成員變量mClassLoader。

返回到makeApplication,繼續看,首先生成了ContextImpl對象,最終調用了mActivityThread.mInstrumentation.newApplication來生成Application對象,並把生成的Context對象放到這個Application對象中。這部分可參考博客Android中Context詳解 ---- 你所不知道的Context。再附一張Context類圖,幫大家理解。

\

那麼我們講了這麼多,到底是什麼時候執行的ProxyApplication類的方法attachBaseContext的呢?答案就在mActivityThread.mInstrumentation.newApplication,我們繼續分析此方法。

 

    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
    }
代碼位於frameworks\base\core\java\android\app\Instrumentation.java。

 

其中context對象就是我們剛剛生成的,繼續分析newApplication方法。

 

    static public Application newApplication(Class clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }
代碼位於frameworks\base\core\java\android\app\Instrumentation.java。
這個函數首先生成了一個Application對象app,然後調用了他的方法attach,我們來分析這個方法。

 

 

final void attach(Context context) {
        attachBaseContext(context);
    }
代碼位於frameworks\base\core\java\android\app\Application.java

 

答案在此揭曉,此時調用了ProxyApplication類的方法attachBaseContext,注意此時還沒有調用ProxyApplication類的方法onCreate。

 

0x02

知道了執行到ProxyApplication類的方法attachBaseContext之前的流程,我們接下來重點分析下這個方法。

	protected void attachBaseContext(Context base) {
		super.attachBaseContext(base);
		try {
			......
			// 配置動態加載環境
			Object currentActivityThread = RefInvoke.invokeStaticMethod(
					"android.app.ActivityThread", "currentActivityThread",
					new Class[] {}, new Object[] {});//獲取主線程對象 http://blog.csdn.net/myarrow/article/details/14223493
			String packageName = this.getPackageName();//當前apk的包名
			//下面兩句不是太理解
			HashMap mPackages = (HashMap) RefInvoke.getFieldOjbect(
					"android.app.ActivityThread", currentActivityThread,
					"mPackages");
			WeakReference wr = (WeakReference) mPackages.get(packageName);
			//創建被加殼apk的DexClassLoader對象  加載apk內的類和本地代碼(c/c++代碼)
			DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,
					libPath, (ClassLoader) RefInvoke.getFieldOjbect(
							"android.app.LoadedApk", wr.get(), "mClassLoader"));
			//base.getClassLoader(); 是不是就等同於 (ClassLoader) RefInvoke.getFieldOjbect()? 有空驗證下//?
			//把當前進程的DexClassLoader 設置成了被加殼apk的DexClassLoader  ----有點c++中進程環境的意思~~
			RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader",
					wr.get(), dLoader);
			
			Log.i("demo","classloader:"+dLoader);
			
			......
			

		} catch (Exception e) {
			Log.i("demo", "error:"+Log.getStackTraceString(e));
			e.printStackTrace();
		}
	}
代碼位於Android中的Apk的加固(加殼)原理解析和實現。

省略部分的代碼還請大家參考Android中的Apk的加固(加殼)原理解析和實現。

首先通過反射調用了ActivityThread類的currentActivityThread方法,該方法是靜態的,返回當前的ActivityThread,代碼如下:

 

    public static final ActivityThread currentActivityThread() {
        return sThreadLocal.get();
    }
代碼位於frameworks\base\core\java\android\app\ActivityThread.java。

 

然後再獲取ActivityThread的成員變量mPackages,mPackages也位於frameworks\base\core\java\android\app\ActivityThread.java中:

 

    final HashMap> mPackages
            = new HashMap>();
他是一個HashMap,鍵是包名,值是LoadedApk的軟引用。然後通過當前的包名在HashMap中獲取對應LoadedApk的軟引用。

 

然後根據要加載的apk,也就是實際要執行的apk,生成DexClassLoader對象,其中parentClassLoader就是剛剛獲取的LoadedApk對象中的mClassLoader變量。

大家可能會有個疑問,這裡獲取的LoadedApk對象和data.info對象是一樣的麼?答案是一樣的,代碼的關鍵在handleBindApplication中getPackageInfoNoCheck,代碼如下:

    private final LoadedApk getPackageInfo(ApplicationInfo aInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
        synchronized (mPackages) {
            WeakReference ref;
            if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }
            LoadedApk packageInfo = ref != null ? ref.get() : null;
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                        : "Loading resource-only package ") + aInfo.packageName
                        + " (in " + (mBoundApplication != null
                                ? mBoundApplication.processName : null)
                        + ")");
                packageInfo =
                    new LoadedApk(this, aInfo, this, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
                if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference(packageInfo));
                }
            }
            return packageInfo;
        }
    }
代碼位於frameworks\base\core\java\android\app\ActivityThread.java。

 

這裡生成了一個LoadedApk對象,並以當前包名為鍵,LoadedApk對象為值存入了mPackages這個HashMap中,並且返回LoadedApk對象,並賦值給data.info。

data.info = getPackageInfoNoCheck(data.appInfo);

 

回到attachBaseContext中,最後把這個新生成DexClassLoader對象賦值給LoadedApk對象的mClassLoader變量,也就是更新了這個mClassLoader變量。

 

0x03

執行完mActivityThread.mInstrumentation.newApplication,返回到makeApplication,繼續執行下面兩句代碼:

 

 mActivityThread.mAllApplications.add(app);
 mApplication = app;
執行完data.info.makeApplication,我們返回到handleBindApplication(代碼請參考上面),繼續執行下面一句代碼:
mInitialApplication = app;
這三行代碼對於理解ProxyApplication類的onCreate方法有幫助,此時application是ProxyApplication,我們要把它替換為我們自己的application,本例中為MyApplication。

0x04

執行完data.info.makeApplication,我們返回到handleBindApplication(代碼請參考上面),繼續執行mInstrumentation.callApplicationOnCreate(app),此時ProxyApplication類的onCreate方法開始執行。

 

	@Override
	public void onCreate() {
		{
			//loadResources(apkFileName);
			
			Log.i("demo", "onCreate");
			// 如果源應用配置有Appliction對象,則替換為源應用Applicaiton,以便不影響源程序邏輯。
			String appClassName = null;
			try {
				ApplicationInfo ai = this.getPackageManager()
						.getApplicationInfo(this.getPackageName(),
								PackageManager.GET_META_DATA);
				Bundle bundle = ai.metaData;
				if (bundle != null && bundle.containsKey("APPLICATION_CLASS_NAME")) {
					appClassName = bundle.getString("APPLICATION_CLASS_NAME");//className 是配置在xml文件中的。
				} else {
					Log.i("demo", "have no application class name");
					return;
				}
			} catch (NameNotFoundException e) {
				Log.i("demo", "error:"+Log.getStackTraceString(e));
				e.printStackTrace();
			}
			//有值的話調用該Applicaiton
			Object currentActivityThread = RefInvoke.invokeStaticMethod(
					"android.app.ActivityThread", "currentActivityThread",
					new Class[] {}, new Object[] {});
			Object mBoundApplication = RefInvoke.getFieldOjbect(
					"android.app.ActivityThread", currentActivityThread,
					"mBoundApplication");
			Object loadedApkInfo = RefInvoke.getFieldOjbect(
					"android.app.ActivityThread$AppBindData",
					mBoundApplication, "info");
			//把當前進程的mApplication 設置成了null
			RefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication",
					loadedApkInfo, null);
			Object oldApplication = RefInvoke.getFieldOjbect(
					"android.app.ActivityThread", currentActivityThread,
					"mInitialApplication");
			//http://www.codeceo.com/article/android-context.html
			ArrayList mAllApplications = (ArrayList) RefInvoke
					.getFieldOjbect("android.app.ActivityThread",
							currentActivityThread, "mAllApplications");
			mAllApplications.remove(oldApplication);//刪除oldApplication
			
			ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke
					.getFieldOjbect("android.app.LoadedApk", loadedApkInfo,
							"mApplicationInfo");
			ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke
					.getFieldOjbect("android.app.ActivityThread$AppBindData",
							mBoundApplication, "appInfo");
			appinfo_In_LoadedApk.className = appClassName;
			appinfo_In_AppBindData.className = appClassName;
			Application app = (Application) RefInvoke.invokeMethod(
					"android.app.LoadedApk", "makeApplication", loadedApkInfo,
					new Class[] { boolean.class, Instrumentation.class },
					new Object[] { false, null });//執行 makeApplication(false,null)
			RefInvoke.setFieldOjbect("android.app.ActivityThread",
					"mInitialApplication", currentActivityThread, app);


			......
			
			app.onCreate();
		}
	}
代碼位於Android中的Apk的加固(加殼)原理解析和實現。

首先appClassName為MyApplication,然後依然是通過反射調用了ActivityThread類的currentActivityThread方法,該方法是靜態的,返回當前的ActivityThread對象。

再通過反射獲取當前ActivityThread對象的mBoundApplication變量,這個mBoundApplication對象還記得麼?是在handleBindApplication被賦值的。

 

private final void handleBindApplication(AppBindData data) {
	mBoundApplication = data;
	......
}
然後再獲取mBoundApplication對象裡面的info,這個info實際上就是data.info,是LoadedApk對象。
data.info = getPackageInfoNoCheck(data.appInfo);
	......
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
繼續執行,把LoadedApk對象中的mApplication變量設置為null,為什麼要這麼做呢?我們稍後解釋。

 

繼續執行到如下函數:

 

Object oldApplication = RefInvoke.getFieldOjbect(
					"android.app.ActivityThread", currentActivityThread,
					"mInitialApplication");
			//http://www.codeceo.com/article/android-context.html
			ArrayList mAllApplications = (ArrayList) RefInvoke
					.getFieldOjbect("android.app.ActivityThread",
							currentActivityThread, "mAllApplications");
			mAllApplications.remove(oldApplication);//刪除oldApplication
這幾句函數實際上就對應0x03中函數,從原來ActivityThread對象中移除了原有的application對象。

 

 

回到onCreate中繼續看,我們先過掉幾行代碼,直接看makeApplication生成新的application對象。在解釋這個對象的生成過程中,我們會講解在生成此對象前的一些操作的意義。

既然要重新生成,那麼我們首先看一下makeApplication的實現:

 

    public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            ContextImpl appContext = new ContextImpl();
            appContext.init(this, null, mActivityThread);
            app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            if (!mActivityThread.mInstrumentation.onException(app, e)) {
                throw new RuntimeException(
                    "Unable to instantiate application " + appClass
                    + ": " + e.toString(), e);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

		......
        
        return app;
    }
首先mApplication對象需為null,這就是為什麼剛剛把LoadedApk對象中的mApplication變量設置為null的原因。

 

然後需要獲取ApplicationInfo對象mApplicationInfo的成員變量className,因為現在我要啟動是被加殼的apk中MyApplication,所以我們要把名字設置為MyApplication。這就是下面幾行代碼的作用。

ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke
					.getFieldOjbect("android.app.LoadedApk", loadedApkInfo,
							"mApplicationInfo");
			ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke
					.getFieldOjbect("android.app.ActivityThread$AppBindData",
							mBoundApplication, "appInfo");
			appinfo_In_LoadedApk.className = appClassName;
			appinfo_In_AppBindData.className = appClassName;
代碼位於ProxyApplication類的onCreate方法中。

最後返回到onCreate方法中,把新生成的application對象賦值給ActivityThread對象的mInitialApplication變量。

 

對了,還有一點忘記說了,在makeApplication時,getClassLoader獲得的ClassLoader對象,已經被替換為DexClassLoader對象,這個對象加載的是被加殼的apk。

 

0x05

那麼MyApplication類什麼時候執行onCreate呢?答案在ProxyApplication類的onCreate方法最後,會調用app.onCreate()。

 

0x06

那麼什麼時候開啟MainActivtiy呢?怎麼樣開啟的呢?

我們再一次看圖1,從桌面啟動一個應用Activity的啟動過程,怎麼開啟的MainActivity呢?

 

 private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
        Activity a = performLaunchActivity(r, customIntent);
		......
}
private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            r.intent.setExtrasClassLoader(cl);
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            ......
                mInstrumentation.callActivityOnCreate(activity, r.state);
		} catch {}
                
}

代碼位於frameworks\base\core\java\android\app\ActivityThread.java。

此時獲取的Classloader對象已經被替換為DexClassLoader對象,這個對象加載的是被加殼的apk。

但是此時獲取的activity信息為什麼是MainActivity呢?答案在AndroidManifest.xml裡面。

 

        <activity android:name="com.example.forceapkobj.MainActivity" android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN">
                <category android:name="android.intent.category.LAUNCHER">
            </category></action></intent-filter>
        </activity>
        
        <activity android:name="com.example.forceapkobj.SubActivity"></activity>
MainActivity啟動起來後,被加殼的apk就可以正常工作了。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved