最近在研究Dex文件的编辑,找到了dexlib2这个库,但是网上没有搜索到相关资料。所以在此记录一下。
1.导入Dexlib2
dexlib2截至目前最新版是2.4.0,可以在 dexlib2查看更新。
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation group: 'org.smali', name: 'dexlib2', version: '2.4.0'
}
2.创建DexBackedDexFile
我们可以通过 DexFileFactory 类来创建一个 DexBackedDexFile实例
val dexBackedDexFile = DexFileFactory.loadDexFile(File("classes.dex"), Opcodes.getDefault())
一个 DexBackedDexFile 实例代表一个dex文件,通过 DexBackedDexFile 我们可以访问到所有Class
//所有classes
val classes = dexBackedDexFile.classes
println(classes.size)
3.ClassDef
ClassDef 代表一个类,我们可以通过 DexBackedDexFile 实例来获取一个类,也可以自行创建一个类.
//从DexBackedDexFile获取ClassDef
val zzz = dexBackedDexFile.classes.find {
//type 代表class name
it.type == "Lcom.xxx.yyy.zzz;"
}
//父类
println(zzz?.superclass)
val type = "Lcom.xxx.yyy.zzz;"
val defClass = ImmutableClassDef(
//类名
type,
AccessFlags.PUBLIC.value,
//父类
"Ljava/lang/Object;",
//继承的接口
null,
//源文件
null,
//注解
null,
//静态成员变量
listOf(
ImmutableField(
//所属类
type,
//成员名称
"field1",
//成员类型
"I",
AccessFlags.PRIVATE.value,
//初始化值
ImmutableIntEncodedValue(666),
null,
null
)
),
//成员变量
null,
//直系方法(自定义)
null,
//非直系(继承重载等)
null
)
3.修改某类的方法返回值
fun main() {
val dexBackedDexFile = DexFileFactory.loadDexFile(File("classes.dex"), Opcodes.getDefault())
val type = javaClassoType("com.uzmap.pkg.EntranceActivity")
//定义一个DexRewriter
val reWriter = DexRewriter(object : RewriterModule() {
//修改方法
override fun getMethodRewriter(rewriters: Rewriters): Rewriter<Method> = Rewriter {
//判断类名和方法名
if (it.definingClass == type && it.name == "isFromNativeSDK") {
//返回修改后的方法
return@Rewriter MethodWrapper(it).apply {
//修改方法的实现
methodImplementation = ImmutableMethodImplementation(
//寄存器个数
1, listOf(
ImmutableInstruction11n(
//指令
Opcode.CONST_4,
//寄存器
0,
//值
1),
//return p0
ImmutableInstruction11x(
Opcode.RETURN,
0
)
), null, null
)
}.build()
}
it
}
})
val newDexFile = reWriter.dexFileRewriter.rewrite(dexBackedDexFile)
DexFileFactory.writeDexFile("new.dex", newDexFile)
}
fun javaClassToType(clz: String): String = "L${clz.replace(".", "/")};"
上面我们修改了 isFromNativeSDK 方法使其返回true,这里有关于寄存器的知识:
当一个方法被调用的时候,方法的参数被置于最后N个寄存器中。如果一个方法有2个参数,5个寄存器(v0-v4),那么参数将置于最后2个寄存器——v3和v4。 非静态方法中的第一个参数总是调用该方法的对象。
这里指定了一个寄存器,就是第一个参数this——代表EntranceActivity实例。
修改后的smali代码:
# virtual methods
.method protected final isFromNativeSDK()Z
.locals 0
const/4 p0, 0x1
return p0
.end method
寄存器都是32位的,能够支持任何类型。64位类型(Long和Double型)用2个寄存器表示。 有两种方式指定一个方法中有多少寄存器是可用的。.registers指令指定了方法中寄存器的总数。.locals指令表明了方法中非参寄存器的数量。
MethodWrapper 类的定义:
import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.immutable.ImmutableMethod
class MethodWrapper(method: Method) {
var definingClass = method.definingClass
var name = method.name
var parameters = method.parameters
var returnType = method.returnType
var accessFlags = method.accessFlags
var annotations = method.annotations
var hiddenApiRestrictions = method.hiddenApiRestrictions
var methodImplementation = method.implementation
fun build(): ImmutableMethod = ImmutableMethod(
definingClass,
name,
parameters,
returnType,
accessFlags,
annotations,
hiddenApiRestrictions,
methodImplementation
)
}