zoukankan      html  css  js  c++  java
  • Android Hook技术

    原文:http://blog.csdn.net/u011068702/article/details/53208825 

    附:Android Hook 全面入侵监听器

    第一步、先爆项目demo照片,代码不多,不要怕

     

    第二步、应该知道Java反射相关知识

    如果不知道或者忘记的小伙伴请猛搓这里,Android插件化开发基础之Java反射机制研究  http://blog.csdn.net/u011068702/article/details/49863931


    第三步、应该知道Java静态代理知识

    如果不知道或者忘记的小伙伴请猛搓这里,Android插件化开发基础之静态代理模式 http://blog.csdn.net/u011068702/article/details/51765578
     

    第四部、应该知道Java动态代理知识

    如果不知道或者忘记的小伙伴请猛搓这里,Android插件化开发基础之Java动态代理(proxy)机制的简单例子  http://blog.csdn.net/u011068702/article/details/53185210
    上面只有代码的解释,如果不是很清楚的小伙伴可以再去到网上搜一搜相关知识。
     

    第五步、应该知道Android里面的ActivityThread类和Instrumentation类

     如果不知道或者忘记的小伙伴请猛搓这里,Android插件化开发之AMS与应用程序(客户端ActivityThread、Instrumentation、Activity)通信模型分析 http://blog.csdn.net/u011068702/article/details/53207039
    希望认真看,知道ActivityThread和Instrumentation是干嘛的,方便下面分析。


    第六步、理解hook,并且分析源码

    如果我们自己创建代理对象,然后把原始对象替换为我们的代理对象,那么就可以在这个代理对象为所欲为了;修改参数,替换返回值,我们称之为Hook。
    接下来我们来实现Hook掉startActivity这个方法,当每次调用这个方法的时候来做我们需要做的事情,我这里之打印了一些信息,读者可以更具自己的需求来修改,
    我们的目的是拦截startActivity方法,有点类是spring 里面AOP的思想。
     

    1、hook一般在哪里hook?

    首先分析我们需要hook哪些对象,也就是说我们要hook的地方,什么样的对象比较好Hook呢?一般是容易找到和不容易改变的对象,这思路就来了,不改变一般在我们类的里面单例对象是唯一对象,静态变量我们一般加上final static 修饰,有了final就不会改变,不变模式里面比如String类,里面就很多final,我们更加这些找到hook的地方。
     

    2、我们hook startActivity,分析startActivity到底是怎么实现的 

    我们每次Context.startActivity,由于Context的实现实际上是ContextImpl来实现的,;我们看ConetxtImpl类的startActivity方法:
     
    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. @Override  
    2. public void startActivity(Intent intent, Bundle options) {  
    3.     warnIfCallingFromSystemProcess();  
    4.     if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {  
    5.         throw new AndroidRuntimeException(  
    6.                 "Calling startActivity() from outside of an Activity "  
    7.                 + " context requires the FLAG_ACTIVITY_NEW_TASK flag."  
    8.                 + " Is this really what you want?");  
    9.     }  
    10.     mMainThread.getInstrumentation().execStartActivity(  
    11.         getOuterContext(), mMainThread.getApplicationThread(), null,  
    12.         (Activity)null, intent, -1, options);  
    13. }  

    我们可以看到startActivity方法最后还是通过Instrumentation对象来执行execStartActivity来实现的,如果你认真看了《Android插件化开发之AMS与应用程序(客户端ActivityThread、Instrumentation、Activity)通信模型分析 》上面这篇博客,我们知道ActivityThread就是主线程,也就是我们常说的UI线程,可以去更新UI,一个进程只有一个主线程,我们可以在这里hook.
    我们需要Hook掉我们的主线程对象,把主线程对象里面的mInstrumentation给替换成我们修改过的代理对象;要替换主线程对象里面的字段

    1)首先我们得拿到主线程对象的引用,如何获取呢?ActivityThread类里面有一个静态方法currentActivityThread可以帮助我们拿到这个对象类;但是ActivityThread是一个隐藏类,我们需要用反射去获取,代码如下:

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. // 先获取到当前的ActivityThread对象  
    2. Class<?activityThreadClass = Class.forName("android.app.ActivityThread");  
    3. Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");  
    4. currentActivityThreadMethod.setAccessible(true);  
    5. Object currentActivityThread = currentActivityThreadMethod.invoke(null);  


    2)拿到这个currentActivityThread之后,我们需要修改它的mInstrumentation这个字段为我们的代理对象,我们先实现这个代理对象,由于JDK动态代理只支持接口,而这个Instrumentation是一个类,我们可以手动写静态代理类,覆盖掉原始的方法,代码如下

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. package com.example.hookstartactivity;  
    2.   
    3. import java.lang.reflect.Method;  
    4.   
    5. import android.app.Activity;  
    6. import android.app.Instrumentation;  
    7. import android.app.Instrumentation.ActivityResult;  
    8. import android.content.Context;  
    9. import android.content.Intent;  
    10. import android.os.Bundle;  
    11. import android.os.IBinder;  
    12. import android.util.Log;  
    13.   
    14.   
    15. public class InstrumentationProxy extends Instrumentation {  
    16.   
    17.   
    18.      public static final String TAG = "InstrumentationProxy";  
    19.      public static final String EXEC_START_ACTIVITY = "execStartActivity";  
    20.        
    21.      // ActivityThread里面原始的Instrumentation对象,这里千万不能写成mInstrumentation,这样写  
    22.      //抛出异常,已亲测试,所以这个地方就要注意了  
    23.      public Instrumentation oldInstrumentation;  
    24.        
    25.      //通过构造函数来传递对象  
    26.      public InstrumentationProxy(Instrumentation mInstrumentation) {  
    27.          oldInstrumentation = mInstrumentation;  
    28.      }  
    29.   
    30.   
    31.      //这个方法是由于原始方法里面的Instrumentation有execStartActivity方法来定的  
    32.      public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,  
    33.              Intent intent, int requestCode, Bundle options) {  
    34.          Log.d(TAG, " 打印调用startActivity相关参数:  " + "who = [" + who + "], " +  
    35.               "ncontextThread = [" + contextThread + "], ntoken = [" + token + "], " +  
    36.               "ntarget = [" + target + "], nintent = [" + intent +  
    37.               "], nrequestCode = [" + requestCode + "], noptions = [" + options + "]");  
    38.   
    39.   
    40.         Log.i(TAG, "------------hook  success------------->");  
    41.         Log.i(TAG, "这里可以做你在打开StartActivity方法之前的事情");  
    42.         Log.i(TAG, "------------hook  success------------->");  
    43.         Log.i(TAG, "");  
    44.               
    45.         //由于这个方法是隐藏的,所以需要反射来调用,先找到这方法  
    46.         try {  
    47.             Method execStartActivity = Instrumentation.class.getDeclaredMethod(  
    48.                     EXEC_START_ACTIVITY,  
    49.                     Context.class, IBinder.class, IBinder.class, Activity.class,   
    50.                     Intent.class, int.class, Bundle.class);  
    51.             execStartActivity.setAccessible(true);  
    52.             return (ActivityResult) execStartActivity.invoke(oldInstrumentation, who,   
    53.                         contextThread, token, target, intent, requestCode, options);  
    54.             } catch (Exception e) {  
    55.                 //如果你在这个类的成员变量Instrumentation的实例写错mInstrument,代码讲会执行到这里来  
    56.                 throw new RuntimeException("if Instrumentation paramerter is mInstrumentation, hook will fail");  
    57.             }  
    58.      }  
    59. }  


    3)然后用代理对象替换,代码如下



    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. package com.example.hookstartactivity;  
    2.   
    3.   
    4. import java.lang.reflect.Field;  
    5. import java.lang.reflect.Method;  
    6.   
    7.   
    8. import android.app.Application;  
    9. import android.app.Instrumentation;  
    10. import android.util.Log;  
    11.   
    12.   
    13. public class MyApplication extends Application {  
    14.   
    15.   
    16.     public static final String TAG = "MyApplication";  
    17.     public static final String ACTIVIT_THREAD = "android.app.ActivityThread";  
    18.     public static final String CURRENT_ACTIVITY_THREAD = "currentActivityThread";  
    19.     public static final String INSTRUMENTATION = "mInstrumentation";  
    20.       
    21.     @Override  
    22.     public void onCreate() {  
    23.         try {  
    24.              //这个方法一般是写在Application的oncreate函数里面,如果你写在activity里面的oncrate函数里面就已经晚了  
    25.              attachContext();  
    26.         } catch (Exception e) {  
    27.              e.printStackTrace();  
    28.         }  
    29.     }  
    30.       
    31.     public static void attachContext() throws Exception{  
    32.           
    33.         //获取当前的ActivityThread对象  
    34.         Class<?activityThreadClass = Class.forName(ACTIVIT_THREAD);  
    35.         Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod(CURRENT_ACTIVITY_THREAD);  
    36.         currentActivityThreadMethod.setAccessible(true);  
    37.         Object currentActivityThread = currentActivityThreadMethod.invoke(null);  
    38.   
    39.   
    40.         //拿到在ActivityThread类里面的原始mInstrumentation对象  
    41.         Field mInstrumentationField = activityThreadClass.getDeclaredField(INSTRUMENTATION);  
    42.         mInstrumentationField.setAccessible(true);  
    43.         Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);  
    44.   
    45.   
    46.         //构建我们的代理对象  
    47.         Instrumentation evilInstrumentation = new InstrumentationProxy(mInstrumentation);  
    48.           
    49.         //通过反射,换掉字段,注意,这里是反射的代码,不是Instrumentation里面的方法  
    50.         mInstrumentationField.set(currentActivityThread, evilInstrumentation);  
    51.           
    52.         //做个标记,方便后面查看  
    53.         Log.i(TAG, "has go in MyApplication attachContext method");  
    54.     }  
    55. }  

    要注意这个替换要在Application里面的oncreate方法里面去执行,如果到Activity方法里面去执行的话就晚了,程序不会报错,但是hook不到。


    然后我是在主页面写了一个按钮,点击来触发startActivity的。
    MainActivity.java 文件如下

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. package com.example.hookstartactivity;  
    2.   
    3.   
    4. import android.content.Intent;  
    5. import android.os.Bundle;  
    6. import android.support.v7.app.ActionBarActivity;  
    7. import android.util.Log;  
    8. import android.view.Menu;  
    9. import android.view.MenuItem;  
    10. import android.view.View;  
    11. import android.view.View.OnClickListener;  
    12. import android.widget.TextView;  
    13.   
    14.   
    15. public class MainActivity extends ActionBarActivity {  
    16.       
    17.     public static final String TAG  =  "MainActivity";  
    18.     public TextView tv;  
    19.     @Override  
    20.     protected void onCreate(Bundle savedInstanceState) {  
    21.         super.onCreate(savedInstanceState);  
    22.         setContentView(R.layout.activity_main);  
    23.         tv = (TextView)findViewById(R.id.start);  
    24.         tv.setOnClickListener(new OnClickListener(){  
    25.             @Override  
    26.             public void onClick(View v) {  
    27.                 try {  
    28.                     Intent intent = new Intent(MainActivity.this, SecondActivity.class);   
    29.                     Bundle bundle = new Bundle();  
    30.                     Log.i(TAG, "-------------------------------->");  
    31.                     Log.i(TAG, "startActivity before");  
    32.                     Log.i(TAG, "-------------------------------->");  
    33.                       
    34.                     startActivity(intent, bundle);  
    35.                       
    36.                     Log.i(TAG, "-------------------------------->");  
    37.                     Log.i(TAG, "startActivity after");  
    38.                     Log.i(TAG, "-------------------------------->");  
    39.                 } catch (Exception e) {  
    40.                     e.printStackTrace();  
    41.                 }  
    42.             }  
    43.         } );  
    44.     }  
    45. }  


    跳转到的Second.java文件如下

    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. package com.example.hookstartactivity;  
    2.   
    3.   
    4. import android.os.Bundle;  
    5. import android.support.v7.app.ActionBarActivity;  
    6.   
    7.   
    8. public class SecondActivity extends ActionBarActivity{  
    9.       
    10.     @Override  
    11.     protected void onCreate(Bundle savedInstanceState) {  
    12.         super.onCreate(savedInstanceState);  
    13.         setContentView(R.layout.activity_second);  
    14.     }  
    15. }  


    第七步、运行代码

    启动项目在ubuntu终端打印的日志图片如下:
     
     
    然后我点击图标调用startActivity方法后ubuntu终端打印的日志图片如下:
     
    日志上面看到,hook success了
    看到ubuntu终端日志打印,前面显示了类,方便通过日志找bug,我用的是pidcat,下载pidcat 
    然后在ubuntu上面运行 
    [html] view plain copy
     
     print?在CODE上查看代码片派生到我的代码片
    1. pidcat.py 包名  
    就可以非常方便看的日志找bug了。
     

    第八步、总结

      通过这篇日志,希望打击可以更好的理解hook、java反射、静态代理、动态代理、ActivityThread、Instrumentation
      最后附上源码下载地址,需要的小伙伴请猛搓这里 HookStartActivityDemo
     

     

  • 相关阅读:
    1074. Reversing Linked List (25)
    1056. Mice and Rice (25)
    1051. Pop Sequence (25)
    1001. A+B Format (20)
    1048. 数字加密(20)
    1073. Scientific Notation (20)
    1061. Dating (20)
    1009. 说反话 (20)
    MyBatis学习总结(8)——Mybatis3.x与Spring4.x整合
    MyBatis学习总结(8)——Mybatis3.x与Spring4.x整合
  • 原文地址:https://www.cnblogs.com/wytiger/p/6136093.html
Copyright © 2011-2022 走看看