From fc0c80875791d047d59e6d2a41b6f686087e30dc Mon Sep 17 00:00:00 2001 From: Omooo <869759698@qq.com> Date: Mon, 27 Apr 2020 07:42:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Framework/插件化/启动未注册的 Activity.md | 211 ++++++++++++++++++ blogs/Android/Framework/智能指针.md | 38 ++++ 2 files changed, 249 insertions(+) create mode 100644 blogs/Android/Framework/插件化/启动未注册的 Activity.md create mode 100644 blogs/Android/Framework/智能指针.md diff --git a/blogs/Android/Framework/插件化/启动未注册的 Activity.md b/blogs/Android/Framework/插件化/启动未注册的 Activity.md new file mode 100644 index 0000000..0978c2e --- /dev/null +++ b/blogs/Android/Framework/插件化/启动未注册的 Activity.md @@ -0,0 +1,211 @@ +--- +启动未在 Manifest 中注册的 Activity +--- + +#### 依据 + +```java +public abstract class ActivityManagerNative extends Binder implements IActivityManager { + static public IActivityManager asInterface(IBinder obj) { + if (obj == null) { + return null; + } + IActivityManager in = + (IActivityManager)obj.queryLocalInterface(descriptor); + if (in != null) { + return in; + } + + return new ActivityManagerProxy(obj); + } + + static public IActivityManager getDefault() { + return gDefault.get(); + } + + private static final Singleton gDefault = new Singleton() { + protected IActivityManager create() { + IBinder b = ServiceManager.getService("activity"); + IActivityManager am = asInterface(b); + return am; + } + }; +} +``` + +### 基本思路 + +通过欺骗 AMS,来启动未在 AndroidManifest 中注册的 Activity,基本思路是: + +1. 发送要启动的 Activity 信息给 AMS 之前,把这个 Activity 替换为一个在 AndroidManifest 中已经声明的 SubActivity,这样就能绕过 AMS 的检查了。在替换的过程中,要把原来的 Activity 信息存放在 Bundle 中。 +2. AMS 通知 App 启动 StubActivity 时,我们自然不会启动 StubActivity,而是在即将启动的时候,把 StubActivity 替换为原先的 Activity,原先的 Activity 信存放在 Bundle 中。 + +#### 实现 + +对 AMN 进行 hook 可以同时适用于 Activity 和 Context 的 startActivity 方法。 + +```java +public class AMSHookHelper { + + public static final String EXTRA_TARGET_INTENT = "extra_target_intent"; + + /** + * Hook AMS + * 主要完成的操作是 "把真正要启动的Activity临时替换为在AndroidManifest.xml中声明的替身Activity",进而骗过AMS + */ + public static void hookAMN() throws ClassNotFoundException { + + //获取AMN的gDefault单例gDefault,gDefault是final静态的 + Object gDefault = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative", "gDefault"); + + // gDefault是一个 android.util.Singleton对象; 我们取出这个单例里面的mInstance字段 + Object mInstance = RefInvoke.getFieldObject("android.util.Singleton", gDefault, "mInstance"); + + // 创建一个这个对象的代理对象MockClass1, 然后替换这个字段, 让我们的代理对象帮忙干活 + Class classB2Interface = Class.forName("android.app.IActivityManager"); + Object proxy = Proxy.newProxyInstance( + Thread.currentThread().getContextClassLoader(), + new Class[]{classB2Interface}, + new MockClass1(mInstance)); + + //把gDefault的mInstance字段,修改为proxy + RefInvoke.setFieldObject("android.util.Singleton", gDefault, "mInstance", proxy); + } + + /** + * 由于之前我们用替身欺骗了AMS; 现在我们要换回我们真正需要启动的Activity + * 不然就真的启动替身了, 狸猫换太子... + * 到最终要启动Activity的时候,会交给ActivityThread 的一个内部类叫做 H 来完成 + * H 会完成这个消息转发; 最终调用它的callback + */ + public static void hookActivityThread() throws Exception { + + // 先获取到当前的ActivityThread对象 + Object currentActivityThread = RefInvoke.getStaticFieldObject("android.app.ActivityThread", "sCurrentActivityThread"); + + // 由于ActivityThread一个进程只有一个,我们获取这个对象的mH + Handler mH = (Handler) RefInvoke.getFieldObject(currentActivityThread, "mH"); + + + //把Handler的mCallback字段,替换为new MockClass2(mH) + RefInvoke.setFieldObject(Handler.class, mH, "mCallback", new MockClass2(mH)); + } +} +``` + + 第一步: + +```java +class MockClass1 implements InvocationHandler { + + private static final String TAG = "MockClass1"; + + Object mBase; + + public MockClass1(Object base) { + mBase = base; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("startActivity".equals(method.getName())) { + // 只拦截这个方法 + // 找到参数里面的第一个Intent 对象 + 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 = raw.getComponent().getPackageName(); + + // 这里我们把启动的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; + return method.invoke(mBase, args); + + } + + return method.invoke(mBase, args); + } +} +``` + +第二步: + +```java +class MockClass2 implements Handler.Callback { + + Handler mBase; + + public MockClass2(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; + + // 把替身恢复成真身 + Intent intent = (Intent) RefInvoke.getFieldObject(obj, "intent"); + + Intent targetIntent = intent.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT); + intent.setComponent(targetIntent.getComponent()); + } +} +``` + +#### 总结 + +1. Hook ActivityManagerNative 的 gDefault 字段,它是一个单例,mInstance 是 IActivityManager 接口,使用动态代理的方式,去代理 startActivity 方法,在 Intent 里的 Activity 临时替换成已经注册的占位 Activity。 +2. Hook ActivityThread 的 mH,它是一个 Handler.Callback,同样使用动态代理的方式,代理 handleMessage 方法,处理 case 为 100 也就是 LAUNCH_ACTIVITY,在这里面去启动真正的 Activity。 + +采用这种思路,第二步还可以 hook ActivityThread 的 mInstrumention 字段,Instrumention 里面有两个方法可以拦截: + +1. Activity newActivity(ClassLoader cl, String className, Intent intent) +2. Void callActivityOnCreate(Activity activity, ...) + +callActivityOnCreate 没有 Intent 参数,所以无法读取之前存放的要启动的 Activity,所以选择拦截 newActivity 方法。 + +#### 弊端 + +1. AMS 会认为每次要打开的 Activity 都是 SubActivity,那就相当于那些没有在 AndroidManifest 文件里面注册的 Activity 的 LaunchMode 就只能是默认类型,即使设置了 singleTop 或 singleTask,也不会生效。 +2. 兼容性问题,目前在 Android 6/7 上可行,但在 Android 7 以上就不行了,因为 AMN 的 gDefault 已经不存在了。 + + + +#### 参考 + +[https://www.cnblogs.com/Jax/p/9316422.html](https://www.cnblogs.com/Jax/p/9316422.html) + +[https://github.com/BaoBaoJianqiang/AndroidSourceCode](https://github.com/BaoBaoJianqiang/AndroidSourceCode) \ No newline at end of file diff --git a/blogs/Android/Framework/智能指针.md b/blogs/Android/Framework/智能指针.md new file mode 100644 index 0000000..e668b6d --- /dev/null +++ b/blogs/Android/Framework/智能指针.md @@ -0,0 +1,38 @@ +--- +智能指针 +--- + +#### 前言 + +智能指针是一种能够自动维护对象引用计数的技术,它是一个对象而不是一个指针,但是它引用了一个实际使用的对象。正是因为它是一个对象,因此它能够自动地维护实际对象的引用计数。简单来说,就是在智能指针构造时,增加它所引用的对象的引用计数;而在智能指针析构时,就减少它所引用的对象的引用计数。由于智能指针的构造和析构都是自动的,因此,它就很自然的实现了自动的对象引用计数技术。 + +但是这样并不能解决对象的相互引用问题,于是就需要采取一种稍微复杂的引用计数技术来维护对象的生周期了,这种引用计数技术将对象的引用计数分为强引用计数和弱引用计数两种,其中,对象的生命周期只受强引用计数控制。 + +Android 系统提供了三种类型的 C++ 智能指针,分别为轻量级指针(Light Pointer)、强指针(Strong Pointer)和弱指针(Weak Pointer)。 + +#### 轻量级指针 + +```c++ +template +class LightRefBase +{ +public: + inline LightRefBase() : mCount(0) { } + inline void incStrong(__attribute__((unused)) const void* id) const { + mCount.fetch_add(1, std::memory_order_relaxed); + } + inline void decStrong(__attribute__((unused)) const void* id) const { + if (mCount.fetch_sub(1, std::memory_order_release) == 1) { + std::atomic_thread_fence(std::memory_order_acquire); + delete static_cast(this); + } + +private: + mutable std::atomic mCount; +}; +``` + +LightRefBase 是一个模版类,其中,模版参数 T 表示对象的实际类型,它只有一个成员变量 mCount,用来描述一个对象的引用计数值。并提供了 incStrong 和 decStrong 来增加和减少它所引用的对象的引用计数。 + +轻量级指针的实现类为 sp,它同时也是强指针的实现类。在本节中,我们只关注它与轻量级指针相关的实现,它的定义如下所示: +