【Android Framework系列】第10章 PMS之Hook实现广播的调用

1 前言

前面章节我们学习了【Android Framework系列】第4章 PMS原理我们了解了PMS原理,【Android Framework系列】第9章 AMS之Hook实现登录页跳转我们知道AMS可以Hook拦截下来实现未注册Activity页面的跳转,本章节我们来尝试一下HookPMS实现广播的发送

这里我们只简单介绍一下HookPMS思路和重点代码,需要详细了解的请到文末处项目地址下载查看。

同学们是否遇到需要动态下发组件的需求?是通过什么方法实现的呢?
之前的章节里我们分析了PMS相关的原理,简单回顾一下PMS
PMS是包管理系统服务,用来管理所有的包信息,包括应用安装、卸载、更新以及解析AndroidManifest.xml。手机开机后,它会遍历设备上/data/app//system/app/目录下的所有apk文件,通过解析所有安装应用的AndroidManifest.xml,将xml中的数据(应用信息权限四大组件等)信息都缓存到内存中,后续提供给AMS等服务使用。

通过HookPMS是否可以来实现动态apk组件的下发,本章节我们通过HookPMS拿到PMS内的receivers(BroadcastReceiver)来实现调用动态下发apkBroadcastReceiver。**

2 实现

PMS内管理了四大组件,会将所有apk都解析后保存对应四大组件的信息,分别保存到对应的集合Activityreceiversprobidersservices

/frameworks/base/core/java/android/content/pm/PackageParser.java

6460          @UnsupportedAppUsage
6461          public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
6462          @UnsupportedAppUsage
6463          public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
6464          @UnsupportedAppUsage
6465          public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
6466          @UnsupportedAppUsage
6467          public final ArrayList<Service> services = new ArrayList<Service>(0);

本章节我们HookPMS的BroadcastReceiver那就得获取到receivers集合。

2.1 实现思路

  1. 先将要下发的apk下载下来(我们这里直接加到设备的存储里)
  2. apk通过Hook到的PMS方法解压解析到PMS
  3. 再通过HookPMS拿到装有BroadcastReceiver信息的receivers集合
  4. 将动态下发apk里的BroadcastReceiver名称作为参数,通过HookPMS的方法进调用,从而实现本章节的目的。

2.2 项目结构

在这里插入图片描述
上图我们可以看到,有两个module,分别为app和pmsbr。
app模块:
HookPMS的主要逻辑在PMSPackageParser类,对apk进行解析和PMSHookClientActivity用于发送和接收广播,对HookPMS进行操作。

pmsbr模块:
这个module其实只是为了打包成动态apk,内部的BroadcastReceiver用于验证HookPMS后是否能调用动态下发apk内的这个BroadcastReceiver

2.3 ClientActivity

package com.yvan.hookpms;import android.Manifest;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import java.io.File;/*** @author yvan* @date 2023/8/7* @description*/
public class ClientActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_client);checkPermission(this);IntentFilter filter = new IntentFilter();filter.addAction("com.yvan.client");registerReceiver(new FinishBroadcastReceiver(), filter);}public static boolean checkPermission(Activity activity) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {activity.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);}return false;}public void registerBroaderCast(View view) {PMSPackageParser pmsPackageParser = new PMSPackageParser();try {pmsPackageParser.parserReceivers(this,new File(getFilesDir(), "input.apk"));} catch (Exception e) {e.printStackTrace();}}public void sendBroaderCast(View view) {Toast.makeText(this, "1.发送消息给server", Toast.LENGTH_SHORT).show();view.postDelayed(() -> {Intent intent = new Intent();intent.setAction("com.yvan.server");sendBroadcast(intent);}, 3000);}static class FinishBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "3.收到server回复消息", Toast.LENGTH_SHORT).show();}}}

