使用Android Native Hook技术解决VLC播放器闪退的问题

news2024/12/22 17:22:18

文章目录

  • 1.概述
  • 2.问题描述
  • 3.问题分析
  • 4.问题解决
  • 5.总结

1.概述

在做公司的一个TOB的需求时,发现调起Unity提供的3D播放器播放网络在线视频时闪退了,然后就拉着相关部门的人一起分析问题,最后定位到是VLC里面用到的系统日志打印函数在部分的系统上会出问题,于是各部门的同事就开始想各种解决方案,当时主要是两个部门的同事提出了两种解决方案,一方面是系统部门的人提出直接在系统上改,因为ROM是我们自己的所以可以改系统的代码。禁用掉日志打印函数中引发闪退的部分,但是这样就会导致其他APP使用这个日志打印函数时就无法获取到日志了。但也不失为一个解决办法。第二种解决办法是C++ 部门提出的,直接将VLC的源码下载下来,修改打印日志的函数。这种方法其实也行,就是比较耗费时间和精力,导致一直没有实施,最后就是系统部门的人改了一个版本验证没有闪退后,也不敢进ROM,怕影响其他APP。于是最后我就提出了使用native hook的技术去解决这个问题,本文就是介绍如何使用native hook技术解决这个问题。

2.问题描述

本问题主要是当打开播放器,传递网络视频链接播放的时候就会闪退,闪退的堆栈如下所示:

 FORTIFY: %n not allowed on Android
2024-03-04 18:02:50.263 15889-15947 AndroidRuntime          xxx.xxx.xxx.vrplayer            
 E  FATAL EXCEPTION: UnityMain
 Process: xxx.xxx.xxx.vrplayer, PID: 15889
 java.lang.Error: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
 Version '2019.4.36f1 (660c164b2fc5)', Build type 'Release', Scripting Backend 'il2cpp', CPU 'arm64-v8a'
 Build fingerprint: 'xxx/xxx/xxx:10/QKQ1.211001.001/02241336:user/release-keys'
 Revision: '0'
 ABI: 'arm64'
 Timestamp: 2024-03-04 18:02:50+0800
 pid: 15889, tid: 16063, name: VlcObject  >>> xxx.xxx.xxx.vrplayer <<<
 uid: 1000
 signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
     x0  0000000000000000  x1  0000000000003ebf  x2  0000000000000006  x3  0000007956eedd90
     x4  0000000000000000  x5  0000000000000000  x6  0000000000000000  x7  0000000000000018
     x8  00000000000000f0  x9  becc2cee9a43a307  x10 0000000000000001  x11 0000000000000000
     x12 fffffff0fffffbdf  x13 0000000065e59c4a  x14 0002348d9b6c4800  x15 00005b4371aa1524
     x16 00000079e94118c0  x17 00000079e93eda60  x18 0000000000000000  x19 0000000000003e11
     x20 0000000000003ebf  x21 00000000ffffffff  x22 00000000ffffffff  x23 0000007956eee954
     x24 0000007956eee170  x25 000000000000001d  x26 000000000000006e  x27 0000000000000000
  x28 0000007956ef0008  x29 0000007956eede30
  sp  0000007956eedd70  lr  00000079e939f0c4  pc  00000079e939f0f0
 
                                                                                                    backtrace:
 #00 pc 00000000000830f0  /apex/com.android.runtime/lib64/bionic/libc.so (abort+160) (BuildId: 35b174c1ce3028d241142fca9906cd01)
 #01 pc 00000000000c4984  /apex/com.android.runtime/lib64/bionic/libc.so (helpers::wcsconv(wchar_t*, int)) (BuildId: 35b174c1ce3028d241142fca9906cd01)
 #02 pc 00000000000c3f38  /apex/com.android.runtime/lib64/bionic/libc.so (__vfprintf+10956) (BuildId: 35b174c1ce3028d241142fca9906cd01)
 #03 pc 00000000000e171c  /apex/com.android.runtime/lib64/bionic/libc.so (snprintf+232) (BuildId: 35b174c1ce3028d241142fca9906cd01)
 #04 pc 00000000008bdabc  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (vasnprintf+3036) (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #05 pc 00000000008bce50  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (rpl_snprintf+132) (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #06 pc 00000000008ecee8  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #07 pc 00000000008ecdd8  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (gnutls_x509_trust_list_add_trust_dir+68) (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #08 pc 00000000009275e0  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (gnutls_x509_trust_list_add_system_trust+76) (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #09 pc 000000000063ca28  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #10 pc 000000000074d190  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (vlc_module_load+1076) (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #11 pc 0000000000785dd4  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (vlc_tls_ClientCreate+60) (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #12 pc 000000000061f3b0  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #13 pc 00000000006262d8  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #14 pc 00000000006263d0  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)
 #15 pc 000000000061eac4  /data/app/xxx.xxx.xxx.vrplayer-UO0jeUwKH6BDD6eJ_Zrx6A==/base.apk (BuildId: fbdb95762b44ab3a3cd3722aba5ebc064ca8abd7)

