在默认debug包里面,是不对class做混淆的,所以Patch编写相对简单,但是应用在发布的时候都是release包,会对代码做混淆,此时class name ,field name,method name都会被混淆,这个时候改如何编写Patch?
在打release包时,在build/outputs/mapping/release/下面有mapping.txt,这个里面记录了混淆以前的类和混淆以后的类的一一对应关系(所以叫mapping.txt),所以每次发版以后要保留好mapping.txt文件,以便以后编写Patch。
在https://github.com/fengcunhan/Hotpatch-Sample项目中的dexposedexamples,下面新定义了一个Test类:
1
2
3
4
5
6
7
8
9
10
11
|
package com.taobao.model;
public class Test {
public String getHello( int a){
if (a== 0 ){
return "PatchSuccess" ;
}
return "hello" ;
}
} |
这个类在TestFragment中使用到,发了版本以后,发现需要将业务的逻辑改成a<=0都返回PatchSuccess,然后就坑爹了,只能发布一个Patch来做这个事情,有两种Patch编写方式都可以达到上述目的。
1.XC_MethodHook 中重写beforeHookedMethod方法,在这个里面修改传入方法的参数,如果a<0,那就设置a==0,不用改变原先方法的逻辑,就可以达成目的。
2.替换原先方法。XC_MethodReplacement,重写 replaceHookedMethod 方法,在这个方法中实现你想要的逻辑。
下面以第二种方式来编写例子。编写Patch,要替换Test类中的getHello()方法,首先要找到release包下面Test类,然后替换掉getHello方法。在mapping.txt文件中找到了Test类以及对应混淆后的新类名和方法名:
1
2
|
com.taobao.model.Test -> com.taobao.a.a: //类混淆了
java.lang.String getHello( int ) -> a //方法名也混淆了
|
TestPatch编写如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
import android.util.Log;
import com.taobao.android.dexposed.DexposedBridge;
import com.taobao.android.dexposed.XC_MethodReplacement;
/** * Created by renxuan on 15/9/8.
*/
public class TestPatch implements IPatch {
private static final String TAG= "TestPatch" ;
@Override
public void handlePatch( final PatchParam patchParam) throws Throwable {
{
Class<?> cls = null ;
try {
//根据混淆以后的类来找到想要Patch的class
cls= patchParam.context.getClass().getClassLoader().loadClass( "com.taobao.a.a" );
} catch (Exception e) {
e.printStackTrace();
return ;
}
Log.e(TAG, "cls:" + cls);
/**
* 修改逻辑,原先是==0的时候返回Patch success,现在改成<=0都是返回Patch Success
*注意添加的int.class,因为getHello是有参数的,如果没有传int.class,是找不到对应方法的。不知道为什么的人,可以去看看反射先
* */
DexposedBridge.findAndHookMethod(cls, "a" , int . class , new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
Log.e(TAG, "methodHookParam:" + methodHookParam.method.getName());
int methodArgsLength=methodHookParam.args.length;
if (methodArgsLength> 0 ){
int a=( int )methodHookParam.args[ 0 ];
if (a<= 0 ){
return "PatchSuccess" ;
}
}
return "hello" ;
}
});
}
}
} |
注意:
1.如果是在debug下测试,因为Test class没有混淆,Patch包这边的类名就应该是com.taobao.model.Test,方法名也应该还是getHello。
2.release包的签名一定要和patch的签名一样(线上情况),如果没有这个检测,那你的应用就很不安全,恶意攻击者可以随意load一个Patch进去,后果不可想象。
3.有问题可以加入Hotpatch QQ群:254384686