本文属于《使用钩子技术改进Android程序安全性》系列的下篇,我们将继续探讨使用钩子技术改进Android程序安全性的问题。
一、 libtest_PIC.so库中的PIC代码研究
如果对象是以PIC模式编译的,那么重定位方式是不同的。通过观察图1所示的libtest_PIC.so库文件的节信息,你会注意到函数printf()重定位信息位于两个重定位节处:一个是rel.dyn节,另一个是rel.plt节。这里使用了两个新的重定位类型R_386_GLOB_DAT和R_386_JMP_SLOT;因此,应该使用这样的偏移地址来填充替代函数的32位绝对地址。
图1︰libtest_PIC.so库文件的重定位节
图2显示了非PIC模式编译的函数libtest2()的汇编代码。用红色标出的函数printf()的入口地址在图1中的重定位节rel.dyn和rel.plt中指定。
图2︰libtest2()的反汇编代码——使用-PIC参数编译而成
图3:语句'printf("libtest2: 1st call to the original printf()\n");'的工作流程
图4:语句'global_printf2("libtest2: global_printf2()\n");'的工作流程
图5:语句local_printf ("libtest2: local_printf()\n");'的工作流程
从图1-5中我们看到,当使用PIC参数生成动态库时,libtest2()中的代码将跳转到放在偏移地址0x1fe0、0x2010和0x2000处的地址,而这正是函数printf()的入口处。
二、 钩子方案
如果钩子模块要拦截对printf()的调用并重定向到另一个函数,那么,在链接器把动态库加载到内存后,它应该把重定向的函数地址写入定义在重定位节中‘printf’符号的偏移量地址处。
为了把对printf()函数的调用替换成对重定向的hooked_printf()函数的调用(如图6中的软件流程图中所示),应该在对dlopen()和libtest()的调用之间实现一个钩子函数。这个钩子函数将首先取得符号printf的偏移量地址,此地址是从命名为rel.dyn的重定位节开始的。在本例中,这个地址值是0x1fe0。然后,钩子函数把hooked_printf()函数的绝对地址写入偏移量地址。之后,当libtest2()中的代码调用printf()时,它将进入到对hooked_printf()的调用。
图6:钩子函数拦截对printf()的调用并重定向到对hooked_printf()的调用。
综合以前列出的所有可能的情形,图7展示钩子函数的完整的流程图。main()函数中的变化部分使用图8来描述。
图7:ELF钩子模块的流程图示意
图8:建立钩子后的main()函数中的代码
程序的输出显示在图9中,你可以看到当第一次对libtest1()/libtest2()的调用执行时,函数内部调用了printf()。当再一次调用这两个函数时,在钩子函数执行之后,对printf()函数的调用被重定向到hooked_printf()函数。Hooked_printf()函数将字符串“is HOOKED”附加到正常打印字符串的末尾。图10显示了创建挂钩后的程序执行流程,与图8所示的原流程进行比较,你会注意到hooked_printf()函数被注入到了libtest1()和libtest2()中。
图9:测试程序的输出情况,printf()函数被钩住。
图10:在创建钩子后的测试项目运行流程示意图。
三、 案例分析——Android应用中基于钩子的保护方案
基于在前面几节中钩子技术的研究,我们开发出一个插件来帮助Android应用程序开发人员改善其应用程序的安全性。开发人员需要将仅有的一个Android 的本机库添加到他们的项目并在程序启动时添加一行Java代码来加载此本机库。然后,此库会把一些保护代码注入到应用程序中其他第三方库中。该保护代码将会有助于加密本地文件的输入/输出流,以及绕过函数__android_log_print()来避免通过对Logcat的调用来打印调试信息,进而实现避免一些用户隐私的泄漏。
为了验证该保护插件的有效性,我们写了一个Android应用程序来模拟一个包含第三方库的应用程序的情形。在此测试程序中,此第三方库做两件事情:
1.当外部Java指令调用库中的函数时,它将通过调用__android_log_print()来打印一些信息。
2.在这个库中,代码会创建一个文件 (/ sdcard/data.dat)来把数据保存在本地存储中而且没有加密,然后再把它读回来并将其打印在屏幕上。此操作是模拟应用程序尝试将一些敏感的信息保存在本地文件系统中的。
图11-14中比较了放置钩子前后测试程序的屏幕快照,Logcat输出,以及在设备的本地文件系统中保存文件的内容。
图11:这里使用的Android*平台是Teclast X89HD,Android 4.2.2
图12:应用程序输出——在建立钩子后没有变化
图13:Logcat输出——在建立钩子后输出为空
图14:在建立钩子后在/sdcard中的本地文件'data.dat'已加密
如图中所展示的,程序运行流程在设置钩子后与设置钩子前完全一样。然而,Logcat在设置钩子后从本机库中获取不到输出内容。进一步来看,本地文件的内容不再以纯文本格式存储。
我们开发的插件可以帮助测试应用程序提高安全性,以防止通过Logcat这样的方式收集信息的恶意攻击,以及防止对本地文件系统的离线攻击。
四、 结论
归纳来看,钩子技术可以应用在许多开发领域,从而提供对Android应用程序的无缝安全保护。进一步来看,基于钩子的保护方案不仅可以应用在安卓系统,还可以扩大到其他操作系统,如 Windows *、嵌入式 Linux或其他为物联网设备设计的操作系统。它可以大大减少开发周期和降低维护成本。实战应用中,开发人员可以开发他们自己的基于钩子的安全方案或者使用市场上可用的专业的第三方安全解决方案。
五、 参考资料
1.Redirecting functions in shared ELF libraries,Apriorit Inc, Anthony Shoumikhin, 25 Jul 2013,http://www.codeproject.com/Articles/70302/Redirecting-functions-in-shared-ELF-libraries。
2. x86 API Hooking Demystified,Jurriaan Bremer,http://jbremer.org/x86-api-hooking-demystified/。
3.Android developer guide,http://developer.android.com/index.html。
4.Android Open Source Project,https://source.android.com/。
【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】