一、Hook技术概述


Hook技术的核心实际上是动态分析技术,动态分析是指在程序运行时对程序进行调试的技术。众所周知,Android系统的代码和回调是按照一定的顺序执行的,这里举一个简单的例子,如图所示。

对象A调用类对象B,对象B处理后将数据回调给对象A。接下来看看采用Hook的调用流程,如下图:上图中的Hook可以是一个方法或者一个对象,它就想一个钩子一样,始终连着AB,在AB之间互传信息的时候,hook会在中间做一些处理,比如修改方法的参数和返回值等,就这样hook起到了欺上瞒下的作用,我们把hook的这种行为称之为劫持。同理,大家知道,系统进程和应该进程之间是相互独立的,应用进程要想直接去修改系统进程,这个是很难实现的,有了hook技术,就可以在进程之间进行行为更改了。如图所示:可见,hook将自己融入到它所劫持的对象B所在的进程中,成为系统进程的一部分,这样我们就可以通过hook来更改对象B的行为了,对象B就称为hook点。

二、Hook Instrumentation


上面讲了Hook可以劫持对象,被劫持的对象叫hook点,用代理对象来替代这个Hook点,这样我们就可以在代理上实现自己想做的操作。这里我们用Hook startActivity来举例。Activity的插件化中需要解决的一个问题就是启动一个没有在AndroidManifest中注册的Activity,如果按照正常的启动流程是会报crash的。这里先简要介绍一下Activity的启动,具体的启动方式讲解还需移步专门的文献。

2.1 Activity的Hook点

启动Activity时应用进程会发消息给AMS,请求AMS创建Activity,AMS在SystemServer系统进程中,其与应用进程是隔离的,AMS管理所有APP的启动,所以我们无法在系统进程下做hook操作,应该在应用进程中。为了绕过AMS的验证,我们需要添加一个在Manifest中注册过的Activity,这个Activity称为占坑,这样可以达到欺上瞒下的效果,当AMS验证通过后再用插件Activity替换占坑去实现相应的功能。 核心功能两点:

  • 替换插件Activity为占坑Activity
  • 绕过AMS验证后需要还原插件Activity

启动Activity的时候会调用Activity的startActivity()如下:

   @Overridepublic void startActivity(Intent intent) {this.startActivity(intent, null);}

