【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.hqwc.cn/news/66613.html

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

相关文章

2023上半年京东手机行业品牌销售排行榜(京东数据平台)

后疫情时代&#xff0c;不少行业都迎来消费复苏&#xff0c;我国智能手机市场在今年上半年也实现温和的复苏&#xff0c;手机市场的出货量回暖。 根据鲸参谋平台的数据显示&#xff0c;2023年上半年&#xff0c;京东平台上手机的销量为2830万&#xff0c;环比增长约4%&#xf…

ORACLE行转列、列转行实现方式及案例

ORACLE行转列、列转行实现方式及案例 行转列案例方式1.PIVOT方式2.MAX和DECODE方式3.CASE WHEN和GROUP BY 列转行案例方式1.UNPIVOT方式2.UNION ALL 行转列 案例 假设我们有一个名为sales的表&#xff0c;其中包含了产品销售数据。表中有三列&#xff1a;product&#xff08;…

六轴机械臂码垛货物堆叠仿真

六轴机械臂码垛货物堆叠仿真 1、建立模型与仿真 clear,clc,close all addpath(genpath(.)) %建立模型参数如下&#xff1a; L(1) Link( d, 0.122, a , 0 , alpha, pi/2,offset,0); L(2) Link( d, 0.019 , a ,0.408 , alpha, 0,offset,pi/2); L(3) Link( d, …

汇编指令练习

1.大小比较&#xff08;循环&#xff09; start: /*mov r0,#0x9mov r1,#0xfb LoopLoop:cmp r0,r1beq stopsubhi r0,r0,r1subcc r1,r1,r0b Loop stop:b stop.end 仿真图 2. 1到100之和 start:mov r0,#0x1mov r1,#0x0b sum sum:add r1,r1,r0add r0,r0,#0x1cmp r0,#0x65beq sto…

HarmonyOS NEXT新能力,一站式高效开发HarmonyOS应用

2023年8月6日华为开发者大会2023&#xff08;HDC.Together&#xff09;圆满收官&#xff0c;伴随着HarmonyOS 4的发布&#xff0c;华为向开发者发布了汇聚所有最新开发能力的HarmonyOS NEXT开发者预览版&#xff0c;并分享了围绕“一次开发&#xff0c;多端部署” “可分可合&a…

k8s service

1、认识Service 程序在容器中、容器在Pod中&#xff0c;可以通过pod的ip来访问应用程序&#xff0c;但是podIP会随着创建销毁而改变。由此&#xff0c;Service出现&#xff1a; Service会对提供同一个服务的多个pod进行聚合&#xff0c;并且提供一个统一的入口地址。通过访问…

new BigDecimal(double val)注意事项 / JWT解析BigDecimal类型数据

前言&#xff1a; 公司项目中有一个板块需要解析JWT令牌获取载荷里面封装的数据&#xff0c;遇到要解析一个BigDecimal类型的数据 问题发现过程&#xff1a; 正常来说&#xff0c;我们解析一个JWT令牌的步骤如下&#xff1a; public static Claims getDataFromToken(String tok…

探索 C++ 标准库:std::string 库函数用法示例

目录 引言 一、构造函数 1.1 string() 1.2 string (const string& str) 1.3 string (const string& str, size_t pos, size_t len npos) 1.4 string (const char* s) 1.5 string (const char* s, size_t n) 1.6 string (size_t n, char c&#xff09;​ 二、容…

EasyPoi导出 导入(带校验)简单示例 EasyExcel

官方文档 : http://doc.wupaas.com/docs/easypoi pom的引入: <!-- easyPoi--><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.0.0</version></dep…

Grounding DINO:根据文字提示检测任意目标

文章目录 1. 背景介绍2. 方法创新2.1 Feature Extraction and Enhancer2.2 Language-Guided Query Selection2.3 Cross-Modality Decoder2.4 Sub-Sentence Level Text Feature2.5 Loss Function3. 实验结果3.1 Zero-Shot Transfer of Grounding DINO3.2 Referring Object Detec…

远程运维大批量IT设备?向日葵批量部署、分组授权与安全功能解析

数字化转型的不断推进&#xff0c;给予了企业全方位的赋能&#xff0c;但任何发展都伴随着成本与代价&#xff0c;比如在数字化转型过程中企业内部办公与外部业务所需的不断增加的IT设备数量&#xff0c;就为日常的运维工作提出了更大的挑战。 针对企业面对海量IT设备时的运维…

NAS搭建指南一——服务器的选择与搭建

一、服务器的选择 有自己的本地的公网 IP 的请跳过此篇文章按需求选择一个云服务器&#xff0c;目的就是为了进行 frp 的搭建&#xff0c;完成内网穿透我选择的是腾讯云服务器&#xff0c;我的配置如下&#xff0c;仅供参考&#xff1a; 4. 腾讯云服务器官网地址 二、服务器…