android 禁止插件化,Android 插件化实现方式(Hook)

news/2024/7/5 4:58:58

一、首先我们要找到Hook的点

1. 分析

我们先大概看下activity的启动流程(图片来自Android 插件化开发指南)

c3ad3ea8d58b

image

当我们调用startActivity的时候,AMS对我们要启动的Activity进行检查,是否在AndroidManifest中声明过,如果没有就报没有在AndroidManifest的错误。这个时候需要欺骗AMS,我们需要hook,要它去检查一个我们预配置的Activity,通过AMS的检查。

为什么不能直接hook掉AMS,AMS是系统的进程,管理者所有app的启动,不仅仅只是我们自己的app。

2. 看看源码怎么启动的(主要就是拦截这个方法startActivity)

//通过ActivityManagerNative.getDefault()获取一个对象,开始启动新的Activity

int result = ActivityManagerNative.getDefault().startActivity(whoThread,

who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),

token, target != null ? target.mEmbeddedID : null,

requestCode, 0, null, options);

3. 看看代码的实现

private Context mContext;

private Class> mProxyClass;

private String TAG = HookUtil.class.getName();

private final String EXTRA_ORIGIN_INTENT = "EXTRA_ORIGIN_INTENT";

public HookUtil(Context context, Class> proxyClass) {

this.mContext = context.getApplicationContext();

this.mProxyClass = proxyClass;

}

public void hookStartActivity() throws Exception {

//1. 通过反射,拿到IActivityManager对象;

Class amnClass = Class.forName("android.app.ActivityManagerNative");

//2. 获得指定的私有属性

Field gDefaultField = amnClass.getDeclaredField("gDefault");

gDefaultField.setAccessible(true);

// 获取字段上面的值传递null 证明是属性是static的,此处返回的是

// new Singleton()

Object gDefault = gDefaultField.get(null);

Class singletonClass = Class.forName("android.util.Singleton");

Field mInstanceField = singletonClass.getDeclaredField("mInstance");

mInstanceField.setAccessible(true);

//不是static的方法 ,需要传入当前使用的对象 此处返回的是IActivityManager

Object iamInstance = mInstanceField.get(gDefault);

Class iamClass = Class.forName("android.app.IActivityManager");

Object proxyInstance = Proxy.newProxyInstance(HookUtil.class.getClassLoader(),

new Class[]{iamClass},

// InvocationHandler 必须执行者,谁去执行

new StartActivityInvocationHandler(iamInstance));

//f.set(obj, "刘德华");

mInstanceField.set(gDefault, proxyInstance);

}

private class StartActivityInvocationHandler implements InvocationHandler {

// 方法执行者

private Object mObject;

public StartActivityInvocationHandler(Object object) {

this.mObject = object;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 替换Intent,过AndroidManifest.xml检测

if (method.getName().equals("startActivity")) {

Log.e(TAG,"Activity已经开始启动");

Log.e(TAG,"小弟到此一游!!!");

// 1.首先获取原来的Intent

Intent originIntent = (Intent) args[2];

// 2.创建一个安全的

Intent safeIntent = new Intent(mContext, mProxyClass);

args[2] = safeIntent;

// 3.绑定原来的Intent

safeIntent.putExtra(EXTRA_ORIGIN_INTENT, originIntent);

}

return method.invoke(mObject, args);

}

}

4. 初始化插件

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButterKnife.bind(this);

mButton.setText("test");

//初始化插件

HookUtil hookUtil =

new HookUtil(this, ProxyActivity.class);

try {

hookUtil.hookStartActivity();

} catch (Exception e) {

e.printStackTrace();

}

}

@OnClick(R.id.btn)

public void btnOnclick() {

Intent intent = new Intent(MainActivity.this, Main3Activity.class

);

startActivity(intent);

}

5. 打印结果(没有报错,打印了当前activity的stop方法,证明通过AMS的检查了)

com.dhcc.net.plug.HookUtil: Activity已经开始启动

com.dhcc.net.plug.HookUtil: 小弟到此一游!!!

二、启动时替换成我们自己的Actvity

1. 当AMS加载activty完成以后,就要启动activity了,这个时候他是通过ActivityThread类中的Handler去处理的。我们首先看看Handler是怎么分发消息的(我们处理msg.callback,将优先级最大化)

/**

* Handle system messages here.

*/

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

handleCallback(msg);

} else {

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}

2. 替换成为自己的Activity

public void hookLaunchActivity() throws Exception{

// 3.4.1 获取ActivityThread实例

Class> atClass = Class.forName("android.app.ActivityThread");

Field scatField = atClass.getDeclaredField("sCurrentActivityThread");

scatField.setAccessible(true);

Object sCurrentActivityThread = scatField.get(null);

// 3.4.2 获取ActivityThread中的mH

Field mhField = atClass.getDeclaredField("mH");

mhField.setAccessible(true);

Object mHandler = mhField.get(sCurrentActivityThread);

// 3.4 设置当前对象(也就是ActivityThread)的mH的成员变量

Class> handlerClass = Class.forName("android.os.Handler");

Field mCallbackField = handlerClass.getDeclaredField("mCallback");

mCallbackField.setAccessible(true);

mCallbackField.set(mHandler,new HandlerCallBack());

}

