android应用程序fps meter[帧数显示]的分析 —— 浅谈root的风险 (2)

来源:互联网 发布:主流的数据分析工具 编辑:程序博客网 时间:2024/05/01 22:19

上一节已经分析应用程序启动后,会通过RootTools库的Shell类,获取root权限并执行/data/data/com.aatt.fpsm/files/0,也就是apk包中的res/raw/bin0这个二进制文件,此二进制文件再通过ptrace系统调用,去绑定到其他进程中,做一些动作。接下来看一下fps meter这个apk到底是做了什么动作。

Ptrace调用过程分析

ptrace是提供一个进程控制另外一个进程运行的机制,通过它可以查看和更改进程的数据,它也是linux上的调试程序gdb的基础。

ptrace调用

为查看ptrace的系统调用情况,最好的方法是在内核对应的系统调处理函数中将相关信息打印出来,修改内核的ptrace调用,加入trace,这样就可以在无源码的情况下,查看到bin0都使用了哪些ptrace操作请求,这里主要有:

ptrace: pid=76 req=16 addr=0 data=0ptrace: pid=76 req=12 addr=0 data=be86f938       ptrace: pid=76 req=5 addr=be84fa28 data=7461642f ptrace: pid=76 req=5 addr=be84fa2c data=61642f61 ptrace: pid=76 req=5 addr=be84fa30 data=632f6174 ptrace: pid=76 req=5 addr=be84fa34 data=612e6d6f ptrace: pid=76 req=5 addr=be84fa38 data=2e747461 ptrace: pid=76 req=5 addr=be84fa3c data=6d737066 ptrace: pid=76 req=5 addr=be84fa40 data=6c69662f ptrace: pid=76 req=5 addr=be84fa44 data=302f7365 ptrace: pid=76 req=5 addr=be84fa48 data=6f732e  ptrace: pid=76 req=13 addr=0 data=be86f938       ptrace: pid=76 req=7 addr=0 data=0               ptrace: pid=76 req=12 addr=0 data=be86f938       ptrace: pid=76 req=5 addr=be84f9a8 data=64616f6c ptrace: pid=76 req=2 addr=be84f9ac data=be86f8bc ptrace: pid=76 req=5 addr=be84f9ac data=40f30000 ptrace: pid=76 req=13 addr=0 data=be86f938       ptrace: pid=76 req=7 addr=0 data=0               ptrace: pid=76 req=12 addr=0 data=be86f938       ptrace: pid=76 req=13 addr=0 data=be86f938       ptrace: pid=76 req=7 addr=0 data=0               ptrace: pid=76 req=12 addr=0 data=be86f938       ptrace: pid=76 req=13 addr=0 data=be86f980       ptrace: pid=76 req=7 addr=0 data=0               ptrace: pid=76 req=17 addr=0 data=0              

从trace出来的pid我们可以知道,ptrace是挂到surfaceflinger进程中,对其进行操作修改的,这从该apk的功能来看,确实合理。先看下surfaceflinger的maps信息(省略了一些非关键信息),这是分析的主要依据:

