首页
论坛
专栏
课程

[原创] 快手签名

2019-9-7 01:34 3362

[原创] 快手签名

2019-9-7 01:34
3362

整理了下,发现还有个和达达类似套路的,一起看看吧。

抓包

发送短信验证码

POST /rest/n/user/requestMobileCode?app=0&lon=146.3516&did_gt=1562212339132&c=MYAPP%2C1&sys=ANDROID_8.1&isp=&mod=LGE%28AOSP%20on%20TTOG%29&did=ANDROID_a515efd201ca2590&hotfix_ver=&ver=6.5&net=WIFI&country_code=CN&iuid=&appver=6.5.5.9591&max_memory=192&oc=MYAPP%2C1&ftt=&kpn=KUAISHOU&ud=0&language=zh-cn&kpf=ANDROID_PHONE&lat=30.005368 HTTP/1.1
Connection: close
Accept-Language: zh-cn
User-Agent: kwai-android
X-REQUESTID: 141829000
Content-Type: application/x-www-form-urlencoded
Content-Length: 117
Host: apissl.gifshow.com
Accept-Encoding: gzip, deflate

mobileCountryCode=%2B86&mobile=13655338668&type=1&os=android&client_key=3c2cd3f3&sig=c8a22b77755169b9ecfc63b30e428d32

老规矩,确定sig为签名字段。

逆向

确定调用链
  


找到实现


 


进入native
   
 

首先确定进入一个解密字符串的函数,有兴趣的可以看看,直接粘处来解密,

