我是靠谱客的博主 忧心樱桃,最近开发中收集的这篇文章主要介绍android应用程序fps meter[帧数显示]的分析 —— SurfaceFlinger被注入统计代码 (1),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

fps meter是常用的检测帧率的软件,该软件需要root权限才能工作,一直比较好奇它一个apk是如何知道系统当前的帧率情况的,就针对此apk分析了一下其工作原理。

Apk组成

首先看一下apk的组成,apk文件就是一个压缩包,可以解压缩软件如winrar解压查看,也可以用[apktools]反编译apk,以供进一步分析。

 

从运行结果和代码组织上的推测

  • java代码主要负责上层控制和显示。
  • bin0和lib0.so是一个真正获取fps的binary工作进程的代码
  • jni层的libnp_read.so,负责和工作进程桥接,通过pipe与工作进程通信,上报分析的数据给java层显示。

Apk的静态分析

Apk的包组成结构如上图。常规的dex/res/lib目录下的内容外,还要看一下res/raw和assets目录下是否有东西,这里通常是藏污纳垢的场所。fps meter这个apk中,在res/raw/下有bin0和lib0.so 两个文件。

使用[dex2jar]反编译java代码,查看其中的信息。对于这个apk,可以看到它使用了[RootTools]的jar库,来实现通过su过的shell执行一些命令或binary程序。从dex文件的常量字符中可以看到,这个apk中使用的库是v2.2版本,该版本的源码可以在[https://code.google.com/p/roottools/source/checkout]下载到到,svn版本号是208。

Java代码:

接下来我们可以分析java代码。java代码都已经被加扰过了,不过如果你有足够的耐心并足够仔细,还是能从中读出很多内容。对java代码的分析,一般都是从activity/Application或service的onCreate方法入手(对外的接口不可能加扰,这些方法还是存在的),可以对照AndroidManifest.xml找到入口的Activity及Service,再查看相关信息

在FPSMActivity的onCreate中,如下两句:

t.a(this, 2131034112, "0", "744");
t.a(this, 2131034113, "0.so", "744");

这是混淆过的代码,不过从参数来分析,这是调用RootTools的installBinary()方法,可以直接对照RootTools的源码来看。这个方法的作用是将apk的res/raw下的bin0和lib0.so分别安装到/data/data/com.aatt.fpsm/files/下,分别命名为0和0.so。

FPSMService的onStartCommand方法中,可以看到这个机制就是创建一个pipe:/data/data/com.aatt.fpsm/pipe,不停的从这个pipe中读取数据,显示在前端创建的的浮动window中。可以在adb shell中,

busybox dumphex /data/data/com.aatt.fpsm/pipe

验证帧率的值,刚好是从这个pipe中读入的。

其他位置未看到特别有用的信息。

JNI代码:

Jni库libnp_reader的分析,主要是一些Java的native方法的实现,并没有看到特别异常的现象。

$ arm-linux-androideabi-readelf -s libnp_read.so  
  
Symbol table '.dynsym' contains 69 entries:  
   Num:    Value  Size Type    Bind   Vis      Ndx Name  
      0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND  
      1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize  
      2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit  
      3: 00000ed5    32 FUNC    GLOBAL DEFAULT    7 Java_com_aatt_fpsm_FPSMSe  
      4: 00000000     0 FUNC    GLOBAL DEFAULT  UND umask  
      5: 00000000     0 FUNC    GLOBAL DEFAULT  UND mknod  
      6: 000017c4     8 FUNC    WEAK   DEFAULT    7 __aeabi_unwind_cpp_pr1  
      7: 00000ef5    16 FUNC    GLOBAL DEFAULT    7 Java_com_aatt_fpsm_FPSMSe  
      8: 00000000     0 FUNC    GLOBAL DEFAULT  UND remove  
      9: 00000f05    40 FUNC    GLOBAL DEFAULT    7 Java_com_aatt_fpsm_FPSMSe  
     10: 00000000     0 FUNC    GLOBAL DEFAULT  UND read  
     11: 00004004     4 OBJECT  GLOBAL DEFAULT   16 fd  
     12: 00004008     4 OBJECT  GLOBAL DEFAULT   16 trash  
     13: 000017cc     8 FUNC    GLOBAL DEFAULT    7 __aeabi_unwind_cpp_pr0  
     14: 00000f2d    36 FUNC    GLOBAL DEFAULT    7 Java_com_aatt_fpsm_FPSMSe  
     15: 00000000     0 FUNC    GLOBAL DEFAULT  UND open  
     16: 00000f51    20 FUNC    GLOBAL DEFAULT    7 Java_com_aatt_fpsm_FPSMSe  
     17: 00000000     0 FUNC    GLOBAL DEFAULT  UND close  
     18: 00000f65    36 FUNC    GLOBAL DEFAULT    7 Java_com_aatt_fpsm_FPSMSe  
     19: 0000400c     4 OBJECT  GLOBAL DEFAULT   16 value  
     20: 00004004     0 NOTYPE  GLOBAL DEFAULT  ABS _edata  
     21: 00004004     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start  
     22: 00004010     0 NOTYPE  GLOBAL DEFAULT  ABS _end  
     23: 00000000     0 FUNC    WEAK   DEFAULT  UND __gnu_Unwind_Find_exidx  
     24: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort  
     25: 00000000     0 FUNC    GLOBAL DEFAULT  UND memcpy  
     26: 000017bc     8 FUNC    WEAK   DEFAULT    7 __aeabi_unwind_cpp_pr2  
     27: 00001d88     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP_  
     28: 00001d78     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP  
     29: 00001d98     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_VFP_  
     30: 00001da8     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_WMMX  
     31: 00001e30     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Restore_WMMX  
     32: 00001d64    20 FUNC    GLOBAL DEFAULT    7 restore_core_regs  
     33: 0000134c    68 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Get  
     34: 000013b8    68 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Set  
     35: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_begin_cleanup  
     36: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_type_match  
     37: 00001f64   916 FUNC    GLOBAL DEFAULT    7 __gnu_unwind_execute  
     38: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_call_unexpected  
     39: 000017d4   856 FUNC    GLOBAL DEFAULT    7 _Unwind_VRS_Pop  
     40: 00001d90     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP_D  
     41: 00001d80     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP  
     42: 00001da0     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_VFP_D_1  
     43: 00001dec     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_WMMXD  
     44: 00001e44     0 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Save_WMMXC  
     45: 00001b2c     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetCFA  
     46: 00001b34   164 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_RaiseExcepti  
     47: 00001bd8    28 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_ForcedUnwind  
     48: 00001bf4   108 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Resume  
     49: 00001c60    32 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Resume_or_Re  
     50: 00001c80     4 FUNC    GLOBAL DEFAULT    7 _Unwind_Complete  
     51: 00001c84    32 FUNC    GLOBAL DEFAULT    7 _Unwind_DeleteException  
     52: 00001ca4   192 FUNC    GLOBAL DEFAULT    7 __gnu_Unwind_Backtrace  
     53: 00001d64    20 FUNC    GLOBAL DEFAULT    7 __restore_core_regs  
     54: 00001e58    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_RaiseException  
     55: 00001e58    36 FUNC    GLOBAL DEFAULT    7 _Unwind_RaiseException  
     56: 00001e7c    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Resume  
     57: 00001e7c    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Resume  
     58: 00001ea0    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Resume_or_Rethr  
     59: 00001ea0    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Resume_or_Rethrow  
     60: 00001ec4    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_ForcedUnwind  
     61: 00001ec4    36 FUNC    GLOBAL DEFAULT    7 _Unwind_ForcedUnwind  
     62: 00001ee8    36 FUNC    GLOBAL DEFAULT    7 ___Unwind_Backtrace  
     63: 00001ee8    36 FUNC    GLOBAL DEFAULT    7 _Unwind_Backtrace  
     64: 000022f8    64 FUNC    GLOBAL DEFAULT    7 __gnu_unwind_frame  
     65: 00002338    44 FUNC    GLOBAL DEFAULT    7 _Unwind_GetRegionStart  
     66: 00002364    56 FUNC    GLOBAL DEFAULT    7 _Unwind_GetLanguageSpecif  
     67: 0000239c     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetDataRelBase  
     68: 000023a4     8 FUNC    GLOBAL DEFAULT    7 _Unwind_GetTextRelBase 

从elf信息可以见到,这个库主要是JNI的实现及将unwind实现包含在库中,unwind库一般是用作异常处理的,用它可以获取函数的调用栈信息。

这里由于使用pipe,则怀疑这里需要跨进程通信;而此pipe的通信使用是apk私有的pipe,则推论此apk通过rootTool中提供的Shell类来运行su后的shell,su的作用是通过linux开的后门,允许用户程序通过setuid系统调用,更改用户id,达到root效果。su root后,执行一个native程序,native程序应该就是res/raw/下的bin0。初步怀疑pipe的写端是这个bin0,bin0的symbol信息:
 

$ readelf.exe -s bin0

Symbol table '.dynsym' contains 32 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __libc_init
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit
     3: 00000000     0 FUNC    GLOBAL DEFAULT  UND snprintf
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND fopen
     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND fgets
     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND strstr
     7: 00000000     0 FUNC    GLOBAL DEFAULT  UND strtok
     8: 00000000     0 FUNC    GLOBAL DEFAULT  UND strtoul
     9: 00000000     0 FUNC    GLOBAL DEFAULT  UND fclose
    10: 00000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail
    11: 00000000     0 OBJECT  GLOBAL DEFAULT  UND __stack_chk_guard
    12: 00000000     0 FUNC    GLOBAL DEFAULT  UND memcpy
    13: 00000000     0 FUNC    GLOBAL DEFAULT  UND ptrace
    14: 00000000     0 FUNC    GLOBAL DEFAULT  UND waitpid
    15: 00000000     0 FUNC    GLOBAL DEFAULT  UND opendir
    16: 00000000     0 FUNC    GLOBAL DEFAULT  UND readdir
    17: 00000000     0 FUNC    GLOBAL DEFAULT  UND atoi
    18: 00000000     0 FUNC    GLOBAL DEFAULT  UND sprintf
    19: 00000000     0 FUNC    GLOBAL DEFAULT  UND strcmp
    20: 00000000     0 FUNC    GLOBAL DEFAULT  UND closedir
    21: 00000000     0 FUNC    GLOBAL DEFAULT  UND dlsym
    22: 00000000     0 FUNC    GLOBAL DEFAULT  UND strlen
    23: 0000c000     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    24: 0000c000     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
    25: 0000c004     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    26: 00000000     0 FUNC    WEAK   DEFAULT  UND __gnu_Unwind_Find_exidx
    27: 00000000     0 FUNC    GLOBAL DEFAULT  UND abort
    28: 00000000     0 FUNC    GLOBAL DEFAULT  UND raise
    29: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_begin_cleanup
    30: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_type_match
    31: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __cxa_call_unexpected

我们通过ps来看一下运行时的进程情况,并没有看到此bin0进程,看样子不像是常驻内存的,那么它太不可能是pipe的write端。

从elf的symbol来看,此bin文件应该会使用dlsym和ptrace。使用ptrace的话,基本可以确定,这个bin文件会通过PTRACE_ATTACH到别的进程中,然后修改别的进程数据或代码,以达到自己的目的。ptrace的执行过程应该就是分析的关键,接下来需要从ptrace入手。

小结

这个fps meter没有按照android应用程序的开发规范,通过使用SDK和NDK开发java和jni代码实现,而是使用第三方库,通过su获取root权限,执行自己的可执行binary程序,apk自己实现的库或binary程序,以android资源的形式打包在apk中。这种运行方式,对用户安全及系统稳定来说,是一个危险的动作。

本小结简单了解了此apk的内容,下节将详细介绍下apk资源包中bin文件通过ptrace感染android系统进程的过程。

 

最后

以上就是忧心樱桃为你收集整理的android应用程序fps meter[帧数显示]的分析 —— SurfaceFlinger被注入统计代码 (1)的全部内容,希望文章能够帮你解决android应用程序fps meter[帧数显示]的分析 —— SurfaceFlinger被注入统计代码 (1)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部