吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4843|回复: 30
收起左侧

[Android 原创] 某APP登录算法分析

  [复制链接]
旧年白白白 发表于 2022-4-15 14:26
本帖最后由 旧年白白白 于 2022-4-21 09:12 编辑

本次分析只作学习交流所用,请勿用于非法用途,如若侵权,请联系删帖,谢谢!

环境

1、主机:win10
2、手机:Pixel 4 ,Android 10
3、APP版本:V5.54.0

工具

IDA、JADX、Frida、Charles

登录逆向分析

RegisterNatives

registerNative.png
registerNativeFunc.png

Request Params

首先抓包看一下登录包:
login.png

看一下登录参数:
login_data.png

verifydata
{
"params": "5F1D4B282F5E89548F98FC30853F4F9289D874AA82EDB0ED82330B07CF5D208ECAAF446BAEA4D4848BB7515573123514EFDEA8E6C1000FEF8F30A9901382B7119822C74B9C91394F9DDFF8B239E241F9",
"clienttime_ms": "1647312358378",
"support_face_verify": "1",
"dfid": "-",
"dev": "Pixel%204",
"plat": "1",
"pk": "7C9E722B7BCCE5B56BAB9D42902B2A4F238BAA40EACCCEECACE72C3EE46A85B1B833F7800E6D2B968EE809303485376180BD8494EBA84F006DBD050E1F876B3713DE64AFADDA4F51FCC522C6DAC0AFFC8A04AABAF47F95FE6D3F2FB6726A16FC4664AB66CE065E3FCFD7D0FACD3DCA05AA96D74DD96EC74DBF54CA72D088E42C",
"t1": "bfaf46951c6a5c58d4bf618c517354de",
"support_verify": "1",
"support_multi": "1",
"t2": "c397de1fd7e436639fa90f367c0b47d290831b64f19d803fc09a91ee47bcc1648e5a87f1f934ed09cfb7046dad52253cf564684d5bdef1d67fc91c74d6245335237a78074f157641a1ddfb5ca4a66c5d72855feae18282b981d896c6f170cb54ca7c21aad3811d69c80a73ad3482c340",
"key": "8d73d8b5da06bad3b466d864812d488b",
"username": "130*****666"
}

params参数解析

通过JADX搜索“params”字符串定位到位置:
this.j.put("params", a.c(jSONObject.toString(), this.g));

进入c方法内可以看到:

public static String c(String str, String str2) throws Exception {
        return a(str, "utf-8", ao.a(str2).substring(0, 32), ao.a(str2).substring(16, 32));
    }

通过objection hook该方法内的a方法,得到如下结果:

(agent) [302556] Called com.xxx.fanxing.allinone.common.utils.a.a(java.lang.String, java.lang.String, java.lang.String, java.lang.String)

(agent) [302556] Arguments com.xxx.fanxing.allinone.common.utils.a.a({"username":"130*****666","clienttime_ms":"1647313862199","pwd":"557998555"}, utf-8, 0dd6da40aea47d05b5f6612596fd2e7f, b5f6612596fd2e7f)

(agent) [793424] Backtrace:
com.xxx.fanxing.allinone.common.utils.a.a(Native Method)
com.xxx.fanxing.allinone.common.utils.a.a(SourceFile:117)
com.xxx.fanxing.allinone.common.utils.a.a(Native Method)
com.xxx.fanxing.allinone.common.utils.a.c(SourceFile:106)
com.xxx.fanxing.core.protocol.g.e.c(SourceFile:85)
com.xxx.fanxing.core.protocol.g.e.c(Native Method)
com.xxx.fanxing.core.protocol.g.a.d(SourceFile:76)
com.xxx.fanxing.core.modul.user.login.c.a(SourceFile:50)
com.xxx.fanxing.core.modul.user.login.f.a(SourceFile:228)
com.xxx.fanxing.core.modul.user.ui.a.a(SourceFile:577)
com.xxx.fanxing.core.modul.user.ui.a.j(SourceFile:554)
com.xxx.fanxing.core.modul.user.ui.a.i(SourceFile:520)
com.xxx.fanxing.core.modul.user.ui.a.onClick(SourceFile:417)
android.view.View.performClick(View.java:7259)
android.view.View.performClickInternal(View.java:7236)
android.view.View.access$3600(View.java:801) android.view.View$PerformClick.run(View.java:27892)
android.os.Handler.handleCallback(Handler.java:883)
android.os.Handler.dispatchMessage(Handler.java:100)
android.os.Looper.loop(Looper.java:214)
android.app.ActivityThread.main(ActivityThread.java:7356)
java.lang.reflect.Method.invoke(Native Method)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

