一、 法 方法 1
1. Jni Bridge
Jni 提供了让我们在 C++代码层中直接操作 Dalvik(java)数据的接口。我们可以在 jni 中直接操作相关的数据,修改 Android 中的代码。
Android 源码中的实现:dalvik/vm/jni.cpp
2. Object 结构体
Android 源码位置:dalvik/vm/oo/ObjectAndroid 运行时,解析 Dex 文件,并生成相关的结构体:DvmDex。该 结 构 体 中 存 储 了 各 种 字 符 串 、 类 、 方 法 等 信 息 。 加 载 的 时 候 调 用dvmDexFileOpenPartial(/dalvik/vm/DvmDex.cpp)对 Dex 文件进行解析,并转化为可执行的结构体。这也是为什么这个函数可以作为脱壳用的函数的原因之一,以前的爱加密可以直接通过钩这个函数进行脱壳。
其中,Method 结构体则是根据 DexMethod 生成的执行方法类。Dalvik 执行代码时,都是从 Method 中取出代码来执行的。因此可以直接通过操作 Method 结构体来修改执行的代码。
3. Android 源码导出
因为安全性,jni 中的结构体是部分导出的,所以需要补全相关的结构体才能操作其数据。
定义了两个返回数值的函数,并且在按钮点击时调用函数 ret1()
编译运行后取出 classes.dex 文件进行查看,得到两个函数的 isns 结构
通过反射来获取函数调用
需要导入部分源码才能使 Method*被识别
最后调用函数被更改成功
二、 法 方法 2
1. DexFile 修改
根据示例一,可以看到直接修改内存中的 DexFile 文件数据可以达到修改代码的效果。具体为:DexFile -> DexClassDef -> DexClassData -> DexMethod -> DexCode ->insns
2. 内存中 DexFile
遍历 Map 的函数
内存 Map 已经被打印出,找到 dex 的位置
解析出 Dex 文件
将 dex 的内存访问属性从只读修改为可写
cppFlag 添加编译选项
编译报错,数据太大,需要进行修改
最后修改成功
3. 修改方法定位
dexClassDef 遍历,获取 MethodId,对比 MethodName 与 proto,获取目标 Method,然后对相应的 DexCode 进行修改。由于 Dex 加载到内存中是只有只读权限的,所以需要修改内存页的权限才能正常地修改 DexCode 数据。具体解析 Dex 结构的方法可以参考:https://github.com/F8LEFT/FDA