【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)
来实现调用动态下发apk
的BroadcastReceiver
。**
2 实现
PMS内管理了四大组件,会将所有apk都解析后保存对应四大组件的信息,分别保存到对应的集合Activity
、receivers
、probiders
、services
。
/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 实现思路
- 先将要下发的
apk
下载下来(我们这里直接加到设备的存储里)- 将
apk
通过Hook
到的PMS
方法解压解析到PMS
内- 再通过
HookPMS
拿到装有BroadcastReceiver
信息的receivers
集合- 将动态下发
apk
里的BroadcastReceiver
名称作为参数,通过HookPMS
的方法进调用,从而实现本章节的目的。
2.2 项目结构
上图我们可以看到,有两个module,分别为app和pmsbr。
app模块:
HookPMS
的主要逻辑在PMSPackageParser
类,对apk
进行解析和PMS
的Hook
,ClientActivity
用于发送和接收广播,对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>
- 页面打开后,即创建一个
BroadcastReceiver
用于接收来自server
的回复消息,两个按钮分别“注册广播”、“发送广播”。 - 点击“注册广播”:将下发的apk(此处是用pmsbr模块打包的
input.pak
放于设备的data/data/com.yvan.hookpms/files这个私有目录下)通过PMS解析,然后通过HookPMS
实现下发apk中的广播注册。 - 点击“发送广播”:给上面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
做了以下操作:
Hook
的是PMS
解析类android.content.pm.PackageParser
的parsePackage()
方法获取到Package
对象- 然后获取到
Package
对象内存储BroadcastReceiver
的receivers
集合 - 将动态下发的apk通过类加载器加载
- 然后遍历PMS的
receivers
集合找到这个下发apk中注册的广播 - 最后注册这个广播。
这里注册的这个广播是动态下发组件input.apk
的PMSBroadcastReceiver
,我们继续往下看
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喔,谢谢!!
相关文章:

【Android Framework系列】第10章 PMS之Hook实现广播的调用
1 前言 前面章节我们学习了【Android Framework系列】第4章 PMS原理我们了解了PMS原理,【Android Framework系列】第9章 AMS之Hook实现登录页跳转我们知道AMS可以Hook拦截下来实现未注册Activity页面的跳转,本章节我们来尝试一下HookPMS实现广播的发送。…...
Mysql锁实战
mysql版本:8.0.32 通过实战验证mysql的Record lock 与 Gap lock原理 准备工作 设置隔离级别为:RR,以及innodb状态输出锁相关信息 show variables like %innodb_status_output_locks%; show variables like %isolation%;set global innodb_…...

HCIP-OpenStack发放云主机
1、云中的概念 在云平台注册了一个账号,这个账号对于云平台来说,就是一个租户或者一个项目。 租户/项目(tenant/project),租户就是项目的意思。主机聚合就是主机组的意思。 region(区域)&…...

时序预测 | MATLAB基于扩散因子搜索的GRNN广义回归神经网络时间序列预测(多指标,多图)
时序预测 | MATLAB基于扩散因子搜索的GRNN广义回归神经网络时间序列预测(多指标,多图) 目录 时序预测 | MATLAB基于扩散因子搜索的GRNN广义回归神经网络时间序列预测(多指标,多图)效果一览基本介绍程序设计学习小结参考资料效果一览...

Vulhub之Apache HTTPD 换行解析漏洞(CVE-2017-15715)
Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞,在解析PHP时,1.php\x0A将被按照PHP后缀进行解析,导致绕过一些服务器的安全策略。 1、docker-compose build、docker-compo…...

ARTS 挑战打卡的第7天 --- Ubuntu中的WindTerm如何设置成中文,并且关闭shell中Tab键声音(Tips)
前言 (1)Windterm是一个非常优秀的终端神器。关于他的下载我就不多说了,网上很多。今天我就分享一个国内目前没有找到的这方面的资料——Ubuntu中的WindTerm如何设置成中文,并且关闭shell中Tab键声音。 将WindTerm设置成中文 &…...
Oracle之执行计划
1、查看执行计划 EXPLAIN PLAN FOR SELECT * FROM temp_1 a ; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); 2、执行计划说明 2.1、执行顺序 根据缩进来判断,缩进最多的最先执行;(缩进相同时,最上面的最先执行) 2.2…...
【Vue框架】菜单栏权限的使用与显示
前言 在 【Vue框架】Vue路由配置 中的getters.js里,可以看到有一个应用程序的状态(变量)叫 permission_routes,这个就是管理前端菜单栏的状态。具体代码的介绍,都以注释的形式来说明。 1、modules\permission.js 1…...

