Cover photo

币安加持币圈热门APP(Wild Cash) 算法解密

昨天一个名为Wild Cash的App火遍全网,每个群都在讨论它,币安加持,零撸赚钱,光这些就足够吸引眼球啦,用Fiddler 抓包发现是https协议的,但是经过了加密,好在APK没有加密,让我们来试试能不能找出算法

可以看到,发包和收包都是加密过的
可以看到,发包和收包都是加密过的

用jadx解码然后搜索关键字定位到加密算法的地方,可以发现用的是aes算法

package com.bo.hooked.common.d.d;

import androidx.exifinterface.media.ExifInterface;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.android.codec.binary.Base64;
import org.apache.commons.android.codec.language.Soundex;

public class b {
    private static final String a = a(a());
    private static final String b = new String(new char[]{'A', 'E', 'S'});

    public static String a(String str, String str2, byte[] bArr) throws Exception {
        byte[] a = a(str);
        Cipher instance = Cipher.getInstance(a);
        instance.init(2, new SecretKeySpec(str2.getBytes(), b), new GCMParameterSpec(128, bArr));
        return new String(instance.doFinal(a));
    }

    private static String a(List<String> list) {
        StringBuffer stringBuffer = new StringBuffer();
        for (String append : list) {
            stringBuffer.append(append);
        }
        return stringBuffer.toString().replace(new String(new char[]{Soundex.SILENT_MARKER}), "");
    }

    private static String a(byte[] bArr) {
        return Base64.encodeBase64String(bArr);
    }

    private static List<String> a() {
        ArrayList arrayList = new ArrayList();
        arrayList.add(ExifInterface.GPS_MEASUREMENT_IN_PROGRESS);
        arrayList.add(new String(new char[]{'E', 'S', '/'}));
        arrayList.add("-G");
        arrayList.add("-C-M/");
        arrayList.add(new String(new char[]{'N', 'o', 'p', 'a', Soundex.SILENT_MARKER, 'd', 'd'}));
        arrayList.add("in-g");
        return arrayList;
    }

    private static byte[] a(String str) {
        return Base64.decodeBase64(str);
    }

    public static String b(String str, String str2, byte[] bArr) throws Exception {
        byte[] bytes = str.getBytes();
        Cipher instance = Cipher.getInstance(a);
        instance.init(1, new SecretKeySpec(str2.getBytes(), b), new GCMParameterSpec(128, bArr));
        return a(instance.doFinal(bytes));
    }
}

经过frida hook 和调试,发现用的是AES/GCM/Nopadding,加密和解密用的KEY一致,但是vi有区别,两者区别在于发包时以时间戳为基础生成一个vi,而返回数据解包时用的vi是固定的

加密算法
加密算法
固定KEY值
固定KEY值

用frida注入代码做了测试,能成功加密解密:

function RequestBodyEncrypt(currentTimeMillis,data)  {
    var b = Java.use("com.bo.hooked.common.d.d.b")
    var a = Java.use("com.bo.hooked.common.d.g.a")
    var GCMParameterSpec = Java.use("javax.crypto.spec.GCMParameterSpec")
    //var currentTimeMillis = 1669299738910
    var key = a.h()
    var vi = a.a(currentTimeMillis)
    return b.b(data, key, vi)
}

function RequestBodyDecrypt(currentTimeMillis,data)  {
    var b = Java.use("com.bo.hooked.common.d.d.b")
    var a = Java.use("com.bo.hooked.common.d.g.a")
    var GCMParameterSpec = Java.use("javax.crypto.spec.GCMParameterSpec")
    //var currentTimeMillis = 1669299738910
    var key = a.h()
    var vi = a.a(currentTimeMillis)
    return b.a(data, key, vi)
}


function ResponseBodDecrypt(data)  {
    var b = Java.use("com.bo.hooked.common.d.d.b")
    var a = Java.use("com.bo.hooked.common.d.g.a")
    var GCMParameterSpec = Java.use("javax.crypto.spec.GCMParameterSpec")
    var key = a.h()
    var vi = a.d()
    return b.a(data, key, vi)
}

function encodeBase64String(data) {
    return Java.use("com.bo.hooked.common.d.d.b").a(data)
}

ResponseBodDecrypt("h60M054rQgQtxxVjZXwJDDMHFftNihcRgL9EBM0drMqjK2VAdqmhAvZTpIojbqIImLQQ9ac0R0xNUhVd7Y4pHNnV/qK9fkKCCKJ/KhZHgqOyHkY=")
RequestBodyDecrypt(1669299738910,"XXoGEyU6pa+fo28P2YN5ySJ+Ad9vKi73kuropUlvVsvcNNiNwrLBAA/qhjT6")
RequestBodyEncrypt(1669299738910,"{\"status\":\"0\",\"tapCount\":\"1\"}")
成功解码
成功解码