Hook技术之Hook Activity

一、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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(第三周)周报

此作业要求https://edu.cnblogs.com/campus/nenu/2018fall/homework/2143 1.本周PSP 总计&#xff1a;1422 min 2.本周进度条 (1)代码累积折线图 (2)博文字数累积折线图 4.PSP饼状图 转载于:https://www.cnblogs.com/gongylx/p/9761852.html

功能测试代码python_如何使您的Python代码更具功能性

功能测试代码pythonFunctional programming has been getting more and more popular in recent years. Not only is it perfectly suited for tasks like data analysis and machine learning. It’s also a powerful way to make code easier to test and maintain.近年来&am…

layou split 属性

layou split&#xff1a;true - 显示侧分栏 转载于:https://www.cnblogs.com/jasonlai2016/p/9764450.html

BZOJ4503:两个串(bitset)

Description 兔子们在玩两个串的游戏。给定两个字符串S和T&#xff0c;兔子们想知道T在S中出现了几次&#xff0c;分别在哪些位置出现。注意T中可能有“?”字符&#xff0c;这个字符可以匹配任何字符。Input 两行两个字符串&#xff0c;分别代表S和TOutput 第一行一个正整数k&…

C#Word转Html的类

C#Word转Html的类/**//******************************************************************** created: 2007/11/02 created: 2:11:2007 23:13 filename: D:C#程序练习WordToChmWordToHtml.cs file path: D:C#程序练习WordToChm file bas…

分库分表的几种常见形式以及可能遇到的难题

前言 在谈论数据库架构和数据库优化的时候&#xff0c;我们经常会听到“分库分表”、“分片”、“Sharding”…这样的关键词。让人感到高兴的是&#xff0c;这些朋友所服务的公司业务量正在&#xff08;或者即将面临&#xff09;高速增长&#xff0c;技术方面也面临着一些挑战。…

iOS 钥匙串的基本使用

级别&#xff1a; ★☆☆☆☆ 标签&#xff1a;「钥匙串」「keychain」「iOS」 作者&#xff1a; WYW 审校&#xff1a; QiShare团队 前言 &#xff1a; 项目中有时会需要存储敏感信息&#xff08;如密码、密钥等&#xff09;&#xff0c;苹果官方提供了一种存储机制--钥匙串&a…

线性回归和将线拟合到数据

Linear Regression is the Supervised Machine Learning Algorithm that predicts continuous value outputs. In Linear Regression we generally follow three steps to predict the output.线性回归是一种监督机器学习算法&#xff0c;可预测连续值输出。 在线性回归中&…

Spring Boot MyBatis配置多种数据库

mybatis-config.xml是支持配置多种数据库的&#xff0c;本文将介绍在Spring Boot中使用配置类来配置。 1. 配置application.yml # mybatis配置 mybatis:check-config-location: falsetype-aliases-package: ${base.package}.modelconfiguration:map-underscore-to-camel-case: …

小米盒子4 拆解图解_我希望当我开始学习R时会得到的盒子图解指南

小米盒子4 拆解图解Customizing a graph to transform it into a beautiful figure in R isn’t alchemy. Nonetheless, it took me a lot of time (and frustration) to figure out how to make these plots informative and publication-quality. Rather than hoarding this …

组态王仿真随机数

1、新建IO设备&#xff0c;选择PLC---亚控---仿真PLC&#xff0c;一直“下一步”。 2、在“数据词典”中新建变量“Tag1”&#xff0c;双击Tag1&#xff0c;变量类型选&#xff1a;I/O实数&#xff1b;初始值设为&#xff1a;0.6&#xff1b;最小值设为&#xff1a;0.5&#xf…

蓝牙一段一段_不用担心,它在那里存在了一段时间

蓝牙一段一段You’re sitting in a classroom. You look around and see your friends writing something down. It seems they are taking the exam, and they know all the answers (even Johnny who, how to say it… wasn’t the brilliant one). You realize that your ex…

Linux基础命令---ifup、ifdown

ifupifup指令用来启动网络接口设备&#xff0c;设备必须是定义在“/etc/sysconfig/network-scripts/ifcfg-ethX”或者“/etc/sysconfig/network”的文件。这些脚本通常使用一个参数&#xff1a;配置的名称(例如eth0)。在引导序列中&#xff0c;使用“boot”的第二个参数调用它们…

OllyDBG 入门之四--破解常用断点设

OllyDBG 入门之四--破解常用断点&#xff08;转&#xff09; 软件汉化2010-07-08 16:25:23 阅读76评论0 字号&#xff1a;大中小 订阅 bpx hmemcpy 破解万能断点&#xff0c;拦截内存拷贝动作 bpx Lockmytask 当你用其它断点都无效时可以试一下&#xff0c;这个断点拦截…

POJ1204 Word Puzzles

传送门 这题果然是AC自动机的大好题&#xff01; 题目的大意是&#xff0c;给定一个l*c的大网格&#xff0c;每个格子里有一个字符&#xff0c;每个格子可以向八个方向形成字符串&#xff0c;问给定的字符串在哪里能被匹配以及在网格中出现的方向&#xff08;A代表北&#xff0…

普通话测试系统_普通话

普通话测试系统Traduzido/adaptado do original por Vincius Barqueiro a partir do texto original “Writing Alt Text for Data Visualization”, escrito por Amy Cesal e publicado no blog Nightingale.Traduzido / adaptado由 VinciusBarqueiro 提供原始 文本“为数据可…

Mac OS 被XCode搞到无法正常开机怎么办?

Mac OS 被XCode搞到无法正常开机怎么办&#xff1f; 第一天拿到这台air的时候&#xff0c;迫不及待地把从别处搜集来的XCode 3.2.5iOS SDK 4.1的dmg安装了上来&#xff0c;结果系统直接崩溃&#xff0c;再开机就不能正常开机&#xff0c;总是碰到kernel panic。这不坑爹吗…… …

美国队长3:内战_隐藏的宝石:寻找美国最好的秘密线索

美国队长3:内战There are plenty of reasons why one would want to find solitude in the wilderness, from the therapeutic effects of being immersed in nature, to not wanting to contribute to trail degradation and soil erosion on busier trails.人们有很多理由想要…

Java入门第三季——Java中的集合框架(中):MapHashMap

1 package com.imooc.collection;2 3 import java.util.HashSet;4 import java.util.Set;5 6 /**7 * 学生类8 * author Administrator9 * 10 */ 11 public class Student { 12 13 public String id; 14 15 public String name; 16 17 public Set<…

【译】 WebSocket 协议第八章——错误处理(Error Handling)

概述 本文为 WebSocket 协议的第八章&#xff0c;本文翻译的主要内容为 WebSocket 错误处理相关内容。 错误处理&#xff08;协议正文&#xff09; 8.1 处理 UTF-8 数据错误 当终端按照 UTF-8 的格式来解析一个字节流&#xff0c;但是发现这个字节流不是 UTF-8 编码&#xff0c…