上篇文章介绍了so加壳(加密特定section)的原理,回顾点这。这篇文章将介绍如何使加壳的so正常运行。

1.动态解密原理

在上篇文章说的加壳原理中,将特定section的长度和偏移分别写入so头部的e_entry和e_shoff字段,那么在解密时只需读取这两个字段即可找到特定section的偏移。接下来还需解决以下几个问题:

  • 如何找到so被加载的起始地址
  • 什么时候进行解密
  • 如何动态修改内存权限

问题一:通过/proc/pid/maps查找。/proc/pid/maps列出了某进程虚拟地址空间的“线性区”,只要遍历这个文件的每一行,查找是否出现so名,出现的话则读取该行第一串十六进制地址,这个地址即是so在进程虚拟空间的其实地址。

问题二:由于我们最终的目的是保护APK,所以不能影响JNI_OnLoad之后的操作,并且希望能尽早执行解密的操作,最合适的方法是直接放在init段中,linker在加载so时会先遍历执行init段中注册的函数,然后再调用JNI_OnLoad。

问题三:进程地址空间被分为不同的线性区(segment),不同segment的权限不同,要被修改的地址必须要有写权限,才能进行section的解密。Linux中可以使用mprotect来修改内存权限,只要添加上写权限即可。有一点要注意:mprotect以页(4k)为单位,修改的起始地址和长度必须为页的整数倍。

进程虚拟地址空间大致如下图:

elfsec8

通过/proc/pid/maps可以找到so起始地址,再读取so文件头的e_shoff,获取so起始地址至特定section的偏移,再从e_entry中读取section总长度。然后计算so起始地址至特定section末尾总共占用了多少页(so起始地址一定为页的整数倍,所以无需考虑),接下来就可以用mprotect修改权限,进而解密特定section。

2.实现代码

测试例子自定义了一个名为hackme的section,并且在这个section中有两个函数hello,hello2,另外在Java层注册了一个名为checkflag的函数,该函数负责在运行时调用hello函数。动态解密方法注册在init段中,函数名为new_init,具体见代码:

运行结果如下:

elfsec11

可以看到section中的函数被正确解密。注意:在解密完自定义section后要刷新cache,因为加密过的section可能已经被加载进cache,若不刷新,则会读取到未解密的代码。刷新使用如下函数:

需要完整源码的可以给我发邮件。

PS:前段时间重装系统,部分project忘了备份(哭晕。。),需要源码的可能得等一阵子了。。。

 

观看更多有关 的文章?

*

+
跳转到评论