activity_client.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".ClientActivity"><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:gravity="center"android:onClick="registerBroaderCast"android:text="注册广播" /><Buttonandroid:layout_width="match_parent"android:layout_height="50dp"android:gravity="center"android:onClick="sendBroaderCast"android:text="发送广播" /></LinearLayout>
  1. 页面打开后,即创建一个BroadcastReceiver用于接收来自server的回复消息,两个按钮分别“注册广播”、“发送广播”。
  2. 点击“注册广播”:将下发的apk(此处是用pmsbr模块打包的input.pak放于设备的data/data/com.yvan.hookpms/files这个私有目录下)通过PMS解析,然后通过HookPMS实现下发apk中的广播注册。
  3. 点击“发送广播”:给上面1中注册的广播发送消息

我们主要来看看第2步注册广播,这里是本章的重点HookPMS

2.4 PMSPackageParser

package com.yvan.hookpms;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.PackageManager;import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;import dalvik.system.DexClassLoader;/*** @author yvan* @date 2023/8/7* @description*/
public class PMSPackageParser {public void parserReceivers(Context context, File apkFile) throws Exception {Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser");Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage",File.class, int.class);parsePackageMethod.setAccessible(true);Object packageParser = packageParserClass.newInstance();Object packageObj = parsePackageMethod.invoke(packageParser, apkFile,PackageManager.GET_RECEIVERS);packageObj.hashCode();Field receiversField = packageObj.getClass().getDeclaredField("receivers");List receivers = (List) receiversField.get(packageObj);// AndroidManifest---> Package对象   描述信息DexClassLoader dexClassLoader = new DexClassLoader(apkFile.getAbsolutePath(),context.getDir("plugin", Context.MODE_PRIVATE).getAbsolutePath(),null, context.getClassLoader());// 动态注册Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component");Field intentsField = componentClass.getDeclaredField("intents");for (Object receiverObject : receivers) {String name = (String) receiverObject.getClass().getField("className").get(receiverObject);// class  --->对象try {BroadcastReceiver broadcastReceiver = (BroadcastReceiver) dexClassLoader.loadClass(name).newInstance();List<? extends IntentFilter> filters = (List<? extends IntentFilter>)intentsField.get(receiverObject);for (IntentFilter filter : filters) {context.registerReceiver(broadcastReceiver, filter);}} catch (Exception e) {e.printStackTrace();}}}
}

我们看到HookPMS做了以下操作:

  1. Hook的是PMS解析类android.content.pm.PackageParserparsePackage()方法获取到Package对象
  2. 然后获取到Package对象内存储BroadcastReceiverreceivers集合
  3. 将动态下发的apk通过类加载器加载
  4. 然后遍历PMS的receivers集合找到这个下发apk中注册的广播
  5. 最后注册这个广播。

这里注册的这个广播是动态下发组件input.apkPMSBroadcastReceiver,我们继续往下看

2.5 PMSBroadcastReceiver

package com.yvan.pmsbr;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.widget.Toast;/*** @author yvan* @date 2023/8/7* @description*/
public class PMSBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 收到你的信息了Toast.makeText(context, "2.接收到client的消息", Toast.LENGTH_SHORT).show();new Handler(Looper.getMainLooper()).postDelayed(() -> {Intent intent1 = new Intent();intent1.setAction("com.yvan.client");context.sendBroadcast(intent1);}, 3000);}
}

PMSBroadcastReceiver中主要是接收来自client的广播,然后给client回复一个广播。我们在上面FinishBroadcastReceiver中收到server的回复后,弹出Toast展示则表示完成了,发送、接收动态下发组件input.apk的消息。
在这里插入图片描述

3 总结

