Bpazy / wl520

自动化完成微爱所有任务,你要做的仅仅是第一次的配置。
https://github.com/Bpazy/wl520
MIT License
14 stars 4 forks source link

welove农场签到更新了sig #9

Open zixing131 opened 5 years ago

zixing131 commented 5 years ago

新的sig好像是md5形式的,不是之前的base64了

Bpazy commented 5 years ago

这两天试验了下,确实更新了sig,简单试了下几种组合后的md5都不对,可能需要反编译下apk或找到动态加载下来的农场的代码。 我最近比较忙可能没空去看这个问题,希望大家能一起来研究下。

HikaruChang commented 3 years ago

另外,这个版本似乎可以重放攻击,sig没有时间戳,尝试抓包获取sig后,写死在程序内,依旧可以获取数据。

zixing131 commented 3 years ago

另外,这个版本似乎可以重放攻击,sig没有时间戳,尝试抓包获取sig后,写死在程序内,依旧可以获取数据。

我刚才试了一下,浇花是可以的,https://blog.csdn.net/hanziyuan08/article/details/52606908

zixing131 commented 3 years ago

这是我之前写的代码

encoding:utf-8

""" @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()

zixing131 commented 3 years ago

另外,看了下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;
}
zixing131 commented 3 years ago

accesstoken+“8b5b6eca8a9d1d1f” 然后求md5

HikaruChang commented 3 years ago

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; }

似乎是这一段?

zixing131 commented 3 years ago

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签名,不过我已经分手好多年了,很久没看过这个了

HikaruChang commented 3 years ago

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的算法

HikaruChang commented 3 years ago

尴尬,我下错包了,因为没有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; } }

zixing131 commented 3 years ago

尴尬,我下错包了,因为没有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加密啊

HikaruChang commented 3 years ago
  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;
    } 
  }
HikaruChang commented 3 years ago

@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 commented 3 years ago

@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

HikaruChang commented 3 years ago

@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进去,监测到外挂,秒闪退。。。

HikaruChang commented 3 years ago

现在可以确定的一点,签名内容依旧是{GET|POST}&{url}&{content},但是HmacMD5用的不是那个写死的SigSecret

HikaruChang commented 3 years ago

在这个issue里提供一下我这边抓取的Android apk,iOS ipa,以及farm的lua脚本,均为原始档案,请自行解密。

https://mega.nz/file/6SowwK4J#-b5As_kScGfZW318gQ9LMxUmiQUapalBOQTIKpNW0kE

woshixinan commented 2 years ago

大佬们带带我,我想知道这个sig怎么算的

zixing131 commented 2 years ago

大佬们带带我,我想知道这个sig怎么算的

我分手好多年了,现在还单身呢,早就不研究这个了

lz1998 commented 1 year ago

大佬们带带我,我想知道这个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);
        }
    }
}
lz1998 commented 1 year ago

简单用 Rust 写了个现在的版本 https://github.com/lz1998/welove520