char *__fastcall deStr(int a1, size_t a2)
{
  int v2; // r4
  size_t i_32; // r8
  _DWORD *v4; // r1
  char *v5; // r5
  int v6; // r1
  int v7; // r0
  int v8; // r3
  int v9; // r4
  unsigned int v10; // r6
  unsigned int v11; // lr
  int v12; // r11
  char *v13; // r9
  unsigned int v14; // r0
  int v15; // r5
  unsigned int v16; // r2
  unsigned int v17; // r0
  unsigned int v18; // r1
  unsigned int v19; // ST1C_4
  __int64 v20; // kr10_8
  unsigned int v21; // r1
  unsigned int v22; // r1
  unsigned int v23; // r0
  unsigned int v24; // r11
  unsigned int v25; // r0
  unsigned int v26; // r8
  unsigned int v27; // r2
  unsigned int v28; // r0
  unsigned int v29; // r2
  int v30; // r6
  int v31; // r5
  unsigned int v32; // r2
  unsigned int *v33; // r0
  size_t v35; // [sp+4h] [bp-44h]
  char *v36; // [sp+8h] [bp-40h]
  int v37; // [sp+Ch] [bp-3Ch]
  int v38; // [sp+10h] [bp-38h]
  int v39; // [sp+14h] [bp-34h]
  _DWORD *ptr; // [sp+18h] [bp-30h]
  signed int v41; // [sp+28h] [bp-20h]

  v2 = a1;
  i_32 = a2;
  v4 = malloc(32u);
  *v4 = 0xFFF3A2E6;
  v4[1] = 0xA66E1F1C;
  v4[2] = 0x21772905;
  v4[3] = 0xC0D58234;
  *((_WORD *)v4 + 8) = 0x706;
  *(_DWORD *)((char *)v4 + 18) = 0x24ED1653;
  *(_DWORD *)((char *)v4 + 22) = 0xCB39377A;
  *(_DWORD *)((char *)v4 + 26) = 0xA90383A3;
  *((_WORD *)v4 + 15) = 0xF68Bu;
  if ( i_32 << 28 )
  {
    free(v4);
    v5 = 0;
  }
  else
  {
    ptr = v4;
    v5 = (char *)malloc(i_32);
    if ( i_32 >> 4 )
    {
      v6 = 0;
      v35 = i_32 >> 4;
      v36 = v5;
      v37 = v2;
      do
      {
        v7 = v2 + 16 * v6;
        v39 = v6;
        v8 = 0;
        v38 = 16 * v6;
        v9 = *(_DWORD *)(v2 + 16 * v6);
        v10 = *(_QWORD *)(v7 + 4) >> 32;
        v11 = *(_QWORD *)(v7 + 4);
        v12 = *(_DWORD *)(v7 + 12);
        v41 = 8;
        do
        {
          v13 = (char *)&unk_7B226754 + v8;
          v8 -= 28;
          v14 = ptr[*((_DWORD *)v13 + 54)] + v12;
          v15 = byte_7B226652[(unsigned __int16)v14 >> 8];
          v16 = ((((byte_7B226652[(v14 >> 16) & 0xFF] << 16) | (v15 << 8) | ((unsigned int)byte_7B226652[v14 >> 24] << 24)) >> 11) | ((byte_7B226652[(unsigned __int8)v14] | (v15 << 8)) << 21)) ^ v10;
          v17 = ptr[*((_DWORD *)v13 + 55)] + v9;
          v18 = (32
               * (byte_7B226652[(unsigned __int8)v17] | (byte_7B226652[(unsigned __int16)v17 >> 8] << 8) | (byte_7B226652[(v17 >> 16) & 0xFF] << 16) | (byte_7B226652[v17 >> 24] << 24)) | ((unsigned int)byte_7B226652[v17 >> 24] >> 3)) ^ v11;
          v19 = v18;
          v20 = *(_QWORD *)(v13 + 204);
          v21 = v18 + v16 + ptr[HIDWORD(v20)];
          v22 = ((((byte_7B226652[(v21 >> 16) & 0xFF] << 16) | (byte_7B226652[(unsigned __int16)v21 >> 8] << 8) | ((unsigned int)byte_7B226652[v21 >> 24] << 24)) >> 11) | ((byte_7B226652[(unsigned __int8)v21] | (byte_7B226652[(unsigned __int16)v21 >> 8] << 8)) << 21)) ^ v41;
          v23 = ptr[(_DWORD)v20] + v16 - v22;
          v24 = v12
              + ((((byte_7B226652[v23 >> 24] << 24) | ((unsigned int)byte_7B226652[(v23 >> 16) & 0xFF] << 16)) >> 19) | ((byte_7B226652[(unsigned __int8)v23] | (byte_7B226652[(unsigned __int16)v23 >> 8] << 8) | (byte_7B226652[(v23 >> 16) & 0xFF] << 16)) << 13));
          v25 = ptr[(unsigned int)*(_QWORD *)(v13 + 196)] + v24;
          v26 = (32
               * (byte_7B226652[(unsigned __int8)v25] | (byte_7B226652[(unsigned __int16)v25 >> 8] << 8) | (byte_7B226652[(v25 >> 16) & 0xFF] << 16) | (byte_7B226652[v25 >> 24] << 24)) | ((unsigned int)byte_7B226652[v25 >> 24] >> 3)) ^ (v16 - v22);
          v27 = ptr[*((_DWORD *)v13 + 53)] + v19;
          v28 = v9
              - ((((byte_7B226652[v27 >> 24] << 24) | ((unsigned int)byte_7B226652[(v27 >> 16) & 0xFF] << 16)) >> 19) | ((byte_7B226652[(unsigned __int8)v27] | (byte_7B226652[(unsigned __int16)v27 >> 8] << 8) | (byte_7B226652[(v27 >> 16) & 0xFF] << 16)) << 13));
          v29 = ptr[*(_QWORD *)(v13 + 196) >> 32] + v28;
          v30 = byte_7B226652[(unsigned __int16)v29 >> 8];
          v31 = byte_7B226652[(unsigned __int8)v29] | (v30 << 8);
          --v41;
          v11 = v28;
          v32 = (byte_7B226652[(v29 >> 16) & 0xFF] << 16) | (v30 << 8) | (byte_7B226652[v29 >> 24] << 24);
          v10 = v24;
          v9 = v26;
          v12 = ((v32 >> 11) | (v31 << 21)) ^ (v22 + v19);
        }
        while ( v8 != -224 );
        v5 = v36;
        *(_DWORD *)&v36[v38] = v10;
        v33 = (unsigned int *)&v36[v38 + 4];
        *v33 = v26;
        v33[1] = v12;
        v33[2] = v11;
        v2 = v37;
        v6 = v39 + 1;
      }
      while ( v39 + 1 != v35 );
    }
    free(ptr);
  }
  return v5;
}

接下来进入一个签名校验的函数,其实都可以跳过,因为我们可以看到上面的函数都没有参与最后返回值的生成。

