历史逆向回顾-Leisu

40 阅读3分钟

目的

个人业余项目研究(非盈利用途)

写本文目的是单纯将平台用作不限量云盘存储用途

样本版本

aHR0cHM6Ly9zai5xcS5jb20vYXBwZGV0YWlsL2NvbS5sZWlzdS5zcG9ydHM=

样本版本差异

6.5.0 版本检测反调试,但不影响静态分析。使用frida_dex_dump.js脱壳,dex文件10个
5.5.1 版本无检测反调试,但不影响静态分析。使用frida_dex_dump.js脱壳,dex文件12个

请求参数分析

:method: GET
:path: /v1/app/match/football/match_live?auth_key=1661915993-0-0-db9fd07d3adfa8310d76992ac50d126c
:authority: app-gateway.leisuapi.com
:scheme: https
ver: 5.5.1
cdid: 4f857114-41fa-3d83-bbcf-5bb1a614264d
device_id: 
user-agent: leisu/5.5.1/Android/9/28/google/Pixel 3/arm64-v8a/armeabi-v7a/armeabi/10
sign: a695973cdf56ec90f64936fac29fb7b0
start: 1661912164
channel: Ali
sn: unknown
time: 1661915983
aid: 62d33b5544bc26a4
platform: 1
devicetoken: Tk9SSUQuMSNhMT****dG1RUT09
accept-encoding: gzip

主要分析参数

  • devicetoken

  • aid

  • sign

  • device_id

  • auth_key

参数纯静态分析

device_token 来源

net.security.device.api.SecurityDevice
private native SecuritySession getSessionRaw()

sign

auth_key

cdid

cdid: 4f857114-41fa-3d83-bbcf-5bb1a614264d 
device_id: 
user-agent: leisu/5.5.1/Android/9/28/google/Pixel 3/arm64-v8a/armeabi-v7a/armeabi/10 channel: Ali 
sn: unknown 
aid: 62d33b5544bc26a4

固定疑似与设备绑定

数据解密

解密入口 com.**.http.net.JsonResponseBodyConverter

