Open zixing131 opened 5 years ago
这两天试验了下,确实更新了sig,简单试了下几种组合后的md5都不对,可能需要反编译下apk或找到动态加载下来的农场的代码。 我最近比较忙可能没空去看这个问题,希望大家能一起来研究下。
另外,这个版本似乎可以重放攻击,sig没有时间戳,尝试抓包获取sig后,写死在程序内,依旧可以获取数据。
另外,这个版本似乎可以重放攻击,sig没有时间戳,尝试抓包获取sig后,写死在程序内,依旧可以获取数据。
我刚才试了一下,浇花是可以的,https://blog.csdn.net/hanziyuan08/article/details/52606908
这是我之前写的代码
""" @author:zixing @email:zixing131@qq.com @date:2017-10-26 02:46 """ from hashlib import sha1 import hmac import base64 import urllib import hashlib import requests import collections
def cn(x): return x.decode("u8").encode("gbk")
class WeLove: def init(self,token,appkey): self.urlhead = "http://sweet.api.welove520.com" # http://api.welove520.com self.access_token = token self.app_key = appkey
def md5(self,v):
m2 = hashlib.md5()
m2.update(v)
return m2.hexdigest()
def signpass(self,pas):
pas = pas + "8eb4afa4"
return self.md5(pas)
def hmacsha1(self,key,value):
my_sign = hmac.new(key, value, sha1).digest()
my_sign = base64.b64encode(my_sign)
return my_sign
def getsig(self,url,data,method="POST"):
key = "8b5b6eca8a9d1d1f"
value = "%s&%s&%s"%(method,urllib.quote_plus(url),urllib.quote_plus(urllib.urlencode(data)))
result = self.hmacsha1(key,value)
return result
def post_post(self,url,data):
url = self.urlhead + url
data2 = sorted(data.items(), key=lambda d:d[0], reverse = False)
sig = self.getsig(url,data2)
data2.append(("sig",sig))
result = requests.post(url,data2)
return cn(result.content)
def post_get(self,url,data):
url = self.urlhead + url
data2 = sorted(data.items(), key=lambda d:d[0], reverse = False)
sig = self.getsig(url,data2)
data2.append(("sig",sig))
url = url +"?"+ urllib.urlencode(data2)
result = requests.post(url)
return cn(result.content)
#打卡,成功返回 {"result":1}
# 其他返回值
# {"result":2205,"error_msg":"已经打过卡了"}
# {"result":2204,"error_msg":"没有找到对应打卡"}
def punch(self,user_card):
if(user_card["is_punched"] == 1):
return cn("%s 已经打卡了,共打卡次数:%d,持续天数:%d") %(user_card["text"],user_card["punch_sum"],user_card["duration_num"])
user_card_id = user_card["user_card_id"]
url = "/v5/punch/punch"
data = {
"access_token" : self.access_token,
"app_key" : self.app_key ,
"user_card_id" : user_card_id
}
result = eval(self.post_get(url,data))
if(result["result"] == 1):
return cn("%s 打卡成功,共打卡次数:%d,持续天数:%d") %(user_card["text"],user_card["punch_sum"],user_card["duration_num"])
else:
return cn("%s %s,共打卡次数:%d,持续天数:%d") %(user_card["text"],result["error_msg"],user_card["punch_sum"],user_card["duration_num"])
#获取打卡列表,返回 user_cards
def getPunchIndex(self):
url = "/v5/punch/index"
data = {
"access_token" : self.access_token,
"app_key" : self.app_key
}
result = eval(self.post_get(url,data))
if(result["result"]!=1):
print result["error_msg"]
return []
result = result["user_cards"]
print cn("共获取到 %d条 打卡操作"%(len(result)))
return result
#给小树浇水,晒太阳,op=1时为浇树,op=2为晒阳光
def treeOp(self,op):
url = "/v1/game/tree/op"
data = {
"access_token" : self.access_token,
"app_key" : self.app_key,
"op" : op
}
result = eval(self.post_post(url,data))
if(result["result"]!=1):
return result["error_msg"]
return cn("%s成功,我已连续浇树%d天,对方连续浇树%d天,小树等级:%d级,当前成长值:%d,升级还需要成长值:%d,小树留言板:'%s'")%(
op==1 and cn("浇水") or cn("晒阳光"),
result["lasting_days"],
result["lover_lasting_days"],
result["level"],
result["level_growth"],
result["next_stage_growth"],
result["name"]
)
#农场签到
def signin(self):
url = "/v1/game/farm/signin"
data = {
"access_token" : self.access_token,
"app_key" : self.app_key,
"ph" : "farm"
}
result = eval(self.post_post(url,data))
if(result["result"]!=1):
return result["error_msg"]
messages = result["messages"][0]
lasting_days = messages["lasting_days"] #连续经营时间
return cn("签到完成,连续经营时间:%d ") %(lasting_days)
#开宝箱
def openGiftBox(self):
url = "/v1/game/farm/giftbox/open"
data = {
"access_token" : self.access_token,
"app_key" : self.app_key,
"rainbow_coin" : 0 ,
"ph" : "farm"
}
result = eval(self.post_post(url,data))
if(result["result"]!=1):
print result
return result["error_msg"]
print result
if(name == "main"):
print(cn("-->执行微爱签到打卡任务..."))
print(cn("\n-->执行打卡任务"))
welove = WeLove("","ac5f34563a4344c4")
punchList = welove.getPunchIndex()
for i in punchList:
print welove.punch(i)
print(cn("\n-->执行浇树任务"))
print welove.treeOp(1)
print welove.treeOp(2)
print(cn("\n-->执行农场签到"))
print welove.signin()
另外,看了下md5的那个算法,可能是这个
public static C9409d m46162a() {
String c = UserSpaceData.m42989a().mo55128c();
String digest = MD5Utils.digest(c + WeloveK.SIG_SECRET);
String deviceId = DeviceInfoUtil.getDeviceId(WeloveAppContext.m41093b().mo53325c());
String macAddr = DeviceInfoUtil.getMacAddr(WeloveAppContext.m41093b().mo53325c());
C9409d dVar = new C9409d();
dVar.mo59935a(c);
dVar.mo59936b(digest);
dVar.mo59937c(UserAgentInfo.m45422g().mo59396a() + ResourceUtil.getAppVersionName());
dVar.mo59938d(deviceId);
dVar.mo59939e(macAddr);
dVar.mo59940f(null);
return dVar;
}
accesstoken+“8b5b6eca8a9d1d1f” 然后求md5
accesstoken+“8b5b6eca8a9d1d1f” 然后求md5
你看一下我上传的这个版本。 private boolean a(String paramString, byte[] paramArrayOfbyte1, byte[] paramArrayOfbyte2) { if (TextUtils.isEmpty(paramString)) return false; if (paramArrayOfbyte1 != null) { if (paramArrayOfbyte1.length <= 0) return false; if (paramArrayOfbyte2 != null) { if (paramArrayOfbyte2.length <= 0) return false; try { X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decode(paramString, 0)); PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec); Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(paramArrayOfbyte1); return signature.verify(paramArrayOfbyte2); } catch (Exception exception) { exception.printStackTrace(); } } } return false; }
似乎是这一段?
accesstoken+“8b5b6eca8a9d1d1f” 然后求md5
你看一下我上传的这个版本。 private boolean a(String paramString, byte[] paramArrayOfbyte1, byte[] paramArrayOfbyte2) { if (TextUtils.isEmpty(paramString)) return false; if (paramArrayOfbyte1 != null) { if (paramArrayOfbyte1.length <= 0) return false; if (paramArrayOfbyte2 != null) { if (paramArrayOfbyte2.length <= 0) return false; try { X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decode(paramString, 0)); PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec); Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(paramArrayOfbyte1); return signature.verify(paramArrayOfbyte2); } catch (Exception exception) { exception.printStackTrace(); } } } return false; }
似乎是这一段?
也可能是这个RSA签名,不过我已经分手好多年了,很久没看过这个了
accesstoken+“8b5b6eca8a9d1d1f” 然后求md5
你看一下我上传的这个版本。 private boolean a(String paramString, byte[] paramArrayOfbyte1, byte[] paramArrayOfbyte2) { if (TextUtils.isEmpty(paramString)) return false; if (paramArrayOfbyte1 != null) { if (paramArrayOfbyte1.length <= 0) return false; if (paramArrayOfbyte2 != null) { if (paramArrayOfbyte2.length <= 0) return false; try { X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decode(paramString, 0)); PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec); Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(paramArrayOfbyte1); return signature.verify(paramArrayOfbyte2); } catch (Exception exception) { exception.printStackTrace(); } } } return false; } 似乎是这一段?
也可能是这个RSA签名,不过我已经分手好多年了,很久没看过这个了
ORZ,我只是想研究研究TX的算法
尴尬,我下错包了,因为没有Android手机,没法测apk包,结果看了一晚上发现,这玩意是应用宝( 以下为新版加密:
package com.welove520.welove.tools;
import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec;
public class EncryUtils { private static final byte[] AES_KEY = new byte[] { 61, -3, 17, 56, -75, 118, -73, 41, 47, 73, -55, -21, 35, -54, Byte.MIN_VALUE, -92 };
public static byte[] decrypt(byte[] paramArrayOfbyte) { if (paramArrayOfbyte == null) return null; try { SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(2, secretKeySpec); return cipher.doFinal(paramArrayOfbyte); } catch (NoSuchAlgorithmException noSuchAlgorithmException) { noSuchAlgorithmException.printStackTrace(); return null; } catch (NoSuchPaddingException noSuchPaddingException) { noSuchPaddingException.printStackTrace(); return null; } catch (InvalidKeyException invalidKeyException) { invalidKeyException.printStackTrace(); return null; } catch (IllegalBlockSizeException illegalBlockSizeException) { illegalBlockSizeException.printStackTrace(); return null; } catch (BadPaddingException badPaddingException) { badPaddingException.printStackTrace(); return null; } catch (Exception exception) { exception.printStackTrace(); return null; } }
public static byte[] encrypt(String paramString) { if (paramString == null) return null; try { SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY, "AES"); Cipher cipher = Cipher.getInstance("AES"); null = paramString.getBytes("utf-8"); cipher.init(1, secretKeySpec); return cipher.doFinal(null); } catch (NoSuchAlgorithmException noSuchAlgorithmException) { noSuchAlgorithmException.printStackTrace(); return null; } catch (NoSuchPaddingException noSuchPaddingException) { noSuchPaddingException.printStackTrace(); return null; } catch (InvalidKeyException invalidKeyException) { invalidKeyException.printStackTrace(); return null; } catch (UnsupportedEncodingException unsupportedEncodingException) { unsupportedEncodingException.printStackTrace(); return null; } catch (IllegalBlockSizeException illegalBlockSizeException) { illegalBlockSizeException.printStackTrace(); return null; } catch (BadPaddingException badPaddingException) { badPaddingException.printStackTrace(); return null; } catch (Exception exception) { exception.printStackTrace(); return null; } }
public static String parseByte2HexStr(byte[] paramArrayOfbyte) { if (paramArrayOfbyte == null) return null; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < paramArrayOfbyte.length; i++) { String str2 = Integer.toHexString(paramArrayOfbyte[i] & 0xFF); String str1 = str2; if (str2.length() == 1) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append('0'); stringBuilder.append(str2); str1 = stringBuilder.toString(); } stringBuffer.append(str1.toUpperCase()); } return stringBuffer.toString(); }
public static byte[] parseHexStr2Byte(String paramString) { if (paramString == null) return null; if (paramString.length() < 1) return null; byte[] arrayOfByte = new byte[paramString.length() / 2]; for (int i = 0; i < paramString.length() / 2; i++) { int j = i 2; int k = j + 1; arrayOfByte[i] = (byte)(Integer.parseInt(paramString.substring(j, k), 16) 16 + Integer.parseInt(paramString.substring(k, j + 2), 16)); } return arrayOfByte; } }
尴尬,我下错包了,因为没有Android手机,没法测apk包,结果看了一晚上发现,这玩意是应用宝( 以下为新版加密:
package com.welove520.welove.tools;
import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec;
public class EncryUtils { private static final byte[] AES_KEY = new byte[] { 61, -3, 17, 56, -75, 118, -73, 41, 47, 73, -55, -21, 35, -54, Byte.MIN_VALUE, -92 };
public static byte[] decrypt(byte[] paramArrayOfbyte) { if (paramArrayOfbyte == null) return null; try { SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(2, secretKeySpec); return cipher.doFinal(paramArrayOfbyte); } catch (NoSuchAlgorithmException noSuchAlgorithmException) { noSuchAlgorithmException.printStackTrace(); return null; } catch (NoSuchPaddingException noSuchPaddingException) { noSuchPaddingException.printStackTrace(); return null; } catch (InvalidKeyException invalidKeyException) { invalidKeyException.printStackTrace(); return null; } catch (IllegalBlockSizeException illegalBlockSizeException) { illegalBlockSizeException.printStackTrace(); return null; } catch (BadPaddingException badPaddingException) { badPaddingException.printStackTrace(); return null; } catch (Exception exception) { exception.printStackTrace(); return null; } }
public static byte[] encrypt(String paramString) { if (paramString == null) return null; try { SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY, "AES"); Cipher cipher = Cipher.getInstance("AES"); null = paramString.getBytes("utf-8"); cipher.init(1, secretKeySpec); return cipher.doFinal(null); } catch (NoSuchAlgorithmException noSuchAlgorithmException) { noSuchAlgorithmException.printStackTrace(); return null; } catch (NoSuchPaddingException noSuchPaddingException) { noSuchPaddingException.printStackTrace(); return null; } catch (InvalidKeyException invalidKeyException) { invalidKeyException.printStackTrace(); return null; } catch (UnsupportedEncodingException unsupportedEncodingException) { unsupportedEncodingException.printStackTrace(); return null; } catch (IllegalBlockSizeException illegalBlockSizeException) { illegalBlockSizeException.printStackTrace(); return null; } catch (BadPaddingException badPaddingException) { badPaddingException.printStackTrace(); return null; } catch (Exception exception) { exception.printStackTrace(); return null; } }
public static String parseByte2HexStr(byte[] paramArrayOfbyte) { if (paramArrayOfbyte == null) return null; StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < paramArrayOfbyte.length; i++) { String str2 = Integer.toHexString(paramArrayOfbyte[i] & 0xFF); String str1 = str2; if (str2.length() == 1) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append('0'); stringBuilder.append(str2); str1 = stringBuilder.toString(); } stringBuffer.append(str1.toUpperCase()); } return stringBuffer.toString(); }
public static byte[] parseHexStr2Byte(String paramString) { if (paramString == null) return null; if (paramString.length() < 1) return null; byte[] arrayOfByte = new byte[paramString.length() / 2]; for (int i = 0; i < paramString.length() / 2; i++) { int j = i 2; int k = j + 1; arrayOfByte[i] = (byte)(Integer.parseInt(paramString.substring(j, k), 16) 16 + Integer.parseInt(paramString.substring(k, j + 2), 16)); } return arrayOfByte; } }
这只是一个aes加密啊
public static byte[] getHMAC(String paramString1, String paramString2) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(paramString1.getBytes(), "hmacmd5");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(secretKeySpec);
mac.update(paramString2.getBytes());
return mac.doFinal();
} catch (Exception exception) {
a.b(exception);
return null;
}
}
@zixing131 找到了新的加密,但是,它做了一个isEnableLog的判断,我尝试改apk包删除这个是否显示日志判断,因重新签名后签名改变,导致QQ登陆无法使用,所以放弃。目前根据上下程序代码判断,依旧采用的{GET|POST}&{url}&{content},之后用的应该是HmacMD5,但是目前还是无法对上它程序加密的密文,正在尝试其他顺序组合。
private static String b(String str, String str2, Map<String> map) {
int size = map.size();
String[] strArr = new String[size];
StringBuilder stringBuilder = new StringBuilder(str2);
StringBuilder stringBuilder2 = new StringBuilder();
Map linkedHashMap = new LinkedHashMap();
int i = 0;
for (Entry key : map.entrySet()) {
trArr[i] = (String) key.getKey();
i++;
}
Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER);
for (i = 0; i < size; i++) {
Object obj = strArr[i];
linkedHashMap.put(obj, a((String) map.get(obj)));
}
for (Entry key2 : linkedHashMap.entrySet()) {
stringBuilder2.append((String) key2.getKey());
stringBuilder2.append("=");
stringBuilder2.append((String) key2.getValue());
stringBuilder2.append("&");
}
stringBuilder.append("&");
stringBuilder.append(a(str));
stringBuilder.append("&");
stringBuilder.append(a(stringBuilder2.substring(0, stringBuilder2.length() - 1)));
if (WeloveLog.isLogEnabled()) {
StringBuilder stringBuilder3 = new StringBuilder();
stringBuilder3.append("SigUtils # ");
stringBuilder3.append(stringBuilder.toString());
WeloveLog.d(stringBuilder3.toString());
}
return stringBuilder.toString();
}
@zixing131 找到了新的加密,但是,它做了一个isEnableLog的判断,我尝试改apk包删除这个是否显示日志判断,因重新签名后签名改变,导致QQ登陆无法使用,所以放弃。目前根据上下程序代码判断,依旧采用的{GET|POST}&{url}&{content},之后用的应该是HmacMD5,但是目前还是无法对上它程序加密的密文,正在尝试其他顺序组合。
private static String b(String str, String str2, Map<String> map) { int size = map.size(); String[] strArr = new String[size]; StringBuilder stringBuilder = new StringBuilder(str2); StringBuilder stringBuilder2 = new StringBuilder(); Map linkedHashMap = new LinkedHashMap(); int i = 0; for (Entry key : map.entrySet()) { trArr[i] = (String) key.getKey(); i++; } Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER); for (i = 0; i < size; i++) { Object obj = strArr[i]; linkedHashMap.put(obj, a((String) map.get(obj))); } for (Entry key2 : linkedHashMap.entrySet()) { stringBuilder2.append((String) key2.getKey()); stringBuilder2.append("="); stringBuilder2.append((String) key2.getValue()); stringBuilder2.append("&"); } stringBuilder.append("&"); stringBuilder.append(a(str)); stringBuilder.append("&"); stringBuilder.append(a(stringBuilder2.substring(0, stringBuilder2.length() - 1))); if (WeloveLog.isLogEnabled()) { StringBuilder stringBuilder3 = new StringBuilder(); stringBuilder3.append("SigUtils # "); stringBuilder3.append(stringBuilder.toString()); WeloveLog.d(stringBuilder3.toString()); } return stringBuilder.toString(); }
推荐使用frida进行hook
@zixing131 找到了新的加密,但是,它做了一个isEnableLog的判断,我尝试改apk包删除这个是否显示日志判断,因重新签名后签名改变,导致QQ登陆无法使用,所以放弃。目前根据上下程序代码判断,依旧采用的{GET|POST}&{url}&{content},之后用的应该是HmacMD5,但是目前还是无法对上它程序加密的密文,正在尝试其他顺序组合。
private static String b(String str, String str2, Map<String> map) { int size = map.size(); String[] strArr = new String[size]; StringBuilder stringBuilder = new StringBuilder(str2); StringBuilder stringBuilder2 = new StringBuilder(); Map linkedHashMap = new LinkedHashMap(); int i = 0; for (Entry key : map.entrySet()) { trArr[i] = (String) key.getKey(); i++; } Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER); for (i = 0; i < size; i++) { Object obj = strArr[i]; linkedHashMap.put(obj, a((String) map.get(obj))); } for (Entry key2 : linkedHashMap.entrySet()) { stringBuilder2.append((String) key2.getKey()); stringBuilder2.append("="); stringBuilder2.append((String) key2.getValue()); stringBuilder2.append("&"); } stringBuilder.append("&"); stringBuilder.append(a(str)); stringBuilder.append("&"); stringBuilder.append(a(stringBuilder2.substring(0, stringBuilder2.length() - 1))); if (WeloveLog.isLogEnabled()) { StringBuilder stringBuilder3 = new StringBuilder(); stringBuilder3.append("SigUtils # "); stringBuilder3.append(stringBuilder.toString()); WeloveLog.d(stringBuilder3.toString()); } return stringBuilder.toString(); }
推荐使用frida进行hook
绝了,这玩意验证程序完整性、签名和注入,我绕过签名覆盖安装,保留accessToken,秒闪退。写了hook进去,监测到外挂,秒闪退。。。
现在可以确定的一点,签名内容依旧是{GET|POST}&{url}&{content},但是HmacMD5用的不是那个写死的SigSecret
在这个issue里提供一下我这边抓取的Android apk,iOS ipa,以及farm的lua脚本,均为原始档案,请自行解密。
https://mega.nz/file/6SowwK4J#-b5As_kScGfZW318gQ9LMxUmiQUapalBOQTIKpNW0kE
大佬们带带我,我想知道这个sig怎么算的
大佬们带带我,我想知道这个sig怎么算的
我分手好多年了,现在还单身呢,早就不研究这个了
大佬们带带我,我想知道这个sig怎么算的
我从 QQ 小程序 /data/data/com.tencent.mobileqq/files/minigame/XXX/game.js 找到了两个 calculateSig 函数,但是试了都不太对。
var failNum = 1;
var timestamp = "8b5b6eca8a9d1d1f";
var VERSION = window.GAME_VERSION ? Number('3' + window.GAME_VERSION.split('.').join('')) : 3091;
var M1 = [0x24, 0xfa, 0xc7, 0x22, 0x09, 0xec, 0x66, 0x27];
var M2 = [0x33, 0x94, 0xa0, 0xe0, 0x2b, 0x3b, 0x9c, 0x69];
var BASEURL = "/v1/game/farm/";
var TEST_HOST = "https://sweet.apitt.welove520.com";
var BUILD_HOST = "https://sweet.api.welove520.com";
var md5 = require('loadScene/md5');
function calculateSig(method, url, params, key) {
params.ts = new Date().getTime();
var paraList = [];
for (var _key in params) {
var element = params[_key];
if (_key != "sig") {
if (Array.isArray(element) || _typeof(element) == 'object') {
// paraList.push("" + key + '=' + JSON.stringify(element))
paraList.push("" + JSON.stringify(element) + '=' + _key);
} else {
// paraList.push("" + key + '=' + element);
paraList.push("" + element + '=' + _key);
}
}
}
paraList.sort();
var connector = "&"; // let paramsString = paraList.join(connector);
// let baseString = String(method).toUpperCase() + connector + url + connector + paramsString + connector
var paramsString = paraList.join(";");
var baseString = String(method).toUpperCase() + connector + url + connector + paramsString + ";";
var createString = function createString(string, element) {
return string + String.fromCharCode(element);
}; // TODO
// md5 计算时会encodeURIComponent,是为了汉字等特殊字符
// M1 M2中的数转成字符md5不需要encodeURIComponent,计算不一致
// 所以原来为 md5(encodeURIComponent(M1 + S + M2)) 改为md5withnnoencodeURIComponent((M1 + encodeURIComponent(S) + M2))
var s = M1.reduce(createString, '') + unescape(encodeURIComponent(baseString)) + M2.reduce(createString, '');
return md5(s, null, false, true);
}
还有对情侣空间用 jadx,找到了 SigUtils,里面的也不对。
jadx -d out -r -j 1 --show-bad-code --deobf --deobf-max 256 --deobf-cfg-file-mode overwrite --respect-bytecode-access-modifiers --no-replace-consts --deobf-use-sourcename base.apk
package org.example;
// import android.util.Base64;
// import com.welove520.welove.tools.json.JsonUtil;
// import com.welove520.welove.tools.log.WeloveLog;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/* renamed from: com.welove520.welove.network.c */
/* loaded from: classes3.dex */
public class SigUtils {
/* renamed from: b */
private static String m10692b(String str, String str2, Map<String, String> map) {
int size = map.size();
String[] strArr = new String[size];
StringBuilder sb = new StringBuilder(str2);
StringBuilder sb2 = new StringBuilder();
LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap();
int i = 0;
for (Map.Entry<String, String> entry : map.entrySet()) {
strArr[i] = entry.getKey();
i++;
}
Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER);
for (int i2 = 0; i2 < size; i2++) {
String str3 = strArr[i2];
linkedHashMap.put(str3, m10696a(map.get(str3)));
}
for (Map.Entry entry2 : linkedHashMap.entrySet()) {
sb2.append((String) entry2.getKey());
sb2.append("=");
sb2.append((String) entry2.getValue());
sb2.append("&");
}
sb.append("&");
sb.append(m10696a(str));
sb.append("&");
sb.append(m10696a(sb2.substring(0, sb2.length() - 1)));
// if (WeloveLog.isLogEnabled()) {
// WeloveLog.m8149d("SigUtils # " + sb.toString());
// }
return sb.toString();
}
/* renamed from: a */
public static String m10693a(String str, String str2, byte[] bArr) {
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec("8b5b6eca8a9d1d1f".getBytes(), "HmacSHA1"));
return Base64.getEncoder().encodeToString(mac.doFinal(bArr)).replaceAll("\r", "").replaceAll("\n", "").trim();
} catch (InvalidKeyException e) {
// WeloveLog.m8144e("", e);
return "";
} catch (NoSuchAlgorithmException e2) {
// WeloveLog.m8144e("", e2);
return "";
}
}
// /* renamed from: a */
// public static String m10695a(String str, String str2, String str3) {
// return m10694a(str, str2, JsonUtil.jsonParamToMap(str3));
// }
/* renamed from: a */
public static String m10694a(String str, String str2, Map<String, String> map) {
return m10693a(str, str2, m10692b(str, str2, map).getBytes());
}
/* renamed from: a */
public static String m10696a(String str) {
int i;
try {
String encode = URLEncoder.encode(str, "UTF-8");
StringBuffer stringBuffer = new StringBuffer(encode.length());
int i2 = 0;
while (i2 < encode.length()) {
char charAt = encode.charAt(i2);
if (charAt == '*') {
stringBuffer.append("%2A");
} else if (charAt == '+') {
stringBuffer.append("%20");
} else if (charAt == ':') {
stringBuffer.append("%3A");
} else {
if (charAt == '%' && (i = i2 + 1) < encode.length() && encode.charAt(i) == '7') {
int i3 = i2 + 2;
if (encode.charAt(i3) == 'E') {
stringBuffer.append('~');
i2 = i3;
}
}
stringBuffer.append(charAt);
}
i2++;
}
return stringBuffer.toString();
} catch (UnsupportedEncodingException unused) {
throw new IllegalArgumentException("Unsupported UTF-8 Encoding for value: " + str);
}
}
}
简单用 Rust 写了个现在的版本 https://github.com/lz1998/welove520
新的sig好像是md5形式的,不是之前的base64了