堆栈做了脱敏处理,应该不影响问题分析。

3.问题分析

从上面的堆栈中,我们可以看到这个问题的一行日志如下:
在这里插入图片描述
结合日志,我们发现并不是我们打印的日志,最有可能是第三方库VLC或者是系统打印的,但是VLC我们暂时没有源码,更何况VLC是Unity插件中引用的,我们无法准确的知道对应的代码版本,所以就先从系统分析起,到系统源码中去搜索这行代码。幸运的是,我们在系统源码中找到了这行代码:
在这里插入图片描述上图代码的地址
知道是系统打印的日志后就好办了,我们接着去找下都是谁调用了这个函数,这里需要看堆栈了。我们从堆栈中可以看到如下两个函数:
在这里插入图片描述
然后我们又可以根据日志中的信息:
在这里插入图片描述
推断出调用上面两个函数打印日志的是vlc播放器模块,接下来我们要想解决这个问题,主要有3种办法,第一种,系统修改,屏蔽掉这个日志函数。第二种。修改VLC源码。第三种就是本文介绍的从应用测通过native hook技术去修改。

4.问题解决

通过我的亲身经历也证明了,从系统侧改不现实,因为这是应用侧的库中的原因导致的闪退问题,系统侧没有责任也没有义务去为了一个应用冒着风险去修改这个地方,影响太大。第二,从VLC源码中修改重新编译,这也不现实,因为修改源码意味着需要花时间和精力去熟悉VLC源码,这需要公司投入人力,而且收益不高,并且VLC后续如果更新还得继续去维护,代价太大。所以我提出了native hook的技术。所谓的native hook,就是劫持函数的调用。让原本调用A函数的操作改为调用我自己定义的B函数。这样就绕过了A函数的执行。对应到当前的问题中就是,A函数调用了系统的日志打印函数,会引起闪退,那么我们就hook住A函数,让VLC不去调用A函数,去调用我们的B函数,这样就不会引起应用闪退了。接下来就看下如何去实现对应的函数hook.

通过分析日志我们发现,引起应用闪退的函数主要有两个。
在这里插入图片描述
所以我们只要hook住这两个函数,就可以避免这个native crash。知道了原理后,我们就可以开始hook代码的编写了。这里我们使用的是字节跳动开源的bhook库来hook native的函数调用。主要代码如下所示:

static int my_vfprintf_proxy(FILE *fp, const char *path, va_list va) {
    debug("my_vfprintf_proxy", path, BYTEHOOK_RETURN_ADDRESS());
    LOG("zhongxj:my_vfprintf_proxy = %d,return fd: %d", 1129);
    BYTEHOOK_POP_STACK();
    return 1129;
}

static int my_rpl_snprintf_proxy(char * a, size_t s, const char * cc, ...) {
   // debug("my_rpl_snprintf_proxy", path, BYTEHOOK_RETURN_ADDRESS());
    LOG("zhongxj:my_vfprintf_proxy = %d,return fd: %d", 904);
    BYTEHOOK_POP_STACK();
    return 904;
}

static int hacker_hook_rpl_snprintf() {
    if (NULL != rplsnprintf_stub) return -1;
    void *rpl_snprintf_proxy;
    rpl_snprintf_proxy = (void *) my_rpl_snprintf_proxy;


    rplsnprintf_stub =
            bytehook_hook_single("libvlcjni.so",
                                 NULL,
                                 "rpl_snprintf",
                                 rpl_snprintf_proxy,
                                 rplsnprintf_hooked_callback,
                                 NULL);
    return 0;
}


static int hacker_hook_vasnprintf(JNIEnv *env, jobject thiz) {
    (void) env, (void) thiz;

    if (NULL != vfprintf_stub) return -1;
    void *vfprintf_proxy;
    vfprintf_proxy = (void *) my_vfprintf_proxy;

    vfprintf_stub =
            bytehook_hook_single("libvlcjni.so",
                                 NULL,
                                 "vasnprintf",
                                 vfprintf_proxy,
                                 vfprintf_hooked_callback,
                                 NULL
            );

    int res = hacker_hook_rpl_snprintf();
    LOG("zhongxj:hook rpl res: %d",res);
    return 0;
}



static int hacker_unhook(JNIEnv *env, jobject thiz) {
    (void) env, (void) thiz;
    if (NULL != vfprintf_stub) {
        bytehook_unhook(vfprintf_stub);
        vfprintf_stub = NULL;
    }

    if(NULL != rplsnprintf_stub){
        bytehook_unhook(rplsnprintf_stub);
        rplsnprintf_stub = NULL;
    }

    return 0;
}