# cat /proc/76/mapsmaps:  root@android:/proc/76 # cat maps                                                 400a8000-400a9000 r-xp 00000000 5d:10 241        /system/bin/surfaceflinger  400a9000-400aa000 r--p 00000000 5d:10 241        /system/bin/surfaceflinger  400aa000-400ab000 rw-p 00000000 00:00 0   400ab000-400b9000 r-xp 00000000 5d:10 167        /system/bin/linker  400b9000-400ba000 r--p 00000000 00:00 0   400ba000-400bb000 r--p 0000e000 5d:10 167        /system/bin/linker  400bb000-400bc000 rw-p 0000f000 5d:10 167        /system/bin/linker  400bc000-400c6000 rw-p 00000000 00:00 0   400c6000-400f5000 r-xp 00000000 5d:10 771        /system/lib/libsurfaceflinger.so  400f5000-400fa000 r--p 0002e000 5d:10 771        /system/lib/libsurfaceflinger.so  400fa000-400fb000 rwxp 00033000 5d:10 771        /system/lib/libsurfaceflinger.so  400fb000-400fc000 rw-p 00034000 5d:10 771        /system/lib/libsurfaceflinger.so  ................40146000-40147000 ---p 00000000 00:00 0   40147000-40149000 r--p 00045000 5d:10 644        /system/lib/libc.so  40149000-4014b000 rw-p 00047000 5d:10 644        /system/lib/libc.so  4014b000-40156000 rw-p 00000000 00:00 0   ................401d3000-40210000 r-xp 00000000 5d:10 606        /system/lib/libEGL.so  40210000-40212000 r--p 0003c000 5d:10 606        /system/lib/libEGL.so  40212000-40216000 rw-p 0003e000 5d:10 606        /system/lib/libEGL.so  ................40306000-4030e000 r--s 00000000 00:0a 2126       /dev/__properties__ (deleted)  4030e000-4040c000 r--p 00000000 00:0a 2687       /dev/binder  4040c000-4040d000 ---p 00000000 00:00 0   4040d000-4050c000 rw-p 00000000 00:00 0          [stack:125]  ................40b7a000-40b80000 rw-p 00000000 00:00 0          [heap]  ................40c39000-40c3c000 r-xp 00000000 5d:10 1031       /system/vendor/lib/hw/hwcomposer.default.so  40c3c000-40c43000 ---p 00000000 00:00 0   40c43000-40c44000 r--p 00002000 5d:10 1031       /system/vendor/lib/hw/hwcomposer.default.so  40c44000-40c45000 rw-p 00003000 5d:10 1031       /system/vendor/lib/hw/hwcomposer.default.so  40c45000-40c46000 ---p 00000000 00:00 0   40c46000-40d45000 rw-p 00000000 00:00 0          [stack:184]  40d45000-40d46000 ---p 00000000 00:00 0   40d46000-40e45000 rw-p 00000000 00:00 0          [stack:187]  40e45000-40e47000 rw-p 00000000 00:00 0   ................4163c000-41641000 r-xp 00000000 5d:20 311        /data/data/com.aatt.fpsm/files/0.so  41641000-41642000 r--p 00004000 5d:20 311        /data/data/com.aatt.fpsm/files/0.so  41642000-41643000 rw-p 00005000 5d:20 311        /data/data/com.aatt.fpsm/files/0.so  ................bea46000-bea67000 rw-p 00000000 00:00 0          [stack]  ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]  

从maps信息中,其实已经能看出一些门道了:

4163c000-41641000 r-xp 00000000 5d:20 311        /data/data/com.aatt.fpsm/files/0.so  41641000-41642000 r--p 00004000 5d:20 311        /data/data/com.aatt.fpsm/files/0.so  41642000-41643000 rw-p 00005000 5d:20 311        /data/data/com.aatt.fpsm/files/0.so  
这个信息可以看出,fps meter中的0.so,被注入到surfaceflinger中,这在android的安全规则中是不可能的。也就是说surfaceFlinger已经被入侵感染了,不是原来的surfaceFlinger了!


继续分析之前,首先还是看一下ptrace的操作,上述的trace中,request id就是各个ptrace请求命令。这里用到的有如下几个:

   Request

Macro

Description

16

PTRACE_ATTACH

Attach到被调试进程,attach之后,被调试进程将暂停执行

12

PTRACE_GETREGS

获取被调试线程的各个寄存器值

5

PTRACE_POKEDATA  

修改内存地址的值

13

PTRACE_SETREGS

设置寄存器的值

7

PTRACE_CONT

继续执行暂停的程序

2

PTRACE_PEEKDATA

从被调试进程的指定内存位置读取数据

17

PTRACE_DETACH

停止对进程的调试,恢复其常态运行

调用细节

为了解更细节的信息,需要更详尽的trace,继续将相关的输入输出参数都打印出来,结合上面的maps信息,对每一步的ptrace调用,详细分析流程如下:

ptrace_request: pid=76 req=16 addr=0 data=0  ptrace_request: pid=76 req=12 addr=0 data=beb68938   PTRACE_GETREGS:           r0 00000003  r1 c0186201      r2 bea66ac8  r3 bea66ac4           r4 40b7e020  r5 40b7dff0      r6 40b7e050  r7 00000036           r8 00000001  r9 40b7dff8      sl 00000000  fp bea66b7c           ip 402bbf24  sp bea66aa8      lr 4012b9f5  pc 40117ffc  cpsr 80000010  

这里是attach到surfaceflinger线程,暂停surfaceflinger线程的执行,然后获取暂停时的寄存器数据。对于这里分析的fps meter的注入入侵,需要保存surfaceflinger线程暂停点的寄存器信息,入侵完毕后再恢复现场,继续surfaceflinger的正常运行。

