[转]Android Apk加壳原理分析

[置顶] Android Apk加壳原理分析

4168人阅读 评论(3) 收藏 举报
本文章已收录于:
分类:

    0x00

    阅读本文前,建议读者首先阅读Android加壳原理,参考文章Android中的Apk的加固(加壳)原理解析和实现。如果没有看过这篇文章,本文理解起来比较困难。

    0x01

    下面我们来分析脱壳代码为什么要这样写,核心脱壳代码在ProxyApplication类里面,首先执行成员方法attachBaseContext,然后执行成员方法onCreate。

    那么attachBaseContext是什么时候被执行的呢,为什么先于onCreate执行呢?那就需要看android的源码了,我们选用的是Android2.3源码。

    我们首先看一张图,这张图表述了从桌面启动一个应用Activity的启动过程。

   

                       图  1

    其中当执行到ApplicationThread.bindApplication时,会向ActivityThreadl类的Handler对象mH发送消息。

 

  1. public final void bindApplication(String processName,  
  2.         ApplicationInfo appInfo, List<ProviderInfo> providers,  
  3.         ComponentName instrumentationName, String profileFile,  
  4.         Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,  
  5.         int debugMode, boolean isRestrictedBackupMode, Configuration config,  
  6.         Map<String, IBinder> services) {  
  7.   
  8.     if (services != null) {  
  9.         // Setup the service cache in the ServiceManager  
  10.         ServiceManager.initServiceCache(services);  
  11.     }  
  12.   
  13.     AppBindData data = new AppBindData();  
  14.     data.processName = processName;  
  15.     data.appInfo = appInfo;  
  16.     data.providers = providers;  
  17.     data.instrumentationName = instrumentationName;  
  18.     data.profileFile = profileFile;  
  19.     data.instrumentationArgs = instrumentationArgs;  
  20.     data.instrumentationWatcher = instrumentationWatcher;  
  21.     data.debugMode = debugMode;  
  22.     data.restrictedBackupMode = isRestrictedBackupMode;  
  23.     data.config = config;  
  24.     queueOrSendMessage(H.BIND_APPLICATION, data);  
  25. }  
        public final void bindApplication(String processName,
                ApplicationInfo appInfo, List<ProviderInfo> providers,
                ComponentName instrumentationName, String profileFile,
                Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
                int debugMode, boolean isRestrictedBackupMode, Configuration config,
                Map<String, IBinder> 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发送消息。

 

  1. private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) {  
  2.         synchronized (this) {  
  3.             if (DEBUG_MESSAGES) Slog.v(  
  4.                 TAG, "SCHEDULE " + what + " " + mH.codeToString(what)  
  5.                 + ": " + arg1 + " / " + obj);  
  6.             Message msg = Message.obtain();  
  7.             msg.what = what;  
  8.             msg.obj = obj;  
  9.             msg.arg1 = arg1;  
  10.             msg.arg2 = arg2;  
  11.             mH.sendMessage(msg);  
  12.         }  
  13.     }  
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的流程如下。

  1. private final class H extends Handler {  
  2.     ......  
  3.        }  
  4.        public void handleMessage(Message msg) {  
  5.            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what);  
  6.            switch (msg.what) {  
  7.         ......  
  8.                case BIND_APPLICATION:  
  9.                    AppBindData data = (AppBindData)msg.obj;  
  10.                    handleBindApplication(data);  
  11.                    break;  
  12.         ......  
  13.        }  
  14.   
  15.    }  
 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对象。

 

  1.    private final void handleBindApplication(AppBindData data) {  
  2. mBoundApplication = data;  
  3. ......  
  4.        data.info = getPackageInfoNoCheck(data.appInfo);  
  5. ......  
  6.        Application app = data.info.makeApplication(data.restrictedBackupMode, null);  
  7.        mInitialApplication = app;  
  8. ......  
  9.   
  10.        try {  
  11.            mInstrumentation.callApplicationOnCreate(app);  
  12.        } catch (Exception e) {  
  13.            if (!mInstrumentation.onException(app, e)) {  
  14.                throw new RuntimeException(  
  15.                    "Unable to create application " + app.getClass().getName()  
  16.                    + ": " + e.toString(), e);  
  17.            }  
  18.        }  
  19.    }  
    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这个方法。

 

 

  1.   public Application makeApplication(boolean forceDefaultAppClass,  
  2.           Instrumentation instrumentation) {  
  3.       if (mApplication != null) {  
  4.           return mApplication;  
  5.       }  
  6.   
  7.       Application app = null;  
  8.   
  9.       String appClass = mApplicationInfo.className;  
  10.       if (forceDefaultAppClass || (appClass == null)) {  
  11.           appClass = "android.app.Application";  
  12.       }  
  13.   
  14.       try {  
  15.           java.lang.ClassLoader cl = getClassLoader();  
  16.           ContextImpl appContext = new ContextImpl();  
  17.           appContext.init(thisnull, mActivityThread);  
  18.           app = mActivityThread.mInstrumentation.newApplication(  
  19.                   cl, appClass, appContext);  
  20.           appContext.setOuterContext(app);  
  21.       } catch (Exception e) {  
  22.           if (!mActivityThread.mInstrumentation.onException(app, e)) {  
  23.               throw new RuntimeException(  
  24.                   "Unable to instantiate application " + appClass  
  25.                   + ": " + e.toString(), e);  
  26.           }  
  27.       }  
  28.       mActivityThread.mAllApplications.add(app);  
  29.       mApplication = app;  
  30.   
  31. ......  
  32.         
  33.       return app;  
  34.   }  
    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的实现。

 

  1. public ClassLoader getClassLoader() {  
  2.     synchronized (this) {  
  3.         if (mClassLoader != null) {  
  4.             return mClassLoader;  
  5.         }  
  6. ....  
  7. }  
    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,我们继续分析此方法。

 

  1. public Application newApplication(ClassLoader cl, String className, Context context)  
  2.         throws InstantiationException, IllegalAccessException,   
  3.         ClassNotFoundException {  
  4.     return newApplication(cl.loadClass(className), context);  
  5. }  
    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方法。

 

  1. static public Application newApplication(Class<?> clazz, Context context)  
  2.         throws InstantiationException, IllegalAccessException,   
  3.         ClassNotFoundException {  
  4.     Application app = (Application)clazz.newInstance();  
  5.     app.attach(context);  
  6.     return app;  
  7. }  
    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,我们来分析这个方法。

 

 

  1. final void attach(Context context) {  
  2.         attachBaseContext(context);  
  3.     }  
final void attach(Context context) {
        attachBaseContext(context);
    }
    代码位于frameworks\base\core\java\android\app\Application.java

 

    答案在此揭晓,此时调用了ProxyApplication类的方法attachBaseContext,注意此时还没有调用ProxyApplication类的方法onCreate。

 

    0x02

    知道了执行到ProxyApplication类的方法attachBaseContext之前的流程,我们接下来重点分析下这个方法。

  1. protected void attachBaseContext(Context base) {  
  2.     super.attachBaseContext(base);  
  3.     try {  
  4.         ......  
  5.         // 配置动态加载环境  
  6.         Object currentActivityThread = RefInvoke.invokeStaticMethod(  
  7.                 "android.app.ActivityThread""currentActivityThread",  
  8.                 new Class[] {}, new Object[] {});//获取主线程对象 http://blog.csdn.net/myarrow/article/details/14223493  
  9.         String packageName = this.getPackageName();//当前apk的包名  
  10.         //下面两句不是太理解  
  11.         HashMap mPackages = (HashMap) RefInvoke.getFieldOjbect(  
  12.                 "android.app.ActivityThread", currentActivityThread,  
  13.                 "mPackages");  
  14.         WeakReference wr = (WeakReference) mPackages.get(packageName);  
  15.         //创建被加壳apk的DexClassLoader对象  加载apk内的类和本地代码(c/c++代码)  
  16.         DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,  
  17.                 libPath, (ClassLoader) RefInvoke.getFieldOjbect(  
  18.                         "android.app.LoadedApk", wr.get(), "mClassLoader"));  
  19.         //base.getClassLoader(); 是不是就等同于 (ClassLoader) RefInvoke.getFieldOjbect()? 有空验证下//?  
  20.         //把当前进程的DexClassLoader 设置成了被加壳apk的DexClassLoader  ----有点c++中进程环境的意思~~  
  21.         RefInvoke.setFieldOjbect("android.app.LoadedApk""mClassLoader",  
  22.                 wr.get(), dLoader);  
  23.           
  24.         Log.i("demo","classloader:"+dLoader);  
  25.           
  26.         ......  
  27.           
  28.   
  29.     } catch (Exception e) {  
  30.         Log.i("demo""error:"+Log.getStackTraceString(e));  
  31.         e.printStackTrace();  
  32.     }  
  33. }  
	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,代码如下:

 

  1. public static final ActivityThread currentActivityThread() {  
  2.     return sThreadLocal.get();  
  3. }  
    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中:

 

  1. final HashMap<String, WeakReference<LoadedApk>> mPackages  
  2.         = new HashMap<String, WeakReference<LoadedApk>>();  
    final HashMap<String, WeakReference<LoadedApk>> mPackages
            = new HashMap<String, WeakReference<LoadedApk>>();
    他是一个HashMap,键是包名,值是LoadedApk的软引用。然后通过当前的包名在HashMap中获取对应LoadedApk的软引用。

 

