/maps文件,得到libbinder.so的加载基址(get_module_base也是LibInject中提供的)。代码:
欢迎登录清源教育官网www.tsingyuan.cn 查看更多视频教程 void * binder_addr = get_module_base(target_pid, BINDER_LIB_PATH);
拿到新旧ioctl地址和libbinder.so基址后,就要搜索libbinder.so的GOT表,找匹配项。搜索libbinder.so既可以搜/system/lib/libbinder.so文件的内容,也可以通过ptrace的PEEKTEXT来搜system_server中被加载的libbinder.so在内存中的映像,我选择了前者,因为后者在实现时似乎有点问题,读到的Section内容不太对头,不确定是我程序问题还是被linker给改了。
打开/system/lib/libbinder.so文件,获取其ELF头。
代码:
read(fd, ehdr, sizeof(Elf32_Ehdr));
获得其Section Header区表的地址、数量和大小,并获得字符串表Section的索引号:
代码:
unsigned long shdr_addr = ehdr->e_shoff;
int shnum = ehdr->e_shnum;
int shent_size = ehdr->e_shentsize;
欢迎登录清源教育官网www.tsingyuan.cn 查看更多视频教程 unsigned long stridx = ehdr->e_shstrndx;
先提前把字符串表的内容读出来供遍历Section时比对其name用:
代码:
// 读取Section Header中关于字符串表的描述,得到其尺寸和位置
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);
再重新遍历Section Header,找名为.got表的Section:
代码:
欢迎登录清源教育官网www.tsingyuan.cn 查看更多视频教程 lseek(fd, shdr_addr, SEEK_SET);
int i;
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\") == 0 )
{
/* 就是 GOT 表! */
*out_addr = base_addr + shdr->sh_offset;
*out_size = shdr->sh_size;
欢迎登录清源教育官网www.tsingyuan.cn 查看更多视频教程 return 0;
}
}
}
这样,out_addr和out_size,就是system_server中libbinder.so的GOT表所在的位置和长度。
然后搜索与Hook就好办了:
代码:
for ( i = 0; i < out_size; i ++)
{
ptrace_readdata(target_pid, out_addr, &got_item, 4);
if ( got_item == old_ioctl_addr )
{
/* !!! 拿到了 ioctl 地址 !!! 改成我们的。 */
欢迎登录清源教育官网www.tsingyuan.cn 查看更多视频教程 ptrace_writedata(target_pid, out_addr, &new_ioctl_addr, sizeof(new_ioctl_addr));
break;
}
else if ( got_item == new_ioctl_addr )
{
/* 已经是我们的了,不重复Hook。 */
break;
}
out_addr++;
}
写Android.mk将其在 Android 2.3 源码树下编译后,adb push上去(共有注入程序、shellcode使用的共享库、我们的共享库三个文件),运行注入程序,提示注入成功。如果再次运行,则提示已经注入过了。
再跑命令:
欢迎登录清源教育官网www.tsingyuan.cn 查看更多视频教程 # getprop persist.sys.ioctl.callcount
getprop persist.sys.ioctl.callcount
502
随便动动手机,数字在不断增加中,不过有点影响性能。
======================================================================
四、补充说明
======================================================================
1、每个被加载的模块,无论是可执行程序还是共享库,均有自己的PLT和GOT表。所以拦截这个模块的对外调用的GOT,不影响其他模块。
2、本文只实现了拦截模块的调出到其他模块的动作,其他模块的调入没有涉及到(可能还涉及到比较复杂的重定位操作)。
3、system_server是system用户,不是有权限写所有名字的Property,这里用了persist.sys.开头的属性名,而persist.sys.开头的属性会保存至磁盘,因此性能会差点儿。
欢迎登录清源教育官网www.tsingyuan.cn 查看更多视频教程 4、ioctl虽然实质声明是个可变参数:int new_ioctl (int __fd, unsigned long int __request, /*void * arg*/ ...),这种声明的函数要直接透明地将参数从旧函数传递给新函数似乎还不可行,搜了很多资料也没找到。幸好搜了一把libbinder.so源码,里头对ioctl的调用参数均是仨,干脆就不处理变长形式了。
5、如果不以root身份运行注入程序,则ptrace附加时会失败。
6、Andriod系统的大部分Service都运行在system_server进程中,可以拦截到。但部分自定义的用户Service在用户进程中,如需要拦截,则要ptrace到那个用户进程才行,拦截方法也类似。
7、至于拦截Binder的数据分析与修改,则是下一篇文章的内容了。
======================================================================
五、参考资料
======================================================================
http://wenku.baidu.com/view/222f348f84868762caaed558.html
http://blog.csdn.net/lingfong_cool/article/details/7976112
欢迎登录清源教育官网www.tsingyuan.cn 查看更多视频教程 http://blog.csdn.net/ylyuanlu/article/details/6638825
http://os.pku.edu.cn:8080/gaikuang/submission/TN05.ELF.Format.Summary.pdf
http://blog.csdn.net/fengkehuan/article/details/6223406
http://blog.csdn.net/innost/article/details/6124685
还有不少,没法全写下来,在此向所有无私共享自己研究成果的人致以诚挚的谢意。
来源:清源教育