到这里我们就完成了整个动态下发apk的调用及被调用,这里我们再稍微总结一下:
主要通过HookPMS实现将动态下发的apk进行解析,将信息存储在PMS内,然后对PMS中装有BroadcastReceiver信息的receivers集合拿到,程序(Client)发送广播给动态下发apk内定义好的广播(Server),该广播(Server)对程序(Client)作出回应,然后在程序(Client)接收回应(类似TCP的三次握手逻辑)。从而实现本章节对PMS进行Hook的目的。
文章只做核心HookPMS代码思路的分析,这里是项目地址,小伙伴可以自行下载查看,别忘了点Star喔,谢谢!!

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

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

相关文章

Stable Diffusion + Deform制作指南

1.安装sd以及deform插件,更新后记得重启 需要安装ffmpeg https://ffmpeg.org/download.html 选择对应版本然后安装 如果是windows需要解压后将ffmpeg的bin目录配置在电脑的环境变量里面。 2.准备一张初始开始图片 3.填写参数,这里面参数要注意,宽高一定是32的倍数。如果填写…

matplotlib绘制位置-时序甘特图

文章目录 1 前言2 知识点2.1 matplotlib.pyplot.barh2.2 matplotlib.legend的handles参数 3 代码实现4 绘制效果5 总结参考 1 前言 这篇文章的目的是&#xff0c;总结记录一次使用matplotlib绘制时序甘特图的经历。之所以要绘制这个时序甘特图&#xff0c;是因为22年数模研赛C…

电力能源管理系统在生物制药行业的应用

安科瑞 华楠 摘要&#xff1a;根据生物制品类企业的电力能源使用特点&#xff0c;制定了符合公司实际情况的能源管理系统&#xff0c;介绍了该系统的架构及其在企业的应用情况&#xff0c;提升了公司能源数据的实时监控能力&#xff0c;优化了公司能源分配&#xff0c;降低了公…

【数据结构】八大排序详解

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f40c; 个人主页&#xff1a;蜗牛牛啊 &#x1f525; 系列专栏&#xff1a;&#x1f6f9;数据结构、&#x1f6f4;C &#x1f4d5; 学习格言&#xff1a;博观而约取&…

Python实现透明隧道爬虫ip:不影响现有网络结构

作为一名专业爬虫程序员&#xff0c;我们常常需要使用隧道代理来保护个人隐私和访问互联网资源。本文将分享如何使用Python实现透明隧道代理&#xff0c;以便在保护隐私的同时不影响现有网络结构。通过实际操作示例和专业的解析&#xff0c;我们将带您深入了解透明隧道代理的工…

物联网和不断发展的ITSM

物联网将改变社会&#xff0c;整个技术行业关于对机器连接都通过嵌入式传感器、软件和收集和交换数据的电子设备每天都在更新中。Gartner 预测&#xff0c;全球将有4亿台互联设备投入使用。 无论企业采用物联网的速度如何&#xff0c;连接设备都将成为新常态&#xff0c;IT服务…

Dubbo1-架构的演变

分布式系统上的相关概念 项目&#xff1a;传统项目、互联网项目 传统项目&#xff1a; 一般为公司内部使用&#xff0c;或者小群体小范围的使用&#xff0c;一般不要求性能&#xff0c;美观&#xff0c;并发等 互联网项目的特点&#xff1a; 1.用户多 2.流量大&#xff0c;并…

【CSS】透明背景的圆角渐变边框实现方案

css的渐变边框可以用下面方式实现 border-image: linear-gradient(rgb(89, 0, 255),pink) 30 30; css的圆角边框可以用下面方式实现 border-radius: 20px; 那想要实现一个圆角的渐变边框呢&#xff0c;可能会以为&#xff0c;两个都用上不就可以了&#xff0c;但事实是&…

JSON字符串转换

大家好 , 我是苏麟 , 今天带来一个JSON序列化库 Gson . GitHub 地址 : GitHub - google/gson: A Java serialization/deserialization library to convert Java Objects into JSON and back java 中 json 序列化库有很多&#xff1a; gson (谷歌的) fastjson (阿里的) jack…

Python——添加照片边框

