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

这篇具有很好参考价值的文章主要介绍了【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 项目结构

【Android Framework系列】第10章 PMS之Hook实现广播的调用,Framework,Android,Java,android,Framework,PMS
上图我们可以看到,有两个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 {

    @Override
    protected 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 {
        @Override
        public 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">

    <Button
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center"
        android:onClick="registerBroaderCast"
        android:text="注册广播" />

    <Button
        android: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 {

    @Override
    public 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的消息。
【Android Framework系列】第10章 PMS之Hook实现广播的调用,Framework,Android,Java,android,Framework,PMS

3 总结

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

到了这里,关于【Android Framework系列】第10章 PMS之Hook实现广播的调用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android 10.0 pms中关于启动app时获取app的ActivityInfo信息相关源码分析

     在android10.0的系统rom定制化开发中,在对于app启动时,通过Launcher调用pms来查询app的相关ActivityInfo的相关信息,然后调用 ams来启动activity,这篇来分析pms中获取app的ActivityInfo的相关信息的相关源码分析

    2024年02月02日
    浏览(21)
  • Android 9.0 pms获取应用列表时过滤掉某些app功能实现

     在9.0的系统rom定制化开发中,对系统定制的功能也是很多的,在一次产品开发中,要求在第三方app获取应用列表的时候,需要过滤掉某些app,就是不显示在app应用列表中,这就需要在pms查询app列表时过滤掉这些app就可以了,接下来就实现这些功能 pms获取应用列表时过滤掉某些

    2024年02月10日
    浏览(19)
  • 【Android】Android Framework系列---CarPower电源管理

    智能座舱通常包括中控系统、仪表系统、IVI系统 、后排娱乐、HUD、车联网等。这些系统需要由汽车电源进行供电。由于汽车自身的特殊供电环境(相比手机方便的充电环境,汽车的蓄电池如果没有电是需要专业人士操作的),其电源状态会比较复杂,既要满足车内的座舱系统

    2024年02月07日
    浏览(24)
  • 【Android】Android Framework系列---CarPower深度睡眠STR

    之前博客说了CarPower的开机启动流程 这里分析一下,Android CarPower实现深度睡眠的流程。 首先, 什么是深度睡眠(Deep Sleep) ? Android进入Deep Sleep后, 关闭屏幕、关闭CPU的电源,保持RAM的电源(激活状态) 。深度睡眠会进行 Suspend-to-RAM 挂起到内存( 做车载的经常会听到的STR )。

    2024年02月05日
    浏览(20)
  • 【Android Framework系列】第7章 WMS原理

    前面【Android Framework系列】第5章 AMS启动流程和【Android Framework系列】第6章 AMS原理之Launcher启动流程我们分析了 AMS启动 以及 Launcher启动 的整体流程,那 Launcher(Activity启动)后 , UI 是如 何渲染到屏幕 并且 展示 出来的呢?我们这章节来探讨一下。 WindowManagerService 简称 WMS ,是

    2024年02月16日
    浏览(20)
  • 【Android Framework系列】5章 AMS启动流程

    AMS(Activity Manager Service) 是 Android 中最核心的服务,管理着 四大组件的启动 、 切换 、 调度 及 应用进程的管理和调度 等工作。AndroidQ将Activity移到了 ActivityTaskManagerService 中,但也和AMS相关联。 AMS 通过使用一些系统资源和数据结构(如进程、任务栈、记录四大组件生命周期

    2024年02月15日
    浏览(21)
  • 【Android Framework系列】第3章 Zygote进程相关

    Zygote是Android中最重要的一个进程, Zygote进程和Init进程、SystemServer进程是Android最重要的三大进程 。 Zygote是Android系统创建新进程的核心进程,负责启动Dalvik虚拟机,加载一些必要的系统资源和系统类,启动system_server进程,随后进入等待处理app应用请求。 在Android系统中,应用

    2024年02月11日
    浏览(23)
  • 【Android Framework系列】第2章 Binder机制大全

       Binder是Android中主要的跨进程通信方式 。Android系统中,每个应用程序是由Android的Activity,Service,BroadCast,ContentProvider这四剑客中一个或多个组合而成,这四剑客所涉及的多进程间的通信底层都是依赖于BinderIPC机制。例如当进程A中的Activity要向进程B中的Service通信,这便需

    2024年02月10日
    浏览(23)
  • 【Android Framework系列】第11章 LayoutInflater源码分析

    本章节我们主要目目的是了解 Activity 的 xml 布局解析、对 LayoutInfater 源码进行分析。 我们知道Android界面上的每一个控件都是一个个 View ,但是Android也提供了通过 xml 文件来进行布局控制,那么 xml 布局文件如何转成最终的 View 的呢?转换利器就是 LayoutInflater 。在分析 Layout

    2024年02月12日
    浏览(20)
  • Android系统重要组件PMS

    Android PMS(Package Manager Service)是Android系统中的一个重要组件,它管理着Android系统中所有应用程序的安装、卸载、更新等工作。下面我们来详细介绍一下Android PMS的原理及其代码分析,以及其常见方法。 Android PMS是Android系统的一个系统服务,主要用于管理应用程序的安装、卸

    2023年04月09日
    浏览(15)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包