接着又调用了startActivity()

    @Overridepublic void startActivity(Intent intent, @Nullable Bundle options) {if (options != null) {startActivityForResult(intent, -1, options);} else {// Note we want to go through this call for compatibility with// applications that may have overridden the method.startActivityForResult(intent, -1);}}

查看startActivityForResult方法

    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {options = transferSpringboardActivityOptions(options);Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);if (ar != null) {mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(),ar.getResultData());}if (requestCode >= 0) {// If this start is requesting a result, we can avoid making// the activity visible until the result is received.  Setting// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the// activity hidden during this time, to avoid flickering.// This can only be done when a result is requested because// that guarantees we will get information back when the// activity is finished, no matter what happens to it.mStartedActivity = true;}cancelInputsAndStartExitTransition(options);// TODO Consider clearing/flushing other event sources and events for child windows.} else {if (options != null) {mParent.startActivityFromChild(this, intent, requestCode, options);} else {// Note we want to go through this method for compatibility with// existing applications that may have overridden it.mParent.startActivityFromChild(this, intent, requestCode);}}}

上述方法中调用mInstrumentation的execStartActivity方法来启动Activity,这个mInstrumentation是Activity的成员变量,我们就选择Instrumentation为Hook点,用代理的Instrumentation去替换原始的Instrumentation来完成Hook,如下是代理类:

public class InstrumentationProxy extends Instrumentation {private Instrumentation mInstrumentation;private PackageManager mPackageManager;public InstrumentationProxy(Instrumentation instrumentation, PackageManager packageManager) {this.mInstrumentation = instrumentation;this.mPackageManager = packageManager;}public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {List<ResolveInfo> resolveInfo = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL);//判断启动的插件Activity是否在AndroidManifest.xml中注册过if (null == resolveInfo || resolveInfo.size() == 0) {//保存目标插件intent.putExtra(HookHelper.REQUEST_TARGET_INTENT_NAME, intent.getComponent().getClassName());//设置为占坑Activityintent.setClassName(who, "replugin.StubActivity");}try {Method execStartActivity = Instrumentation.class.getDeclaredMethod("execStartActivity",Context.class, IBinder.class, IBinder.class, Activity.class,Intent.class, int.class, Bundle.class);return (ActivityResult) execStartActivity.invoke(mInstrumentation, who, contextThread, token, target, intent, requestCode, options);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return null;}public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException,IllegalAccessException, ClassNotFoundException {String intentName = intent.getStringExtra(HookHelper.REQUEST_TARGET_INTENT_NAME);if (!TextUtils.isEmpty(intentName)) {return super.newActivity(cl, intentName, intent);}return super.newActivity(cl, className, intent);}}

InstrumentationProxy类继承类Instrumentation,实现了类execStartActivity方法,接着通过反射去用原始Instrumentation的execStartActivity方法,这就是替换为占坑Activity的过程。Activity的创建是在ActivityThread中,里面有个performLaunchActivity方法;

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {...try {java.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);StrictMode.incrementExpectedActivityCount(activity.getClass());r.intent.setExtrasClassLoader(cl);r.intent.prepareToEnterProcess();if (r.state != null) {r.state.setClassLoader(cl);}}...activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.configCallback);...
}

这里的newActivity就是创建Activity的过程,我们同样的在代理类中去实现这个方法,这就是还原插件Activity 的过程。

接下来我们看个例子: 占位坑Activity:

public class StubActivity extends BaseActivity {@Overridepublic int bindLayout() {return R.layout.activity_stub;}@Overridepublic void initViews() {}@Overridepublic void onClick(View v) {}
}

这个Activity一定是需要在AndroidManifest中去注册。 再写一个插件Activity

public class TargetActivity extends BaseActivity {@Overridepublic int bindLayout() {return R.layout.activity_target;}@Overridepublic void initViews() {}@Overridepublic void onClick(View v) {}
}

都是很简单的Activity,TargetActivity并没有注册,现在我们需要启动这个Activity。代理类上面代码已经贴出来了。接下来就是替换代理类,达到Hook的目的,我们在Application中做这个事情:

public class MyApplication extends Application {@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);hookActivityThreadInstrumentation();}private void hookActivityThreadInstrumentation() {try {Class<?> activityThreadClass=Class.forName("android.app.ActivityThread");Field activityThreadField=activityThreadClass.getDeclaredField("sCurrentActivityThread");activityThreadField.setAccessible(true);//获取ActivityThread对象sCurrentActivityThreadObject activityThread=activityThreadField.get(null);Field instrumentationField=activityThreadClass.getDeclaredField("mInstrumentation");instrumentationField.setAccessible(true);//从sCurrentActivityThread中获取成员变量mInstrumentationInstrumentation instrumentation= (Instrumentation) instrumentationField.get(activityThread);//创建代理对象InstrumentationProxyInstrumentationProxy proxy=new InstrumentationProxy(instrumentation,getPackageManager());//将sCurrentActivityThread中成员变量mInstrumentation替换成代理类InstrumentationProxyinstrumentationField.set(activityThread,proxy);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

这样就把原始的Instrumentation替换为代理的了,具体的操作我们在InstrumentationProxy中去做实现。接下来我们就是从主界面跳转插件Activity了:

public class PluginActivity extends BaseActivity {@Overridepublic int bindLayout() {return R.layout.activity_stub;}@Overridepublic void initViews() {Log.d("", "initViews: ");findViewById(R.id.btn_start_replugin).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {startActivity(new Intent(PluginActivity.this, TargetActivity.class));}});}@Overridepublic void onClick(View v) {}public static void startActivity(Context context) {Intent i = new Intent(context, PluginActivity.class);context.startActivity(i);}}

转载于:https://juejin.im/post/5c67da6251882562547b99ab