然后在Java层使用对应的jni函数,在打开播放器的地方hook上面的两个函数vasnprintf,rpl_snprintf这样当VLC库调用这两个函数的时候,就会被hook住,然后调用我们的my_vfprintf_proxy,my_vfprintf_proxy函数,这样就避免了调用系统的函数导致闪退,然后我们的播放器停止退出的时候可以释放掉我们的hook点就可以了。有人会觉得这种native hook的技术好像不是啥好技术,可能会有害,但其实不是,bhook库被字节跳动广泛用于抖音,火山引擎,飞书等,所以就放心用吧,用得好的话收益很大的。

5.总结

本文主要介绍了使用native hook的方案解决第三方库中的函数调用引起的闪退,这里只是提供了这个思路和大体实现,具体的细节还需要读者自己添加上,比如何时需要hook,hook的时机等。解决这次问题的过程中,我也深深感受到了知识的力量。所以我们应该多读书,多看别人的经验。很多时候这种解决问题的方法是AI和搜索引擎无法提供的,很遗憾我即将30了才明白这个道理,希望年轻的读者,对编程感兴趣的一定要多学习,多看书,多动手。下篇文章我打算出一个bhook的使用方法示例。希望能帮到新手朋友。敬请期待。

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

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

相关文章

《UE5_C++多人TPS完整教程》学习笔记26 ——《P27 在线会话测试(Testing An Online Session)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P27 在线会话测试&#xff08;Testing An Online Session&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&#xff0…

二分查找是偏爱细节的魔鬼

大家好&#xff0c;我是 方圆。二分查找本质上是一个规模退化且固定规模减小一半的分治算法&#xff0c;它的 思路很简单&#xff0c;但细节是魔鬼。通常我们会认为二分查找的应用场景是数组有序&#xff08;单调&#xff09;&#xff0c;但实际上它也能在无序数组中应用&#…

【轻快图片管理系统】-系统预览截图

在线体验 如果你觉得项目不错&#xff0c;还望动动你的手指给点点star&#xff0c;让更多人看到优秀的项目&#xff01;&#xff01;&#xff01; 为了便于大家在线体验&#xff0c;本系统提供了演示地址&#xff0c;可以通过下面的演示地址和账号进行登录体验系统功能。 演示…

leetcode 热题 100_和为 K 的子数组

题解一&#xff1a; 前缀和数组哈希表&#xff1a;可以计算所有子数组之和暴力求解&#xff0c;但复杂度太高。对于子数组求和的过程&#xff0c;我们可以采用前缀和数组进行优化&#xff0c;前缀和数组中pre[index]代表nums[0]~nusm[index]之和&#xff0c;当我们要计算子数组…

【树】【异或】【深度优先】【DFS时间戳】2322. 从树中删除边的最小分数

作者推荐 【二分查找】【C算法】378. 有序矩阵中第 K 小的元素 涉及知识点 树 异或 DFS时间戳 LeetCode2322. 从树中删除边的最小分数 存在一棵无向连通树&#xff0c;树中有编号从 0 到 n - 1 的 n 个节点&#xff0c; 以及 n - 1 条边。 给你一个下标从 0 开始的整数数组…

Vscode连接外部虚拟环境

如果vscode工程目录里面有一个超级大的虚拟环境文件夹&#xff0c;怎么说都不是一件优雅的事&#xff0c;因此我们希望这个虚拟环境在工程目录外部&#xff0c;我们开始&#xff1a; 1. 复制虚拟环境目录路径&#xff1a;E:\envs\test 2. 在vscode中打开文件夹&#xff0c;CT…

全球首个隐私计算一体机国际标准发布 蚂蚁摩斯参与编制

近日&#xff0c;IEEE 标准协会&#xff08;IEEE-SA&#xff09;正式发布并推行了全球首个隐私计算一体机国际标准《隐私计算一体机技术要求》&#xff08;IEEE 3156-2023&#xff09;。该标准由蚂蚁集团推动&#xff0c;中科院信息工程研究所、北京交通大学、中国信息通信研究…

第十六天-爬虫selenium库

目录 1.介绍 2.使用 selenium 1.安装 2.使用 1.测试打开网页&#xff0c;抓取雷速体育日职乙信息 2.通过xpath查找 3.输入文本框内容 send_keys 4.点击事件 click 5.获取网页源码&#xff1a; 6.获取cookies 7.seleniumt提供元素定位方式&#xff1a;8种 8.控制浏览…

第一弹:Flutter安装和配置

