上一节已经分析应用程序启动后,会通过RootTools库的Shell类,获取root权限并执行/data/data/com.aatt.fpsm/files/0,也就是apk包中的res/raw/bin0这个二进制文件,此二进制文件再通过ptrace系统调用,去绑定到其他进程中,做一些动作。接下来看一下fps meter这个apk到底是做了什么动作。
ptrace是提供一个进程控制另外一个进程运行的机制,通过它可以查看和更改进程的数据,它也是linux上的调试程序gdb的基础。
为查看ptrace的系统调用情况,最好的方法是在内核对应的系统调处理函数中将相关信息打印出来,修改内核的ptrace调用,加入trace,这样就可以在无源码的情况下,查看到bin0都使用了哪些ptrace操作请求,这里主要有:
ptrace: pid=76 req=16 addr=0 data=0 ptrace: 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/maps maps: 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执行。
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/maps 40025000-40026000 r-xp 00000000 5d:10 1304 /system/bin/test_linker 40026000-40027000 r--p 00000000 5d:10 1304 /system/bin/test_linker 40027000-40028000 rw-p 00000000 00:00 0 40028000-40036000 r-xp 00000000 5d:10 144 /system/bin/linker 40036000-40037000 r--p 00000000 00:00 0 40037000-40038000 r--p 0000e000 5d:10 144 /system/bin/linker 40038000-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 80000010surfaceflinger进程暂停,这里读取寄存器的值,可以看到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=beb68938 PTRACE_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=beb68938 PTRACE_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系统的话,是很难被注入代码的。