Android Hook技术

【Android Hook技术】五陵年少金市东,银鞍白马渡春风。这篇文章主要讲述Android Hook技术相关的知识,希望能为你提供帮助。
http://blog.csdn.net/u011068702/article/details/53208825   第一步、先爆项目demo照片,代码不多,不要怕

Android Hook技术

文章图片
  第二步、应该知道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?
Android Hook技术

文章图片
Android Hook技术

文章图片
  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?
Android Hook技术

文章图片
Android Hook技术

文章图片
  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?
Android Hook技术

文章图片
Android Hook技术

文章图片
  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,  "\n打印调用startActivity相关参数:  \n"  +  "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?
Android Hook技术

文章图片
Android Hook技术

文章图片
  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?
Android Hook技术

文章图片
Android Hook技术

文章图片
  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?
Android Hook技术

文章图片
Android Hook技术

文章图片
  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终端打印的日志图片如下:
Android Hook技术

文章图片
    然后我点击图标调用startActivity方法后ubuntu终端打印的日志图片如下:
Android Hook技术

文章图片
  日志上面看到,hook success了 看到ubuntu终端日志打印,前面显示了类,方便通过日志找bug,我用的是pidcat,下载pidcat  然后在ubuntu上面运行  [html]  view plain  copy     print?
Android Hook技术

文章图片
Android Hook技术

文章图片
  1. pidcat.py  包名   
就可以非常方便看的日志找bug了。   第八步、总结   通过这篇日志,希望打击可以更好的理解hook、java反射、静态代理、动态代理、ActivityThread、Instrumentation   最后附上源码下载地址,需要的小伙伴请猛搓这里  HookStartActivityDemo

























    推荐阅读