8.插件化开发--Hook基础

作者: android_赵乐玮 | 来源:发表于2017-04-06 10:17 被阅读188次

    1.基础:Activity的启动流程

    1. class loader
    2. AMS、PMS
    3. 反射及动态代理模式
    4. ActivityThread
    • 启动流程
      Activity1(A1)【IActicityManager】--IPC--->AMS------>PackageManagerServer(扫描清单文件)----->找到Activity2()--->启动A2并停止A1

    Hook后:

    Activity1(A1)【IActicityManager】----->Hook(使用代理Activity)--intent(proxy)--->AMS------>PackageManagerServer(扫描清单文件)----->找到Activity2()-intent(real)-->启动A2并停止A1

    2.具体项目示例

    • 基础Hook案例:
      启动未在清单文件中注册的Activity。
    • 实现思路:

    根据ActivityThread源码可以看Activity的启动仅仅需要提供intent(主要提供目标Activity)就能启动一个Activity

    //源码流程:ActivityThread.handleMessage() ==>handleLaunchActivity() ==>performLaunchActivity()==>mInstrumentation.newActivity()
    //主要查看ActivityClientRecord数据包的流向
      public Activity newActivity(Class<?> clazz, Context context, 
                IBinder token, Application application, Intent intent, ActivityInfo info, 
                CharSequence title, Activity parent, String id,
                Object lastNonConfigurationInstance) throws InstantiationException, 
                IllegalAccessException {
            Activity activity = (Activity)clazz.newInstance();
            ActivityThread aThread = null;
            activity.attach(context, aThread, this, token, 0, application, intent,
                    info, title, parent, id,
                    (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
                    new Configuration(), null, null, null);
            return activity;
        }
    
    • 但是大家都知道,如果Activity没有在清单文件中注册就会混抛异常。那怎么办呢?(怎样能让系统不抛异常?)

      思路很简单 , 抛出异常原因是intent经过PMS后会进行扫描清单文件,如果没有就会抛异常。
      那么只要在PMS处理之前把intent的目的Activity地址改为已知的代理Activity,在PMS之后再恢复成原地址即可,

    • 那么如何去替换intent呢?在哪里换呢?
      替换intent可以使用反射机制去动态替换intent内容
      由上面的Activity启动流程图可以看出,最好的替换地点就在Activity intent发出和Acitvity的启动前进行替换和逆替换.其他地方都不是在UI线程上也不好替换。
      咱们从源码一个一个分析:
      发送Intent的具体流程:

    //当然看源码从Acitvity.startActivity()中开始,看intent的流向
    Acitvity.startActivity()==>startActivityForResult()
    ==>startActivityFromChild()
    ==>Instrumentation.execStartActivity()
    ==>ActivityManagerNative.getDefault().startActivity()
    

    哈哈找到IActivityManager(ActivityManagerNative.getDefault()就是它)了
    Activity就是根据IActivityManager与AMS进行跨进程通信的(IPC)
    那么只要代理替换掉IActivityManager中的intent就可以了。

    • 下面是具体实现方法(其实为什么比怎么实现的更重要!)
         public void hookAms() throws Exception {
           Logger.d(TAG, "start hook ");
           Class<?> forName = Class.forName("android.app.ActivityManagerNative");
           Field defaultField = forName.getDeclaredField("gDefault");
           defaultField.setAccessible(true);
           Object defaultValue = defaultField.get(null);
    
           Class<?> singleton = Class.forName("android.util.Singleton");
           Field instanceField = singleton.getDeclaredField("mInstance");
           instanceField.setAccessible(true);
           //获取到IActivityManager
           Object iActivityManagerObj = instanceField.get(defaultValue);
           //代理对象-hook
           Class<?> iActivityManagerIntercept = Class.forName("android.app.IActivityManager");
           AmsInvocationHanadel hanadel = new AmsInvocationHanadel(iActivityManagerObj);
    
           Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{iActivityManagerIntercept}, hanadel);
    
           instanceField.set(defaultValue, proxy);
       }
    
       class AmsInvocationHanadel implements InvocationHandler {
           private Object iActivityManagerObj;
    
           public AmsInvocationHanadel(Object iActivityManagerObj) {
               this.iActivityManagerObj = iActivityManagerObj;
           }
    
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               Logger.d(TAG, "----------------AmsInvocationHanadel--------------------");
               if ("startActivity".equals(method.getName())) {
                   Logger.d(TAG, "methodName:" + method.getName());
                   Intent intent = null;
                   int index = 0;
                   for (int i = 0; i < args.length; i++) {
                       if (args[i] instanceof Intent) {
                           //找到intent;
                           index = i;
                           intent = (Intent) args[i];
                           break;
                       }
                   }
                   Intent proxyIntent = new Intent();
    
                   ComponentName compnnentName = new ComponentName(context, proxyActivity);
                   proxyIntent.setComponent(compnnentName);
                   proxyIntent.putExtra("oldIntent", intent);
                   Logger.d(TAG, "index :" + index);
                   args[index] = proxyIntent;
                   Logger.d(TAG, "替换成proxyIntent :" + proxyIntent.getComponent().getPackageName() + "; " + proxyIntent.getComponent().getShortClassName());
                   return method.invoke(iActivityManagerObj, args);
               }
               return method.invoke(iActivityManagerObj, args);
           }
       }
    
    • 逆替换
               //----------逆替换------------
            public void hookSystemHandler() {
            try {
                Class<?> forName = Class.forName("android.app.ActivityThread");
                Field sCurrentActivityThread = forName.getDeclaredField("sCurrentActivityThread");
                sCurrentActivityThread.setAccessible(true);
                Object activityThreadValue = sCurrentActivityThread.get(null); //系统程序入口
                Field handlerField = forName.getDeclaredField("mH");
                handlerField.setAccessible(true);
                Handler handlerObj = (Handler) handlerField.get(activityThreadValue);
                Field mCallbackField = Handler.class.getDeclaredField("mCallback");
                mCallbackField.setAccessible(true);
                mCallbackField.set(handlerObj, new ActivityThreadHandlerCallback(handlerObj));
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        class ActivityThreadHandlerCallback implements Handler.Callback {
    
            Handler handler;
    
            public ActivityThreadHandlerCallback(Handler handler) {
                super();
                this.handler = handler;
            }
    
            @Override
            public boolean handleMessage(Message msg) {
                Logger.d(TAG, "message callback");
                //替换回去
                if (msg.what == 100) {
                    Logger.d(TAG, "what:" + msg.what);
                    Logger.d(TAG, "Activity lunchActivity");
                    handleLunchActivity(msg);
                }
                handler.handleMessage(msg);
                return true;
            }
    
            private void handleLunchActivity(Message msg) {
                Object obj = msg.obj;//ActivityClientRecord
    
                try {
                    Field intentField = obj.getClass().getDeclaredField("intent");
                    intentField.setAccessible(true);
                    Intent proxyIntent = (Intent) intentField.get(obj); //代理Intent
                    Logger.d(TAG, "获取到proxyIntent");
                    Intent realIntent = proxyIntent.getParcelableExtra("oldIntent");
                    if (realIntent != null) {
                        Logger.d(TAG, " have  realIntent ");
                        proxyIntent.setComponent(realIntent.getComponent());//替换属性
                    } else {
                        Logger.d(TAG, "realIntent is null");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    
    • 重写Application方法
     /**
     * Created by zlw on 2017/4/5.
     */
    public class MyApp extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            HookAmsUtils amsUtils = new HookAmsUtils(this, ProxyActivity.class);
            try {
                amsUtils.hookAms();
                amsUtils.hookSystemHandler();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    最后配置下清单文件就可以了

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.zlw.main.myhookdemo">
    
        <application
            android:name=".base.MyApp"
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity android:name=".base.ProxyActivity" />
        </application>
    
    </manifest>
    

    相关文章

      网友评论

        本文标题:8.插件化开发--Hook基础

        本文链接:https://www.haomeiwen.com/subject/mbatattx.html