Android跨进程通信,binder传输数据过大导致客户端APP,Crash,异常捕获,监听异常的数值临界值,提前Hook拦截。

文章目录

  • Android跨进程通信,binder传输数据过大导致Crash,异常捕获,监听异常的数值临界值,提前Hook拦截。
    • 1.binder在做跨进程传输时,最大可以携带多少数据
      • 1.1有时候这个1m的崩溃系统捕获不到异常,
    • 2.监测异常,提前上报
    • Hook IActivityTaskManager
  • 扩展:Hook AMS绕过Manifest的Activity注册检测

Android跨进程通信,binder传输数据过大导致Crash,异常捕获,监听异常的数值临界值,提前Hook拦截。

Java Crash捕获

public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();Thread.setDefaultUncaughtExceptionHandler((t, e) -> {Log.e("crash", "当前进程id:" + android.os.Process.myPid());Log.e("crash", Log.getStackTraceString(e));if (uncaughtExceptionHandler != null) {uncaughtExceptionHandler.uncaughtException(t, e);}});}
}

1.binder在做跨进程传输时,最大可以携带多少数据

测试代码,跨进程传输1m数据

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startBinding();mBtn = findViewById(R.id.btn);mBtn.setOnClickListener(v -> {Bundle bundle = new Bundle();bundle.putByteArray("binder_data", new byte[1 * 1024 * 1024]);Intent intent = new Intent();intent.putExtras(bundle);ComponentName componentName = new ComponentName("com.example.myapplication", "com.example.myapplication.MainActivity");intent.setComponent(componentName);startActivity(intent);});}

不出意外崩了

在这里插入图片描述

在这里插入图片描述

1.1有时候这个1m的崩溃系统捕获不到异常,

把数据传输从1M改成了800k测试

还是崩了,崩溃的数据量大概是500bk(不崩溃)-600kb(崩溃)之间。

在这里插入图片描述

核心log

在这里插入图片描述

IActivityTaskManager是个aidl文件;

IActivityTaskManager$Stub是binder服务端的类,运行在system进程的

在这里插入图片描述

Proxy对象是运行在我们app进程的,称之为binder代理

Proxy对象通过transact方法调用到Stub对象的onTransact方法。这个异常发生在App进程,所以代码捕获到了这个异常。

2.监测异常,提前上报

大概500k就是危险的极限大小,容易发生异常。

hook拦截startActivity的方法,到达一个危险的大小容易崩溃的时候,我们就上报。

android.os.BinderProxy.transact(BinderProxy.java:510)

这里面Parcel有个dataSize记录数据的大小

Hook IActivityTaskManager

看日志调用流程,在startActivity时候就可以拦截数据,Instrumentation.execStartActivity

Caused by: android.os.TransactionTooLargeException: data parcel size 1049052 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:510)
at android.app.IActivityTaskManager$Stub$Proxy.startActivity(IActivityTaskManager.java:3823)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1705)
at android.app.Activity.startActivityForResult(Activity.java:5173) 

在这里插入图片描述

在这里插入图片描述

ActivityTaskManager对象

final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);

在这里插入图片描述

sCache,是个静态的ArrayMap对象:

@UnsupportedAppUsage
private static Map<String, IBinder> sCache = new ArrayMap<String, IBinder>();

反射换掉IActivityTaskManager对象的IBinder对象,IBinder有监控的transact方法。

invoke方法中,关注transact方法获得跨进程传输的参数的大小

IBinde的transact方法:Parcel data

public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)throws RemoteException;
/*** Returns the total amount of data contained in the parcel.*/
public final int dataSize() {return nativeDataSize(mNativePtr);
}

完整Demo