ptrace: pid=76 req=5 addr= bea66a28 data=7461642f ptrace: pid=76 req=5 addr= bea66a2c data=61642f61 ptrace: pid=76 req=5 addr= bea66a30 data=632f6174 ptrace: pid=76 req=5 addr= bea66a34 data=612e6d6f ptrace: pid=76 req=5 addr= bea66a38 data=2e747461 ptrace: pid=76 req=5 addr= bea66a3c data=6d737066 ptrace: pid=76 req=5 addr= bea66a40 data=6c69662f ptrace: pid=76 req=5 addr= bea66a44 data=302f7365 ptrace: pid=76 req=5 addr= bea66a48 data=006f732e  
这里的req=5就是PTRACE_POKEDATA,修改栈上的数据,修改的指针是addr=0xbea66a28, 则以上几个POKEDATA的调用,作用将(char )addr[]的内容修改为:“/data/data/com.aatt.fpsm/files/0.so”,这即是要注入的so的路径。

ptrace_request: pid=76 req=13 addr=0 data=beb68938   PTRACE_SETREGS:           r0 bea66a28  r1 00000000      r2 bea66ac8  r3 bea66ac4           r4 40b7e020  r5 40b7dff0      r6 40b7e050  r7 00000036           r8 00000001  r9 40b7dff8      sl 00000000  fp bea66b7c           ip 402bbf24  sp bea66a28      lr 00000000  pc 400b00ad  cpsr 80000030   ptrace_request: pid=76 req=7 addr=0 data=0  
调用PTRACE_SETREGS设置寄存器,然后再调用PTRACE_CONT执行。
这里面设置的寄存器,主要变动如下:
  • R0:应参数,是字符串“/data/data/com.aatt.fpsm/files/0.so”的指针
  • sp : 堆栈地址,也是字符串的指针位置
  • lr: 函数返回地址
  • pc:需要注入代码的起始地址
这里的lr是0,程序出错后跳到0去,肯定会出现异常,导致程序停止,调试端进程可以通过waitpid得知该次注入的代码已经被执行。

pc的地址 400b00ad,从上节的maps表中可以看到,这是android的动态连接器linker中的代码,PC是位于linker的代码偏移位置:400b00ad-400ab000=50ad的位置。50ad说明实际偏移是0x50AC,最低位的1表明是thumb指令。

那这个位置具体是什么呢(肯定是某个函数)?这个函数没有导出符号,我也没看出来,不过从参数和上下文分析,猜测可能是dlopen,不然用so作参数,调用linker的函数干嘛呢? 写个小程序验证了一下:

#define LOG_TAG "TEST"#include <dlfcn.h>#include <unistd.h>#include <cutils/log.h>int main(int argc, char *argv){ALOGD("dlopen:%p dlsym:%p", (void*)dlopen, (void*)dlsym);while(1){sleep(100);}}
运行后,输出的结果:

D/TEST    (27539): dlopen:0x4002d0ad dlsym:0x4002d019
对比此测试进程的maps信息:

root@android:/ # cat /proc/27539/maps40025000-40026000 r-xp 00000000 5d:10 1304       /system/bin/test_linker40026000-40027000 r--p 00000000 5d:10 1304       /system/bin/test_linker40027000-40028000 rw-p 00000000 00:00 040028000-40036000 r-xp 00000000 5d:10 144        /system/bin/linker40036000-40037000 r--p 00000000 00:00 040037000-40038000 r--p 0000e000 5d:10 144        /system/bin/linker40038000-40039000 rw-p 0000f000 5d:10 144        /system/bin/linker...400d5000-400dd000 r--s 00000000 00:0a 2180       /dev/__properties__ (deleted)beb4d000-beb6e000 rw-p 00000000 00:00 0          [stack]ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]
4002d0ad-40028000=50ad, 正好验证,确定这个ptrace是让surfaceflinger调用linker中的dlopen,加载/data/data/com.aatt.fpsm/files/0.so

so库加载完成后,系统waitpid返回,再看一下寄存器:

ptrace_request: pid=76 req=12 addr=0 data=beb68938   PTRACE_GETREGS:           r0 400be054  r1 00000000      r2 00000001  r3 004b4001           r4 40b7e020  r5 40b7dff0      r6 40b7e050  r7 00000036           r8 00000001  r9 40b7dff8      sl 00000000  fp bea66b7c           ip 00000000  sp bea66a28      lr 00001ffc  pc 00000000  cpsr 80000010 
surfaceflinger进程暂停,这里读取寄存器的值,可以看到PC是0,注入的程序执行完毕
ptrace: pid=76 req=5 addr=bea669a8 data=64616f6c ptrace: pid=76 req=2 addr=bea669ac data=40b7e020 ptrace: pid=76 req=5 addr=bea669ac data=40b7e000 