案例研究|大福中国通过JumpServer满足等保合规和资产管理双重需求
“大福中国为了满足安全合规要求引入堡垒机产品,在对比了传统型堡垒机后,发现JumpServer使用部署更加灵活,功能特性丰富,能够较好地满足公司在等保合规和资产管理方面的双重需求。” ——大福(中国)有限公…...

大数据课程I4——Kafka的零拷贝技术
文章作者邮箱:yugongshiyesina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 掌握Kafka的零拷贝技术; ⚪ 了解常规的文件传输过程; 一、常规的网络传输原理 表面上一个很简单的网络文件输出的过程,在OS底层&…...

红日ATT&CK VulnStack靶场(三)
网络拓扑 web阶段 1.扫描DMZ机器端口 2.进行ssh和3306爆破无果后访问web服务 3.已知目标是Joomla,扫描目录 4.有用的目录分别为1.php 5.configuration.php~中泄露了数据库密码 6.administrator为后台登录地址 7.直接连接mysql 8.找到管理员表,密码加密了…...

JavaScript之BOM+window对象+定时器+location,navigator,history对象
一.BOM概述 BOM即浏览器对象模型,它提供了独立于内容而与窗口进行交互的对象 BOM的顶级对象是window 二.window对象的常见事件 1.窗口加载事件window.onload window.onload function(){} 或者 window.addEventListener("onload" , function(){}); window.onlo…...

为MySQL新增一张performance_schema表 | StoneDB 技术分享会 #4
StoneDB开源地址 https://github.com/stoneatom/stonedb 设计:小艾 审核:丁奇、李浩 编辑:宇亭 作者:王若添 中国科学技术大学-软件工程-在读硕士、StoneDB 内核研发实习生 performance_schema 简介 MySQL 启动后会自动创建四…...

2023/8/12总结
增加了管理员功能点:(管理标签和分类) 另外加了一个转换成pdf的功能 主要是通过wkhtmltopdf实现的,之前看过很多说用adobe的还有其他但是都没成功。 然后就是在学习websocket和协同过滤算法实现,还只是初步了解了这些。…...
win10电脑npm run dev报错解决
npm run dev报错解决 出现错误前的操作步骤错误日志解决步骤 出现错误前的操作步骤 初始化Vue项目 $ npm create vue3.6.1创建项目文件夹client Vue.js - The Progressive JavaScript Framework✔ Project name: › client ✔ Add TypeScript? › No ✔ Add JSX Support? …...
如何使用PHP编写爬虫程序
在互联网时代,信息就像一条无休无止的河流,源源不断地涌出来。有时候我们需要从Web上抓取一些数据,以便分析或者做其他用途。这时候,爬虫程序就显得尤为重要。爬虫程序,顾名思义,就是用来自动化地获取Web页…...

分布式 - 服务器Nginx:一小时入门系列之HTTP反向代理
文章目录 1. 正向代理和反向代理2. 配置代理服务3. proxy_pass 命令解析4. 设置代理请求headers 1. 正向代理和反向代理 正向代理是客户端通过代理服务器访问互联网资源的方式。在这种情况下,客户端向代理服务器发送请求,代理服务器再向互联网上的服务器…...
Android Fragment (详细版)
经典好文推荐,通过阅读本文,您将收获以下知识点: 一、Fragment 简介 二、Fragment的设计原理 三、Fragment 生命周期 四、Fragment 在Activity中的使用方法 五、动态添加Fragment到Activity的方法 六、Activity 中获取Fragment 七、Fragment 获取宿主Activity的方法 八、两个…...
如何使用Flask-RESTPlus构建强大的API
如何使用Flask-RESTPlus构建强大的API 引言: 在Web开发中,构建API(应用程序接口)是非常常见和重要的。API是一种允许不同应用程序之间交互的方式,它定义了如何请求和响应数据的规范。Flask-RESTPlus是一个基于Flask的…...

UGUI事件系统EventSystem
一. 事件系统概述 Unity的事件系统具有通过鼠标、键盘、游戏控制柄、触摸操作等输入方式,将事件发送给对象的功能。事件系统通过场景中EventSystem对象的组件EventSystem和Standalone Input Module发挥功能。EventSystem对象通常实在创建画布的同时被创建的…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...