public static void hook() {Log.e(TAG, "hook: ");try {Class serviceManager = Class.forName("android.os.ServiceManager");Method getServiceMethod = serviceManager.getMethod("getService", String.class);Field sCacheField = serviceManager.getDeclaredField("sCache");sCacheField.setAccessible(true);Map<String, IBinder> sCache = (Map<String, IBinder>) sCacheField.get(null);Map<String, IBinder> sNewCache;sNewCache = new ArrayMap<>();sNewCache.putAll(sCache);IBinder activityTaskRemoteBinder = (IBinder) getServiceMethod.invoke(null, "activity_task");sNewCache.put("activity_task", (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader(),new Class[]{IBinder.class},new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Log.e(TAG, "activity_task method = " + method.getName() + ", args = " + Arrays.toString(args));if ("transact".equals(method.getName())) {if (args != null && args.length > 1) {Object arg = args[1];if (arg instanceof Parcel) {Parcel parcelArg = (Parcel) arg;int dataSize = parcelArg.dataSize();if (dataSize > 300 * 1024) {// TODO 报警Log.e(TAG, Log.getStackTraceString(new RuntimeException("[error]TransactionTooLargeException:  300Kb:" + dataSize)));if (BuildConfig.DEBUG) {if (dataSize > 512 * 1024) {throw new RuntimeException("[error]TransactionTooLargeException:300Kb:" + dataSize);}}}}}}return method.invoke(activityTaskRemoteBinder, args);}}));sCacheField.set(null, sNewCache);} catch (Exception e) {e.printStackTrace();}}

测试

在这里插入图片描述

从日志看出我们是hook系统服务成功了。

总结:

  1. binder数据量过大崩溃,Java异常捕获机制捕获不到,提前拦截。
  2. DEBUG,超过512k,崩溃可以看到日志;
  3. ServiceManager中的sCache获取到原来activity_task对应的IBinder实例对象; IBinder是接口,通过动态代理创造一个IBinder的代理对象IBinderProxy; 把IBinderProxy放到ServiceManager的sCache,Application attachBaseContext中调用Hook方法;

在这里插入图片描述

扩展:Hook AMS绕过Manifest的Activity注册检测

思路,先启动一个注册的代理的Activity,然后绕过manifest的检测后,把代理的Activity替换成未注册的

package com.example.myapplication;import android.content.Intent;
import android.os.Handler;
import android.os.Message;import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;public class HookUtil {private static final String TARGET_INTENT = "target_intent";// 使用代理的Activity替换需要启动的未注册的Activitypublic static void hookAMS() {try {Class<?> clazz = Class.forName("android.app.ActivityTaskManager");Field singletonField = clazz.getDeclaredField("IActivityTaskManagerSingleton");singletonField.setAccessible(true);Object singleton = singletonField.get(null);Class<?> singletonClass = Class.forName("android.util.Singleton");Field mInstanceField = singletonClass.getDeclaredField("mInstance");mInstanceField.setAccessible(true);Method getMethod = singletonClass.getMethod("get");Object mInstance = getMethod.invoke(singleton);Class IActivityTaskManagerClass = Class.forName("android.app.IActivityTaskManager");Object mInstanceProxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{IActivityTaskManagerClass}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if ("startActivity".equals(method.getName())) {int index = -1;// 获取 Intent 参数在 args 数组中的index值for (int i = 0; i < args.length; i++) {if (args[i] instanceof Intent) {index = i;break;}}// 生成代理proxyIntentIntent proxyIntent = new Intent();proxyIntent.setClassName("com.example.myapplication",ProxyActivity.class.getName());// 保存原始的Intent对象Intent intent = (Intent) args[index];proxyIntent.putExtra(TARGET_INTENT, intent);// 使用proxyIntent替换数组中的Intentargs[index] = proxyIntent;}// 被代理对象调用return method.invoke(mInstance, args);}});// 用代理的对象替换系统的对象mInstanceField.set(singleton, mInstanceProxy);} catch (Exception e) {e.printStackTrace();}}// 需要启动的未注册的Activity 替换回来  ProxyActivitypublic static void hookHandler() {try {Class<?> clazz = Class.forName("android.app.ActivityThread");Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");activityThreadField.setAccessible(true);Object activityThread = activityThreadField.get(null);Field mHField = clazz.getDeclaredField("mH");mHField.setAccessible(true);final Handler mH = (Handler) mHField.get(activityThread);Field mCallbackField = Handler.class.getDeclaredField("mCallback");mCallbackField.setAccessible(true);mCallbackField.set(mH, new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case 159:try {Field mActivityCallbacksField = msg.obj.getClass().getDeclaredField("mActivityCallbacks");mActivityCallbacksField.setAccessible(true);List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);for (int i = 0; i < mActivityCallbacks.size(); i++) {if (mActivityCallbacks.get(i).getClass().getName().equals("android.app.servertransaction.LaunchActivityItem")) {Object launchActivityItem = mActivityCallbacks.get(i);Field mIntentField = launchActivityItem.getClass().getDeclaredField("mIntent");mIntentField.setAccessible(true);Intent proxyIntent = (Intent) mIntentField.get(launchActivityItem);Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);if (intent != null) {mIntentField.set(launchActivityItem, intent);}}}} catch (Exception e) {e.printStackTrace();}break;}return false;}});} catch (Exception e) {e.printStackTrace();}}}

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

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

相关文章

志愿填报指南:为什么我强烈建议你报考计算机专业

首先恭喜2024届高考的同学们&#xff0c;你们已经通过了高考的考验&#xff0c;即将进入人生的新阶段——大学。 现在正是高考完填报志愿的时刻&#xff0c;Left听到身边朋友提到报考志愿的诸多问题&#xff1a; 志愿填报怎么填&#xff1f;我要报考什么专业&#xff1f;这个…

[Cloud Networking] OSPF

OSPF 开放式最短路径优先&#xff08;Open Shortest Path First&#xff09;是一种动态路由协议&#xff0c;它属于链路状态路由协议&#xff0c;具有路由变化收敛速度快、无路由环路、支持变长子网掩码和汇总、层次区域划分等优点。 1 OSPF Area 为了适应大型网络&#xff0…

可编程定时计数器8253/8254 - 8253入门

时钟-给设备打拍子 概述 在计算机系统中&#xff0c;为了使所有设备之间的通信井然有序&#xff0c;各通信设备间必须有统一的节奏&#xff0c;不能各干各的&#xff0c;这个节奏就被称为定时或时钟 时钟并不是计算机处理速度的衡量&#xff0c;而是一种使设备间相互配合而避…

Linux 磁盘挂载与分区

Linux 磁盘挂载与分区 vda1: 其中vd表示虚拟磁盘&#xff0c;a表示第一块磁盘&#xff0c;b表示第二块磁盘&#xff0c;1表示第一块磁盘的第一分区&#xff08;显然两块磁盘都只有一个分区&#xff09;图中可以看到&#xff0c;vda1磁盘只有一个分区&#xff0c;且全部挂载到根…

vue3使用vant4的列表vant-list点击进入详情自动滚动到对应位置,踩坑日记(一天半的踩坑经历)

1.路由添加keepAlive <!-- Vue3缓存组件&#xff0c;写法和Vue2不一样--><router-view v-slot"{ Component }"><keep-alive><component :is"Component" v-if"$route.meta.keepAlive"/></keep-alive><component…

如何在MySQL中按字符串中的数字排序

在管理数据库时&#xff0c;我们经常遇到需要按嵌入在字符串中的数字进行排序的情况。这在实际应用中尤为常见&#xff0c;比如文件名、代码版本号等字段中通常包含数字&#xff0c;而这些数字往往是排序的关键。本文将详细介绍如何在MySQL中利用正则表达式提取字符串中的数字并…

Java家教系统小程序APP公众号h5源码

让学习更高效&#xff0c;更便捷 &#x1f31f; 引言&#xff1a;家教新选择&#xff0c;小程序来助力 在快节奏的现代生活中&#xff0c;家长们越来越注重孩子的教育问题。然而&#xff0c;如何为孩子找到一位合适的家教老师&#xff0c;成为了许多家长头疼的问题。现在&…

谷歌个人号,20人连续封测14天所需设备该怎么解决?

现在&#xff0c;在Google Play上架应用&#xff0c;对于大部分开发者来说&#xff0c;真的是不小的挑战&#xff0c;因为目前谷歌上架政策越来越严格了。特别是从2023年11月13日起&#xff0c;新政策要求个人开发者账号的应用必须经过20个独立用户连续14天的封闭测试&#xff…

【C语言】--分支和循环(1)

&#x1f37f;个人主页: 起名字真南 &#x1f9c7;个人专栏:【数据结构初阶】 【C语言】 目录 前言1 if 语句1.1 if1.2 else1.3 嵌套if1.4 悬空else 前言 C语言是结构化的程序设计语言&#xff0c;这里的结构指的是顺序结构、选择结构、循环结构。 我们可以用if、switch实现分支…

vue2实例实现一个初步的vuex

vue2实例实现一个初步的vuex 实现源码&#xff1a;vue2-review 1.App.vue 2.store目录下的index.js 3.效果 微信公众号&#xff1a;刺头拾年

扩展阅读:什么是中断

如果用一句话概括操作系统的原理,那就是:整个操作系统就是一个中断驱动的死循环,用最简单的代码解释如下: while(true){doNothing(); } 其他所有事情都是由操作系统提前注册的中断机制和其对应的中断处理函数完成的。我们点击一下鼠标,敲击一下键盘,执行一个程序,…

Spring MVC中的DispatcherServlet、HandlerMapping和ViewResolver的作用

在Spring MVC框架中&#xff0c;DispatcherServlet、HandlerMapping和ViewResolver是核心组件&#xff0c;它们各自承担着不同的角色和任务&#xff1a; 1.DispatcherServlet&#xff1a;它是Spring MVC生命周期中的前端控制器&#xff0c;负责接收HTTP请求并将它们分发给相应的…

5.x86游戏实战-CE定位基地址

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;4.x86游戏实战-人物状态标志位 上一个内容通过CE未知的初始值、未变动的数值、…

JVM相关总结

JVM的些许问题 1.JVM内存区域划分 2.JVM类加载过程 3.JVM的垃圾回收机制 1.JVM的内存区域划分 一个运行起来的Java进程就是一个JVM虚拟机,需要从操作系统申请一大片内存,就会把内存划分成几个区域,每个区域都有不同的作用 常见的面试题 2.JVM类加载过程 熟练背诵 ! ! !…

WordPress中文网址导航栏主题风格模版HaoWa

模板介绍 WordPress响应式网站中文网址导航栏主题风格模版HaoWa1.3.1源码 HaoWA主题风格除行为主体导航栏目录外&#xff0c;对主题风格需要的小控制模块都开展了敞开式的HTML在线编辑器方式的作用配备&#xff0c;另外预埋出默认设置的编码构造&#xff0c;便捷大伙儿在目前…

入门JavaWeb之 JDBC 连接数据库

JDBC&#xff1a;Java Database Connectivity&#xff0c;Java 数据库连接 需要 jar 包支持&#xff1a; java.sql javax.sql mysql-connector-java&#xff08;连接驱动&#xff0c;必须导入&#xff09; 在 MySQL 先建个 jdbc 数据库后 USE jdbc; 执行后再 CREATE TABLE…

15- 22题聚合函数 - 高频 SQL 50 题基础版

目录 1. 相关知识点2. 例子2.15 - 有趣的电影2.16 - 平均售价2.17 - 项目员工 I2.18 - 各赛事的用户注册率2.19 - 查询结果的质量和占比2.20 - 每月交易 I2.21 - 即时食物配送 II2.22 - 游戏玩法分析 IV 1. 相关知识点 函数 函数含义order by排序group by分组between 小值 an…

Chrome备份数据

Chrome备份数据 1、 导出谷歌浏览器里的历史记录 参考&#xff1a;https://blog.csdn.net/qq_32824605/article/details/127504219 在资源管理器中找到History文件&#xff0c;文件路径&#xff1a; C:\Users\你的电脑用户名\AppData\Local\Google\Chrome\User Data\Default …

堆排序思想分享

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

五、Pentium 微处理器保护模式存储管理,《微机系统》第一版,赵宏伟

一、分段存储管理 Pentium支持分段存储管理、分页存储管理和段页式存储管理。 1.1 分段存储管理的基本思想 一个程序由多个模块组成。 每一个模块都是一个特定功能的独立的程序段。 段式管理&#xff1a;把主存按段分配的存储管理方式。 程序模块→段→段描述符→段描述符…