静态代理模式就是我们常说的代理设计模式,我们采用一个代理类调用原有的方法,且对产生的结果进行控制;举个例子:我们现在在玩一款网络游戏,需要打怪升级;太累就找个代理吧,一觉醒来就会发现我们已经当上CEO,迎娶白富美,天下第一了!
本来我们只能打怪,打怪…,但经过代理类增强,我们不仅可以打怪,还可以升级拿装备。就这样子了!
上代码:
* 同一功能接口
public interface PlayNetGame { String beatMonster(); }
public class PlayNetGameImpl implements PlayNetGame { @Override public String beatMonster() { return "我们要去打怪兽了"; }
public class PlayNetGameProxy implements PlayNetGame { private PlayNetGame mPlayNetGame; public PlayNetGameProxy(PlayNetGame playNetGame){ mPlayNetGame = playNetGame; } @Override public String beatMonster() { String weapon = "找到屠龙刀"; String beatMonster = mPlayNetGame.beatMonster(); String upGrade = "我升级了"; String wuDi = "当上CEO,迎娶白富美----无敌了"; return weapon+"\n"+"\r"+beatMonster+"\n"+"\r"+upGrade+"\n"+"\r"+wuDi; } }
public class MainActivity extends AppCompatActivity { private TextView mTvPlay; private TextView mTvProxy; private Button mBtPlay; private Button mBtProxy; private PlayNetGame mPlayNetGame; private PlayNetGame mPlayNetGameProxy; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); } private void initData() { mPlayNetGame = new PlayNetGameImpl(); mPlayNetGameProxy = new PlayNetGameProxy(mPlayNetGame); mBtPlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //未被代理时结果 mTvPlay.setText(mPlayNetGame.beatMonster()); } }); mBtProxy.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //代理以后的结果 mTvProxy.setText(mPlayNetGameProxy.beatMonster()); } }); } private void initView() { mTvPlay = (TextView) findViewById(R.id.tv_play); mTvProxy = (TextView) findViewById(R.id.tv_proxy); mBtPlay = (Button) findViewById(R.id.bt_play); mBtProxy = (Button) findViewById(R.id.bt_proxy); } }
public class DynamicProxy implements InvocationHandler { private Object mObject; public DynamicProxy(Object object) { mObject = object; } /** * 因为我要增强的方法会返回String字符串,这里Return一个增强的字符串 * @param proxy proxy: 指代我们所代理的那个真实对象 * @param method 指代的是我们所要调用真实对象的某个方法的Method对象 * @param args args: 指代的是调用真实对象某个方法时接受的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String weapon = "找到屠龙刀"; String ing=null; if(method.getName().equals("beatMonster")){ ing = "遇到怪物"; method.invoke(mObject, args); }else { method.invoke(mObject, args); } String upGrade = "我升级了"; String wuDi = "当上CEO,迎娶白富美----无敌了"; return weapon+"\n"+"\r"+ing+"\n"+"\r"+ method.invoke(mObject, args)+"\n"+"\r"+upGrade+"\n"+"\r"+wuDi; } }
mPlayNetGame = new PlayNetGameImpl(); // mPlayNetGameProxy = new PlayNetGameProxy(mPlayNetGame); DynamicProxy dynamicProxy = new DynamicProxy(mPlayNetGame); //CLassLoader loader:类的加载器 //Class<?> interfaces:得到全部的接口 //InvocationHandler h:得到InvocationHandler接口的子类的实例 final PlayNetGame playNetGame = (PlayNetGame) Proxy.newProxyInstance(dynamicProxy.getClass().getClassLoader(), mPlayNetGame.getClass().getInterfaces(), dynamicProxy);
其实所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。
代理对象就是把被代理对象包装一层,在其内部做一些额外的工作,比如用户需要上facebook,而普通网络无法直接访问,网络代理帮助用户先FQ,然后再访问facebook。这就是代理的作用了。
Activity生命周期管理
Activity,Service等组件是有生命周期的,它们统一由系统服务AMS(ActivityManagerService)管理;
Activity启动过程
通信过程原理:(1)App进程会委托AMS进程完成Activity生命周期的管理以及任务栈的管理;这个通信过程AMS是Server端,App进程通过持有AMS的client代理ActivityManagerNative完成通信过程;
(2)AMS进程完成生命周期管理以及任务栈管理后,会把控制权交给App进程,让App进程完成Activity类对象的创建,以及生命周期回调;这个通信过程也是通过Binder完成的,App进程变为server端,Binder对象存在于ActivityThread的内部类ApplicationThread中;AMS所在client通过持有IApplicationThread的代理对象完成对于App进程的通信。
完成启动:handler类里直接调用了ActivityThread的handleLaunchActivity方法,这个方法做了两件很重要的事情:
(1)使用ClassLoader加载并通过反射创建Activity对象
java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl);
(2)如果Application还没有创建,那么创建Application对象并回调相应的生命周期方法
Application app = r.packageInfo.makeApplication(false, mInstrumentation);// ... 省略 if (r.isPersistable()) { mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); }
盗图总结-0-
<!-- 替身Activity, 用来欺骗AMS --> <activity android:name=".StubActivity"/>
startActivity(new Intent(MainActivity.this, TargetActivity.class));
ps1: 这个TargetActivity继承的是Activity,不是AppCompatActivity,因为startActivity()是调用的Activity内的方法
ps2:TargetActivity和StubActivity这俩个类文件要放在根包下,不要放到分包里,若把他们放在根目录下新建的包内(会替换不了)
经过debug调试,替代类包名路径和目标类包名路径不同,会替换失败?但是我改成下图就会成功
3.使用替身Activity绕过AMS:由于AMS进程会对Activity做显式声明验证,因此在
启动Activity的控制权转移到AMS进程之前,我们需要想办法临时把TargetActivity替换成替身StubActivity;
/** * 主要完成的操作是 "把真正要启动的Activity临时替换为在AndroidManifest.xml中声明的替身Activity" */ public static void hookActivityManagerNative(){ try { Class<?> mActivityManagerNative = Class.forName("android.app.ActivityManagerNative"); Field gDefaultField = mActivityManagerNative.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); Object gDefault = gDefaultField.get(null); // gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的字段 Class<?> mSingletion = Class.forName("android.util.Singleton"); Field mInstanceField = mSingletion.getDeclaredField("mInstance"); mInstanceField.setAccessible(true); // ActivityManagerNative 的gDefault对象里面原始的 IActivityManager对象 Object mIActivityManager = mInstanceField.get(gDefault); // 创建一个这个对象的代理对象, 然后替换这个字段, 让我们的代理对象帮忙干活 Class<?> iActivityManagerInterfance = Class.forName("android.app.IActivityManager"); Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{iActivityManagerInterfance}, new IActivityManagerHandler(mIActivityManager)); mInstanceField.set(gDefault,proxyInstance); } catch (Exception e) { e.printStackTrace(); } }
动态代理类IActivityManagerHandler
class IActivityManagerHandler implements InvocationHandler { private static final String TAG = "IActivityManagerHandler"; private Object mBase; public IActivityManagerHandler(Object base) { mBase = base; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 只拦截这个方法 // 替换参数, 任你所为;甚至替换原始Activity启动别的Activity偷梁换柱 // API 23: // public final Activity startActivityNow(Activity parent, String id, // Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state, // Activity.NonConfigurationInstances lastNonConfigurationInstances) { // 找到参数里面的第一个Intent 对象 if ("startActivity".equals(method.getName())) { Intent raw; int index = 0; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Intent) { index = i; break; } } raw = (Intent) args[index]; Intent newIntent = new Intent(); // 替身Activity的包名, 也就是我们自己的包名 String stubPackage = "com.example.happyghost.proxystudy"; // 这里我们把启动的Activity临时替换为 StubActivity ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName()); newIntent.setComponent(componentName); // 把我们原始要启动的TargetActivity先存起来 newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT, raw); // 替换掉Intent, 达到欺骗AMS的目的 args[index] = newIntent; Log.d(TAG, "hook success"); return method.invoke(mBase, args); } return method.invoke(mBase, args); } }
(1)Handler是如何处理接收到的Message的
1. 如果传递的Message本身就有callback,那么直接使用Message对象的callback方法;2. 如果Handler类的成员变量mCallback存在,那么首先执行这个mCallback回调;3. 如果mCallback的回调返回true,那么表示消息已经成功处理;直接结束。4. 如果mCallback的回调返回false,那么表示消息没有处理完毕,会继续使用Handler类的handleMessage方法处理消息。
(2)Handler.Callback是一个接口,我们可以使用动态代理或者普通代理完成Hook,这里我们使用普通的静态代理方式;创建一个自定义的Callback类:
class ActivityThreadHandlerCallback implements Handler.Callback { Handler mBase; public ActivityThreadHandlerCallback(Handler base) { mBase = base; } @Override public boolean handleMessage(Message msg) { switch (msg.what) { // ActivityThread里面 "LAUNCH_ACTIVITY" 这个字段的值是100 // 本来使用反射的方式获取最好, 这里为了简便直接使用硬编码 case 100: handleLaunchActivity(msg); break; } mBase.handleMessage(msg); return true; } private void handleLaunchActivity(Message msg) { // 这里简单起见,直接取出TargetActivity; Object obj = msg.obj; // 根据源码: // 这个对象是 ActivityClientRecord 类型 // 我们修改它的intent字段为我们原来保存的即可. /* switch (msg.what) { / case LAUNCH_ACTIVITY: { / Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); / final ActivityClientRecord r = (ActivityClientRecord) msg.obj; / / r.packageInfo = getPackageInfoNoCheck( / r.activityInfo.applicationInfo, r.compatInfo); / handleLaunchActivity(r, null); */ try { // 把替身恢复成真身 Field intent = obj.getClass().getDeclaredField("intent"); intent.setAccessible(true); Intent raw = (Intent) intent.get(obj); Intent target = raw.getParcelableExtra(HookHelper.EXTRA_TARGET_INTENT); raw.setComponent(target.getComponent()); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
/** * 由于之前我们用替身欺骗了AMS; 现在我们要换回我们真正需要启动的Activity * <p/> * 不然就真的启动替身了, 狸猫换太子... * <p/> * 到最终要启动Activity的时候,会交给ActivityThread 的一个内部类叫做 H 来完成 * H 会完成这个消息转发; 最终调用它的callback */ public static void hookActivityThreadHandler() throws Exception { // 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); currentActivityThreadField.setAccessible(true); Object currentActivityThread = currentActivityThreadField.get(null); // 由于ActivityThread一个进程只有一个,我们获取这个对象的mH Field mHField = activityThreadClass.getDeclaredField("mH"); mHField.setAccessible(true); Handler mH = (Handler) mHField.get(currentActivityThread); // 设置它的回调, 根据源码: // 我们自己给他设置一个回调,就会替代之前的回调; // public void dispatchMessage(Message msg) { // if (msg.callback != null) { // handleCallback(msg); // } else { // if (mCallback != null) { // if (mCallback.handleMessage(msg)) { // return; // } // } // handleMessage(msg); // } // } Field mCallBackField = Handler.class.getDeclaredField("mCallback"); mCallBackField.setAccessible(true); mCallBackField.set(mH, new ActivityThreadHandlerCallback(mH)); }
@Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(newBase); try { AMSHookHelper.hookActivityManagerNative(); AMSHookHelper.hookActivityThreadHandler(); } catch (Throwable throwable) { throw new RuntimeException("hook failed", throwable); } }
将插件的dex或者apk文件告诉『合适的』DexClassLoader,借助它完成插件类的加载
Activity的创建过程的代码
java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl);
ClassLoader对象的获取
加载插件类的第一种方案:Hook掉ClassLoader,自己操刀
在获取LoadedApk的过程中使用了一份缓存数据;这个缓存数据是一个Map,从包名到LoadedApk的一个映射。正常情况下,我们的插件肯定不会存在于这个对象里面;但是如果我们手动把我们插件的信息添加到里面呢?系统在查找缓存的过程中,会直接找到缓存!进而使用我们添加进去的LoadedApk的ClassLoader来加载这个特定的Activity类!这样我们就能接管我们自己插件类的加载过程了!
这个缓存对象mPackages存在于ActivityThread类中
// 先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 获取到 mPackages 这个静态成员变量, 这里缓存了dex包的信息 Field mPackagesField = activityThreadClass.getDeclaredField("mPackages"); mPackagesField.setAccessible(true); Map mPackages = (Map) mPackagesField.get(currentActivityThread);
我们把插件的信息塞进这个map里面,以便系统在查找的时候能找到插件缓存。填充这个Map我们出了需要包名之外,还需要一个LoadedApk对象(可以直接反射调用它的构造函数直接创建出需要的对象,不严谨)
(1)我们在这里选择使用 **getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo)** 获取LoadedApk信息。为了调用这个函数,我们需要构造两个参数。其一是ApplicationInfo,其二是CompatibilityInfo;第二个参数顾名思义,代表这个App的兼容性信息,比如targetSDK版本等等,这里我们只需要提取出app的信息,因此直接使用默认的兼容性即可;在CompatibilityInfo类里面有一个公有字段DEFAULT_COMPATIBILITY_INFO代表默认兼容性信息
(2) 获取这个ApplicationInfo信息,这个类就是AndroidManifest.xml里面的 这个标签下面的信息;这个AndroidManifest.xml无疑是一个标准的xml文件,因此我们完全可以自己使用parse来解析这个信息。
(3) 使用PackageParser类的 generateApplicationInfo(Package p, int flags, PackageUserState state) 方法获取ApplicationInfo信息,反射调用其
Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser"); // 首先拿到我们得终极目标: generateApplicationInfo方法 // API 23 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // public static ApplicationInfo generateApplicationInfo(Package p, int flags, // PackageUserState state) { // 其他Android版本不保证也是如此. Class<?> packageParser$PackageClass = Class.forName("android.content.pm.PackageParser$Package"); Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState"); Method generateApplicationInfoMethod = packageParserClass.getDeclaredMethod("generateApplicationInfo", packageParser$PackageClass, int.class, packageUserStateClass);
要成功调用这个方法,还需要三个参数;因此接下来我们需要一步一步构建调用此函数的参数信息。
// 首先, 我们得创建出一个Package对象出来供这个方法调用 // 而这个需要得对象可以通过 android.content.pm.PackageParser#parsePackage 这个方法返回得 Package对象得字段获取得到 // 创建出一个PackageParser对象供使用 Object packageParser = packageParserClass.newInstance(); // 调用 PackageParser.parsePackage 解析apk的信息 Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class); // 实际上是一个 android.content.pm.PackageParser.Package 对象 Object packageObj = parsePackageMethod.invoke(packageParser, apkFile, 0);
这样,我们就得到了generateApplicationInfo的第一个参数;第二个参数是解析包使用的flag,我们直接选择解析全部信息,也就是0;
// 第三个参数 mDefaultPackageUserState 我们直接使用默认构造函数构造一个出来即可 Object defaultPackageUserState = packageUserStateClass.newInstance();
// 万事具备!!!!!!!!!!!!!! ApplicationInfo applicationInfo = (ApplicationInfo) generateApplicationInfoMethod.invoke(packageParser, packageObj, 0, defaultPackageUserState); String apkPath = apkFile.getPath(); applicationInfo.sourceDir = apkPath; applicationInfo.publicSourceDir = apkPath;
// android.content.res.CompatibilityInfo Class<?> compatibilityInfoClass = Class.forName("android.content.res.CompatibilityInfo"); Method getPackageInfoNoCheckMethod = activityThreadClass.getDeclaredMethod("getPackageInfoNoCheck", ApplicationInfo.class, compatibilityInfoClass); Field defaultCompatibilityInfoField = compatibilityInfoClass.getDeclaredField("DEFAULT_COMPATIBILITY_INFO"); defaultCompatibilityInfoField.setAccessible(true); Object defaultCompatibilityInfo = defaultCompatibilityInfoField.get(null); ApplicationInfo applicationInfo = generateApplicationInfo(apkFile); Object loadedApk = getPackageInfoNoCheckMethod.invoke(currentActivityThread, applicationInfo, defaultCompatibilityInfo);
String odexPath = Utils.getPluginOptDexDir(applicationInfo.packageName).getPath(); String libDir = Utils.getPluginLibDir(applicationInfo.packageName).getPath(); ClassLoader classLoader = new CustomClassLoader(apkFile.getPath(), odexPath, libDir, ClassLoader.getSystemClassLoader()); Field mClassLoaderField = loadedApk.getClass().getDeclaredField("mClassLoader"); mClassLoaderField.setAccessible(true); mClassLoaderField.set(loadedApk, classLoader); // 由于是弱引用, 因此我们必须在某个地方存一份, 不然容易被GC; 那么就前功尽弃了. sLoadedApk.put(applicationInfo.packageName, loadedApk); WeakReference weakReference = new WeakReference(loadedApk); mPackages.put(applicationInfo.packageName, weakReference);
private static void hookPackageManager() throws Exception { // 这一步是因为 initializeJavaContextClassLoader 这个方法内部无意中检查了这个包是否在系统安装 // 如果没有安装, 直接抛出异常, 这里需要临时Hook掉 PMS, 绕过这个检查. Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 获取ActivityThread里面原始的 sPackageManager Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager"); sPackageManagerField.setAccessible(true); Object sPackageManager = sPackageManagerField.get(currentActivityThread); // 准备好代理对象, 用来替换原始的对象 Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager"); Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader(), new Class<?>[] { iPackageManagerInterface }, new IPackageManagerHookHandler(sPackageManager)); // 1. 替换掉ActivityThread里面的 sPackageManager 字段 sPackageManagerField.set(currentActivityThread, proxy); }
加载插件类的第二种方案:委托系统,让系统帮忙加载
public static void patchClassLoader(ClassLoader cl, File apkFile, File optDexFile) throws IllegalAccessException, NoSuchMethodException, IOException, InvocationTargetException, InstantiationException, NoSuchFieldException { // 获取 BaseDexClassLoader : pathList Field pathListField = DexClassLoader.class.getSuperclass().getDeclaredField("pathList"); pathListField.setAccessible(true); Object pathListObj = pathListField.get(cl); // 获取 PathList: Element[] dexElements Field dexElementArray = pathListObj.getClass().getDeclaredField("dexElements"); dexElementArray.setAccessible(true); Object[] dexElements = (Object[]) dexElementArray.get(pathListObj); // Element 类型 Class<?> elementClass = dexElements.getClass().getComponentType(); // 创建一个数组, 用来替换原始的数组 Object[] newElements = (Object[]) Array.newInstance(elementClass, dexElements.length + 1); // 构造插件Element(File file, boolean isDirectory, File zip, DexFile dexFile) 这个构造函数 Constructor<?> constructor = elementClass.getConstructor(File.class, boolean.class, File.class, DexFile.class); Object o = constructor.newInstance(apkFile, false, apkFile, DexFile.loadDex(apkFile.getCanonicalPath(), optDexFile.getAbsolutePath(), 0)); Object[] toAddElementArray = new Object[] { o }; // 把原始的elements复制进去 System.arraycopy(dexElements, 0, newElements, 0, dexElements.length); // 插件的那个element复制进去 System.arraycopy(toAddElementArray, 0, newElements, dexElements.length, toAddElementArray.length); // 替换 dexElementArray.set(pathListObj, newElements); }