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

文章目录

  • 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: UnityMainProcess: xxx.xxx.xxx.vrplayer, PID: 15889java.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+0800pid: 15889, tid: 16063, name: VlcObject  >>> xxx.xxx.xxx.vrplayer <<<uid: 1000signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------x0  0000000000000000  x1  0000000000003ebf  x2  0000000000000006  x3  0000007956eedd90x4  0000000000000000  x5  0000000000000000  x6  0000000000000000  x7  0000000000000018x8  00000000000000f0  x9  becc2cee9a43a307  x10 0000000000000001  x11 0000000000000000x12 fffffff0fffffbdf  x13 0000000065e59c4a  x14 0002348d9b6c4800  x15 00005b4371aa1524x16 00000079e94118c0  x17 00000079e93eda60  x18 0000000000000000  x19 0000000000003e11x20 0000000000003ebf  x21 00000000ffffffff  x22 00000000ffffffff  x23 0000007956eee954x24 0000007956eee170  x25 000000000000001d  x26 000000000000006e  x27 0000000000000000x28 0000007956ef0008  x29 0000007956eede30sp  0000007956eedd70  lr  00000079e939f0c4  pc  00000079e939f0f0backtrace:#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.mzph.cn/news/721147.shtml

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

相关文章

动态代理IP的并发处理技巧

目录 前言 一、什么是动态代理IP&#xff1f; 二、动态代理IP的并发处理技巧 1. 获取代理IP 2. 动态生成代理对象 3. 并发处理 总结 前言 在进行网络爬虫开发时&#xff0c;经常会遇到限制IP访问频率的情况。为了突破这个限制&#xff0c;我们可以通过使用代理IP来实现…

华为配置基于VLAN限速示例

华为配置基于VLAN限速示例 组网图形 图1 流量监管配置组网图 表1 Switch为上行流量提供的QoS保障 流量类型 CIR(kbps) PIR(kbps) DSCP优先级 语音 2000 10000 46 视频 4000 10000 30 数据 4000 10000 14 ^^^ 流分类简介配置注意事项组网需求配置思路操作步…

C#与欧姆龙PLC实现CIP通讯

参考文档&#xff1a; 欧姆龙PLC使用-CSDN博客 CIP通讯介绍&#xff08;欧姆龙PLC&#xff09;-CSDN博客 使用NuGet添加引用&#xff1a;CIPCompolet 基础参考我的CIP协议介绍&#xff0c;默认TCP端口为&#xff1a;44818 类NXCompolet 类的功能可以在安装PLC开发软件后帮…

vue svelte solid 虚拟滚动性能对比

前言 由于svelte solid 两大无虚拟DOM框架&#xff0c;由于其性能好&#xff0c;在前端越来越有影响力。 因此本次想要验证&#xff0c;这三个框架关于实现表格虚拟滚动的性能。 比较版本 vue3.4.21svelte4.2.12solid-js1.8.15 比较代码 这里使用了我的 stk-table-vue(np…

web坦克大战小游戏

H5小游戏源码、JS开发网页小游戏开源源码大合集。无需运行环境,解压后浏览器直接打开。有需要的订阅后,私信本人,发源码,含60+小游戏源码。如五子棋、象棋、植物大战僵尸、贪吃蛇、飞机大战、坦克大战、开心消消乐、扑鱼达人、扫雷、打地鼠、斗地主等等。 <!DOCTYPE htm…

【图论】Dijkstra 算法求最短路 - 构建邻接矩阵(带权无向图)

文章目录 例题&#xff1a;到达目的地的方案数题目描述代码与解题思路构建带权无向图的邻接矩阵 例题&#xff1a;到达目的地的方案数 题目链接&#xff1a;1976. 到达目的地的方案数 题目描述 代码与解题思路 func countPaths(n int, roads [][]int) int {g : make([][]int…

数据库和缓存如何保持一致性

目录 前言 更新数据库更新缓存&#xff1a; 1.在更新缓存前先加一个分布式锁 2.在更新完缓存时&#xff0c;给缓存加上较短的过期时间 Cache Aside策略 1.先删除缓存&#xff0c;再更新数据库 延迟双删 2.先更新数据库&#xff0c;再删除缓存 保证两个操作都能执行成功…

