双开DroidPlugin 源代码一

作者: kason_zhang | 来源:发表于2017-03-12 18:49 被阅读1231次

    Android 插件化,动态代理???一开始我们都会丈二摸不到头脑,晕晕乎乎,不知道如何下手去写这么样的一个东西,学习别人的原理思想是一个非常好的开始,360就开源了一个这样的框架,而且很6,DroidPlugin。但是研究DroidPlugin的原理是非常好的开始,如果你想要去做插件化技术。

    一 PluginApplication 启动初始化

    现在我们就按照从上至下的逻辑来研究一下DroidPlugin的启动初始化逻辑:先看个最基本的图

    DroidPlugin_start.png

    一开始我们新建一个应用然后在<application/>标签中加入android:name="com.morgoo.droidplugin.PluginApplication",标识我们会去使用PluginApplication。然后我们就要去看看com.morgoo.droidplugin.PluginApplication这个类。见代码code1

    code1.1

    public class PluginApplication extends Application {
    
        private static final String TAG = PluginApplication.class.getSimpleName();
    
        @Override
        public void onCreate() {
            super.onCreate();
            PluginHelper.getInstance().applicationOnCreate(getBaseContext());
        }
    
    
        @Override
        protected void attachBaseContext(Context base) {
            PluginHelper.getInstance().applicationAttachBaseContext(base);
            super.attachBaseContext(base);
        }
    }
    

    可以看到里面就是执行了两部分:
    PluginHelper.getInstance().applicationOnCreate(getBaseContext());和
    PluginHelper.getInstance().applicationAttachBaseContext(base);
    现在我们来看看PluginHelper类的实现,它是一个单例类

    code1.2 PluginHelper

    public class PluginHelper implements ServiceConnection {
    
        private static final String TAG = PluginHelper.class.getSimpleName();
        private static PluginHelper sInstance = null;
        private PluginHelper() {
        }
        public static final PluginHelper getInstance() {
            if (sInstance == null) {
                sInstance = new PluginHelper();
            }
            return sInstance;
        }
        public void applicationOnCreate(final Context baseContext) {
            mContext = baseContext;
            initPlugin(baseContext);
        }
        private Context mContext;
        private void initPlugin(Context baseContext) {
            long b = System.currentTimeMillis();
            try {
                try {
                    fixMiUiLbeSecurity();
                } catch (Throwable e) {
                    Log.e(TAG, "fixMiUiLbeSecurity has error", e);
                }
    
                try {
                    PluginPatchManager.getInstance().init(baseContext);
                    PluginProcessManager.installHook(baseContext);
                } catch (Throwable e) {
                    Log.e(TAG, "installHook has error", e);
                }
    
                try {
                    if (PluginProcessManager.isPluginProcess(baseContext)) {
                        PluginProcessManager.setHookEnable(true);
                    } else {
                        PluginProcessManager.setHookEnable(false);
                    }
                } catch (Throwable e) {
                    Log.e(TAG, "setHookEnable has error", e);
                }
    
                try {
                    PluginManager.getInstance().addServiceConnection(PluginHelper.this);
                    PluginManager.getInstance().init(baseContext);
                } catch (Throwable e) {
                    Log.e(TAG, "installHook has error", e);
                }
    
    
            } finally {
                Log.i(TAG, "Init plugin in process cost %s ms", (System.currentTimeMillis() - b));
            }
        }
        //...省略
        public void applicationAttachBaseContext(Context baseContext) {
            MyCrashHandler.getInstance().register(baseContext);
        }
    }
    

    现在我们来重点分析分析applicationOnCreate和applicationAttachBaseContext方法:

    code1.3

    public void applicationOnCreate(final Context baseContext) {
            mContext = baseContext;
            initPlugin(baseContext);
        }
    

    可以看到主要是调用的initPlugin method

    private void initPlugin(Context baseContext) {
            long b = System.currentTimeMillis();
            try {
                try {
    //此函数就不细分了,他是为了解决小米的手机的适配问题的
                    fixMiUiLbeSecurity();
                } catch (Throwable e) {
                    Log.e(TAG, "fixMiUiLbeSecurity has error", e);
                }
    
    //可以看到后面的代码都是调用的PluginPatchManager和PluginProcessManager和PluginManager这几个类//的方法
                try {
    //初始化PluginPatchManager,专门用于处理插件异常情况的。
                    PluginPatchManager.getInstance().init(baseContext);
    //installHook就是安装各种各样的程序修改原始Android API的Hook方法
    //比如IActivityManagerHook就是Hook了系统的ActivityManager等等,分析见code2.2
                    PluginProcessManager.installHook(baseContext);
                } catch (Throwable e) {
                    Log.e(TAG, "installHook has error", e);
                }
                try {
    //判断是否有必要去开启Hook,其分析代码见code2.3
                    if (PluginProcessManager.isPluginProcess(baseContext)) {
                        PluginProcessManager.setHookEnable(true);
                    } else {
                        PluginProcessManager.setHookEnable(false);
                    }
                } catch (Throwable e) {
                    Log.e(TAG, "setHookEnable has error", e);
                }
                try {
                    PluginManager.getInstance().addServiceConnection(PluginHelper.this);
                    PluginManager.getInstance().init(baseContext);
                } catch (Throwable e) {
                    Log.e(TAG, "installHook has error", e);
                }
            } finally {
                Log.i(TAG, "Init plugin in process cost %s ms", (System.currentTimeMillis() - b));
            }
        }
    

    code1.4

    public static void installHook(Context hostContext) throws Throwable {
    //又去调的HookFactory的installHook方法
            HookFactory.getInstance().installHook(hostContext, null);
        }
    
    public final void installHook(Context context, ClassLoader classLoader) throws Throwable {
            installHook(new IClipboardBinderHook(context), classLoader);
            //for ISearchManager
            installHook(new ISearchManagerBinderHook(context), classLoader);
           //...省略
            if (VERSION.SDK_INT >= VERSION_CODES.M) {
                installHook(new IAppOpsServiceBinderHook(context), classLoader);
            }
    
            installHook(new IPackageManagerHook(context), classLoader);
            installHook(new IActivityManagerHook(context), classLoader);
            installHook(new PluginCallbackHook(context), classLoader);
            installHook(new InstrumentationHook(context), classLoader);
            installHook(new LibCoreHook(context), classLoader);
    
            installHook(new SQLiteDatabaseHook(context), classLoader);
            installHook(new IDisplayManagerBinderHook(context), classLoader);
        }
    

    可以看到上面都是install了各种xxxHook。而这些xxxHook就是程序所要替换Android原始API的。xxxHook我们其实只要看到一个基本就可以看懂其他的了,目前先继续往下分析
    applicationAttachBaseContext,这个类其实就比较简单了,它是用于捕获异常的,自定义了一些异常处理方法。

    小结

    这里我们来看一个这个流程,当你使用DroidPlugin库的时候,当你的App应用启动时,就会初始化PluginApplication中的内容,然后就会install一些xxxHook比如各种ManagerHook以及Binder Hook。
    看看installHook方法:

    public void installHook(Hook hook, ClassLoader cl) {
            try {
                hook.onInstall(cl);
                synchronized (mHookList) {
                    mHookList.add(hook);
                }
            } catch (Throwable throwable) {
                Log.e(TAG, "installHook %s error", throwable, hook);
            }
        }
    

    看到没,他调用了Hook抽象类的onInstall方法,相当于实现Hook接口的各种xxxHook都会分别实现onInstall方法。

    二 Hook

    首先我们来看一下基本类图,暂时就以IActivityManagerHook为例,

    Hook.png

    hook包的目录

    hook_dir.png

    code 2.1

    首先我们看看Hook抽象类

    public abstract class Hook {
    
        private boolean mEnable = false;//是否启用Hook,true启用,false不启用
    
        protected Context mHostContext;
        protected BaseHookHandle mHookHandles;
    //省略代码
        protected Hook(Context hostContext) {
            mHostContext = hostContext;
            mHookHandles = createHookHandle();//留给子类去实现
        }
    
        protected abstract BaseHookHandle createHookHandle();
    
        protected abstract void onInstall(ClassLoader classLoader) throws Throwable;
    
        protected void onUnInstall(ClassLoader classLoader) throws Throwable {
    
        }
    }
    

    可以看到Hook抽象类里面有这两个方法:createHookHandle()和onInstall(),还记得第一节最后的那个onInstall方法吗。createHookHandle()方法是为了获取BaseHookHandle 类,那我们来看看BaseHookHandle

    code2.2

    public abstract class BaseHookHandle {
    
        protected Context mHostContext;
    
        //这个Map就是用来存储键值对,其中键是Android系统API方法名,值是HookedMethodHandler
        protected Map<String, HookedMethodHandler> sHookedMethodHandlers = new HashMap<String, HookedMethodHandler>(5);
    
        public BaseHookHandle(Context hostContext) {
            mHostContext = hostContext;
            init();
        }
    
        protected abstract void init();
    
        public Set<String> getHookedMethodNames(){
            return sHookedMethodHandlers.keySet();
        }
        //根据API方法名得到HookedMethodHandler
        public HookedMethodHandler getHookedMethodHandler(Method method) {
            if (method != null) {
                return sHookedMethodHandlers.get(method.getName());
            } else {
                return null;
            }
        }
    //省略代码
        //将此类中的所有方法都Hook进去
        protected void addAllMethodFromHookedClass(){
            try {
                Class clazz = getHookedClass();//要Hook的类
                if(clazz!=null){
                    Method[] methods = clazz.getDeclaredMethods();//获取所有的方法
                    if(methods!=null && methods.length>0){
                        for (Method method : methods) {
                            //当此方法是Public方法时,并且sHookedMethodHandlers并不存在此函数的名字作为键,那么存进去
                            if(Modifier.isPublic(method.getModifiers()) && !sHookedMethodHandlers.containsKey(method.getName())){
                                //method.getName()就是简写的名字,比如xxx.display()就是display
                                sHookedMethodHandlers.put(method.getName(),newBaseHandler());
                            }
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                Log.w(getClass().getSimpleName(),"init addAllMethodFromHookedClass error",e);
            }
        };
    }
    

    根据此类可以看到很重要的几个地方,其中属性sHookedMethodHandlers非常的重要,他是一个Map,键就是要Hook系统中的API方法名,值就是HookedMethodHandler对象,这个HookedMethodHandler对象就是真正用户替换执行的方法,相当于是系统API的对应Hook。然后有个init抽象方法,听这个名字就知道这货是初始化的,初始化啥呢,来我们看看其子类IActivityManagerHookHandler的init方法:具体分析见code2.5

    protected void init() {
            sHookedMethodHandlers.put("startActivity", new startActivity(mHostContext));
            sHookedMethodHandlers.put("startActivityAsUser", new startActivityAsUser(mHostContext));
            sHookedMethodHandlers.put("startActivityAsCaller", new startActivityAsCaller(mHostContext));
            sHookedMethodHandlers.put("startActivityAndWait", new startActivityAndWait(mHostContext));
    //此处省略
    

    很明显就是往MAP里面塞东西。

    上面也说了HookedMethodHandler才是真正的要去执行的方法。那就来看看此类的实现吧

    code2.3 HookedMethodHandler

    public class HookedMethodHandler {
    
        protected final Context mHostContext;
    
        public HookedMethodHandler(Context hostContext) {
            this.mHostContext = hostContext;
        }
    
      
        public synchronized Object doHookInner(Object receiver, Method method, Object[] args) throws Throwable {
            long b = System.currentTimeMillis();
            try {
                mUseFakedResult = false;
                mFakedResult = null;
                boolean suc = beforeInvoke(receiver, method, args);//切入,预处理
                Object invokeResult = null;//函数的返回结果,return的结果存放到Object
                //如果beforeInvoke返回true那么实际要执行的方法(就是hook后要执行的代码)就不会再执行
                //false则执行invoke方法
                if (!suc) {
                    invokeResult = method.invoke(receiver, args);
                }
                afterInvoke(receiver, method, args, invokeResult);//切入,后处理
                if (mUseFakedResult) {
                    return mFakedResult;
                } else {
                    return invokeResult;
                }
            } finally {
                long time = System.currentTimeMillis() - b;
                if (time > 5) {
                    Log.i(TAG, "doHookInner method(%s.%s) cost %s ms", method.getDeclaringClass().getName(), method.getName(), time);
                }
            }
        }
    
    //
        public void setFakedResult(Object fakedResult) {
            this.mFakedResult = fakedResult;
            mUseFakedResult = true;
        }
    
        /**
         * 在某个方法被调用之前执行,如果返回true,标识是插件,则不执行原始的方法,否则执行原始方法
         */
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            return false;
        }
        protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable {
        }
    }
    

    看看doHookInner的方法内容,我们可以看到他会现在真正的系统API之前调用beforeInvoke,而且如果beforeInvoke返回true则实际要去执行的方法就不执行了,然后还有个afterInvoke去做善后工作。那我们来看看beforeInvoke的实现,在类里面看到他是个protected方法,看样子有子类继承HookedMethodHandler来复写此方法。可以找到ReplaceCallingPackageHookedMethodHandler

    code2.4 ReplaceCallingPackageHookedMethodHandler

    class ReplaceCallingPackageHookedMethodHandler extends HookedMethodHandler {
    
        public ReplaceCallingPackageHookedMethodHandler(Context hostContext) {
            super(hostContext);
        }
    
        @Override
        protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                if (args != null && args.length > 0) {
                    for (int index = 0; index < args.length; index++) {
                        if (args[index] != null && (args[index] instanceof String)) {
                            String str = ((String) args[index]);
                            if (isPackagePlugin(str)) {
                                args[index] = mHostContext.getPackageName();
                            }
                        }
                    }
                }
            }
            return super.beforeInvoke(receiver, method, args);
        }
    
        private static boolean isPackagePlugin(String packageName) throws RemoteException {
            return PluginManager.getInstance().isPluginPackage(packageName);
        }
    }
    

    beforeInvoke里面有个重要的方法isPackagePlugin,PluginManager.getInstance().isPluginPackage(packageName)
    具体去看

     public boolean isPluginPackage(String packageName) throws RemoteException {
            try {
                if (mHostContext == null) {
                    return false;
                }
                if (TextUtils.equals(mHostContext.getPackageName(), packageName)) {
                    return false;
                }
    
                if (mPluginManager != null && packageName != null) {
                    return mPluginManager.isPluginPackage(packageName);
                } else {
                    Log.w(TAG, "Plugin Package Manager Service not be connect");
                }
            } catch (RemoteException e) {
                throw e;
            } catch (Exception e) {
                Log.e(TAG, "isPluginPackage", e);
            }
            return false;
        }
    

    很显然它是根据包名比较传进来的包名和应用的包名是否相同然后判断一下是不是插件包。

    现在看完了ReplaceCallingPackageHookedMethodHandler我们就可以回过来看IActivityManagerHookHandler

    code2.5 IActivityManagerHookHandler

    此类太庞大就不全部贴了,主要来分析分析这几个
    init函数:

    protected void init() {
            sHookedMethodHandlers.put("startActivity", new startActivity(mHostContext));
            sHookedMethodHandlers.put("startActivityAsUser", new startActivityAsUser(mHostContext));
            sHookedMethodHandlers.put("startActivityAsCaller", new startActivityAsCaller(mHostContext));
            sHookedMethodHandlers.put("startActivityAndWait", new startActivityAndWait(mHostContext));
            sHookedMethodHandlers.put("startActivityWithConfig", new startActivityWithConfig(mHostContext));
            sHookedMethodHandlers.put("startActivityIntentSender", new startActivityIntentSender(mHostContext));
            sHookedMethodHandlers.put("startVoiceActivity", new startVoiceActivity(mHostContext));
            sHookedMethodHandlers.put("startNextMatchingActivity", new startNextMatchingActivity(mHostContext));
            sHookedMethodHandlers.put("startActivityFromRecents", new startActivityFromRecents(mHostContext));
            sHookedMethodHandlers.put("finishActivity", new finishActivity(mHostContext));
            sHookedMethodHandlers.put("registerReceiver", new registerReceiver(mHostContext));
            sHookedMethodHandlers.put("broadcastIntent", new broadcastIntent(mHostContext));
            sHookedMethodHandlers.put("unbroadcastIntent", new unbroadcastIntent(mHostContext));
            sHookedMethodHandlers.put("getCallingPackage", new getCallingPackage(mHostContext));
            sHookedMethodHandlers.put("getCallingActivity", new getCallingActivity(mHostContext));
            sHookedMethodHandlers.put("getAppTasks", new getAppTasks(mHostContext));
            sHookedMethodHandlers.put("addAppTask", new addAppTask(mHostContext));
            sHookedMethodHandlers.put("getTasks", new getTasks(mHostContext));
            sHookedMethodHandlers.put("getServices", new getServices(mHostContext));
            sHookedMethodHandlers.put("getProcessesInErrorState", new getProcessesInErrorState(mHostContext));
            sHookedMethodHandlers.put("getContentProvider", new getContentProvider(mHostContext));
            sHookedMethodHandlers.put("getContentProviderExternal", new getContentProviderExternal(mHostContext));
            sHookedMethodHandlers.put("removeContentProviderExternal", new removeContentProviderExternal(mHostContext));
            sHookedMethodHandlers.put("publishContentProviders", new publishContentProviders(mHostContext));
            sHookedMethodHandlers.put("getRunningServiceControlPanel", new getRunningServiceControlPanel(mHostContext));
            sHookedMethodHandlers.put("startService", new startService(mHostContext));
            sHookedMethodHandlers.put("stopService", new stopService(mHostContext));
            sHookedMethodHandlers.put("stopServiceToken", new stopServiceToken(mHostContext));
            sHookedMethodHandlers.put("setServiceForeground", new setServiceForeground(mHostContext));
            sHookedMethodHandlers.put("bindService", new bindService(mHostContext));
            sHookedMethodHandlers.put("publishService", new publishService(mHostContext));
            sHookedMethodHandlers.put("unbindFinished", new unbindFinished(mHostContext));
            sHookedMethodHandlers.put("peekService", new peekService(mHostContext));
            sHookedMethodHandlers.put("bindBackupAgent", new bindBackupAgent(mHostContext));
            sHookedMethodHandlers.put("backupAgentCreated", new backupAgentCreated(mHostContext));
            sHookedMethodHandlers.put("unbindBackupAgent", new unbindBackupAgent(mHostContext));
            sHookedMethodHandlers.put("killApplicationProcess", new killApplicationProcess(mHostContext));
            sHookedMethodHandlers.put("startInstrumentation", new startInstrumentation(mHostContext));
            sHookedMethodHandlers.put("getActivityClassForToken", new getActivityClassForToken(mHostContext));
            sHookedMethodHandlers.put("getPackageForToken", new getPackageForToken(mHostContext));
            sHookedMethodHandlers.put("getIntentSender", new getIntentSender(mHostContext));
            sHookedMethodHandlers.put("clearApplicationUserData", new clearApplicationUserData(mHostContext));
            sHookedMethodHandlers.put("handleIncomingUser", new handleIncomingUser(mHostContext));
            sHookedMethodHandlers.put("grantUriPermission", new grantUriPermission(mHostContext));
            sHookedMethodHandlers.put("getPersistedUriPermissions", new getPersistedUriPermissions(mHostContext));
            sHookedMethodHandlers.put("killBackgroundProcesses", new killBackgroundProcesses(mHostContext));
            sHookedMethodHandlers.put("forceStopPackage", new forceStopPackage(mHostContext));
            sHookedMethodHandlers.put("getRunningAppProcesses", new getRunningAppProcesses(mHostContext));
            sHookedMethodHandlers.put("getRunningExternalApplications", new getRunningExternalApplications(mHostContext));
            sHookedMethodHandlers.put("getMyMemoryState", new getMyMemoryState(mHostContext));
            sHookedMethodHandlers.put("crashApplication", new crashApplication(mHostContext));
            sHookedMethodHandlers.put("grantUriPermissionFromOwner", new grantUriPermissionFromOwner(mHostContext));
            sHookedMethodHandlers.put("checkGrantUriPermission", new checkGrantUriPermission(mHostContext));
            sHookedMethodHandlers.put("startActivities", new startActivities(mHostContext));
            sHookedMethodHandlers.put("getPackageScreenCompatMode", new getPackageScreenCompatMode(mHostContext));
            sHookedMethodHandlers.put("setPackageScreenCompatMode", new setPackageScreenCompatMode(mHostContext));
            sHookedMethodHandlers.put("getPackageAskScreenCompat", new getPackageAskScreenCompat(mHostContext));
            sHookedMethodHandlers.put("setPackageAskScreenCompat", new setPackageAskScreenCompat(mHostContext));
            sHookedMethodHandlers.put("navigateUpTo", new navigateUpTo(mHostContext));
            sHookedMethodHandlers.put("serviceDoneExecuting", new serviceDoneExecuting(mHostContext));
        }
    

    这是IActivityManagerHookHandler全部Hook的方法,所有Android原始的API方法在这里Hook的时候都是通过一个类来是实现的。这里仅仅以startActivity的Hook为例。它定义了一个内部类,然后判断是否可以启动插件的Activity,如果可以就把信息先获取出来,然后封装给newIntent,加载到pluginClassLoader中进行启动。

    //静态内部类,用于劫持原有的startActivity方法替换成自己定义的。
        private static class startActivity extends ReplaceCallingPackageHookedMethodHandler {
    
            public startActivity(Context hostContext) {
                super(hostContext);
            }
    
            protected boolean doReplaceIntentForStartActivityAPIHigh(Object[] args) throws RemoteException {
                int intentOfArgIndex = findFirstIntentIndexInArgs(args);
                if (args != null && args.length > 1 && intentOfArgIndex >= 0) {
                    Intent intent = (Intent) args[intentOfArgIndex];
                    //XXX String callingPackage = (String) args[1];
                    if (!PluginPatchManager.getInstance().canStartPluginActivity(intent)) {
                        PluginPatchManager.getInstance().startPluginActivity(intent);
                        return false;
                    }
                    ActivityInfo activityInfo = resolveActivity(intent);
                    if (activityInfo != null && isPackagePlugin(activityInfo.packageName)) {
                        ComponentName component = selectProxyActivity(intent);
                        if (component != null) {
                            Intent newIntent = new Intent();
                            try {
                                ClassLoader pluginClassLoader = PluginProcessManager.getPluginClassLoader(component.getPackageName());
                                setIntentClassLoader(newIntent, pluginClassLoader);
                            } catch (Exception e) {
                                Log.w(TAG, "Set Class Loader to new Intent fail", e);
                            }
                            newIntent.setComponent(component);
                            newIntent.putExtra(Env.EXTRA_TARGET_INTENT, intent);
                            newIntent.setFlags(intent.getFlags());
                            String callingPackage = (String) args[1];
                            if (TextUtils.equals(mHostContext.getPackageName(), callingPackage)) {
                                newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            args[intentOfArgIndex] = newIntent;
                            args[1] = mHostContext.getPackageName();
                        } else {
                            Log.w(TAG, "startActivity,replace selectProxyActivity fail");
                        }
                    }
                }
    
                return true;
            }
    

    其他的API Hook都合上面类似就不做陈述了。

    在第一部分我们看到了install各种各样的xxxHook,可以知道其实都是Hook的子类,通过第二部分开始的类图可以看到他们都是继承的ProxyHook类。

    public abstract class ProxyHook extends Hook implements InvocationHandler {
    
        protected Object mOldObj;//moldObj用来保存将被代理的原始对象。执行此Object的原始方法时都会去调用下面的invoke
    
        public ProxyHook(Context hostContext) {
            super(hostContext);
        }
    
        public void setOldObj(Object oldObj) {
            this.mOldObj = oldObj;
        }
    
        //代理对象时,看着是执行代理对象的方法时,然而实际上真正执行的函数是此处的invoke
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            try {
                if (!isEnable()) {//启用没有启用Hook
                    return method.invoke(mOldObj, args);
                }
                //BaseHookHandler存放了键值对,键是函数名字,值是HookedMethodHandler,它才是真正处理函数。
                HookedMethodHandler hookedMethodHandler = mHookHandles.getHookedMethodHandler(method);
                if (hookedMethodHandler != null) {
                    //invoke其实也只是个幌子,真正执行代码的确是HookedMethodHandler里面的doHookInner
                    return hookedMethodHandler.doHookInner(mOldObj, method, args);
                }
                return method.invoke(mOldObj, args);
            } 
    //代码省略
            }
        }
    }
    

    可以看到一个熟悉的东西:InvocationHandler 这不是JAVA里面典型的代理模式吗,调用所有Android系统API时都会转向调用此处的invoke()方法。来看看invoke函数验证上面我们说的原理,mHookHandles.getHookedMethodHandler(method);通过方法名获取真正的Hook方法,然后调用了其doHookInner方法,和上面我们的分析正好一致。
    然后就是IActivityManagerHook继承了ProxyHook

    public class IActivityManagerHook extends ProxyHook {
    
        private static final String TAG = IActivityManagerHook.class.getSimpleName();
    
        public IActivityManagerHook(Context hostContext) {
            super(hostContext);
        }
    
        @Override
        public BaseHookHandle createHookHandle() {
            return new IActivityManagerHookHandle(mHostContext);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                return super.invoke(proxy, method, args);
            } catch (SecurityException e) {
                String msg = String.format("msg[%s],args[%s]", e.getMessage(), Arrays.toString(args));
                SecurityException e1 = new SecurityException(msg);
                e1.initCause(e);
                throw e1;
            }
        }
    
        //利用反射去替换掉Singleton<IActivityManager> gDefault
        @Override
        public void onInstall(ClassLoader classLoader) throws Throwable {
            Class cls = ActivityManagerNativeCompat.Class();
            //gDefault 是一个Android原声ActivityManagerNative代码中的Singleton<IActivityManager>单例对象
            Object obj = FieldUtils.readStaticField(cls, "gDefault");
            if (obj == null) {
                ActivityManagerNativeCompat.getDefault();
                obj = FieldUtils.readStaticField(cls, "gDefault");
            }
    
            if (IActivityManagerCompat.isIActivityManager(obj)) {
                setOldObj(obj);//传入代理对象,就是原生Android自己的
                Class<?> objClass = mOldObj.getClass();
                List<Class<?>> interfaces = Utils.getAllInterfaces(objClass);
                Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
                Object proxiedActivityManager = MyProxy.newProxyInstance(objClass.getClassLoader(), ifs, this);
                FieldUtils.writeStaticField(cls, "gDefault", proxiedActivityManager);
                Log.i(TAG, "Install ActivityManager Hook 1 old=%s,new=%s", mOldObj, proxiedActivityManager);
            } else if (SingletonCompat.isSingleton(obj)) {
                Object obj1 = FieldUtils.readField(obj, "mInstance");
                if (obj1 == null) {
                    SingletonCompat.get(obj);
                    obj1 = FieldUtils.readField(obj, "mInstance");
                }
                setOldObj(obj1);
                List<Class<?>> interfaces = Utils.getAllInterfaces(mOldObj.getClass());
                Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
                final Object object = MyProxy.newProxyInstance(mOldObj.getClass().getClassLoader(), ifs, IActivityManagerHook.this);
                Object iam1 = ActivityManagerNativeCompat.getDefault();
    
                //这里先写一次,防止后面找不到Singleton类导致的挂钩子失败的问题。
                FieldUtils.writeField(obj, "mInstance", object);
    
                //这里使用方式1,如果成功的话,会导致上面的写操作被覆盖。
                FieldUtils.writeStaticField(cls, "gDefault", new android.util.Singleton<Object>() {
                    @Override
                    protected Object create() {
                        Log.e(TAG, "Install ActivityManager 3 Hook  old=%s,new=%s", mOldObj, object);
                        return object;
                    }
                });
    
                Log.i(TAG, "Install ActivityManager Hook 2 old=%s,new=%s", mOldObj.toString(), object);
                Object iam2 = ActivityManagerNativeCompat.getDefault();
                // 方式2
                if (iam1 == iam2) {
                    //这段代码是废的,没啥用,写这里只是不想改而已。
                    FieldUtils.writeField(obj, "mInstance", object);
                }
            } else {
                throw new AndroidRuntimeException("Can not install IActivityManagerNative hook");
            }
        }
    }
    

    这段代码我想使用过JAVA进行动态代的应该比较熟悉

    setOldObj(obj);//传入代理对象,就是原生Android自己的
                Class<?> objClass = mOldObj.getClass();
                List<Class<?>> interfaces = Utils.getAllInterfaces(objClass);
                Class[] ifs = interfaces != null && interfaces.size() > 0 ? interfaces.toArray(new Class[interfaces.size()]) : new Class[0];
                Object proxiedActivityManager = MyProxy.newProxyInstance(objClass.getClassLoader(), ifs, this);
                FieldUtils.writeStaticField(cls, "gDefault", proxiedActivityManager);
    

    在IActivityManagerHook中oninstall方法首先通过反射get到了Android ActivityManager中的gDefault变量然后进行偷梁换柱。 FieldUtils.writeStaticField(cls, "gDefault", proxiedActivityManager);

    写不动了,周天也就暂时先写到这里了。在这里写个博客也算给自己个交代,看看能不能坚持下来,顺便练练文笔。
    如果你喜欢研究可以加入QQ群大家一起交流交流:94839608

    相关文章

      网友评论

      本文标题:双开DroidPlugin 源代码一

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