int __fastcall checkSign(JNIEnv *a1, jobject a2, char *a3)
{
  JNIEnv *env; // r11
  jfieldID obj_ctx; // r9
  JNIEnv v5; // r10
  char *v6; // r4
  char *v7; // r6
  jclass jcls_ctx; // r5
  jstring v9; // r8
  const char *v10; // r0
  const char *v11; // r6
  int v12; // r4
  int v13; // r5
  char *v14; // r4
  char *v15; // r0
  char *v16; // r6
  int v17; // r5
  int v18; // r6
  bool v19; // zf
  __int64 v21; // r4
  void *v22; // r6
  __int64 v23; // r4
  jstring v24; // r0
  void *v25; // r8
  char *v26; // r5
  jclass v27; // r6
  jobject v28; // r6
  jobject v29; // r5
  char *v30; // r8
  char *v31; // r0
  char *v32; // r8
  unsigned int v33; // r0
  char *v34; // r4
  int v35; // r6
  char *v36; // r4
  const char *v37; // r0
  const char *v38; // r4
  JNIEnv v39; // [sp+10h] [bp-E8h]
  const char *s2; // [sp+14h] [bp-E4h]
  _jfieldID *v41; // [sp+18h] [bp-E0h]
  void *v42; // [sp+20h] [bp-D8h]
  void *v43; // [sp+28h] [bp-D0h]
  int v44; // [sp+30h] [bp-C8h]
  jstring v45; // [sp+38h] [bp-C0h]
  char v46; // [sp+47h] [bp-B1h]
  _QWORD v47[11]; // [sp+48h] [bp-B0h]
  unsigned __int8 v48; // [sp+A0h] [bp-58h]
  unsigned __int8 v49; // [sp+A1h] [bp-57h]
  unsigned __int8 v50; // [sp+A2h] [bp-56h]
  unsigned __int8 v51; // [sp+A3h] [bp-55h]
  unsigned __int8 v52; // [sp+A4h] [bp-54h]
  unsigned __int8 v53; // [sp+A5h] [bp-53h]
  unsigned __int8 v54; // [sp+A6h] [bp-52h]
  unsigned __int8 v55; // [sp+A7h] [bp-51h]
  unsigned __int8 v56; // [sp+A8h] [bp-50h]
  unsigned __int8 v57; // [sp+A9h] [bp-4Fh]
  unsigned __int8 v58; // [sp+AAh] [bp-4Eh]
  unsigned __int8 v59; // [sp+ABh] [bp-4Dh]
  unsigned __int8 v60; // [sp+ACh] [bp-4Ch]
  unsigned __int8 v61; // [sp+ADh] [bp-4Bh]
  unsigned __int8 v62; // [sp+AEh] [bp-4Ah]
  unsigned __int8 v63; // [sp+AFh] [bp-49h]
  char s1; // [sp+B0h] [bp-48h]
  __int16 v65; // [sp+B2h] [bp-46h]
  int v66; // [sp+B4h] [bp-44h]
  int v67; // [sp+B8h] [bp-40h]
  int v68; // [sp+BCh] [bp-3Ch]
  int v69; // [sp+C0h] [bp-38h]
  int v70; // [sp+C4h] [bp-34h]
  int v71; // [sp+C8h] [bp-30h]
  int v72; // [sp+CCh] [bp-2Ch]
  int v73; // [sp+D8h] [bp-20h]

  env = a1;
  obj_ctx = (jfieldID)a2;
  s2 = a3;
  v5 = *a1;
  v6 = deStr((int)&unk_7B226944, 0x10u);        // getName
  v7 = deStr((int)&c_mtd_sign_ret_str, 0x20u);  // ()Ljava/lang/String;
  jcls_ctx = (*env)->GetObjectClass(env, (jobject)obj_ctx);
  sub_7B223814(&v45, env, &v46, (unsigned int)jcls_ctx, __PAIR__((unsigned int)v7, (unsigned int)v6));// 获取application的类名
  v9 = v45;
  v5->DeleteLocalRef(env, jcls_ctx);
  free(v6);
  free(v7);
  if ( v9 && !v46 )
  {
    v39 = v5;
    v5 = (JNIEnv)deStr((int)&unk_7B226884, 0x20u);// com.yxcorp.gifshow.App
    if ( v5 && (v10 = (*env)->GetStringUTFChars(env, v9, 0), (v11 = v10) != 0) )
    {
      v12 = strcasecmp(v10, (const char *)v5);
      (*env)->ReleaseStringUTFChars(env, v9, v11);
      if ( v12 )
        v12 = -1;
    }
    else
    {
      v12 = -1;
    }
    (*env)->DeleteLocalRef(env, v9);
    if ( v5 )
      free((void *)v5);
    if ( v12 )
    {
      v13 = -2;
      goto LABEL_28;
    }
    v14 = deStr((int)&c_getPackageName, 0x10u); // getPackageName
    v15 = deStr((int)&c_mtd_sign_ret_str, 0x20u);// ()Ljava/lang/String;
    v16 = v15;
    sub_7B223814((jstring *)&v44, env, &v46, (unsigned int)obj_ctx, __PAIR__((unsigned int)v15, (unsigned int)v14));// 获取包名
    v17 = v44;
    free(v14);
    free(v16);
    if ( v46 )
      goto LABEL_27;
    if ( !((unsigned int)s2 | v17) )
    {
      v18 = 0;
      v5 = v39;
      if ( !v17 )
        goto LABEL_26;
LABEL_25:
      (*env)->DeleteLocalRef(env, (jobject)v17);
      goto LABEL_26;
    }
    v5 = v39;
    v19 = s2 == 0;
    v18 = -1;
    if ( s2 )
      v19 = v17 == 0;
    if ( v19 )
    {
      if ( !v17 )
        goto LABEL_26;
      goto LABEL_25;
    }
    v37 = (*env)->GetStringUTFChars(env, (jstring)v17, 0);// 肯定是com.smile.gifmaker
    v38 = v37;
    if ( v37 )
    {
      v18 = strcasecmp(v37, s2);
      (*env)->ReleaseStringUTFChars(env, (jstring)v17, v38);
      if ( v18 )
        v18 = -1;
      if ( v17 )
        goto LABEL_25;
    }
    else
    {
      v18 = -1;
      if ( v17 )
        goto LABEL_25;
    }
LABEL_26:
    if ( !v18 )
      goto LABEL_30;
LABEL_27:
    v13 = -3;
    goto LABEL_28;
  }
  if ( v9 )
    (*env)->DeleteLocalRef(env, v9);
  v13 = -1;
LABEL_28:
  while ( MEMORY[0x40103384] != v73 )
  {
LABEL_30:
    LODWORD(v21) = deStr((int)&c_getPackageManager, 0x20u);// getPackageManager
    HIDWORD(v21) = deStr((int)&c_mtd_sign_ret_PM, 0x30u);// ()Landroid/content/pm/PackageManager;
    sub_7B223814(&v43, env, &v46, (unsigned int)obj_ctx, v21);// 得到pm
    v22 = v43;
    free((void *)v21);
    free((void *)HIDWORD(v21));
    if ( !v22 || v46 )
    {
      if ( v22 )
        v5->DeleteLocalRef(env, v22);
      v13 = -4;
    }
    else
    {
      LODWORD(v23) = deStr((int)&c_getPackageInfo, 0x10u);// getPackageInfo
      HIDWORD(v23) = deStr((int)&c_mtd_sign_ret_PkInfo, 0x40u);// (Ljava/lang/String;I)Landroid/content/pm/PackageInfo;
      v24 = v5->NewStringUTF(env, s2);
      sub_7B223814(&v42, env, &v46, (unsigned int)v22, v23, v24, 64);// 得到pkackageinfo
      v25 = v42;                                // 后面懒得看了,猜测肯定是获取签名md5,判断是不是debug或者release版本,就是校验签名
      v5->DeleteLocalRef(env, v22);
      free((void *)v23);
      free((void *)HIDWORD(v23));
      if ( !v25 || v46 )
      {
        if ( v25 )
          v5->DeleteLocalRef(env, v25);
        v13 = -5;
      }
      else
      {
        v5 = (JNIEnv)deStr((int)&unk_7B226B04, 0x10u);
        v26 = deStr((int)&unk_7B226B44, 0x20u);
        v27 = v39->GetObjectClass(env, v25);
        obj_ctx = v39->GetFieldID(env, v27, (const char *)v5, v26);
        free((void *)v5);
        free(v26);
        v39->DeleteLocalRef(env, v27);
        v28 = v39->GetObjectField(env, v25, obj_ctx);
        v39->DeleteLocalRef(env, v25);
        if ( v28 )
        {
          v29 = v39->GetObjectArrayElement(env, v28, 0);
          if ( v29 )
          {
            v30 = deStr((int)&unk_7B226B84, 0x10u);
            v31 = deStr((int)&unk_7B226BC4, 0x10u);
            v5 = (JNIEnv)v31;
            sub_7B223814((jstring *)&v41, env, &v46, (unsigned int)v29, __PAIR__((unsigned int)v31, (unsigned int)v30));
            obj_ctx = v41;
            free(v30);
            free((void *)v5);
            v39->DeleteLocalRef(env, v28);
            if ( !obj_ctx || v46 )
            {
              if ( obj_ctx )
                v39->DeleteLocalRef(env, (jobject)obj_ctx);
              v13 = -8;
            }
            else
            {
              v13 = 0;
              v5 = (JNIEnv)&s1;
              v32 = v39->GetByteArrayElements(env, (jbyteArray)obj_ctx, 0);
              _aeabi_memclr8(&s1, 33);
              md5_init(v47);
              v33 = v39->GetArrayLength(env, (jarray)obj_ctx);
              md5_update(v47, v32, v33);
              md5_final(v47);
              sprintf(&s1, "%02x", v48);
              sprintf((char *)&v65, "%02x", v49);
              sprintf((char *)&v66, "%02x", v50);
              sprintf((char *)((unsigned int)&s1 | 6), "%02x", v51);
              sprintf((char *)&v67, "%02x", v52);
              sprintf((char *)&v67 + 2, "%02x", v53);
              sprintf((char *)&v68, "%02x", v54);
              sprintf((char *)&v68 + 2, "%02x", v55);
              sprintf((char *)&v69, "%02x", v56);
              sprintf((char *)&v69 + 2, "%02x", v57);
              sprintf((char *)&v70, "%02x", v58);
              sprintf((char *)&v70 + 2, "%02x", v59);
              sprintf((char *)&v71, "%02x", v60);
              sprintf((char *)&v71 + 2, "%02x", v61);
              sprintf((char *)&v72, "%02x", v62);
              sprintf((char *)&v72 + 2, "%02x", v63);
              v34 = deStr((int)&unk_7B2268C4, 0x30u);
              v35 = strcasecmp(&s1, v34);
              free(v34);
              if ( v35 )
              {
                v36 = deStr((int)&unk_7B226904, 0x30u);
                v13 = strcasecmp(&s1, v36);
                free(v36);
                if ( v13 )
                  v13 = -9;
              }
              v39->ReleaseByteArrayElements(env, (jbyteArray)obj_ctx, v32, 0);
              v39->DeleteLocalRef(env, (jobject)obj_ctx);
            }
          }
          else
          {
            v39->DeleteLocalRef(env, v28);
            v13 = -7;
          }
        }
        else
        {
          v13 = -6;
        }
      }
    }
  }
  return v13;
}

