android一代壳脱壳方法总结
一代壳主要是使用dex整体加密技术,无论是使用DexClassLoader从文件中动态加载dex还是使用InMemoryDexClassloader从内存中加载dex文件,dex文件在被加载后在内存中一定是一个解密后完整的dex文件。脱壳的方法就是找到这个dex文件并dump下来,大多数一代壳为了防止dex被dump后直接反编译会修改内存中的dex文件的头部信息,一般是修改checksum
和signature
的值,所以如果dump下来的dex文件反编译报错就说明checksum
和signature
的值可能被修改了,需要手动进行去修正。
利用系统优化产生的缓存文件
在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。脱壳点有很多因此寻找合适的脱壳点很有必要,不同的脱壳点可能会有不同的效果。