插件化原理之hook系统函数

插件化原理之hook系统函数

插件化主要问题之一是如何startActivity一个未在注册表里面注册的acitivity

我们都知道开启一个activity是涉及到app进程和系统服务进程的交互过程,其中验证要打开的acitivity是否在清单文件中也是在系统服务进程进行的,那么”如何”欺骗系统服务进程?

方案一是设置一个代理ProxyActivity,这个ProxyActivity在清单文件中注册过,然后在该ProxyActivity里面注入一个真实的Activity,ProxyActivity所有的生命周期方法里面都回调真实的Activity生命周期方法。
关于这个方案有个框架实现的挺好的,可以看动态代理DL框架。这个方案调用startyActivity是要特别处理,首先要把真实的activity信息隐藏在intent里面,然后在代理ProxyActivity里面在解析出真正的activity信息并且实例化,然后回调真正的activity生命周期方法。

DL 的github地址:dynamic-load-apk

方案二 是直接hook appstartActivity方法
这里先梳理一下startActivity的逻辑。startActivity虽然只有一行代码,但里面涉及的调用却很复杂,app持有一个AMSBinder引用,在app端最终调用ActivityManagerNative.getDefault().startActivity后,系统服务进程开始做一些准备处理,而ActivityManagerNative.getDefault()是个单例模式,我们可以在这里传递一个注册表里面注册过的activity过去,这样系统服务进程验证的时候就会通过,然后回调给ApplicationThreadNative,ApplicationThreadNative再通过一个内部类H发送消息到ActivityThread,这样消息发送过来的时候我们再换回真实的activity,如此一来系统就会认为这个activity已经被系统验证过了,生命周期的调用和其他acitivity的生命周期方法调用过程一模一样。查看startActivity调用流程时,发现ActivityManagerNative.getDefault()是个单例,我们可以反射重新设置我们自己的代理对象,ActivityThread里面所有AMS发送过来的消息都会通过内部类H发送到主线程,查看Handler的源码我们发现,Hander在处理消息的方法是会先查看内部一个 变量Callback是否存在,如果存在则先处理Callback的方法,

HandlerdispatchMessage方法源码,我们通常是复写handleMessage,如果我们要拦截哪个方法,我们看一看设置HandlerCallback 并且设置handleMessage返回true,这样就总不会走HandlerhandleMessage方法了。

     public void dispatchMessage(Message msg) {      
        if (msg.callback != null) {          
                  handleCallback(msg);      
            } else {         
                 if (mCallback != null) {             
                        if (mCallback.handleMessage(msg)) {                  
                                return;           
                           }         
                  }          
                handleMessage(msg);      
          }
    }

我们可以在ActivityThreadH里面的mCallback里面拦截startActivity发出来的消息,然后在这里把真实的activity替换。

本文讲解的是第二种方案,当然这种方案要对startActivity具体流程和动态代理有个清楚的认识,不清楚的可以查看我的另两篇博客
startActivity方法解读
设计模式之代理模式2

主要方法如 下

public void hookSystem() throws Exception{

    // 1 加载ActivityManagerNative类信息         
    Class<?> activityManagerNative=Class.forName("android.app.ActivityManagerNative");         
     //2 获取gDefault字段属性         
    Field field= getField(activityManagerNative,"gDefault");         
     //3 获取Singleton 对象        
    Object o= field.get(null);          
    
    //4获取Singleton类信息          
    Class<?> singleton=Class.forName("android.util.Singleton");  
        //5 获取mInstance 字段信息         
     Field field2= getField(singleton,"mInstance");
    //6 获取该第三步对象里面的变量对象         
     Object mInstance=field2.get(o);         
               
    MyInvocationHandler myInvocationHandler=new MyInvocationHandler(mInstance);          
    //7 生成代理类          
    Object proxy= Proxy.newProxyInstance(mInstance.getClass().getClassLoader(),mInstance.getClass().getInterfaces(),myInvocationHandler);       
     //8 替换Singleton类里面的mInstance属性     
    field2.set(o,proxy);
    
    //9获取ActivityThread类信息  
     activityThreadClass=Class.forName("android.app.ActivityThread");
    
    //10获取mH字段信息,该变量是个Handler对象          
    Field mHField=getField(activityThreadClass,"mH");
    
    //11 ActivityThread类里面有个唯一对象,就是sCurrentActivityThread属性          
    Field sCurrentActivityThread=getField(activityThreadClass,"sCurrentActivityThread");   
    //12 获取到ActivityThread对象
    activityThread=sCurrentActivityThread.get(null);  
     //13 获取ActivityThread对象 mH对象          
    Object mH= mHField.get(activityThread);  

    //14 获取Handler类里面的mCallback字段信息         
     Field field1=getField(Handler.class,"mCallback");   
            
     //15 设置mH对面里面的callback为我们自己写的callback  
      field1.set(mH,myCallback);    
  }