目标&#xff1a; 1&#xff09;配置Flutter开发环境 2&#xff09;创建第一个Flutter Demo项目 Flutter中文开发者网站&#xff1a; https://flutter.cn/ 一、配置Flutter开发环境 Flutter开发环境已经提供集成IDE开发环境&#xff0c;因此需要配置开发环境的时候&#xf…

Gitlab 安装部署

目录 1、Jenkins 结合 Gitlab 构建 CI/CD 环境 CI/CD 介绍 CI/CD 流程 Jenkins 简介 GitLab 简介 项目部署方式 CI系统的工作流程 2、搭建 GitLab 安装 GitLab 配置 GitLab 修改root密码 访问 GitLab 开机自启 3、使用 GitLab 管理 GitLab 关闭 GitLab 注册功能…

Git分支补充

我们在合并分支时并不总是一帆风顺&#xff0c;有些时候也会遇到“合并冲突”的问题。 下面我们来还原一下&#xff1a; 创建分支dev $ git checkout -b dev 切换到一个新分支 dev $ git branch * devmaster我们将 text.txt 内容改为 欢迎关注CSDNkeduo并将修改的内容提交到 d…

【Oracle Database】如何远程连接服务器、创建用户、从本地dmp导入表

C:\Users\test>imp test/123456ip/orcl:1521 fileE:\db.dmp tablestable1,table2Import: Release 11.2.0.3.0 - Production on 星期一 3月 4 12:59:09 2024Copyright (c) 1982, 2011, Oracle and/or its affiliates. All rights reserved.IMP-00058: 遇到 ORACLE 错误 1263…

EdgeX Foundry 安全模式安装部署

文章目录 一、安装准备1.官方文档2. 克隆服务器3.安装 Docker4.安装 docker-compose 二、安装部署1.docker-comepse2.启动 EdgeX Foundry3.访问 UI3.1. consul3.2. EdgeX Console EdgeX Foundry # EdgeX Foundryhttps://iothub.org.cn/docs/edgex/ https://iothub.org.cn/docs…

CUDA学习笔记02:测试程序hello world

参考资料 Win10下在VS2019中配置使用CUDA进行加速的C项目 &#xff08;配置.h文件&#xff0c;.dll以及.lib文件等&#xff09;_vs2019 cuda-CSDN博客 配置流程 1. 新建一个一般的项目 2. 项目建好后&#xff0c;在项目里添加.cu测试文件 测试的.cu文件命名为cuda_utils.cu&…

bert 相似度任务训练简单版本,faiss 寻找相似 topk

目录 任务 代码 train.py predit.py faiss 最相似的 topk 数 任务 使用 bert-base-chinese 训练相似度任务&#xff0c;参考&#xff1a;微调BERT模型实现相似性判断 - 知乎 参考他上面代码&#xff0c;他使用的是 BertForNextSentencePrediction 模型&#xff0c;Bert…

在idea中用模板骨架初始创建maven管理的web项目时没有src有关的目录的解决方案

一.问题如下 二.解决方法 首先关闭当前项目&#xff0c;接着修改全局设置&#xff0c;重新创建项目 在VM Options中添加"-DarchetypeCataloginternal"&#xff0c;点击ok保存 点击创建&#xff0c;如果创建成功没报错且有src&#xff0c;就ok了。 当然如果出现以下…

【C++】十大排序算法之 插入排序 希尔排序

本次介绍内容参考自&#xff1a;十大经典排序算法&#xff08;C实现&#xff09; - fengMisaka - 博客园 (cnblogs.com) 排序算法是《数据结构与算法》中最基本的算法之一。 十种常见排序算法可以分为两大类&#xff1a; 比较类排序&#xff1a;通过比较来决定元素间的相对次序…

大厂报价查询系统性能优化之道!

0 前言 机票查询系统&#xff0c;日均亿级流量&#xff0c;要求高吞吐&#xff0c;低延迟架构设计。提升缓存的效率以及实时计算模块长尾延迟&#xff0c;成为制约机票查询系统性能关键。本文介绍机票查询系统在缓存和实时计算两个领域的架构提升。 1 机票搜索服务概述 1.1 …

C++的类与对象(二)

目录 结构体内存对其规则 相关面试题 this指针 相关面试题 结构体内存对其规则 1、第一个成员在与结构体偏移量为0的地址处 2、其它成员变量要对齐到某个数字&#xff08;对齐数&#xff09;的整数倍的地址处 对齐数 编译器默认对齐数与该成员大小的较小值&#xff08;v…

学习记录12-单片机代码几种常见命名规则

良好的编程习惯&#xff0c;决定了今后代码的质量。 有很多人平时不注意自己的代码规范&#xff0c;函数和变量命命随心所欲&#xff0c;造成一个星期就不认识自己的代码&#xff0c;于是今天就来分享一点关于软件代码常见的几种命名规则。 匈牙利命名法 匈牙利命名法广泛应用…