【译】WordPress Bricks主题安全漏洞曝光,25,000个安装受影响

WordPress的Bricks主题存在一个严重的安全漏洞,恶意威胁行为者正在积极利用该漏洞在易受攻击的安装上运行任意PHP代码。 该漏洞被跟踪为CVE-2024-25600(CVSS评分:9.8),使未经身份验证的攻击者能够实现远程代码执行。它影响Bricks的所有版本,包括1.9.6版本及更早版本。 该…

线程变量ThreadLocal用于解决多线程并发时访问共享变量的问题。

ThreadLocal介绍 ThreadLocal叫做线程变量&#xff0c;用于解决多线程并发时访问共享变量的问题。意思是ThreadLocal中填充的变量属于当前线程&#xff0c;该变量对其他线程而言是隔离的&#xff0c;也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建…

如何用生成式人工智能准备和制作吸引人的美食视频?

YouTube是一个全球性的视频分享平台&#xff0c;上面充满了各式各样的内容&#xff0c;其中美食内容因其视觉和味觉上的双重吸引而备受欢迎。作为一个想要进入这个领域的创作者&#xff0c;你需要知道如何准备和制作吸引人的美食视频。以下是一些基本步骤和技巧&#xff1a; 选…

UE4 Niagara 关卡3.4官方案例解析

Texture sampling is only supported on the GPU at the moment.(纹理采样目前仅在GPU上受支持) 效果&#xff1a;textures can be referenced within GPU particle systems。this demo maps a texture to a grid of particles&#xff08;纹理可以在GPU粒子系统中被引用这个演…

git使用教程14-Pycharm版本控制与分支管理

一、版本控制 1、版本控制介绍 &#xff08;1&#xff09;Version Control System 版本控制系统&#xff0c;简称VCS。 &#xff08;2&#xff09;版本控制系统分类&#xff1a; 集中式版本控制工具&#xff1a;SVN 分布式版本控制工具&#xff1a;Git 2、Pycharm 支持的版本…

Windows安装MySQL8.0详细步骤

目录 一、官网下载MySQL二、将压缩包解压到没有中文和空格的目录下三、设置配置文件四、配置环境变量五、安装初始化mysql服务 一、官网下载MySQL 进入MySQL官网&#xff1a;https://downloads.mysql.com/archives/community/&#xff0c;下载 Windows (x86, 64-bit), ZIP Arch…

项目管理中,项目经理如何搞定跨部门沟通与协作?

在项目管理中&#xff0c;跨部门沟通是一项至关重要的任务。项目经理作为项目的核心协调者&#xff0c;需要确保不同部门之间的顺畅沟通&#xff0c;以推动项目的顺利进行。项目经理如何搞定跨部门沟通&#xff1f; 实际案例&#xff1a; 某公司正在开发一款智能家居系统&am…

深入探索Docker数据卷:实现容器持久化存储的完美方案(下)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Docker入门到精通》 《k8s入门到实战》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 四、Docker数据卷的高级管理 1、数据卷的生命周期管理 2、数据…

如何做代币分析:以 CRO 币为例

作者&#xff1a;lesleyfootprint.network 编译&#xff1a;Mingfootprint.network 数据源&#xff1a;CRO Token Dashboard &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币相关…

【Python】进阶学习:pandas--rename()用法详解

【Python】进阶学习&#xff1a;pandas-- rename()用法详解 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望得到您的…

基于SpringBoot的物业管理系统

** &#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;** 一 、设计说明 1.1 研究…

Springboot 过滤器、拦截器、全局异常处理

Springboot 过滤器、拦截器、全局异常处理 一 过滤器&#xff08;Filter&#xff09; 过滤器是JavaWeb三大组件&#xff08;Servlet&#xff0c;Filter&#xff0c;Listener&#xff09;之一。 Filter可以把对资源的请求拦截下来&#xff0c;从而实现一些功能。 注意&#…

低代码工具APEX的入门使用(未包含安装)

第一次使用APEX是2019年&#xff0c;这个技术成名已久只是我了解的比较晚。请看Oracle ACE的网站&#xff0c;这就是用APEX做的。实际上有一次我看O记的人操作他们的办公流程&#xff0c;都是用APEX做的。 那一年&#xff0c;我用APEX做了一个CMDB的管理系统。那时候还没有流行…