动态代理实现方法以及对象HooK
来源:互联网 发布:qq空间 for mac客户端 编辑:程序博客网 时间:2024/05/21 12:08
上一篇文章里面已经把动态代理的作用以及实现方法分析了一下,很明显我们可以用HooK做很多事情,比如例子里面的代理做了拿了回扣和偷换行货这种肮脏龌龊的事情。
在真正应用的时候我们可以做更多的事情,比如用户登录的时候动态代理他的验证方法,是不是就可以获取用户的账号密码呢?还有比如Activity的启动,我们使用动态代理的手段将contextImp
对象进行动态代理,对startActivity()
函数进行修改,比如修改Intent的flag或者Intent内部数据等等,导致跳转的页面发生变化,或者改变传递的信息等等。
而实现HOOK的步骤一般分为三步:
1. 寻找Hook点,原则是静态变量或者单例对象,尽量Hook pulic的对象和方法,非public不保证每个版本都一样,需要适配。
2. 选择合适的代理方式,如果是接口可以用动态代理;如果是类可以手动写代理也可以使用cglib。
3. 偷梁换柱——用代理对象替换原始对象
下面以改变startActivity()
逻辑为例来展示HOOK的威力。
首先我们得找到被Hook的对象,我称之为Hook点;什么样的对象比较好Hook呢?自然是容易找到的对象。什么样的对象容易找到?静态变量和单例;在一个进程之内,静态变量和单例变量是相对不容易发生变化的,因此非常容易定位,而普通的对象则要么无法标志,要么容易改变。我们根据这个原则找到所谓的Hook点。
然后我们分析一下startActivity的调用链,找出合适的Hook点。
我们知道对于Context.startActivity
和 Activity.startActivity
的调用链与之不同,由于Context的实现实际上是ContextImpl;我们看ConetxtImpl类的startActivity
方法:
@Overridepublic void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity)null, intent, -1, options);}
可以看到contextImp的startActivity的逻辑是:
(1) 先判断intent的flag是不是FLAG_ACTIVITY_NEW_TASK
类型的,如果是走步骤2;如果不是,说明startActivity
不是在activity中调用的,在activity中调用的话,走的流程如下:
Step 1. Activity.startActivity通过指定名称“activity.subactivity”来告诉应用程序框架层,它要隐式地启动SubActivity。所不同的是传入的参数intent没有Intent.FLAG_ACTIVITY_NEW_TASK标志,表示这个SubActivity和启动它的MainActivity运行在同一个Task中。 Step 2. Activity.startActivityForResult Step 3. Instrumentation.execStartActivity Step 4. ActivityManagerProxy.startActivity 详见罗老师源码分析:[Android应用程序内部启动Activity过程(startActivity)的源代码分析](http://blog.csdn.net/Luoshengyang/article/details/6703247)
(2) 调用了ActivityThread类的mInstrumentation成员的execStartActivity方法。
注意到,ActivityThread 实际上是主线程,而主线程一个进程只有一个mInstrumentation,只在应用刚刚打开第一个activity的时候创建(单例模式),之后不会发生变化,而且看到不管是Activity中startActivity还是在其他地方,调用的都是mInstrumentation.execStartActivity()
,因此mInstrumentation对象是一个良好的Hook点。
分析完我们的HOOK的点后,接下来就要替换了我们的mInstrumentation对象了,代码如下:
第一步首先通过反射把当前进程的ActivityThread对象拿到手:
// 先获取到当前的ActivityThread对象Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");currentActivityThreadMethod.setAccessible(true);Object currentActivityThread = currentActivityThreadMethod.invoke(null);
第二步,虽然动态代理可以非常方便的进行代理对象,但是我们的mInstrumentation对象不是接口,因此没有办法采用动态代理方式创建代理类,那就没办法只能通过继承来静态代理我们的mInstrumentation,然后覆写我们的mInstrumentation的execStartActivity方法,代码如下:
public class EvilInstrumentation extends Instrumentation { private static final String TAG = "EvilInstrumentation"; // ActivityThread中原始的对象, 保存起来 Instrumentation mBase; public EvilInstrumentation(Instrumentation base) { mBase = base; } public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { // Hook之前, XXX到此一游! Log.d(TAG, "\n执行了startActivity, 参数如下: \n" + "who = [" + who + "], " + "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " + "\ntarget = [" + target + "], \nintent = [" + intent + "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]"); // 开始调用原始的方法, 调不调用随你,但是不调用的话, 所有的startActivity都失效了. // 由于这个方法是隐藏的,因此需要使用反射调用;首先找到这个方法 try { Method execStartActivity = Instrumentation.class.getDeclaredMethod( "execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); execStartActivity.setAccessible(true); return (ActivityResult) execStartActivity.invoke(mBase, who, contextThread, token, target, intent, requestCode, options); } catch (Exception e) { // 某该死的rom修改了 需要手动适配 throw new RuntimeException("do not support!!! pls adapt it"); } }}
上面代码就是静态代理的代码,execStartActivity中先是打印一些信息,然后通过反射拿到Instrumentation的execStartActivity方法,进行调用,之所以要反射是因为这个方法不可见,必须要反射才能调用,这是静态代理所不能实现的功能。
创建了Instrumentation的代理对象,又找到了HOOK点,最后就只需要把需要替换的对象换掉就可以了。
第三步,使用反射进行Instrumentation对象的替换:代码如下:
public static void attachContext() throws Exception{ // 1先获取到当前的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 2拿到原始的 mInstrumentation字段 Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); //3 创建代理对象,使用反射偷梁换柱 Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation); mInstrumentationField.set(currentActivityThread, evilInstrumentation);}
最后都完成了,那么就要测试一下了,看看能不能打印出我们代理函数里面的数据了,打印结果如下:
07-11 22:19:20 9207-9207/com.dynamic_proxy_hook.app D/EvilInstrumentation:执行了startActivity,参数如下:who = [android.app.Application@76726c01],contextThread = [android.app.ActivityThread$ApplicationThread@4353489dd1],token = [null],target = [null],intent = [Intent { act=android.intent.action.test dat=sadjksadk flg-0x10000000}],requestCode = [-1],options = [null]
可以看到打印出来了,结果就是HOOK成功了。
- 动态代理实现方法以及对象HooK
- hook+android动态代理
- 动态代理hook自己进程的startActivity方法
- JAVA动态代理实现方法
- Android 动态代理以及利用动态代理实现 ServiceHook
- 静态与动态代理以及动态代理代码实现
- Java静态动态代理以及spring实现代理
- java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总
- Java动态代理实现接口方法
- Java动态代理的实现方法
- Xposed: 勾住(Hook) Android应用程序对象的方法,实现AOP
- 24.代理以及动态代理
- Android插件化开发-hook动态代理
- Android插件化开发-hook动态代理
- Android 插件之Hook机制动态代理
- JavaSE-AOP(Hook)实现机制(JDK/cglib动态代理/ASM/Javassist/AspectJ)
- Java动态代理以及InvocationHandler中invoke()方法笔记
- so注入(inject)和挂钩(hook) 以及同进程动态库so文件的函数hook方法介绍
- day01 如有错误还望指教
- [Terminal Game Center]贪吃蛇
- Cocos2d-x 之调度器 Scheduler
- HDU1398(生成函数)
- Ubuntu 安装 RabbitMQ 和PHP扩展
- 动态代理实现方法以及对象HooK
- 英语语法2-一般过去时
- ACE4.6.3编译过程(VS2012)
- LeetCode 79. Word Search
- 在block中滥用weakSelf的教训
- iOS高级调试&逆向技术-汇编寄存器调用
- shell配置java环境变量和批处理配置环境变量
- angularJs中的CSS类和样式
- POJ 1083 Moving Tables