之前的一篇概要文章中主要说了我这次研究的一些具体情况,这里就不在多说了,但是这里还需要指出的是,感谢一下三位大神愿意分享的知识(在我看来,懂得分享和细致的人才算是大神,不一定是技术牛奥~~)
第一篇:http://blog.csdn.net/jinzhuojun/article/details/9900105
第二篇:http://bbs.pediy.com/showthread.php?t=186880
第三篇:http://bbs.pediy.com/showthread.php?t=157419
最重要的还是第一篇,所以这里我就不多介绍了,当然如果要看这篇blog的话,最好是先仔细阅读一下上面的三篇文章。
当然我对第一篇文章做了修改了详细的描述了:http://blog.csdn.net/jiangwei0910410003/article/details/39293635
这篇文章最好是看懂了,而且是必须真的懂了,同时将demo自己执行一边,流程走通了,不然下面也是会遇到问题的。
当然这种拦截方式的前提是:手机必须root,同时需要获取su权限
下面开始进入正题
当然在之前的文章中的摘要中我们可以看到我这次主要拦截的是可以获取系统信息的进程,所以我们要搞清楚拦截的对象,这里就不做介绍了,我们拦截的进程是system_server(关于这个进程可以google一下,当然我们可以使用adb shell以及ps命令查看这个进程的相关信息)
关于inject源码这里就不做太多的解释了,主要来看一下他的main函数:
int main(int argc, char** argv) { pid_t target_pid; target_pid = find_pid_of("system_server"); if (-1 == target_pid) { printf("Can't find the process\n"); return -1; } printf("target_id:%d\n",target_pid); inject_remote_process(target_pid, "/data/libsys.so", "hook_entry", "I'm parameter!", strlen("I'm parameter!")); return 0; }
这里的一个主要的方法就是
inject_remote_process(...)
第一个参数:需要注入的进程id
第二个参数:需要注入的动态库(其实这个库中就是包含我们需要替换的函数地址)
第三个参数:动态库入口的函数名称
第四个参数:动态库入口的函数所需要的参数
第五个参数:动态库入口的函数所需要的参数的长度
当然这里我们还有一个是通过进程名称获取到进程的id的函数find_pid_of(...)
既然现在我们需要注入system_server进程中,那么我们就要需要将我们的代码注入到libbiner.so文件中
在sys.c代码中修改几个地方:
第一个修改的地方就是so文件路径:
#define LIBSF_PATH "/system/lib/libbinder.so"
然后就是注入的函数:我们记得在注入surfaceflinger进程的时候,拦截的是eglSwapBuffers函数,我们注入到system_server的话,就是拦截ioctl函数,因为我们知道想使用一些系统服务都是调用这个方法的,下面就对这个函数进行替换:
int (*old_ioctl) (int __fd, unsigned long int __request, void * arg) = 0;// 欲接替ioctl的新函数地址,其中内部调用了老的ioctlint new_ioctl (int __fd, unsigned long int __request, void * arg){ if ( __request == BINDER_WRITE_READ ) { call_count++; LOGD("call_count:%d",call_count); } int res = (*old_ioctl)(__fd, __request, arg); return res;}
在这个函数中,我们会判断一下请求状态_request,如果是BINDER_WRITE_READ,说明上层有请求服务了,这里就是做一个简单的判断,通过一个int值,然后用log将其值打印出来。
int hook_eglSwapBuffers() { old_ioctl = ioctl; void * base_addr = get_module_base(getpid(), LIBSF_PATH); LOGD("libsurfaceflinger.so address = %p\n", base_addr); int fd; fd = open(LIBSF_PATH, O_RDONLY); if (-1 == fd) { LOGD("error\n"); return -1; } Elf32_Ehdr ehdr; read(fd, &ehdr, sizeof(Elf32_Ehdr)); unsigned long shdr_addr = ehdr.e_shoff; int shnum = ehdr.e_shnum; int shent_size = ehdr.e_shentsize; unsigned long stridx = ehdr.e_shstrndx; Elf32_Shdr shdr; lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET); read(fd, &shdr, shent_size); char * string_table = (char *)malloc(shdr.sh_size); lseek(fd, shdr.sh_offset, SEEK_SET); read(fd, string_table, shdr.sh_size); lseek(fd, shdr_addr, SEEK_SET); int i; uint32_t out_addr = 0; uint32_t out_size = 0; uint32_t got_item = 0; int32_t got_found = 0; for (i = 0; i < shnum; i++) { read(fd, &shdr, shent_size); if (shdr.sh_type == SHT_PROGBITS) { int name_idx = shdr.sh_name; if (strcmp(&(string_table[name_idx]), ".got.plt") == 0 || strcmp(&(string_table[name_idx]), ".got") == 0) { out_addr = base_addr + shdr.sh_addr; out_size = shdr.sh_size; LOGD("out_addr = %lx, out_size = %lx\n", out_addr, out_size); for (i = 0; i < out_size; i += 4) { got_item = *(uint32_t *)(out_addr + i); if (got_item == old_ioctl) { LOGD("Found eglSwapBuffers in got\n"); got_found = 1; uint32_t page_size = getpagesize(); uint32_t entry_page_start = (out_addr + i) & (~(page_size - 1)); mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE); *(uint32_t *)(out_addr + i) = new_ioctl; break; } else if (got_item == new_ioctl) { LOGD("Already hooked\n"); break; } } if (got_found) break; } } } free(string_table); close(fd); }
还有就是 hook_eglSwapBuffers函数(函数名都难得改了~~)
好的,修改差不多了,下面我们来编译吧~~
编译会出错的,因为会提示找不到ioctl的定义以及一些常量值,所以我们得找到这个函数的定义,百度一下之后会发现这个函数的定义是在binder.h中,当然这个头文件我们是可以在Android源码中找到的
(关于源码下载和编译的问题:http://blog.csdn.net/jiangwei0910410003/article/details/37988637)。
然后将这个binder.h拷贝到我们编译的目录中,然后再代码中引用一下即可。
再次编译,擦,还是提示错误,说这个函数没有定义。
原因很简单,我们只是引用了头文件,并没有将函数的具体实现引用进来,所以还需要去找到这个函数的具体定义了。
这个过程就是有点麻烦了,纠结了很长时间呀~~,幸好最后搞定了
具体步骤:
从设备的system/lib/ 目录中找到libbinder.so文件,将其拷贝出来,这是一个动态库文件
然后将其放到我们之前的NDK配置目录中的:具体目录如下:
最后我们在Android.mk文件进行引用:
LOCAL_LDLIBS := -llog -lbinder -lutils -landroid_runtime
当然,这里我们会看到-lXXX是通用的格式,同样的,我们如果要用到JVM中的函数的话,会用到libandroid_runtime.so文件,头文件:android_runtime.h,也是可以在源码中找到的(后面会提及到)
(注:这里就介绍了我们如何在使用Android系统中底层的一些函数,同时编译的时候引用到了动态链接库文件)
扩展:
这里在扩展一下:还有另外的一种方式引用so文件:
操作步骤:
首先在我们编译目录中新建一个文件夹:prebuilt
在这个文件夹中存放两个文件:
1.Android.mk:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := binderLOCAL_SRC_FILES := libbinder.so include $(PREBUILT_SHARED_LIBRARY)
2. libbinder.so(这个动态链接库文件就是我们需要用到的)
然后在回到