libjohn hook系统函数导致的NE问题分析

一段时间,发现某个机型上一些系统级APP高概率出现NE,现场如下:

pid: 20335, tid: 20335, name: m.xxx.market >>> com.xxx.market <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000140
r0 75bf67f8 r1 bec44e10 r2 41526558 r3 00000000
r4 41524fa8 r5 00000000 r6 00000004 r7 6d5acbf4
r8 bec44e88 r9 6d5acbec sl 41526568 fp bec44e9c
ip 6d5acbf4 sp bec44e00 lr 7057f683 pc 400cdac0 cpsr 20000010

backtrace:
#00 pc 00000ac0 /system/lib/libc.so
#01 pc 0001d67f /system/lib/libjavacore.so
#02 pc 0001d30c /system/lib/libdvm.so (dvmPlatformInvoke+112)
#03 pc 0004d8db /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+398)
#04 pc 00026720 /system/lib/libdvm.so
#05 pc 0002d790 /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)
#06 pc 0002adf4 /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+184)
#07 pc 0005fd75 /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list)+336)
#08 pc 0004c9e3 /system/lib/libdvm.so
#09 pc 00002300 /data/app-lib/com.lvtech.ydserver-1/libjohn.so (_JNIEnv::NewObject(_jclass*, _jmethodID*, ...)+32)
#10 pc 00002870 /data/app-lib/com.lvtech.ydserver-1/libjohn.so
#11 pc 00000bd0 /data/app-lib/com.lvtech.ydserver-1/libjohn.so

每次NE的时候几乎都是相同调用栈。

从调用栈来看,com.xxx.market被注入了/data/app-lib/com.lvtech.ydserver-1/libjohn.so后发生了崩溃。

上网搜索这个libjohn.so,发现是由《永德安卓读屏》的提供的,这是一个盲人辅助软件,需要对UI做特殊处理。

再分析FC现场:

#00 pc 00000ac0 /system/lib/libc.so

用objdump查看libc.so

00020abc <stat>:
20abc: e1a0c007 mov ip, r7
20ac0: e3a070c3 mov r7, #195 ; 0xc3
20ac4: ef000000 svc 0x00000000
20ac8: e1a0700c mov r7, ip
20acc: e3700a01 cmn r0, #4096 ; 0x1000
20ad0: 912fff1e bxls lr
20ad4: e2600000 rsb r0, r0, #0
20ad8: ea0074a5 b 3dd74 <__aeabi_uidivmod+0xe0>

FC的地址0x20ac0的指令是mov指令,这里不应该出现SIGSEGV的,唯一的可能是运行时的so被篡改了。

通过tombstone中的pc around查看运行时的pc附近的字节码:

code around pc:
400cdaa0 e3a070c5 ef000000 e1a0700c e3700a01
400cdab0 912fff1e e2600000 ea0074ad e59ff000
400cdac0 75a37140 75a37140 e1a0700c e3700a01
400cdad0 912fff1e e2600000 ea0074a5 e1a0c007
400cdae0 e3a070c4 ef000000 e1a0700c e3700a01

显然,这个stat应该是已经被篡改了。

