最近公司研究一个变速软件,本来就是一个很小的功能,就是在用户游戏的时候能够加速减速这个功能,
并且对于网络的游戏并没有做要求,实现的思路主要是给目标进程注入自己写的so,然后再hook住gettimeofday,
clock_gettime 这两个比较关键的刷帧的时间函数,改变其返回值,达到刷帧变慢或者变快的效果,然后
实现游戏变速的功能。


功能要求那是相当的简单,但是实现起来发现却没有那么简单,有几个技术关键点要完成,并且对月linux下的so
加载要有一些了解才行。

技术关键点

1 能够顺利的将自己编写的so注入到目标进程中

2 能够正确找到目标进程调用要hook的函数的so库,并且替换其got表中的地址,
换成自己的函数地址。

3 直接找到目标进程中的地址空间中的目标函数的地址,比如send,recv,gettimeofday 这些基础的
函数一定在libc.so中也就是glibc中的,所以根据目标进程的maps表这些函数的实现地址也一定在
libc.so所占的地址空间中。找到这个函数的实际地址以后,首先通过mprotect 修改页面的属性,
让页面属性能够写入,然后喂入一段自己编写的汇编代码,跳转到自己的函数里面,达到hook的目的。

4 使用ptrace函数附着到目标进程以后,截获相关的系统调用,当系统调用出现以后,修改系统调用的
参数,达到修改函数实现的目的。

上面几个关键的技术点,其中2和3 需要以1 为基础点,而4 则可以脱离1独立存在。

下面分别描述一下几个技术的关键点的实现方法和需要注意的事项。

关键技术点1 将自己编写的so注入到目标进程中

这个的实现网上一搜索一大堆,关键核心点就是attach到目标进程,然后找到目标进程的mmap函数的地址
调用mmap函数
通过 map_base = (uint8_t *)regs.ARM_r0; 得到mmap函数的返回值,这个时候就可以将精心编写好的
一小段汇编代码写入到这个map_base这个地址上了,其实这个地址是目标进程的地址。
并且权限是 parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
可读,可写,可执行的。
同时还需要得到 目标进程的 dlopen dlsym dlclose 这三个加载库的函数的地址,这是需要在汇编中
调用的,这些都准备好了以后,给汇编代码准备了栈空间传递了要注入的库的路径的空间以后,然后将
那一小段汇编代码拷贝到目标进程的地址
ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 );

接着就执行
memcpy( &regs, &original_regs, sizeof(regs) );
regs.ARM_sp = (long)remote_code_ptr;
// change pc to execute instructions at remote_code_ptr
regs.ARM_pc = (long)remote_code_ptr;

sp指针是r13 堆栈寄存器,然后pc是r15 执行寄存器,强制赋值以后就执行汇编代码了,
而汇编代码也很简单
就是调用了一下 dlopen 将要注入的so注入到目标进程中去了,仅仅将so注入到目标进程中其实没有什么
用的,所以需要dlsym 中获取要注入的so库的一个函数,再调用一下这个函数,这个函数要干很多事情的
最重要的就是修改目标进程中加载的so的got表,修改成要hook的自己的函数,这部分具体要在关键技术点
2 详细说明,记住这个函数是 so_entry 即可

关于技术关键点1 就这么点东西需要分析的。当然最后还需要父进程的上下文,然后detach子进程。

关键技术点2:

根据第一步,将编写好的so给注入到目标进程了,而注入的so就开始 so_entry
so_entry 这个函数首先需要注入者写好要修改目标进程的哪个so库,和哪个函数,哪个函数还好说,毕竟做
不同的功能hook的函数是很明确的,比如你做游戏变速,肯定要hook gettimeofday和clock_gettime 这些
函数了,你做流量监控肯定要hook connet send recvmsg 这些函数了,你做电话或者短信防火墙就要hook
电话或者短信相关的函数。
但是修改目标进程的哪个so库,的确是一个比较麻烦的事情,android上层应用普通的进程都会挂载很多很多
的so库,并且你修改了a.so 的got表,但是java应用程序是通过b.so 调用的要hook函数,那么对不起,你
同样没有办法hook住想要的hook的函数,因为你只是修改了a.so中的got表的该函数的跳转地址,所以这种方
法的弊端在于你需要要了解你要hook的函数在这个应用中使用哪个so库调用的,或者你把maps表所有的so的
got表都修改一遍,反正就是挺麻烦的。

其中具体方法就是 do_hook 这个函数,找到so的基地址以后,再通过解析elf文件,找到got表地址的偏移
量,根据这个偏移量再加上基地址 得到got表的地址 got_shdr->sh_offset + module_base

接着再把 所在页的
mprotect((uint32_t *) entry_page_start, page_size, PROT_READ | PROT_WRITE);
属性变换成可读可写,最后将hookfun的地址替换got表的跳转地址
// replace GOT entry content with hook_func's address
memcpy((uint32_t *) entry_addr, &hook_func, sizeof(uint32_t));

下面的这段trace分析很好的说明了这个过程

maps表(部分)

400dc000-4011f000 r-xp 00000000 103:02 565 /system/lib/libc.so
4011f000-40122000 rw-p 00043000 103:02 565 /system/lib/libc.so

40729000-407cb000 r-xp 00000000 103:02 585 /system/lib/libdvm.so
407cc000-407ce000 r--p 000a2000 103:02 585 /system/lib/libdvm.so
407ce000-407cf000 rw-p 000a4000 103:02 585 /system/lib/libdvm.so
407cf000-407d4000 rw-p 000a5000 103:02 585 /system/lib/libdvm.so