看了一下,大概就可以猜出是干什么的,应该就是获取签名md5后判断是不是被重打包。

int sub_7B223814(jstring *a1, JNIEnv *a2, _BYTE *a3, unsigned int a4, __int64 a5, ...)
{
  jobject (__cdecl *v5)(JNIEnv *, jobject, jmethodID, va_list); // r6
  int v6; // r11
  jstring *v7; // r9
  JNIEnv *env; // r5
  _BYTE *v9; // r10
  unsigned int v10; // r8
  unsigned int v11; // r4
  _jmethodID *v12; // r2
  int v13; // t1
  unsigned int v14; // r0
  int (__fastcall *v15)(JNIEnv *, unsigned int); // r6
  jobject (__cdecl *v16)(JNIEnv *, jobject, jmethodID, va_list); // r1
  unsigned __int16 v17; // r0
  int v18; // r0
  int result; // r0
  jclass v20; // [sp+0h] [bp-28h]
  int v21; // [sp+8h] [bp-20h]
  va_list va; // [sp+38h] [bp+10h]

  va_start(va, a5);
  v7 = a1;
  env = a2;
  v9 = a3;
  v10 = a4;
  if ( !(*env)->EnsureLocalCapacity(env, 2) )
  {
    v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))HIDWORD(a5);
    LOBYTE(v6) = a5;
    v20 = (*env)->GetObjectClass(env, (jobject)v10);
    v12 = (*env)->GetMethodID(env, v20, (const char *)a5, (const char *)HIDWORD(a5));
    if ( v12 )
    {
      do
      {
        v13 = *(unsigned __int8 *)v5;
        v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))((char *)v5 + 1);
      }
      while ( v13 != ')' );
      switch ( *(unsigned __int8 *)v5 )
      {
        case 'B':
          v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))(*env)->CallByteMethodV;
          goto LABEL_26;
        case 'C':
          v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))(*env)->CallCharMethodV;
          v14 = ((int (__fastcall *)(JNIEnv *, unsigned int))v5)(env, v10);
          LOBYTE(v6) = v14;
          v11 = v14 >> 8;
          goto LABEL_20;
        case 'D':
          v15 = (int (__fastcall *)(JNIEnv *, unsigned int))(*env)->CallDoubleMethodV;
          goto LABEL_16;
        case 'F':
          v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))(*env)->CallFloatMethodV;
          goto LABEL_14;
        case 'I':
          v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))(*env)->CallIntMethodV;
          goto LABEL_14;
        case 'J':
          v15 = (int (__fastcall *)(JNIEnv *, unsigned int))(*env)->CallLongMethodV;