00020abc<stat>:   20abc:   e59ff000    ldr pc, [pc]
   20ac0:   75a37140    strvc r7, [r3, #320]! ; 0x140
   20ac4:   75a37140    strvc r7, [r3, #320]! ; 0x140

这里hook代码的原意是pc直接赋值0x75a37140,也就是跳转到0x75a37140。

但处于某种原因并未执行0x20abc的代码,而是pc执行到0x20ac0,错误的将地址0x75a37140当做str指令来执行。

出问题时,r3 = 0,所以0x20ac0上的指令就相当于str r7,[0x140],而这个0x140显然是未映射的地址。

所以报了SIGSEGV,且fault addr为00000140。

那为什么0x20abc中的ldr pc, [pc]没被执行呢?

一种可能性是程序运行0x20abc时,改地址上的指令还是没被篡改前的指令,也就是mov ip, r7。

F如果假设成立,那FC时的ip值应该等于r7:

pid: 20335, tid: 20335, name: m.xxx.market >>> com.xxx.market <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000140
r0 75bf67f8 r1 bec44e10 r2 41526558 r3 00000000
r4 41524fa8 r5 00000000 r6 00000004 r7 6d5acbf4
r8 bec44e88 r9 6d5acbec sl 41526568 fp bec44e9c
ip 6d5acbf4 sp bec44e00 lr 7057f683 pc 400cdac0 cpsr 20000010

恩,确实是这样的,所以实际运行时的指令流为:

00020abc<stat>:   20abc:   e1a0c007    mov ip, r7
   20ac0:   75a37140    strvc r7, [r3, #320]! ; 0x140
   20ac4:   75a37140    strvc r7, [r3, #320]! ; 0x140

这种解释和FC现场完全吻合。

如果真是这种情况,一个线程在执行stat函数的第一条指令的同时,另外一个线程刚好在改写stat函数。

这种情况是存在的,但这个时间窗口实际上是非常小的,而这个问题的概率确实特别高,而且这类问题只是一个特定机型上才会发生。

如果是hook的问题,那应该所有机型上都高概率复现才对,为什么只有一个机型上报这个问题呢?

这时突然注意到一个细节,出问题的地址0x20ac0是64字节对齐的。而其他机型上面的同样位置并不是64位对齐的。

我们知道cache页的长度是64字节,难道是因为没有刷cache导致的?

为了证明跟cache相关,在出问题的机型版本中修改了libc.so的代码,重新编译确保stat相应位置不是64字节边界。

发现原先高概率出现的FC竟然不出现了。

后来联系到永德的工程师,让他们在hook后刷cache,问题就不再出现了。

结论:

程序实际运行时,执行的可能是flash的代码,也可能是ram里的代码,也可能是cache里的代码。三者可能不一样!

时间: 2024-10-25 06:37:07

libjohn hook系统函数导致的NE问题分析的相关文章

系统函数dlopen()被劫持导致symbol找不到的问题记录

问题现象 我们实现了一个名叫libilvrfplugin.so的lib,该lib链接了libiubsntconflib.so, 而libiubsntconflib.so 又链接了libipconflib.so, libipconflib.so里面实现了一个方法check_vrf_r()用于检查VRF的合法性. 简单点来说,A lib链接了B lib,而B lib又链接了C lib,C lib实现了方法check_vrf_r(). 某些场景下,系统会动态加载A lib, 但是A lib根本没用到方

SSDTHook实例--编写稳定的Hook过滤函数

解说怎样写Hook过滤函数,比方NewZwOpenProcess.打开进程. 非常多游戏保护都会对这个函数进行Hook. 因为我们没有游戏保护的代码,无法得知游戏公司是怎样编写这个过滤函数. 我看到非常多奇形怪状的Hook过滤函数的写法.看得蛋痛无比. 比方: http://bbs.pediy.com/showthread.php?t=126802 http://bbs.pediy.com/showthread.php? t=126077 第一个bug: 调用这个函数 status = PsLo

Android系统篇之----Hook系统的AMS服务实现应用启动的拦截功能

技术概念来源:[ 360开源插件框架,项目地址:https://github.com/DroidPluginTeam/DroidPlugin ] 一.Hook系统剪切板服务流程回顾 在之前的一篇文章中已经介绍了 Android中的应用启动流程,这个流程一定要理解透彻,这样我们才可以进行后续的Hook操作,在之前还介绍了Android中如何Hook系统的剪切板服务实现方法的拦截效果,实现原理就是: 1.先找到Hook点,这个一般是分析源码来得到,而一般的Hook点都是静态变量或者是单例方法. 2.

第三章——使用系统函数、存储过程和DBCC SQLPERF命令来监控SQLServer(3)

原文:第三章--使用系统函数.存储过程和DBCC SQLPERF命令来监控SQLServer(3) 本文为这个系列最后一篇.将是如何使用DBCC命令来监控SQLServer日志空间的使用情况. 前言: 每个数据库都必须有事务日志.事务日志记录每个DML操作,并应用于SQLServer的数据库中,如果恢复模式为FULL并经常有DML操作,日志将增长得非常快.几时恢复模式为simple,当数据库处于事务复制或者合并复制时,日志通常会增长.如果日志不是经常备份且日志文件的增长没有受到限制的话,将有可能

4.5 HOOK分发函数

4.5 HOOK分发函数 本节开始深入的探讨键盘的过滤与反过滤.有趣的是,无论是过滤还是反过 滤,其原理都是进行过滤.取胜的关键在于:谁将第一个得到信息. 黑客可能会通过修改一个已经存在的驱动对象(比如前面的KbdClass)的分 发函数指针来实现过滤所有请求的目的.黑客将这些函数指针替换成自己的黑客 驱动中的函数,这样请求将被黑客的程序首先截获.然后通过调用原来被替换过 的旧的函数指针来让Windows的击键过程正常的运作下去. 4.5.1 获得类驱动对象 当然,首先要获得键盘类驱动对象,才能

使用FreeRTOS在SD卡驱动使用非系统延时导致上电重启不工作的情况

一.问题描述在一个使用FreeRTOS的工程中,只做了SD卡的驱动,由于RTOS使用了Systick,故非系统延时函数使用的是 DWT中的时钟周期(CYCCNT)计数功能,但是在SD卡驱动中使用了这个非系统延时导致,烧写程序后板子工作正常,而下电再上电后板子无反应,分析排查去掉了这个非系统延时后工作正常. 二.使用环境1)开发环境使用的是MDK5.20,下载器为JLINK:2)软件工程是V6的FreeRTOS模板工程,SD卡驱动也是V6的,非系统延时函数所在文件为V6的 bsp_dwt.c:3)

利用系统函数模拟实现nginx 系统脚本启动的特殊颜色专业效果

利用系统函数模拟实现nginx 系统脚本启动的特殊颜色专业效果/etc/init.d/nginxd {start/stop/restart/reload}利用if语句实现: =========================================================== 实现特殊颜色实现效果: vim start_nginx.sh [root@lamp01 scripts]# cat bqh_nginx_startup.sh #!/bin/sh . /etc/init.d/

SQL Server系统函数:类型转换函数

原文:SQL Server系统函数:类型转换函数 1.基本的转化 SELECT CAST(2008 as varchar(4)) + ' year!' SELECT CONVERT(varchar(4),2008) + ' year!' 2. 把日期转化为文本 SELECT CONVERT(VARCHAR(30),GETDATE(),120) --年-月-日 时:分:秒(24h) SELECT CONVERT(VARCHAR(10),GETDATE(),120) --年-月-日 时:分:秒(24

php 常用的系统函数

php 常用的系统函数 本文介绍了php 常用的系统函数,具有很好的参考价值,下面跟着 大宝儿 一起来看下吧 字符串函数        strlen():获取字符串长度,字节长度 substr():字符串截取,获取字符串(按照字节进行截取) strchr():与substr相似,从指定位置截取一直到最后 strrchr(获取文件后缀名):与strchr一样,只是从右边开始查找字符 strtolower():所有的字符都小写(针对英文字母) strtoupper():所有的字符都大写 strrev