设置地址0xbea669a8的值为"load",读取的bea669ac值为0x40b7e020,然后再通过PTRACE_POKEDATA将其设置成0x40b7e00,这个地址,位于堆中,应该是malloc出来的一个指针,具体作用不明。

ptrace_request: pid=76 req=13 addr=0 data=beb68938PTRACE_SETREGS:           r0 400be054  r1 bea669a8      r2 00000001  r3 004b4001           r4 40b7e020  r5 40b7dff0      r6 40b7e050  r7 00000036           r8 00000001  r9 40b7dff8      sl 00000000  fp bea66b7c           ip 00000000  sp bea669a8      lr 00000000  pc 400b0019  cpsr 80000030  ptrace_request: pid=75 req=7 addr=0 data=0

PTRACE_CONT执行, 这里设置的pc:400b0019,可以看到是linker中的偏移位置:0x5019,结合刚才写的测例,就是dlsym函数。这里就是查询到load函数的地址。

ptrace_request: pid=76 req=12 addr=0 data=beb68938PTRACE_GETREGS:           r0 4163da81  r1 00000000      r2 400be054  r3 400be054           r4 40b7e020  r5 40b7dff0      r6 40b7e050  r7 00000036           r8 00000001  r9 40b7dff8      sl 00000000  fp bea66b7c           ip 00000000  sp bea669a8      lr 00001ffc  pc 00000000  cpsr 80000010 ptrace_request: pid=75 req=13 addr=0 data=beb68938  PTRACE_SETREGS:           r0 4163da81  r1 00000000      r2 400be054  r3 400be054           r4 40b7e020  r5 40b7dff0      r6 40b7e050  r7 00000036           r8 00000001  r9 40b7dff8      sl 00000000  fp bea66b7c           ip 00000000  sp bea669a8      lr 00000000  pc 4163da81  cpsr 80000030   ptrace_request: pid=76 req=7 addr=0 data=0

 PC为0.so中的函数,在0.so中的偏移为:4163da81-4163c000=1a81,这刚好是刚才dlsym查出来的load函数的位置,见IDA pro的反汇编信息:

这样,就在surfaceflinger中注入了用户提供的so库的代码了!

注入完成后,接下来我们需要恢复之前被暂停的surfaceflinger进程的正常执行:

ptrace_request: pid=76 req=12 addr=0 data=beb68938 PTRACE_GETREGS:           r0 00000000  r1 4272d3f3      r2 00000001  r3 40b7dff8           r4 40b7e020  r5 40b7dff0      r6 40b7e050  r7 00000036           r8 00000001  r9 40b7dff8      sl 00000000  fp bea66b7c           ip 40000002  sp bea669a8      lr 0000001e  pc 00000000  cpsr 60000010  ptrace_request: pid=76 req=13 addr=0 data=beb68938 PTRACE_SETREGS:           r0 00000003  r1 c0186201      r2 bea66ac8  r3 bea66ac4           r4 40b7e020  r5 40b7dff0      r6 40b7e050  r7 00000036           r8 00000001  r9 40b7dff8      sl 00000000  fp bea66b7c           ip 402bbf24  sp bea66aa8      lr 4012b9f5  pc 40117ffc  cpsr 80000010    ptrace_request: pid=76 req=7 addr=0 data=0   ptrace_request: pid=76 req=17 addr=0 data=0

寄存器恢复到暂前的样子,寄生进程从surfaceflinge分离,恢复宿主进程执行。


小结

以上我们可以简单的比喻一下:寄生进程为bin0,宿主进程为surfaceflinger,寄生进程通过ptrace绑到宿主进程上,咬一口(linker调用),下了一个卵(/data/data/com.aatt.fpsm/files/0.so及load调用)到宿主进程,然后飞走了。

通过详细分析了注入过程,应该了解了用ptrace将需要的库或代码植入宿主进程的方法。在不经意间,你运行的正常进程,已经被人为的添加进去可能有害的代码,这可能对你的信息安全或者系统稳定性有很大的影响。

不过linux系统相对来说还是很安全的,这里也并没有利用什么漏洞,如果用户不root系统的话,是很难被注入代码的。



原创粉丝点击