private Reader decrypt(String str) {
        DecryptResult decryptResult = (DecryptResult) this. gson.fromJson(str, (Class) DecryptResult.class) ;
        if (decryptResult.getCode() == 1) {
            String obj = decryptResult.getData().toString();
            if (TextUtils.isEmpty(obj)) {
                return new StringReader(str);
            }
            Object parse = JSON.parse(AESUtils.decrypt(Base64.decode(obj, 0), KeyConstant.Companion.m57939a()).trim(), Feature.OrderedField);
            LinkedHashMap linkedHashMap = new LinkedHashMap();
            linkedHashMap.put("code", 0);
            linkedHashMap.put("msg", decryptResult.getMsg());
            linkedHashMap.put("data", parse);
            return new StringReader(this. gson.toJson(linkedHashMap));
        } else if (decryptResult.getCode() <= 100 || decryptResult.getCode() > 126) {
            return new StringReader(str);
        } else {
            String obj2 = decryptResult.getData().toString();
            if (TextUtils.isEmpty(obj2)) {
                return new StringReader(str);
            }
            String caesarDecrypt = AESUtils.caesarDecrypt(obj2, decryptResult.getCode() - 100);
            C8283w.m57162b(&#34;接口凯撒解密:&#34; + caesarDecrypt);
            Object parse2 = JSON.parse(caesarDecrypt, Feature.OrderedField);
            LinkedHashMap linkedHashMap2 = new LinkedHashMap();
            linkedHashMap2.put(&#34;code&#34;, 0);
            linkedHashMap2.put(&#34;msg&#34;, decryptResult.getMsg());
            linkedHashMap2.put(&#34;data&#34;, parse2);
            return new StringReader(this. gson.toJson(linkedHashMap2));
        }
    }

AES密钥运算

public static String CreateSkey() {
        String AUTH_AES = &#34;c4GJx$jeFK@v*P#t&#34;;
//                         c4GJx$jeDK@v*P#t
        if (AUTH_AES.length() <= 8) {
            return AUTH_AES;
        }
        Charset charset = Charset.forName(&#34;UTF-8&#34;);
        StringBuilder sb = new StringBuilder();
        String str = AUTH_AES;
        int length =AUTH_AES.length() - 8;
        if (str != null) {
            String substring = str.substring(0, length);

            sb.append(substring);
            sb.append(new String(&#34;D&#34;.getBytes(charset)));
            String str2 =AUTH_AES;
            int length2 = AUTH_AES.length() - 7;
            int length3 = AUTH_AES.length();
            if (str2 != null) {
                String substring2 = str2.substring(length2, length3);
                sb.append(substring2);
                return sb.toString();
            }

        }
        return &#34;&#34;;
    }

凯撒解密

package com.leisux;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.zip.GZIPInputStream;

public class Leisux {

    public static String decrypt(byte[] bArr, String sKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

        Charset charset = Charset.forName(&#34;UTF-8&#34;);
        SecretKeySpec skeySpec = new SecretKeySpec(sKey.getBytes(charset), &#34;AES&#34;);
        Cipher cipher = Cipher.getInstance(&#34;AES/ECB/NoPadding&#34;);

        cipher.init(Cipher.DECRYPT_MODE, skeySpec);

//        System.out.println(&#34;Base64 decoded: &#34;+Base64.decode(encData.getBytes()).length);
        byte[] original = cipher.doFinal(bArr);
        return new String(original, Charset.defaultCharset()).trim();
    }


    public static String CreateSkey() {
        String AUTH_AES = &#34;c4GJx$jeFK@v*P#t&#34;;
//                         c4GJx$jeDK@v*P#t
        if (AUTH_AES.length() <= 8) {
            return AUTH_AES;
        }
        Charset charset = Charset.forName(&#34;UTF-8&#34;);
        StringBuilder sb = new StringBuilder();
        String str = AUTH_AES;
        int length =AUTH_AES.length() - 8;
        if (str != null) {
            String substring = str.substring(0, length);

            sb.append(substring);
            sb.append(new String(&#34;D&#34;.getBytes(charset)));
            String str2 =AUTH_AES;
            int length2 = AUTH_AES.length() - 7;
            int length3 = AUTH_AES.length();
            if (str2 != null) {
                String substring2 = str2.substring(length2, length3);
                sb.append(substring2);
                return sb.toString();
            }

        }
        return &#34;&#34;;
    }
    public static final String caesarDecrypt(String sb) {
        String str;
        byte[] decode = Base64.decode(sb, 0);
        byte[] uncompress = uncompress(decode);
        try {
            if (uncompress != null) {
                Charset defaultCharset = Charset.defaultCharset();
                str = new String(uncompress, defaultCharset);
            } else if (decode != null) {
                Charset defaultCharset2 = Charset.defaultCharset();
                str = new String(decode, defaultCharset2);
            } else {
                str = null;
                return str;
            }
            return str;
        } catch (Exception unused) {
            return &#34;&#34;;
        }
    }

    public static byte[] uncompress(byte[] bArr) {
        if (bArr == null) {
            return null;
        }
        if (bArr.length == 0) {
            return null;
        }
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            GZIPInputStream gZIPInputStream = new GZIPInputStream(new ByteArrayInputStream(bArr));
            byte[] bArr2 = new byte[256];
            while (true) {
                int read = gZIPInputStream.read(bArr2);
                if (read < 0) {
                    return byteArrayOutputStream.toByteArray();
                }
                byteArrayOutputStream.write(bArr2, 0, read);
            }
        } catch (Exception unused) {
            return null;
        }
    }



    public static void main(String[] args) throws  NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {

       StringBuilder sbs = new StringBuilder();
        String data = sbs.toString();
        System.out.println(data);
        Integer code = 118; // json中的code
        String sb = Carser.decrypt(data, code-100); // 重组base64

        String s = caesarDecrypt(sb);
        System.out.println(s);

    }
}