懒狗的月更文章。
在Windows / Linux中,经常能听到加壳这个词,最经典的壳当属upx,在商业产品上vmp令人闻风丧胆。
同理,android上也可以通过加壳实现软件防护。
文章已作脱敏处理(手动🐶)
In Java
Java层代码不多,从入口分析下来,能看到样本做的一些简单的处理:
- 字符串 & 字面量混淆
- 壳so为了兼容x86作出相依的处理
这里主要分析arch为arm64
native library
IDA开幕雷击
看下segment
没找到init_array,反而看到几个奇怪的段:note.gnu.proc
、note.gnu.text
符号表也被混淆了
跨出第一步
在Android中,一个动态库的加载经由Java层的System.loadLibrary("native-lib")
直至/system/bin/linker
。
看过Linker源码之后,很容易知道android中动态库经装载 (load) 、链接 (link) 、重定位 (rellocate) 后会执行init
函数,而在一般情况下,init
函数包含在.init_array
区段中
在重定向完成后,linker会调用init
函数
一个动态库的init
函数是不可能被加密的。
出师不利
IDA显示的区段中并不存在.init_array
,如果尝试用其他工具来看区段,就会发现.init_array
的偏移为0x2de830
当IDA跳转到0x2de830
这个地址就会发现,它指向的根本不是一个合法函数。
跨过第一个障碍
样本通过混淆SHT使IDA分析出现错误,实际上IDA分析一个ELF,SHT并不是必须的,这意味着我们可以直接将SHT填充,恢复IDA的解析。
做完这些,下面就是IDA的主场。
进击
重新加载文件,定位至 .init_array
, 这里将关键函数分别命名为 init_*
, 开始分析壳so
壳的第一个函数
简单分析下开头几个函数,其实是混淆。
根据特征写个脚本批量patch
去除之后的效果如下
终于能正常分析了。
第一个函数很简单,读取libc.so
在内存中的位置,判断是否拥有执行权限并修改默认值为PROT_EXEC
的j_PROT_EXEC
。
为什么?(技术所限,未知)
渐入佳境
在宇宙的尺度下,一颗原子的大小微乎其微。
同样的去除混淆之后分析:
smc恢复加密函数,这个函数初始了壳so需要调用的libc中的函数。
具体函数表如下:
遍历phdr,寻找type为dynamic的段,用于后面组装soinfo
link dynamic段,恢复壳so的符号表、字符串表
这里的child_so_header
指向的是子so在壳so中的信息,当然这个信息是被打乱的。
告一段落
恢复壳so后,紧接着就是释放子so
释放子so的过程实际上和linker源码中的装载流程相似
decrypt_and_build_child_soinfo
解密存放在壳so中的加密数据,并将其组装进子so的soinfo中
在子so装载完成之后,壳so会调用子so的init_array,子so开始工作。
至此壳so完成使命。
发表回复