本文为看雪-2014 APP应用攻防竞赛第二阶段第2题(攻击篇)的writeup。

链接:http://bbs.pediy.com/showthread.php?t=193824
平台:Android
类型:CrackMe

我就简单记录下关于SO的脱壳部分,达到能使用IDA动态调试的目的,更多分析看这篇帖子吧:http://bbs.pediy.com/showthread.php?p=1328679。

直接把libcrackme.so拖到IDA,IDA直接挂掉了,拖到010里,用ELF解析脚本跑了下,段解析错误。于是readelf -l之:
1
奇怪的是,只有一个LOAD段,然后一个明显错误的NOTE段,基于NOTE段实际上不会被载入内存,所以直接将其全部清0:
2
然后再拖到IDA里,恩,这次没出错了,不过显然关键代码都被加密了:
3
恩,我用之前研究的SODUMP试了下,但是由于该SO的段表已经发生变化(只有一个LOAD)所以基于相对位置恢复出来的section都错了,不过应该还是基于INIT加壳的。那么还是用IDA将内存DUMP了下来,由于只有一个LOAD段,所以不用进行内存修正,拖到010结果elf头全为0,直接套用原来的头部,再把dynamic段的p_offset的修正一下,当然还得将NOTE段头全部清0,拖到IDA里,一片光明,至少可以静态分析了:
4
接着将原APK中的libcrackme.so替换为处理过后的SO,签名,运行,结果崩溃了。回想了SO加载过后的流程,搞清了为什么崩溃:
Android Linker将SO加载进内存后,首先会依次执行INIT段、INIT_ARRAY段中的函数,而对于libcrackme.so,INIT段、INIT_ARRAY段中的函数执行的功能是进行解密操作,但是我们dump出的SO已经完成了解密,此时再调用INIT段、INIT_ARRAY段中的函数,就发生了不可预期的情况,导致崩溃。
那么知道了原因,解决办法就是不再执行解密函数。
具体方法有很多:

  • 以DEBUG模式启动APP,在linker的CallConstructors函数处下断,然后直接修改函数流程,直接返回。
  • 以DEBUG模式启动APP,在soinfo_link_image函数处下断,通过case DT_INIT,case DT_INIT_ARRAY,获得SO中d->d_tag的位置,直接修改其为任意未知类型。

第一种需要每次调试都修改,第二种一劳永逸。这样终于可以用IDA动态调试。