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/14223493String 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方法开始执行。

@Overridepublic 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();}//有值的话调用该ApplicaitonObject 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 设置成了nullRefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication",loadedApkInfo, null);Object oldApplication = RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mInitialApplication");//http://www.codeceo.com/article/android-context.htmlArrayList mAllApplications = (ArrayList) RefInvoke.getFieldOjbect("android.app.ActivityThread",currentActivityThread, "mAllApplications");mAllApplications.remove(oldApplication);//删除oldApplicationApplicationInfo 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.htmlArrayList 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里面。

                                                                                        
    MainActivity启动起来后,被加壳的apk就可以正常工作了。

更多相关文章

  1. 程序结构设计理论(Android)
  2. android zxing 横屏改为竖屏的配置
  3. blcr加速android启动速度遇到的问题及解决方法
  4. Android(安卓)Launcher 分析
  5. Android文件读写实例代码
  6. 代码讲解Android(安卓)Scroller、VelocityTracker
  7. 浅谈Java中Collections.sort对List排序的两种方法
  8. 类和 Json对象
  9. Python list sort方法的具体使用

随机推荐

  1. android 自定义view之绘制(二)
  2. 深入浅出RxJava四-在Android中使用响应式
  3. Android(安卓)使用NineOldAndroids实现绚
  4. Android进程的内存管理分析
  5. 谷歌Android增速创新高 每日新启用设备超
  6. Windows下的Android(安卓)SDK下载,2.2之前
  7. RelativeLayout的基本对齐方式
  8. Android开发实例——Hello World
  9. Android* 4.1.1 (Jelly Bean) x86 模拟器
  10. 懒骨头的Android文档备份2:运行你的App