Hook技术之Hook Activity相关推荐

  1. java hook技术_API Hook基本原理和实现 - - JavaEye技术网站

    hook是什么? windows系统下的编程,消息message的传递是贯穿其始终的.这个消息我们可以简单理解为一个有特定意义的整数,正如我们看过的老故事片中的"长江长江,我是黄河" ...

  2. python hook技术,python hook监听事件详解

    本文实例为大家分享了python hook监听事件的具体代码,供大家参考,具体内容如下 # -*- coding: utf-8 -*- # # by oldj http://oldj.net/ # i ...

  3. HOOK技术四-插件中Activity启动实战

    系列文章 HOOK技术一-HOOK技术初探 HOOK技术二-未注册Activity的启动 HOOK技术三-插件Activity启动前提分析 HOOK技术四-插件中Activity启动实战 HOOK技术 ...

  4. 【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  5. Android安全:Hook技术

    原址 一.Hook技术  1.Hook英文翻译为"钩子",而钩子就是在事件传送到终点前截获并监控事件的传输,像个钩子钩上事件一样,并且能够在钩上事件时,处理一些自己特定的事件:  ...

  6. 黑客爱用的 HOOK 技术大揭秘

    福利:网络安全重磅福利:入门&进阶全套282G学习资源包免费分享! 什么是 HOOK 技术? 病毒木马为何惨遭杀软拦截? 商业软件为何频遭免费破解? 系统漏洞为何能被补丁修复? 这一切的背后到 ...

  7. Android安全 Hook技术,AndroidHook技术分析.pdf-北京理工大学信息系统及安全对抗实验中心.PDF...

    AndroidHook技术分析.pdf-北京理工大学信息系统及安全对抗实验中心.PDF The name of the DepartmentBeijing Forest Studio 北京理工大学信息 ...

  8. 黑客爱用的 HOOK 技术大揭秘!

    黑客爱用的 HOOK 技术大揭秘! 什么是 HOOK 技术? 病毒木马为何惨遭杀软拦截? 商业软件为何频遭免费破解? 系统漏洞为何能被补丁修复? 这一切的背后到底是人性的扭曲,还是道德的沦丧,敬请收看 ...

  9. 欲知己之所防,先知彼之所攻——论Hook 技术的攻防对抗

    矛盾的同一性与斗争性原理几乎适用于所有攻防对抗. 上期,我们在<当硬件属性不再作为设备指纹的标识,我们该如何保证设备指纹的唯一性>一文中曾介绍了硬件ID 作为设备指纹的基础属性的发展演变- ...

最新文章

  1. NETGEAR拒绝连接请求_3分钟理解HTTP的请求与响应
  2. rsync实现linux间同步
  3. python网课阿里云_关于python视频教程的阿里云网站内容
  4. IDE接口驱动程序移植
  5. 关于不能够精确的对浮点数进行运算的问题
  6. java mongodb gridfs_查询MongoDB GridFS元数据(Java)
  7. 分压式静态工作点稳定电路
  8. 使用php,使用 PHP
  9. Android 系统(178)---Android N to O升级准则
  10. angularjs java 实例_[Java教程]angularjs小练习(分别通过ng
  11. ios safari 模拟器_Safari调试iOS应用
  12. 调用QQ/TIM算法实现获取当前登陆账号和ClientKey
  13. java坦克大战地图_『坦克大战的基本地图系统(一)』Clickteam Fusion系列教程(13)...
  14. 在 Java 应用程序中访问USB设备
  15. 企业网络拓扑图简述20200805
  16. 《漫步华尔街》书中的精髓:在美国市场中,怎样用“随机漫步”的投资方法让自己的投资收益稳步增长
  17. 博士申请 | 美国北卡州立大学郭志山教授招收机器学习方向全奖博士生
  18. 用计算机怎么计算税率,税率计算器 个税计算器2017税率表公式
  19. Ant下载安装配置及使用
  20. uniapp 定位服务_uniapp使用高德地图定位

热门文章

  1. LeetCode 257. 二叉树的所有路径 思考分析
  2. dbms和sql_DBMS | 并发控制和各种并发控制方法
  3. 三、线性回归实验分析
  4. 实训09.11:数据库一些简单操作
  5. jbod ugood 磁盘驱动状态_如何检查Mac磁盘空间,mac磁盘空间其他怎么清理
  6. 被远程机器长时间无响应 (错误码:[308])_自动折叠式“Rollbot”为完全不受束缚的软机器人铺平了道路...
  7. UVA 116——Unidirectional TSP
  8. 数据可视化【十二】 颜色图例和尺寸图例
  9. C++(纯)虚函数重写时访问权限更改问题
  10. 指针与零的比较以及浮点型与零的比较