朝鲜世界杯_2019篮球世界杯 - dyldrk.com

脱壳入门

hook , 抓包,调试和反调试等安卓逆向基础知识都学了,刚准备成就一番事业,出门遇到大boss 360加固,要么就是梆梆加固企业版,打开app就是网络错误,或闪退或连不上网,源码是看不到的,frida一启动就是process terminated,根本无从下手,深感无力,感觉还是功力尚浅,故欲深究加壳和脱壳。

简要原理和过程主流是写一个代理Application,加载so文件,so里面写dex文件的解密逻辑,再用DexClassLoader加载解密后的dex文件。

要懂一点双亲委派机制,Dex文件结构等知识,具体就不多写了,可以见:[原创]Android漏洞之战(11)——整体加壳原理和脱壳技巧详解-Android安全-看雪-安全社区|安全招聘|kanxue.com 以及这篇文章里的提到的其他链接

例如梆梆加固免费版,这里我用的apk是吾爱破解论坛安卓逆向这档事的demo,梆梆加固注册个账号,把apk传上去加固下再下下来就行,jadx打开

尝试IDA打开,函数名基本都是没啥规律的,点开也是JUMP_OUT(地址),要么是一堆LABEL混淆过的,看不明白。

咋办呢,那就从内存里下手呗。例如frida-dexdump。

原理是这样的,/proc//maps放了这个进程分配的所有内存块索引,然后在这些内存块里寻找dex文件特征的部分,如果能匹配到就dump到文件里。

梆梆加固免费版会有frida的检测,可以用frida魔改版server:Release 16.6.6 · Ylarod/Florida

大致的魔改原理可以见:《安卓逆向这档事》十八、表哥,你也不想你的Frida被检测吧!(上) - 吾爱破解 - 52pojie.cn

究其根源还是要看frida的源码,看看可能会有什么特征。如果未来遇到魔改server也不起作用的话,还要深入研究一下这块。

frida-dexdump -U -f com.zj.wuaipojie

拿到20个dex文件,jadx打开:

可以看到成功脱壳。

frida-dexdump脚本原理frida-dexdump/agent/src/index.ts at master · hluwa/frida-dexdump

这里只写关键函数,主要看searchDex,分为普通搜索和深度搜索,先看普通的:

先检查文件头特征”64 65 78 0a 30….”,假如有的话,先检测路径是否为/data/dalvik-cache或/system,如果是的话直接return(系统的dex),然后调用verify函数验证。

先看0x70是否比这块内存大(0x70是dex文件头大小,如果都没这个大,那这块肯定不是dex文件),然后根据maps来判断,关键在verify_by_maps方法。

0x34 是 DEX 文件头(Dex Header)中的 map_off 偏移量,用于获取 map_list 的地址,然后遍历。

1234567891011struct DexMapList { uint32_t size; // 记录 `DexMapItem` 的数量 DexMapItem list[size]; // MapItem 列表};struct DexMapItem { uint16_t type; // **数据类型**(4096 表示 `TYPE_MAP_LIST`) uint16_t unused; uint32_t size; // 该数据项的数量 uint32_t offset; // 该数据项的偏移量};

遍历的是DexMapItem结构,每个大小为0xC,然后判断类型是否为”TYPE_MAP_LIST”。也就是item_type === 4096这一行。这个类型里的offset表示map_list本身的偏移地址。

所以把之前获取的和TYPE_MAP_LIST里的对比下就能判断是否为完整DEX文件了。

deepsearch部分,检测70 00 00 00

在 DEX 文件头(Header)中,**偏移 0x0C 处存储了 header_size**,它表示 DEX 头部的大小,永远是0x70 (112)

并且还考虑了magic字段被抹去的情况dex_base.readCString(4) != "dex\n",还多了一个verify_ids_off

这里是dex文件里各种重要字段的偏移,要比dex文件本身小,比文件头大(0x70)

剩下的就不写了。

其他方法假如打乱了内存里dex文件的内容,这该怎么办?frida-dexdump肯定就没辙了。在App的启动流程里,DexFile数据结构是一个重要的角色,不管怎么加密解密,随便折腾,最后都要经过这个数据结构。所以hook dexfile相关的函数,也可以实现脱壳。

ida打开libart.so,看雪里的文章中挑选的是这个函数。

第一个参数就是Dexfile,ida应该是自动处理了?得找到原来的名字。

鼠标放上面,按X查看引用下,然后copy出来即可。

frida脚本:

12345678910111213141516171819202122232425262728293031323334function hook() { var libartModule = Process.getModuleByName("libart.so"); // 获取 libart.so 模块 var hookName = "_ZN3art11ClassLinker10SetupClassERKNS_7DexFileERKNS_3dex8ClassDefENS_6HandleINS_6mirror5ClassEEENS_6ObjPtrINS9_11ClassLoaderEEE"; // 要查找的函数名 // 遍历模块的符号(包括导出的函数和变量) var addr = libartModule.getExportByName(hookName) if(addr != null) { Interceptor.attach(addr, { onEnter: function(args) { var dexFileAddr = args[1] console.log("dexFileAddr:" + dexFileAddr) var begin = ptr(dexFileAddr).add(Process.pointerSize).readPointer() var size = ptr(dexFileAddr).add(Process.pointerSize * 2).readU32() save(begin, size) } }) }}function save(begin, size) { var path = '/storage/emulated/0/Download/'+size+'.dex' var file = new File(path, 'wb') file.write(ptr(begin).readByteArray(size)) file.flush() file.close() console.log("dump success!")}function main() { hook()}setImmediate(main)

frida -f -U com.zj.wuaipojie -l unpack.js

运行成功后pull出来

成功拿到源码!

思考总结假如,假如说,直接重写DexFile的加载,这该怎么办?也看了一些其他文章,说目前最新的技术是啥vmp保护。总之要走的路还很长,慢慢学吧。