[转]Android Apk加壳原理分析
版权声明:本文为博主原创文章,未经博主允许不得转载。
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<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);
- }
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发送消息。
- 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);
- }
- }
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;
- ......
- }
- }
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);
- }
- }
- }
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;
- }
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;
- }
- ....
- }
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);
- }
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;
- }
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);
- }
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();
- }
- }
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();
- }
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<String, WeakReference<LoadedApk>> mPackages
- = 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,代码如下:
- 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;
- }
- }
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。
- data.info = getPackageInfoNoCheck(data.appInfo);
data.info = getPackageInfoNoCheck(data.appInfo);
回到attachBaseContext中,最后把这个新生成DexClassLoader对象赋值给LoadedApk对象的mClassLoader变量,也就是更新了这个mClassLoader变量。
0x03
执行完mActivityThread.mInstrumentation.newApplication,返回到makeApplication,继续执行下面两句代码:
- mActivityThread.mAllApplications.add(app);
- mApplication = app;
mActivityThread.mAllApplications.add(app); mApplication = app;执行完data.info.makeApplication,我们返回到handleBindApplication(代码请参考上面),继续执行下面一句代码:
- mInitialApplication = app;
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<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();
- }
- }
@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被赋值的。
- private final void handleBindApplication(AppBindData data) {
- mBoundApplication = data;
- ......
- }
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);
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<Application> mAllApplications = (ArrayList<Application>) RefInvoke
- .getFieldOjbect("android.app.ActivityThread",
- currentActivityThread, "mAllApplications");
- 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的实现:
- 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;
- }
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;
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 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 {}
- }
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" />
- </intent-filter>
- </activity>
- <activity
- 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

Android知识库
浙公网安备 33010602011771号