原图&#xff1a; 添加边框后&#xff1a; 添加边框会读取照片的exif信息如时间、相机型号、品牌以及快门焦段等信息&#xff0c;将他们显示在下面的边框中。 获取当前py文件路径 import os #get path that py file located def Get_Currentpath():file_path os.path.abspa…

软考:中级软件设计师:文件管理,索引文件结构,树型文件结构,位示图,数据传输方式,微内核

软考&#xff1a;中级软件设计师: 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的 &#xff08;1…

sudo免密码设置以及设置失败解决方法

使用sudo visudo修改\etc\sudoers文件 打开后有很多已有的设置大致格式username ALL(ALL:ALL) ALL&#xff0c;都不要动&#xff01; 在文件结尾加上一句话&#xff1a; username ALL(ALL:ALL) NOPASSWD: ALLusername就是目前你这个账户的名字&#xff0c;开机时会输密码登录…

数据库内日期类型数据大于小于条件查找注意事项

只传date格式的日期取查datetime的字段的话默认是 00:00:00 日期类型字符串需要使用 ’ ’ 单引号括住 使用大于小于条件查询某一天的日期数据 前后判断条件不能是同一天 一个例子 数据库内数据&#xff1a; 查询2023-08-14之后的数据&#xff1a; select * from tetst…

互联网发展历程:从中继器口不够到集线器的引入

互联网的发展&#xff0c;就像一场不断演进的技术盛宴&#xff0c;每一步的变革都在推动着我们的世界向前。然而&#xff0c;在网络的早期&#xff0c;一项重要的技术问题曾困扰着人们&#xff1a;当中继器的接口数量不足时&#xff0c;如何连接更多的设备&#xff1f;这时&…

移动端预览指定链接的pdf文件流

场景 直接展示外部系统返回的获取文件流时出现了跨域问题&#xff1a; 解决办法 1. 外部系统返回的请求头中调整&#xff08;但是其他系统不会给你改的&#xff09; 2. 我们系统后台获取文件流并转为新的文件流提供给前端 /** 获取传入url文件流 */ GetMapping("/get…

【MySQL】MySQL不走索引的情况分析

未建立索引 当数据表没有设计相关索引时&#xff0c;查询会扫描全表。 create table test_temp (test_id int auto_incrementprimary key,field_1 varchar(20) null,field_2 varchar(20) null,field_3 bigint null,create_date date null );expl…

springboot整合kafka多数据源

整合kafka多数据源 项目背景依赖配置生产者消费者消息体 项目背景 在很多与第三方公司对接的时候&#xff0c;或者处在不同的网络环境下&#xff0c;比如在互联网和政务外网的分布部署服务的时候&#xff0c;我们需要对接多台kafka来达到我们的业务需求&#xff0c;那么当kafk…

读书笔记 |【项目思维与管理】➾ 项目成为一种生存方式

读书笔记 |【项目思维与管理】➾ 项目成为一种生存方式 一、理解项目固有的挑战二、项目对企业的价值三、知识型企业的经营逻辑四、做项目管理的推进者 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 项目无处不在&#xff0c;项目已经成为…

考研408 | 【计算机网络】 网络层

导图 网络层&#xff1a; 路由器功能&#xff1a;转发&路由选择 数据平面 数据平面执行的主要功能是根据转发表进行转发&#xff0c;这是路由器的本地动作。 控制平面 1.传统方法/每路由器法&#xff1a; 2.SDN方法&#xff08;Software-Defined Networking) 控制平面中的…

【变形金刚01】attention和transformer所有信息

图1.来源&#xff1a;Arseny Togulev在Unsplash上的照片 一、说明 这是一篇 长文 &#xff0c;几乎讨论了人们需要了解的有关注意力机制的所有信息&#xff0c;包括自我注意、查询、键、值、多头注意力、屏蔽多头注意力和转换器&#xff0c;包括有关 BERT 和 GPT 的一些细节。因…