private class HandlerCallBack implements Handler.Callback{

@Override

public boolean handleMessage(Message msg) {

Log.e(TAG,"handleMessage");

// 每发一个消息都会走一次这个CallBack发放

if(msg.what == 100){

handleLaunchActivity(msg);

}

return false;

}

/**

* 开始启动创建Activity拦截

* @param msg

*/

private void handleLaunchActivity(Message msg) {

try {

Object record = msg.obj;

// 1.从record 获取过安检的Intent

Field intentField = record.getClass().getDeclaredField("intent");

intentField.setAccessible(true);

Intent proxyInent = (Intent) intentField.get(record);

// 2.从safeIntent中获取原来的originIntent

Intent realIntent = proxyInent.getParcelableExtra(EXTRA_ORIGIN_INTENT);

// 3.重新设置回去

if(realIntent != null){

Log.e(TAG,"启动我们自己的activity了");

intentField.set(record,realIntent);

}

}catch (Exception e){

e.printStackTrace();

}

}

}

3. 修改一下OnCreate方法

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButterKnife.bind(this);

mButton.setText("test");

//初始化插件

HookUtil hookUtil =new HookUtil(this, ProxyActivity.class);

try {

hookUtil.hookStartActivity();

hookUtil.hookLaunchActivity();

} catch (Exception e) {

e.printStackTrace();

}

}

4. 看看打印结果

com.dhcc.net.plug.HookUtil: Activity已经开始启动

com.dhcc.net.plug.HookUtil: 小弟到此一游!!!

com.dhcc.net.plug.HookUtil: 启动我们自己的activity了

com.dhcc.net E/Main3Activity: 我是没有注册的Activity


http://lihuaxi.xjx100.cn/news/275863.html

相关文章

深度学习三巨头也成了大眼萌,这个一键转换动画电影形象的网站竟因「太火」而下线...

机器之心报道作者:魔王、杜伟想不想在动画电影中拥有自己的角色?这个网站一键满足你的需求,不过竟因流量太大成本过高而下线。近期热映的电影《花木兰》总是让人回想起 1998 年上映的同名动画电影。说起来,动漫真人化或动画版翻拍…

VIRTUAL COMMUNITY INFORMATICS

这段时间老师叫我研究下VIRTUAL COMMUNITY INFORMATICS相关的课题,准备写毕业论文.老师说考虑到我开始上班了,时间精力有限,给我个轻松的课题(多体贴的老师啊^_^).不过说实在,我对这方面根本不了解,希望cnBlogs的朋友可以提供一些有用的信息给我,中英文的都行,谢谢!当然我自己也…

tabcontainer控件太长_AjaxControlToolKit--TabContainer控件的介绍收藏[摘录]

AjaxControlToolKit--TabContainer控件的介绍收藏1. Introduction:Tab本身就应该是个以页签形式显示组织网页内容的一个控件。在AJAX Control Tool Kit的控件中有TabContainer控件,它是一些TabPanel控件的载体,而每个TabPanel可以像标准的Panel控件一样,成为其它一些ASP.NET控件…

如何解决触摸屏的电磁干扰问题【转】

转自:http://design.eccn.com/design_2013040815090540.htm 开发具有触摸屏人机界面的移动手持设备是一项复杂的设计挑战,尤其是对于投射式电容触摸屏设计来说更是如此,它代表了当前多点触摸界面的主流技术。投射式电容触摸屏能够精确定位手指…

你真的会写二分检索吗?

点击上方“方志朋”,选择“设为星标”回复”666“获取新整理的面试资料来源:http://t.cn/EiwP9qJ1. 找出第一个与key相等的元素2. 找出最后一个与key相等的元素3. 查找第一个等于或者大于Key的元素4. 查找第一个大于key的元素5. 查找最后一个等于或者小于…

Nature:13个维度,手把手教“研究er”如何做学术报告

↑ 点击蓝字 关注视学算法作者丨蒋宝尚来源丨AI科技评论编辑丨极市平台交流科研进展和想法除了公开发表论文,最好的方式无疑是公开演讲、报告。毕竟,通过自己的语言将自己的科研想法“讲”给大家听,更多的是展示学术魅力。 文章地址&…

“掘金”金融AI落地,英特尔趟出一套通关攻略

有人说,金融业是最大的AI应用场景,但不管怎样,不可否认的事实是金融业已经从数字化走向AI化。某种程度上,AI与金融业有着天然的契合性:其一,金融业本身就是以数据为基本元素的行业,它为AI的模型…

干货 | 基于特征的图像配准用于缺陷检测

点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达特征提取基于特征的图像配准,具有非常广泛的应用,大致流程可以如下:经典的特征匹配算法有SIFT、SURF、ORB等,这三种方法在OpenCV里面都…