这样我们可以得到a.c(jSONObject.toString(), this.g)中两个参数的来源,其中第一个参数为JSON结构:{"username":"130*****666","clienttime_ms":"1647313862199","pwd":"557998555"},第二个参数为AES的KEY,最终将KEY拆分为(0,32)和(16,32)具体生成方式如下:
this.g=com.xxx.fanxing.allinone.common.utils.a.a(com.xxx.fanxing.allinone.common.constant.c.hz() ? 128 : 64);

a方法如下:

public static String a(int i) {
        try {
            KeyGenerator instance = KeyGenerator.getInstance("AES");
            instance.init(i);
            return a(instance.generateKey().getEncoded());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

public static String c(String str, String str2) throws Exception {
        return a(str, "utf-8", ao.a(str2).substring(0, 32), ao.a(str2).substring(16, 32));
    }

public static String a(String str) {
        return a(str.getBytes());
    }

public static String a(byte[] bArr) {
        try {
            MessageDigest instance = MessageDigest.getInstance("MD5");
            instance.update(bArr);
            return bd.a(instance.digest());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
public static String a(String str, String str2, String str3, String str4) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(str3.getBytes(str2), "AES");
        Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
        instance.init(1, secretKeySpec, new IvParameterSpec(str4.getBytes()));
        return a(instance.doFinal(str.getBytes(str2)));
    }

最终为通过KeyGenerator生成一个32位的key,然后对key进行MD5,最后将其分割

dfid参数解析

通过搜索字符串即可定位到如下函数:

public String e() {
        String a2 = com.xxx.fanxing.core.common.fingerprint.a.a();
        return TextUtils.isEmpty(a2) ? "-" : a2;
    }

a2 -> share_data -> device_fingerprint
通过sharedPreferences读取了文件名为share_data里的device_fingerprint字段

pk参数解析

hashMap.put("pk", com.xxx.fanxing.core.protocol.g.a.a(String.valueOf(currentTimeMillis), a2));

public static String a(String str, String str2) throws Exception {
        HashMap hashMap = new HashMap();
        hashMap.put("clienttime_ms", str);
        hashMap.put(ao.M, str2);
        return com.xxx.common.player.kugouplayer.a.d(hashMap);
    }

str为时间戳,str2为AES随机密钥
组成形式如下:

{"clienttime_ms":"1647829236357","key":"E96E510C296711ECEA6C85CAF6152F4D"}

最终调用native层的cc::i加密方法
cci.png

PUBKEY:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD2DT4odzkDd7hMlZ7djdZQH12j38nKxriINW1MGjMry3tXheya113xwmbBOwN0GA4zTwKFauFJRzcsD0nDFq1eaatcFKeDF25R4dnQRX+4BdTwFVS8lIb8nJMluSBwK+i4Z3VF+gfZ0AqQOXda6lJ4jPBt9Ep7VXEAHXUDn9JM8wIDAQAB

Python版RSA_NOPADDING加密方法实现:

import rsa
import base64
from Crypto.PublicKey import RSA

def zfillStrToBin(s):
    b = bytes(s.encode())
    for i in range(128 - len(b)):
        b += b'\0'
    print(len(b))
    return b

class RsaNopadding:

    def __init__(self, key):
        self.pubkey = RSA.importKey(base64.b64decode(key))

    def encrypt(self, message):
        kLen = rsa.common.byte_size(self.pubkey.n)
        msg = zfillStrToBin(message)
        _b = rsa.transform.bytes2int(msg)
        _i = rsa.core.encrypt_int(_b, self.pubkey.e, self.pubkey.n)
        result = rsa.transform.int2bytes(_i, kLen)
        return result.hex().upper()

message = '{"clienttime_ms":"1647829236357","key":"E96E510C296711ECEA6C85CAF6152F4D"}'
msg = RsaNopadding(
    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD2DT4odzkDd7hMlZ7djdZQH12j38nKxriINW1MGjMry3tXheya113xwmbBOwN0GA4zTwKFauFJRzcsD0nDFq1eaatcFKeDF25R4dnQRX+4BdTwFVS8lIb8nJMluSBwK+i4Z3VF+gfZ0AqQOXda6lJ4jPBt9Ep7VXEAHXUDn9JM8wIDAQAB")
print(msg.encrypt(message))

t1参数解析

String t1 = com.xxx.common.player.kugouplayer.a.b(null);

最终调用native层的cc::d方法
ccd.png

这里只分析f4函数内关键步骤部分:

int __fastcall f4(int a1)
{
LABEL_44:                                       // 走这
  v76[0] = (int)v76;
  v76[1] = (int)v76;
  v76[2] = 0;
  v77 = 0;
  v78 = 0;
  v79 = 0;
  std::string::__init((int)&v77, (int)&aEia[2], 1);
  v34 = v77;
  v35 = v78;
  v77 = 0;
  v78 = 0;
  v103 = v34;
  v104 = v35;
  v105 = v79;
  v79 = 0;
  std::string::basic_string((int)v106, (int)&v66);
  std::list<std::pair<std::string,std::string>>::push_back((int)v76, (int)&v103);
  std::string::~string((int)v106);
  std::string::~string((int)&v103);
  std::string::~string((int)&v77);
  v80 = 0;
  v81 = 0;
  v82 = 0;
  std::string::__init((int)&v80, (int)"b", 1);
  f5(&v83);                                     // daytime
  v36 = v80;
  v37 = v81;
  v38 = v82;
  v80 = 0;
  v81 = 0;
  v82 = 0;
  v103 = v36;
  v104 = v37;
  v105 = v38;
  v39 = v83;
  v40 = v84;
  v41 = v85;
  v83 = 0;
  v84 = 0;
  v85 = 0;
  v106[0] = v39;
  v106[1] = v40;
  v106[2] = v41;
  std::list<std::pair<std::string,std::string>>::push_back((int)v76, (int)&v103);
  std::string::~string((int)v106);
  std::string::~string((int)&v103);
  std::string::~string((int)&v83);
  std::string::~string((int)&v80);
  f9((int)v86, (int)v76);
  v87 = 0;
  v42 = v107;
  v88 = 0;
  v89 = 0;
  v95[0] = 0;
  v95[1] = 0;
  v95[2] = 0;
  v43 = aBdeaed243193ce;                        // v42=bdeaed243193ce1i#~DC<M[g
  do
  {
    v44 = *(_DWORD *)v43;
    v43 += 8;
    v45 = *((_DWORD *)v43 - 1);
    *(_DWORD *)v42 = v44;                       // 整个循环将bdeaed243193ce1i#~DC<M[g扩展到bdeaed243193ce1i#~DC<M[g..=2..~4
    *((_DWORD *)v42 + 1) = v45;
    v42 += 8;
  }
  while ( v43 != (const char *)&unk_D90E4 );
  v103 = 0;
  v104 = 0;
  v105 = 0;
  std::string::__init((int)&v103, (int)v107, 32);// v105=v109=bdeaed243193ce1i#~DC<M[g..=2..~4
                                                // 62 64 65 61 65 64 32 34 33 31 39 33 63 65 31 69
                                                // 23 7E 44 43 3C 4D 5B 67 7F 0E 3D 32 01 2E 7E 34
  v46 = (unsigned __int8)v103;
  if ( (v103 & 1) == 0 )
    v46 = (int)(unsigned __int8)v103 >> 1;
  if ( (v103 & 1) != 0 )
    v46 = v104;
  v47 = v46 - 2;
  do
  {
    if ( v47 < 0 )
      break;
    v48 = (v103 & 1) != 0 ? v105 : (int *)((char *)&v103 + 1);
    v49 = (char *)v48 + v47;                    // 转换v105为bdeaed243193ce11ac913bbd48d340a4
    v50 = (v103 & 1) != 0 ? v105 : (int *)((char *)&v103 + 1);
    v51 = *((_BYTE *)v50 + v47);
    v52 = (char *)&unk_D90E4 + v47 - v46;
    --v47;
    *v49 = v51 ^ v52[17];
  }
  while ( v47 != v46 - 18 );
  std::string::basic_string((int)&v100, (int)&v103);
  std::string::~string((int)&v103);
  v53 = v107;
  v54 = aAc913bbd48d340;                        // v54 = ac913bbd48d340a41234567890qwertyuiopasdfghjklzxcvbnm.
  do
  {
    v55 = *(_DWORD *)v54;
    v54 += 8;
    v56 = *((_DWORD *)v54 - 1);
    *(_DWORD *)v53 = v55;                       // 整个循环将ac913bbd48d340a41234567890qwertyuiopasdfghjklzxcvbnm.转换为ac913bbd48d340a4
    *((_DWORD *)v53 + 1) = v56;
    v53 += 8;
  }
  while ( v54 != &aAc913bbd48d340[16] );
  v103 = 0;
  v104 = 0;
  v105 = 0;
  std::string::__init((int)&v103, (int)v107, 16);// v103=v107=ac913bbd48d340a4
  std::string::basic_string((int)v99, (int)&v103);
  std::string::~string((int)&v103);
  sub_394FC(1, v86, v95, (unsigned __int8 *)&v100, v99);// AES加密
  std::string::~string((int)v99);
  std::string::~string((int)&v100);
  f11((int)&v96, (int)v95);                     // HEX格式化
  v58 = v87 & 1;
  if ( (v87 & 1) != 0 )
  {
    v57 = v89;
    v58 = 0;
  }
  else
  {
    BYTE1(v87) = v87 & 1;
  }
  if ( (v87 & 1) != 0 )
  {
    *v57 = v58;
    v88 = v58;
  }
  else
  {
    LOBYTE(v87) = v58;
  }
  std::string::reserve((int)&v87, 0);
  v59 = v96;
  v60 = v97;
  v61 = v98;
  v96 = 0;
  v97 = 0;
  v98 = 0;
  v87 = v59;
  v88 = v60;
  v89 = (_BYTE *)v61;
  std::string::~string((int)&v96);
  std::string::~string((int)v95);
  if ( (*(_BYTE *)a1 & 1) != 0 )
  {
    **(_BYTE **)(a1 + 8) = 0;
    *(_DWORD *)(a1 + 4) = 0;
  }
  else
  {
    *(_BYTE *)(a1 + 1) = 0;
    *(_BYTE *)a1 = 0;
  }
  std::string::reserve(a1, 0);
  v62 = v87;
  v63 = v88;
  v64 = (int)v89;
  v87 = 0;
  v88 = 0;
  v89 = 0;
  *(_DWORD *)a1 = v62;
  *(_DWORD *)(a1 + 4) = v63;
  *(_DWORD *)(a1 + 8) = v64;
  std::string::~string((int)&v87);
  std::string::~string((int)v86);
  std::__list_imp<std::pair<std::string,std::string>>::clear(v76);
  std::string::~string((int)&v66);
  return a1;
}

cc::d方法里为AES_256_CBC_ENCRYPT

encData: |1649905000102
key:bdeaed243193ce11ac913bbd48d340a4
iv:ac913bbd48d340a4

t2参数解析

String t2 = com.xxx.common.player.kugouplayer.a.c(null);

最终调用native层的cc::e方法

int __fastcall cc::e(_JNIEnv *a1)
{
  sub_346F4(&v33);
  v34[2] = 0;
  v34[0] = (int)v34;
  v34[1] = (int)v34;
  v35 = 0;
  v36 = 0;
  v37 = 0;
  std::string::__init((int)&v35, (int)&aEia[2], 1);
  cc::h4((int)&v38, a1);                        // 获取ANDROID_ID
  v2 = v35;
  v3 = v36;
  v35 = 0;
  v36 = 0;
  v66 = v2;
  v67 = v3;
  v68 = v37;
  v4 = v38;
  v5 = v39;
  v37 = 0;
  v38 = 0;
  v39 = 0;
  v69 = v4;
  v70 = v5;
  v71 = v40;
  v40 = 0;
  std::list<std::pair<std::string,std::string>>::push_back(v34, &v66);
  std::pair<std::string,std::string>::~pair(&v66);
  std::string::~string((int)&v38);
  std::string::~string((int)&v35);
  v41 = 0;
  v42 = 0;
  v43 = 0;
  std::string::__init((int)&v41, (int)"b", 1);
  cc::h5((int)&v44, a1);                        // 获取DeviceId
  v6 = v41;
  v7 = v42;
  v8 = v43;
  v41 = 0;
  v42 = 0;
  v43 = 0;
  v66 = v6;
  v67 = v7;
  v68 = v8;
  v9 = v44;
  v10 = v45;
  v11 = v46;
  v44 = 0;
  v45 = 0;
  v46 = 0;
  v69 = v9;
  v70 = v10;
  v71 = v11;
  std::list<std::pair<std::string,std::string>>::push_back(v34, &v66);
  std::pair<std::string,std::string>::~pair(&v66);
  std::string::~string((int)&v44);
  std::string::~string((int)&v41);
  v47 = 0;
  v48 = 0;
  v49 = 0;
  std::string::__init((int)&v47, (int)"c", 1);
  cc::h6((cc *)&v50, a1);                       // 获取network HardwareAddress
  v12 = v47;
  v13 = v48;
  v14 = v49;
  v47 = 0;
  v48 = 0;
  v49 = 0;
  v66 = v12;
  v67 = v13;
  v68 = v14;
  v15 = v50;
  v16 = v51;
  v17 = v52;
  v50 = 0;
  v51 = 0;
  v52 = 0;
  v69 = v15;
  v70 = v16;
  v71 = v17;
  std::list<std::pair<std::string,std::string>>::push_back(v34, &v66);
  std::pair<std::string,std::string>::~pair(&v66);
  std::string::~string((int)&v50);
  std::string::~string((int)&v47);
  v53 = 0;
  v54 = 0;
  v55 = 0;
  std::string::__init((int)&v53, (int)"d", 1);
  cc::h8((cc *)&v56, a1);                       // 获取手机的型号设备名称
  v18 = v53;
  v19 = v54;
  v20 = v55;
  v53 = 0;
  v54 = 0;
  v55 = 0;
  v66 = v18;
  v67 = v19;
  v68 = v20;
  v21 = v56;
  v22 = v57;
  v23 = v58;
  v56 = 0;
  v57 = 0;
  v58 = 0;
  v69 = v21;
  v70 = v22;
  v71 = v23;
  std::list<std::pair<std::string,std::string>>::push_back(v34, &v66);
  std::pair<std::string,std::string>::~pair(&v66);
  std::string::~string((int)&v56);
  std::string::~string((int)&v53);
  v59 = 0;
  v60 = 0;
  v61 = 0;
  std::string::__init((int)&v59, (int)aEia, 1);
  f5();                                         // daytime
  v24 = v59;
  v25 = v60;
  v26 = v61;
  v59 = 0;
  v60 = 0;
  v61 = 0;
  v66 = v24;
  v67 = v25;
  v68 = v26;
  v27 = v62;
  v28 = v63;
  v29 = v64;
  v62 = 0;
  v63 = 0;
  v64 = 0;
  v69 = v27;
  v70 = v28;
  v71 = v29;
  std::list<std::pair<std::string,std::string>>::push_back(v34, &v66);
  std::pair<std::string,std::string>::~pair(&v66);
  std::string::~string((int)&v62);
  std::string::~string((int)&v59);
  f9(v65, v34);                                 // 合并string
  f6(&v66, v65);                                // aes
  if ( (v66 & 1) != 0 )
    v30 = v68;
  else
    v30 = (char *)&v66 + 1;
  v31 = _JNIEnv::NewStringUTF(a1, v30);
  std::string::~string((int)&v66);
  std::string::~string((int)v65);
  std::__list_imp<std::pair<std::string,std::string>>::clear(v34);
  cc::sp<cc::RefJObject>::~sp(&v33);
  return v31;
}

f6函数内部逻辑与t1的f4函数一致
cc::e方法里为AES_256_CBC_ENCRYPT

encData: ||9eea2d301e53|Pixel 4|1649905000118
key:dc8e123f07636a41361b62235fc313ac
iv:361b62235fc313ac

key参数解析

com.xxx.fanxing.core.protocol.g.e.c

map.put(ao.M, com.xxx.fanxing.allinone.common.utils.ao.a("" + this.b + this.e + getVersion() + String.valueOf(this.h).toLowerCase()));

ao.m = key
this.b = AppId
this.e = AppKey
this.h = clienttime_ms
key = MD5("" + AppId + AppKey + APPVersion + String.valueOf(this.h).toLowerCase())

还原

Python 代码实现

免费评分

参与人数 14威望 +1 吾爱币 +34 热心值 +11 收起 理由
qtfreet00 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hanlaoshi + 1 + 1 谢谢@Thanks!
yiwangguli + 1 + 1 我很赞同!
起飞了 + 1 用心讨论,共获提升!
落丶叶 + 1 + 1 请勿灌水,提高回帖质量是每位会员应尽的义务!
笙若 + 1 + 1 我很赞同!
cxp521 + 2 + 1 牛牛牛
mark000 + 1 + 1 谢谢@Thanks!
maus + 1 我很赞同!
dechong + 1 谢谢@Thanks!
Tonyha7 + 1 用心讨论,共获提升!
刑事组之虎 + 1 + 1 用心讨论,共获提升!
漁滒 + 3 + 1 我很赞同!
m99 + 1 学习学习

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 旧年白白白 发表于 2022-9-23 10:52
moda 发表于 2022-9-22 17:41
请问一下返回值secu_params 和t1要怎么解密

t1的话用同样的方法拿到密钥解密就好了
 楼主| 旧年白白白 发表于 2022-4-18 09:34
bhwxha 发表于 2022-4-16 10:56
给个应用名+版本号,可以用base64编码一下,就不敏感了

上面有啊,你仔细看
m99 发表于 2022-4-15 14:27
youngnku 发表于 2022-4-15 14:41
好像MD编辑器没起作用啊,是不是忘了勾选。
jiangshan567 发表于 2022-4-15 14:46
感谢分享 ,大佬666
 楼主| 旧年白白白 发表于 2022-4-15 15:10
youngnku 发表于 2022-4-15 14:41
好像MD编辑器没起作用啊,是不是忘了勾选。

好了,我以为直接cv就行了
yuechaomax 发表于 2022-4-15 16:08
这个要沉下心好好学习一下。
0xchuxiuyun 发表于 2022-4-15 16:59
感谢分享 ,学习了学习了
ZhuanZhuYuIT 发表于 2022-4-15 19:07
膜拜大佬,大佬666
pistachios2008 发表于 2022-4-15 19:10
感谢分享 ,学习了学习了
Misaka0 发表于 2022-4-15 19:40
感谢分享 ,厉害厉害
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 15:29

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表