android一代壳脱壳方法总结

一代壳主要是使用dex整体加密技术,无论是使用DexClassLoader从文件中动态加载dex还是使用InMemoryDexClassloader从内存中加载dex文件,dex文件在被加载后在内存中一定是一个解密后完整的dex文件。脱壳的方法就是找到这个dex文件并dump下来,大多数一代壳为了防止dex被dump后直接反编译会修改内存中的dex文件的头部信息,一般是修改checksumsignature的值,所以如果dump下来的dex文件反编译报错就说明checksumsignature的值可能被修改了,需要手动进行去修正。

利用系统优化产生的缓存文件

在android 10 之前对于使用DexClassLoader加载的dex文件系统默认会执行dex2oat进行优化,优化后会生成odex文件并存放在/data/dalvik-cache或者是/data/app/packagename/目录下。得到odex文件就可以将ODEX文件还原成DEX文件(需要先将ODEX文件反编译成smali文件,再将smali文件编译成DEX文件,这个过程称为deodex)。但是android 10之后系统默认不再对使用DexClassLoader加载的dex文件执行dex2oat优化。

通过hook Dex文件加载过程中的一些方法

ART下Dex文件加载在native层函数的调用链如下,其中ArtDexFileLoader::OpenCommon通过调用MemMap::MapFile将dex文件从磁盘中映射到内存中,所以在ArtDexFileLoader::OpenCommon函数之后调用的函数都可以成为hook的目标。

DexFile_openDexFileNative()
 ├─ OpenDexFilesFromOat
 │  └─ ArtDexFileLoader::Open
 │      └─ OpenAndReadMagic
 │         OpenWithMagic
 │          └─ ArtDexFileLoader::OpenFile
 │              └─ ArtDexFileLoader::OpenCommon
 │                  └─ DexFileLoader::OpenCommon
 │                       magic == "dex\n" new StandardDexFile
 │                       magic == "cdex"  new CompactDexFile
 └───────────└─ DexFile::DexFile()

通过frida脚本hook DexFileLoader::OpenCommon函数,这里注意不同android版本的此函数的导出符号有所差别,另外32位版本和64位版本也会略有差异。

Interceptor.attach(addrOpenCommon, {
    
    onEnter: function (args) {
        //dex起始位置
        //64位var begin = this.context.x0
        var begin = args[1]
        //打印magic
        console.log("magic : " + Memory.readUtf8String(begin))
        var address = parseInt(begin,16) + 0x20
        var dex_size = Memory.readInt(ptr(address))
        console.log("dex_size :" + dex_size)
        var file = new File("/data/data/%s/" + dex_size + ".dex", "wb")
        file.write(Memory.readByteArray(begin, dex_size))
        file.flush()
        file.close()
    },
    onLeave: function (retval) {
    }
});

frida_dump

ART下类加载过程在native层的函数调用链如下,其中FindClassDef函数从classloader加载的所有dex文件中匹配到需要加载的类后,就向ClassLinker::DefineClass传入此需要加载类所在dex文件的DexFile参数。

DexFile_defineClassNative()
 ├─ FindClassDef
 │  ClassLinker::DefineClass
 │  └─ ClassLinker::LoadClass
 │      └─ LoadField
 │         LoadMethod
 │         LinkCode
 └─ InsertDexFileInToClassLoader

frida_dump是github上一个开源的工具,查看frida_dump的源码就可以发现其就是通过hook ClassLinker::DefineClass获取到DexFile参数并进一步得到dex文件的begin和size将其dump下来。


所以在FindClassDef调用后的很多函数都可以成为脱壳点。

frida_Dexdump

此工具是通过在内存中寻找一些dex文件的特征并进行dump,其不单单是通过dex.035来定位dex文件还使用了一些其他的技巧,感兴趣可以看作者写的文章。但是因为其是通过dex文件特征在内存中定位dex文件并dump,所以难免会出现有错误的情况,反正我在使用的时候就有一些会出现dump不完整的情况。
https://mp.weixin.qq.com/s/n2XHGhshTmvt2FhxyFfoMA

自定义脱壳机

上述方法都是基于frida的,一旦程序有反调试那么frida就会无法使用。那么就可以通过修改android源码并编译生成脱壳机来进行脱壳。这里通过在DexFileLoader::OpenCommon函数开头通过base和size参数对dex进行dump。

总结:通过分析android源码可以发现有很多脱壳点,时机主要就是在类/方法的装载链接或者在方法的执行流程中,只要能够获取到DexFile结构体得到begin和size就可以对dex进行dump。脱壳点有很多因此寻找合适的脱壳点很有必要,不同的脱壳点可能会有不同的效果。

posted @ 2022-10-28 11:20  怎么可以吃突突  阅读(941)  评论(0编辑  收藏  举报