LABEL_16:
          v6 = v15(env, v10);
          v5 = v16;
          v10 = v6 & 0xFFFF0000;
          v11 = (unsigned __int16)v6 >> 8;
          goto LABEL_21;
        case 'L':
        case '[':
          v5 = (*env)->CallObjectMethodV;
LABEL_14:
          v6 = (int)v5(env, (jobject)v10, v12, va);
          v10 = v6 & 0xFFFF0000;
          v11 = (unsigned __int16)v6 >> 8;
          goto LABEL_21;
        case 'S':
          v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))(*env)->CallShortMethodV;
          v17 = ((int (__fastcall *)(JNIEnv *, unsigned int))v5)(env, v10);
          LOBYTE(v6) = v17;
          v11 = v17 >> 8;
          goto LABEL_20;
        case 'V':
          v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))(*env)->CallVoidMethodV;
          ((void (__fastcall *)(JNIEnv *, unsigned int))v5)(env, v10);
          break;
        case 'Z':
          goto LABEL_25;
        default:
          (*env)->FatalError(env, "illegaldescriptor");
          break;
      }
    }
    v11 = 0;
    goto LABEL_20;
  }
  v11 = 0;
  v10 = 0;
  if ( v9 )
    goto LABEL_22;
  while ( 1 )
  {
    *v7 = (jstring)((unsigned __int8)v6 | (v11 << 8) | v10);
    v7[1] = v5;
    result = MEMORY[0x40103384] - v21;
    if ( MEMORY[0x40103384] == v21 )
      break;
LABEL_25:
    v5 = (jobject (__cdecl *)(JNIEnv *, jobject, jmethodID, va_list))(*env)->CallBooleanMethodV;
LABEL_26:
    LOBYTE(v6) = ((int (__fastcall *)(JNIEnv *, unsigned int))v5)(env, v10);
    v11 = 0;
LABEL_20:
    v10 = 0;
LABEL_21:
    (*env)->DeleteLocalRef(env, v20);
    if ( v9 )
    {
LABEL_22:
      v18 = (*env)->ExceptionCheck(env);
      *v9 = v18;
      if ( v18 )
        (*env)->ExceptionClear(env);
    }
  }
  return result;
}


