一、说在前面的话
这两天遇到一个特别让我DT的问题,估计大家通过标题就能知道问题了。没错,就是在应用中集成了微信SDK后,它自动支持了微信分享、登录、收藏、支付等功能。这一点没啥,TM的关键点就是在上传到应用宝时,他们既然提示了未通过,问题如下:
我集成微信SDK,主要是想使用登录和分享功能,支付功能压根不是我想集成的,但是这两个功能也没有单独的SDK,这就很让人DT了,关键这两个平台都是腾讯的,他们是要闹那样呀,不知道一家人要相亲相爱吗。尽给我们添麻烦,算了不说,吐槽没有用,继续搬砖吧
二、炁体源流(回归主题)
接入的流程在这里就不说了,不知道请自行查看教程。
现在我们怀疑他们是根据特定的关键字以作为是否具有支付功能。而微信SDK中的支付关键字为PayReq,而在集成微信SDK时,他们明确说明在 proguard.cfg需要配置:
-keep class com.tencent.mm.opensdk.** {
*;
}
-keep class com.tencent.wxop.** {
*;
}
-keep class com.tencent.mm.sdk.** {
*;
}
但是我反编译时,并未发现他们有携带相关的so库,那为什么需要keep那么多包中的类那,我就把所有防混淆的配置全部移除,并验证登录功能发现完全没有问题,我找了好多手机都验证完全没有问题。
然后提交到应用宝后台后,果然通过了,看来他们就是根据一些Pay关键字做的校验。自此问题。。。
在上线后之前未注意的点暴露了问题,那就是主要使用两个功能点:登录和分享,而当时只是测试了登录并未测试分享,所以就出问题了。
既然这样那肯定是不该混淆的类被混淆了,那我们就通过微信SDK源码分析吧。
我们在编写分享时代码一般如下:
LocalBroadcastManager.getInstance(mContext).registerReceiver(wxBroadcastReceiver, new IntentFilter(Constants.WX_OPTIONS_RESULT_ACTION));
//初始化 WXImageObject 和 WXMediaMessage 对象
WXImageObject imgObj = new WXImageObject(bitmap);
WXMediaMessage msg = new WXMediaMessage();
msg.mediaObject = imgObj;
//构造一个Req
SendMessageToWX.Req req = new SendMessageToWX.Req();
req.transaction = String.valueOf(System.currentTimeMillis());
req.message = msg;
req.scene = shareFlag == 0 ? SendMessageToWX.Req.WXSceneSession : SendMessageToWX.Req.WXSceneTimeline;
//调用api接口,发送数据到微信
iwxapi.sendReq(req);
也就是说主要的关键接口是IWXAPI,而它的实现类是BaseWXApiImplV10,那我就看它的sendReq具体实现:
public boolean sendReq(BaseReq var1) {
...
} else {
com.tencent.mm.opensdk.modelmsg.SendMessageToWX.Req var3;
int var4;
if (var1.getType() == 2 && d.a(var4 = (var3 = (com.tencent.mm.opensdk.modelmsg.SendMessageToWX.Req)var1).message.getType())) {
WXWebpageObject var9;
if (this.getWXAppSupportAPI() < 620756993) {
(var9 = new WXWebpageObject()).webpageUrl = var2.getString("_wxminiprogram_webpageurl");
var3.message.mediaObject = var9;
} else if (var4 == 46 && this.getWXAppSupportAPI() < 620953856) {
(var9 = new WXWebpageObject()).webpageUrl = var2.getString("_wxminiprogram_webpageurl");
var3.message.mediaObject = var9;
} else {
WXMiniProgramObject var8 = (WXMiniProgramObject)var3.message.mediaObject;
var8.userName = var8.userName + "@app";
String var5;
if (!d.b(var5 = var8.path)) {
String[] var11;
if ((var11 = var5.split("\\?")).length > 1) {
var5 = var11[0] + ".html?" + var11[1];
} else {
var5 = var11[0] + ".html";
}
var8.path = var5;
}
}
if (var3.scene != 3 && var3.scene != 1) {
var3.scene = 0;
}
var1.toBundle(var2);
}
Args var7;
(var7 = new Args()).bundle = var2;
var7.content = "weixin://sendreq?appid=" + this.appId;
var7.targetPkgName = "com.tencent.mm";
var7.targetClassName = "com.tencent.mm.plugin.base.stub.WXEntryActivity";
if (var1.getType() == 2) {
try {
String var10 = this.getTokenFromWX(this.context);
var7.token = var10;
} catch (Exception var6) {
Log.e("MicroMsg.SDK.WXApiImplV10", "getTokenFromWX fail, exception = " + var6.getMessage());
}
}
return MMessageActV2.send(this.context, var7);
}
} else {
return this.sendPayReq(this.context, var2);
}
}
}
我们从中并未发现混淆后会导致不可用的地方。
那就继续看发送类型的model类:SendMessageToWX,我们会发现其中有一处:
public void toBundle(Bundle var1) {
super.toBundle(var1);
var1.putAll(Builder.toBundle(this.message));
var1.putInt("_wxapi_sendmessagetowx_req_scene", this.scene);
var1.putInt("_wxapi_sendmessagetowx_req_media_type", this.message.getType());
var1.putString("_wxapi_sendmessagetowx_req_use_open_id", this.userOpenId);
}
我们在继续查看Builder.toBundle(this.message) 的源码:
public static Bundle toBundle(WXMediaMessage var0) {
Bundle var1;
(var1 = new Bundle()).putInt("_wxobject_sdkVer", var0.sdkVer);
var1.putString("_wxobject_title", var0.title);
var1.putString("_wxobject_description", var0.description);
var1.putByteArray("_wxobject_thumbdata", var0.thumbData);
if (var0.mediaObject != null) {
var1.putString("_wxobject_identifier_", pathNewToOld(var0.mediaObject.getClass().getName()));
var0.mediaObject.serialize(var1);
}
var1.putString("_wxobject_mediatagname", var0.mediaTagName);
var1.putString("_wxobject_message_action", var0.messageAction);
var1.putString("_wxobject_message_ext", var0.messageExt);
return var1;
}
这时大家可能已经发现了病毒的所在了,没错就是它:pathNewToOld(var0.mediaObject.getClass().getName())
pathNewToOld方法的源码:
private static String pathNewToOld(String var0) {
if (var0 != null && var0.length() != 0) {
return var0.replace("com.tencent.mm.opensdk.modelmsg", "com.tencent.mm.sdk.openapi");
} else {
Log.e("MicroMsg.SDK.WXMediaMessage", "pathNewToOld fail, newPath is null");
return var0;
}
}
那既然知道了问题的所在了,那就直接防止modelmsg包下的所有类不被混淆即可了。所以我们只需要在proguard.cfg中配置如下即可:
-keep class com.tencent.mm.opensdk.modelmsg.** {
*;
}
自此问题就真的解决了。奥利给
三、总结
移除在proguard.cfg中配置的:
-keep class com.tencent.mm.opensdk.** {
*;
}
-keep class com.tencent.wxop.** {
*;
}
-keep class com.tencent.mm.sdk.** {
*;
}
只需要在proguard.cfg中配置:
-keep class com.tencent.mm.opensdk.modelmsg.** {
*;
}
即可,这是凝结的精华,需要慢慢领悟