41c20000-41c23000 r-xp 00000000 103:04 588087 /data/data/com.example.socketcomm/lib/libsocketclient.so
41c23000-41c25000 rw-p 00002000 103:04 588087 /data/data/com.example.socketcomm/lib/libsocketclient.so

5c06c000-5c070000 r-xp 00000000 00:0c 84482 /dev/libhook.so
5c070000-5c071000 r--p 00003000 00:0c 84482 /dev/libhook.so
5c071000-5c072000 rw-p 00004000 00:0c 84482 /dev/libhook.so


11-20 13:12:31.509: I/cheatecore-hookso(19676): [+] lib loaded ...
11-20 13:12:31.509: I/hook(19676): [+] base address of /system/lib/libdvm.so: 0x40729000(libdvm.so 虽然got表中有send函数,但是应用并没有通过这个库来调用
socket 的send函数,所以修改这个so的got表项其实是没有用处的)
11-20 13:12:31.517: I/hook(19676): [+] got entry offset of send: 0xa5f40
11-20 13:12:31.517: I/hook(19676): [----]1-407cef40
11-20 13:12:31.517: D/hook(19676): [+] hook_fun addr: 0x5c06d24d(这个地址在libhook.so中r-xp)
11-20 13:12:31.517: D/hook(19676): [+] got entry addr: 0x407cef40(这个地址在libdvm.so中rw-p)
11-20 13:12:31.517: D/hook(19676): [+] original addr: 0x400f5f15(这是send的真实实现地址,在glib也就是libc.so这个库中r-xp中)
11-20 13:12:31.517: D/hook(19676): [+] page size: 0x1000
11-20 13:12:31.517: D/hook(19676): [+] entry page start: 0x407ce000(mprotect 修改页属性的时候需要从整页开始)
11-20 13:12:31.517: I/cheatecore-hookso(19676): [+] module_path /system/lib/libdvm.so function send address is : 0x400f5f15


11-20 13:12:31.524: I/hook(19676): [+] base address of /data/data/com.example.socketcomm/lib/libsocketclient.so: 0x41c20000
(libsocketclient.so got表中有send函数,但是应用通过这个库来调用socket 的send函数,所以修改这个so的got表项是可以生效的)
11-20 13:12:31.524: I/hook(19676): [+] got entry offset of send: 0x3fdc
11-20 13:12:31.524: I/hook(19676): [----]1-41c23fdc
11-20 13:12:31.524: D/hook(19676): [+] hook_fun addr: 0x5c06d24d
11-20 13:12:31.524: D/hook(19676): [+] got entry addr: 0x41c23fdc
11-20 13:12:31.524: D/hook(19676): [+] original addr: 0x400f5f15 (这是send的真实实现地址,在glib也就是libc.so这个库中r-xp中和上面的一样)
11-20 13:12:31.524: D/hook(19676): [+] page size: 0x1000
11-20 13:12:31.524: D/hook(19676): [+] entry page start: 0x41c23000
11-20 13:12:31.524: I/cheatecore-hookso(19676): [+] module_path /data/data/com.example.socketcomm/lib/libsocketclient.so function send address is : 0x400f5f15

目前这种调试方法在实践中有一个问题,就是在变速 unity3d比如神庙逃亡 这类游戏的时候,即便修改了所有的so的got表,发现也没有生效,这个问题尚在研究,
当中。



关键技术点3: 这个方法比较关键技术点2来说可以说是简单粗暴,不美观,但是很霸道,分析elf文件,查找got表等操作都和关键技术点2是一样,唯一不同的是
对于 original addr: 0x400f5f15(这是send的真实实现地址,在glib也就是libc.so这个库中r-xp中)
这个send函数的真实地址的处理方式不同,因为已经找到send函数的地址了

entry_page_start = PAGE_START(original_addr, page_size);
LOGD("[+] entry page start: 0x%x", entry_page_start);
result = mprotect((uint32_t *) entry_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);

找到页的边沿地址,然后通过mprotect 函数给其赋予 PROT_READ | PROT_WRITE | PROT_EXEC 这样权限
在那个地址上喂入一段汇编代码,完成跳转即可。


关键技术点4:
只是以前实验了一下,觉得原理可行,但是尚未深究...


附件有个例子,里面包含两个程序,一个是android应用,一个是命令行的注入程序,里面实现了so注入和拦截socket 的send recv函数,但是没有是实现直接向地址里面写入自己的汇编代码的功能...



更多相关文章

  1. Android(安卓)OpenGLES2.0(七)——着色器语言GLSL
  2. 史上最强Android保活思路:深入剖析腾讯TIM的进程永生技术
  3. 【Android】Android中WebView实现Java与JS交互
  4. android回调机制总结
  5. 【Bugly干货分享】手把手教你逆向分析 Android(安卓)程序
  6. Android是如何管理App内存的--Android内存优化第二弹
  7. 作为Android开发者 你真的知道app从启动到主页显示的过程吗?
  8. Android从驱动层到应用程序层的通信
  9. Android(安卓)驱动和系统开发 2. 解析模拟器GPS模块 (原创)

随机推荐

  1. 还在用android.support?该考虑迁移Android
  2. Android 2.3新特性:Web Apps概述
  3. Android系统架构概述
  4. [Android--Tool]不在Android设备运行而打
  5. Android Studio NDK开发在C代码中将Log输
  6. Android的两种数据存储方式分析(二)
  7. Android轻量型数据库SQLite详解
  8. android的logcat详细用法
  9. Android进程与内存及内存泄露
  10. AsyncTask 很好