然后根据要加载的apk,也就是实际要执行的apk,生成DexClassLoader对象,其中parentClassLoader就是刚刚获取的LoadedApk对象中的mClassLoader变量。

大家可能会有个疑问,这里获取的LoadedApk对象和data.info对象是一样的么?答案是一样的,代码的关键在handleBindApplication中getPackageInfoNoCheck,代码如下:

  1. private final LoadedApk getPackageInfo(ApplicationInfo aInfo,  
  2.         ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {  
  3.     synchronized (mPackages) {  
  4.         WeakReference<LoadedApk> ref;  
  5.         if (includeCode) {  
  6.             ref = mPackages.get(aInfo.packageName);  
  7.         } else {  
  8.             ref = mResourcePackages.get(aInfo.packageName);  
  9.         }  
  10.         LoadedApk packageInfo = ref != null ? ref.get() : null;  
  11.         if (packageInfo == null || (packageInfo.mResources != null  
  12.                 && !packageInfo.mResources.getAssets().isUpToDate())) {  
  13.             if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "  
  14.                     : "Loading resource-only package ") + aInfo.packageName  
  15.                     + " (in " + (mBoundApplication != null  
  16.                             ? mBoundApplication.processName : null)  
  17.                     + ")");  
  18.             packageInfo =  
  19.                 new LoadedApk(this, aInfo, this, baseLoader,  
  20.                         securityViolation, includeCode &&  
  21.                         (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);  
  22.             if (includeCode) {  
  23.                 mPackages.put(aInfo.packageName,  
  24.                         new WeakReference<LoadedApk>(packageInfo));  
  25.             } else {  
  26.                 mResourcePackages.put(aInfo.packageName,  
  27.                         new WeakReference<LoadedApk>(packageInfo));  
  28.             }  
  29.         }  
  30.         return packageInfo;  
  31.     }  
  32. }  
    private final LoadedApk getPackageInfo(ApplicationInfo aInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
        synchronized (mPackages) {
            WeakReference<LoadedApk> 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<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }
    代码位于frameworks\base\core\java\android\app\ActivityThread.java。  

 

这里生成了一个LoadedApk对象,并以当前包名为键,LoadedApk对象为值存入了mPackages这个HashMap中,并且返回LoadedApk对象,并赋值给data.info。

  1. data.info = getPackageInfoNoCheck(data.appInfo);  
data.info = getPackageInfoNoCheck(data.appInfo);

 

回到attachBaseContext中,最后把这个新生成DexClassLoader对象赋值给LoadedApk对象的mClassLoader变量,也就是更新了这个mClassLoader变量。

 

0x03

执行完mActivityThread.mInstrumentation.newApplication,返回到makeApplication,继续执行下面两句代码:

 

  1. mActivityThread.mAllApplications.add(app);  
  2. mApplication = app;  
 mActivityThread.mAllApplications.add(app);
 mApplication = app;
执行完data.info.makeApplication,我们返回到handleBindApplication(代码请参考上面),继续执行下面一句代码:

 

 

  1. mInitialApplication = app;  
mInitialApplication = app;
    这三行代码对于理解ProxyApplication类的onCreate方法有帮助,此时application是ProxyApplication,我们要把它替换为我们自己的application,本例中为MyApplication。

 

 

0x04

执行完data.info.makeApplication,我们返回到handleBindApplication(代码请参考上面),继续执行mInstrumentation.callApplicationOnCreate(app),此时ProxyApplication类的onCreate方法开始执行。

 

  1. @Override  
  2. public void onCreate() {  
  3.     {  
  4.         //loadResources(apkFileName);  
  5.           
  6.         Log.i("demo""onCreate");  
  7.         // 如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。  
  8.         String appClassName = null;  
  9.         try {  
  10.             ApplicationInfo ai = this.getPackageManager()  
  11.                     .getApplicationInfo(this.getPackageName(),  
  12.                             PackageManager.GET_META_DATA);  
  13.             Bundle bundle = ai.metaData;  
  14.             if (bundle != null && bundle.containsKey("APPLICATION_CLASS_NAME")) {  
  15.                 appClassName = bundle.getString("APPLICATION_CLASS_NAME");//className 是配置在xml文件中的。  
  16.             } else {  
  17.                 Log.i("demo""have no application class name");  
  18.                 return;  
  19.             }  
  20.         } catch (NameNotFoundException e) {  
  21.             Log.i("demo""error:"+Log.getStackTraceString(e));  
  22.             e.printStackTrace();  
  23.         }  
  24.         //有值的话调用该Applicaiton  
  25.         Object currentActivityThread = RefInvoke.invokeStaticMethod(  
  26.                 "android.app.ActivityThread""currentActivityThread",  
  27.                 new Class[] {}, new Object[] {});  
  28.         Object mBoundApplication = RefInvoke.getFieldOjbect(  
  29.                 "android.app.ActivityThread", currentActivityThread,  
  30.                 "mBoundApplication");  
  31.         Object loadedApkInfo = RefInvoke.getFieldOjbect(  
  32.                 "android.app.ActivityThread$AppBindData",  
  33.                 mBoundApplication, "info");  
  34.         //把当前进程的mApplication 设置成了null  
  35.         RefInvoke.setFieldOjbect("android.app.LoadedApk""mApplication",  
  36.                 loadedApkInfo, null);  
  37.         Object oldApplication = RefInvoke.getFieldOjbect(  
  38.                 "android.app.ActivityThread", currentActivityThread,  
  39.                 "mInitialApplication");  
  40.         //http://www.codeceo.com/article/android-context.html  
  41.         ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke  
  42.                 .getFieldOjbect("android.app.ActivityThread",  
  43.                         currentActivityThread, "mAllApplications");  
  44.         mAllApplications.remove(oldApplication);//删除oldApplication  
  45.           
  46.         ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke  
  47.                 .getFieldOjbect("android.app.LoadedApk", loadedApkInfo,  
  48.                         "mApplicationInfo");  
  49.         ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke  
  50.                 .getFieldOjbect("android.app.ActivityThread$AppBindData",  
  51.                         mBoundApplication, "appInfo");  
  52.         appinfo_In_LoadedApk.className = appClassName;  
  53.         appinfo_In_AppBindData.className = appClassName;  
  54.         Application app = (Application) RefInvoke.invokeMethod(  
  55.                 "android.app.LoadedApk""makeApplication", loadedApkInfo,  
  56.                 new Class[] { boolean.class, Instrumentation.class },  
  57.                 new Object[] { falsenull });//执行 makeApplication(false,null)  
  58.         RefInvoke.setFieldOjbect("android.app.ActivityThread",  
  59.                 "mInitialApplication", currentActivityThread, app);  
  60.   
  61.   
  62.         ......  
  63.           
  64.         app.onCreate();  
  65.     }  
  66. }  
	@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<Application> mAllApplications = (ArrayList<Application>) 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被赋值的。

 

  1. private final void handleBindApplication(AppBindData data) {  
  2.     mBoundApplication = data;  
  3.     ......  
  4. }  
private final void handleBindApplication(AppBindData data) {
	mBoundApplication = data;
	......
}
    然后再获取mBoundApplication对象里面的info,这个info实际上就是data.info,是LoadedApk对象。
  1. data.info = getPackageInfoNoCheck(data.appInfo);  
  2.     ......  
  3.         Application app = data.info.makeApplication(data.restrictedBackupMode, null);  
data.info = getPackageInfoNoCheck(data.appInfo);
	......
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    继续执行,把LoadedApk对象中的mApplication变量设置为null,为什么要这么做呢?我们稍后解释。

 

继续执行到如下函数:

 

  1. Object oldApplication = RefInvoke.getFieldOjbect(  
  2.                     "android.app.ActivityThread", currentActivityThread,  
  3.                     "mInitialApplication");  
  4.             //http://www.codeceo.com/article/android-context.html  
  5.             ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke  
  6.                     .getFieldOjbect("android.app.ActivityThread",  
  7.                             currentActivityThread, "mAllApplications");  
  8.             mAllApplications.remove(oldApplication);//删除oldApplication  
Object oldApplication = RefInvoke.getFieldOjbect(
					"android.app.ActivityThread", currentActivityThread,
					"mInitialApplication");
			//http://www.codeceo.com/article/android-context.html
			ArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke
					.getFieldOjbect("android.app.ActivityThread",
							currentActivityThread, "mAllApplications");
			mAllApplications.remove(oldApplication);//删除oldApplication
    这几句函数实际上就对应0x03中函数,从原来ActivityThread对象中移除了原有的application对象。

 

 

回到onCreate中继续看,我们先过掉几行代码,直接看makeApplication生成新的application对象。在解释这个对象的生成过程中,我们会讲解在生成此对象前的一些操作的意义。

既然要重新生成,那么我们首先看一下makeApplication的实现:

 

  1.   public Application makeApplication(boolean forceDefaultAppClass,  
  2.           Instrumentation instrumentation) {  
  3.       if (mApplication != null) {  
  4.           return mApplication;  
  5.       }  
  6.   
  7.       Application app = null;  
  8.   
  9.       String appClass = mApplicationInfo.className;  
  10.       if (forceDefaultAppClass || (appClass == null)) {  
  11.           appClass = "android.app.Application";  
  12.       }  
  13.   
  14.       try {  
  15.           java.lang.ClassLoader cl = getClassLoader();  
  16.           ContextImpl appContext = new ContextImpl();  
  17.           appContext.init(thisnull, mActivityThread);  
  18.           app = mActivityThread.mInstrumentation.newApplication(  
  19.                   cl, appClass, appContext);  
  20.           appContext.setOuterContext(app);  
  21.       } catch (Exception e) {  
  22.           if (!mActivityThread.mInstrumentation.onException(app, e)) {  
  23.               throw new RuntimeException(  
  24.                   "Unable to instantiate application " + appClass  
  25.                   + ": " + e.toString(), e);  
  26.           }  
  27.       }  
  28.       mActivityThread.mAllApplications.add(app);  
  29.       mApplication = app;  
  30.   
  31. ......  
  32.         
  33.       return app;  
  34.   }  
    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。这就是下面几行代码的作用。

  1. ApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke  
  2.                     .getFieldOjbect("android.app.LoadedApk", loadedApkInfo,  
  3.                             "mApplicationInfo");  
  4.             ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke  
  5.                     .getFieldOjbect("android.app.ActivityThread$AppBindData",  
  6.                             mBoundApplication, "appInfo");  
  7.             appinfo_In_LoadedApk.className = appClassName;  
  8.             appinfo_In_AppBindData.className = appClassName;  
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呢?

 

  1.  private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
  2.         ......  
  3.         Activity a = performLaunchActivity(r, customIntent);  
  4.         ......  
  5. }  
 private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ......
        Activity a = performLaunchActivity(r, customIntent);
		......
}
  1. private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  
  2.         // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");  
  3.   
  4.         ActivityInfo aInfo = r.activityInfo;  
  5.         if (r.packageInfo == null) {  
  6.             r.packageInfo = getPackageInfo(aInfo.applicationInfo,  
  7.                     Context.CONTEXT_INCLUDE_CODE);  
  8.         }  
  9.   
  10.         ComponentName component = r.intent.getComponent();  
  11.         if (component == null) {  
  12.             component = r.intent.resolveActivity(  
  13.                 mInitialApplication.getPackageManager());  
  14.             r.intent.setComponent(component);  
  15.         }  
  16.   
  17.         if (r.activityInfo.targetActivity != null) {  
  18.             component = new ComponentName(r.activityInfo.packageName,  
  19.                     r.activityInfo.targetActivity);  
  20.         }  
  21.   
  22.         Activity activity = null;  
  23.         try {  
  24.             java.lang.ClassLoader cl = r.packageInfo.getClassLoader();  
  25.             activity = mInstrumentation.newActivity(  
  26.                     cl, component.getClassName(), r.intent);  
  27.             r.intent.setExtrasClassLoader(cl);  
  28.             if (r.state != null) {  
  29.                 r.state.setClassLoader(cl);  
  30.             }  
  31.         } catch (Exception e) {  
  32.             if (!mInstrumentation.onException(activity, e)) {  
  33.                 throw new RuntimeException(  
  34.                     "Unable to instantiate activity " + component  
  35.                     + ": " + e.toString(), e);  
  36.             }  
  37.         }  
  38.   
  39.         try {  
  40.             ......  
  41.                 mInstrumentation.callActivityOnCreate(activity, r.state);  
  42.         } catch {}  
  43.                   
  44. }  
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里面。

 

  1. <activity  
  2.     android:name="com.example.forceapkobj.MainActivity"  
  3.     android:label="@string/app_name" >  
  4.     <intent-filter>  
  5.         <action android:name="android.intent.action.MAIN" />  
  6.         <category android:name="android.intent.category.LAUNCHER" />  
  7.     </intent-filter>  
  8. </activity>  
  9.   
  10. <activity  
  11.     android:name="com.example.forceapkobj.SubActivity"></activity>  
        <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" />
            </intent-filter>
        </activity>
        
        <activity
            android:name="com.example.forceapkobj.SubActivity"></activity>
    MainActivity启动起来后,被加壳的apk就可以正常工作了。
 
0
0
 

 

posted @ 2017-06-23 16:54  sudenbutcher  阅读(456)  评论(0)    收藏  举报