分析出是md5算法,那么在源字符串后面拼接,确定是加盐。。。

梳理逻辑

get请求,取?后面的参数,以key进行排序,组合
post请求,额,这个post请求也有get的参数,所以把?后面的参数和post的参数一起排序,组合

代码实现

package com.zhuo.tong.kuaishou;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.*;

public class Main {


    public static String md5(String dataStr) {
        try {
            MessageDigest m = MessageDigest.getInstance("MD5");
            m.update(dataStr.getBytes("UTF8"));
            byte s[] = m.digest();
            String result = "";
            for (int i = 0; i < s.length; i++) {
                result += Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00).substring(6);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }

    
    private static class Holder{
        List<String> list = new ArrayList<>();
        HashMap<String, String> map = new HashMap<>();

        public String getData() {
            Collections.sort(list);
            StringBuilder sb = new StringBuilder();
            for (String s : list) {
                String value = map.get(s);
                sb.append(s).append("=").append(value);
            }
            return sb.toString();
        }
    }

    private static Holder getHolder(Holder holder, String data) {
        data = URLDecoder.decode(data, Charset.defaultCharset());
        System.out.println(data);
        String[] split = data.split("&");
        for (String s : split) {
            if (s.startsWith("sig=")) {
                continue;
            }
            String[] split1 = s.split("=");//应该是取第一个=,懒得完善了
            //额,某个包触发了这个bug,还是要修复
            int index = s.indexOf("=");
            String first = s.substring(0, index);
            String secend = s.substring(index + 1, s.length());
            split1 = new String[]{first, secend};
            holder.list.add(split1[0]);
            holder.map.put(split1[0], split1.length==2?split1[1]:"");
        }

        return holder;
    }

    public static void main(String[] args) {

        String data = "app=0&lon=146.351638&did_gt=1562212339132&c=MYAPP,1&sys=ANDROID_8.1&isp=&mod=LGE(AOSP on TTOG)&did=ANDROID_a515efd201ca2590&hotfix_ver=&ver=6.5&net=WIFI&country_code=CN&iuid=&appver=6.5.5.9591&max_memory=192&oc=MYAPP,1&ftt=&kpn=KUAISHOU&ud=0&language=zh-cn&kpf=ANDROID_PHONE&lat=30.005315";
        
        //去除一些参数,
//        data = "app=0";//返回{"result":3,"error_msg":"客户端版本太旧,请升级到最新版本。","host-name":"bjfk-rs8594.yz02"}

        

        Holder holder = new Holder();
        getHolder(holder, data);
        String postData = "op=test&os=android&sig=6a1f5f5c7ba6feeeff52b383815a0435&client_key=3c2cd3f3";
        
        getHolder(holder, postData);

        data = holder.getData();

        String salt = "382700b563f4";
        data += salt;

        String sig = md5(data);
        System.out.println(sig);
        

        
    }
}


之后改包,签名,可以了。

有些api,对ANDROID有限制,例如判断用户是否注册的接口,就返回

{"result":705,"error_url":"https://app.m.kuaishou.com/verify/captcha.html?key=oJ6ByMKELaoQWPXqHxD0weaclfXUn313nri0x8REq7OaNjhHczNypR1DsIwzGHYp&type=4&uri=%2Frest%2Fn%2Fuser%2Fmobile%2Fchecker&provider=tencent","error_msg":"","host-name":"bjm7-rs1856.jxq"}

应该是风控的部分,懒得再看了。



[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com

最后于 2019-9-7 01:41 被卓桐编辑 ,原因:
最新回复 (12)
Editor 2019-9-7 08:52
2
0
太强了,顶
阿仁哟 2019-9-7 17:58
3
0
你这个,算出来结果并不对,方法应该是类似,是对最新版的破解吗?
Mars. 2019-9-7 19:55
4
0
眯着眼睛 2019-9-7 20:55
5
0
这个并不通用,在某些包,算出来的结果对不上,会提示签名效验失败。
大部分的包是可以用的
Ive_406746 2019-9-8 02:56
6
0
这个算法市面上都泄露好多年了..那个salt至今有效..也是醉了
阿仁哟 2019-9-8 23:11
7
0
Ive_406746 这个算法市面上都泄露好多年了..那个salt至今有效..也是醉了
现在加了另一个salt,用于另一个参数的加密,并非所有的api都只用这个sig就能拿到数据了
Hiyokunotori 2019-9-9 17:55
8
0
请问so里的salt  "382700b563f4"这个是使用什么工具的什么技术来得到结果的呢。。。
hczhong 2019-9-10 11:24
9
0
我记得当时我获取salt的时候是hook得到的。
neilwu 2019-9-11 14:40
10
1
Hiyokunotori 请问so里的salt "382700b563f4"这个是使用什么工具的什么技术来得到结果的呢。。。
文中deStr是个解密函数,将加密串传进去可以得到salt "382700b563f4"


Ive_406746 3天前
11
0
阿仁哟 现在加了另一个salt,用于另一个参数的加密,并非所有的api都只用这个sig就能拿到数据了
你指的是sig2 还是sig3? sig2就很简单本质上核心的算法还是这个算法,sig3的话就是另外一个算法了。
阿仁哟 2天前
12
0
Ive_406746 你指的是sig2 还是sig3? sig2就很简单本质上核心的算法还是这个算法,sig3的话就是另外一个算法了。
用于__NStokensig参数的token_client_salt,这个每个设备都不一样吧
Ive_406746 21小时前
13
0
阿仁哟 用于__NStokensig参数的token_client_salt,这个每个设备都不一样吧
一样的
游客
登录 | 注册 方可回帖
返回