第二步我们就获取到ActivityManagerNative类里面的gDefault属性了

图片1.png

由于该变量是个static 所以第三步我们调用get传入null参数就可以获取该对象了,然后通过动态代理生成一个代理对象,由于代理对象的方法都会调用我们设置的MyInvocationHandler对象的invoke方法,故我们可以在这里拦截startActivity方法,我们把真实的activity随便替换一个在注册表里面注册过的activity信息传输到AMS那里。

查看Singleton源代码我们可以知道,该get方法返回的就是create方法创建的对象,也是内部的一个变量,我们只要把这个对象替换成我们的对象就可以了。

public abstract class Singleton<T> {      
    private T mInstance;    
    protected abstract T create();    
    public final T get() {          
      synchronized (this) {              
           if (mInstance == null) {                  
                   mInstance = create();            
            }             
             return mInstance;  
       }     
     }  
}

到第八步的时候我们已经把ActivityManagerNative里面的gDefault变量里面的mInstance变量换成我们自己的代理对象,这样AcitivityManagerNative.getDefault对象也就是我们的代理对象了。

图片2.png

12步的时候我们已经获取到当前ActivityThread类的唯一对象,这个和我们在自定义Application返回Application实例一样。

图片3.png

13步的我们已经获取到ActivityThread里面mH这个变量

图片4.png

15步的时候我们已经把ActivityThread里面的mH对象里面的mCallback设置成我们自己的callback,这样AMS发送给APP的消息,我们这里都能进行拦截。

MyInvocationHandler类代码如下

这里我们拦截了APP的startActivity方法,把要开启的Activity信息保存起来,替换一个新注册过的Activity信息放在里面

class MyInvocationHandler implements InvocationHandler {  
    Object target;  
    public MyInvocationHandler(Object o){              
            this.target=o;         
     }         
    @Override          
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {   
        //拦截startActivity方法           
        if (method.getName().equals("startActivity")){                     
            for (Object o:args){                     
               if (o instanceof Intent){ 
                    // intent.setComponent(componentName);                         
                    Intent oldIntent= (Intent) o;                        
                    //把原来的intent的跳转类信息保存起来  //                       
                    Intent intent=new Intent();                                 
                     //替换一个在清单文件中存在的Activity类                          
                    ComponentName componentName=new ComponentName(context,TestAidlActivity.class);  
                    oldIntent.putExtra("realComponentName",oldIntent.getComponent());  
                    oldIntent.setComponent(componentName);  
                    break;                     
                }                  
            }             
                   
          }             
         return method.invoke(target,args);      
     }   
   }

我们注入H里面的callback

Handler.Callback myCallback=new Handler.Callback() {  
    @Override     
     public boolean handleMessage(Message msg) {      
       if (msg.what==100){                         
        handleStartActivity(msg);         
     }         
        return false;     
     };  
};

查看H源码我们知道开启一个activity消息是100.所以我们这里处理msg.what==100的情况,看下面知道所有的生命周期回调都有对应的消息,我们都能进行拦截处理。目前我们暂时处理开启activity的消息

图片5.png

这个方法就好理解了,获取msg里面的intent对象,然后把里面的Component设置成我们保持的那个Component信息

private void handleStartActivity(Message msg){      
     Object activityClientRecord= msg.obj; 
     try {           
        isReplaceOncreate=false;          
        Field field=getField(activityClientRecord.getClass(),"intent");    
         //拿到intent对象。         
        Intent intent= (Intent) field.get(activityClientRecord);  
        ComponentName componentName=intent.getParcelableExtra("realComponentName");           
        String className=componentName.getClassName();   
        Class<?> clazz=Class.forName(className); 
        if (clazz.newInstance() instanceof AppCompatActivity){  
              isReplaceOncreate=true;          
        }          
        //重新替换过来        
         intent.setComponent(componentName);       
     } catch (Exception e) {         
        e.printStackTrace();     
     }  
}

Manifest.xml信息如下


图片6.png

TestAidi2Activity我并没有在清单文件中注册。运行后打印信息如下


图片7.png

所有代码上传到github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容