先charles抓包,api.xxxxxx.com域名的包
分析包 看到路径参数如下

appkey=1d8b6e7d45233436&build=5531000&channel=dw056&mobi_app=android&mode=0&oid=326052200&plat=2&platform=android&ps=20&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%225.53.1%22%2C%22abtest%22%3A%22%22%7D&ts=1705305495&type=1&sign=2c9086d4acc853a017ec087699902634
可以看到一个sign,是个32字符,128位,直觉告诉我可能是个md5签名然后用jadx打开xxx.apk包,全局搜索sign字符串,在众多函数方法中,找到一个SignedQuery函数,跟进去看,发现又个loadLibrary(xxx),那么直接告诉我是在一个so里
用apktool把xxx.apk decode出来,拿到libxxx.so
用ida打开该libxxx.so,直接看JNI_OnLoad函数,找到这一行
if ( (*v5)->RegisterNatives(v5, v4, (const JNINativeMethod *)®ister_native_struct, 5) < 0
register_native_struct跟进去

每个函数进去看下,有惊喜,确实是个md5算法,找到 md5_impl,跟进去看看算法参数
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168int__fastcall md5_impl(JNIEnv*a1, jobject (*a2)(JNIEnv*, jclass, jmethodID, ...)){JNIEnv*v2;//r11jobject (*v3)(JNIEnv*, jclass, jmethodID, ...);//r6const char*v4;//r2const char*v5;//r1intresult;//r0intv7;//r0JNIEnv v8;//r1intv9;//r0intv10;//r4intv11;//r0intv12;//r10signedintv13;//r9intv14;//r5intv15;//r8time_t v16;//r0intv17;//r6intv18;//r5const char*input_str;//r8_DWORD*v20;//r10intv21;//r4_DWORD*v22;//r0intv23;//r1intv24;//r2intv25;//r0unsignedintinput_len;//r5signedintv27;//r4char*v28;//r5intv29;//r4const char*v30;//[sp+8h] [bp-C0h]intv31;//[sp+8h] [bp-C0h]intv32;//[sp+Ch] [bp-BCh]char v33;//[sp+10h] [bp-B8h]ints;//[sp+38h] [bp-90h]intv35;//[sp+3Ch] [bp-8Ch]intv36;//[sp+40h] [bp-88h]intv37;//[sp+44h] [bp-84h]char v38[24];//[sp+90h] [bp-38h]intv39;//[sp+A8h] [bp-20h]v2=a1;v3=a2;if( !sub_2D20((int)a1) )goto LABEL_5;v4="com.xxxxxxxx.nativelibrary.SignedQuery";v5="java/lang/ClassNotFoundException";LABEL_3:sub_401C((int)v2, (int)v5, (int)v4);result=0;while( _stack_chk_guard !=v39 ){LABEL_5:if( !v3 ){v5="java/lang/NullPointerException";v4="Null params!";goto LABEL_3;}v7=is_empty(v2, (int)v3);v8=*v2;if( v7 ){v3=v8->NewObject;result=((int(__fastcall*)(JNIEnv*,int,int, _DWORD, _DWORD))v3)(v2,cls_signed_query,signed_query_init,0,0);}else{v9=((int(__fastcall*)(JNIEnv*, const char*))v8->NewStringUTF)(v2,"appkey");v10=v9;v11=sub_60CC(v2, (int)v3, v9);////校验appkeyv12=v11;if( v11 ){v30=(const char*)((int(__fastcall*)(JNIEnv*,int, _DWORD))(*v2)->GetStringUTFChars)(v2, v11,0);v13=check_app_key_get_type(v30);}else{v13=-1;v30=0;}v14=((int(__fastcall*)(JNIEnv*, const char*))(*v2)->NewStringUTF)(v2,"ts");v15=sub_60CC(v2, (int)v3, v14);if( !v15 ){v36=0;v37=0;s=0;v35=0;v16=time(0);sprintf((char*)&s,"%ld", v16);((void (__fastcall*)(JNIEnv*,int*))(*v2)->NewStringUTF)(v2, &s);sub_6188((int)v2);}sub_41D0(v2, v14);sub_41D0(v2, v15);v17=((int(__fastcall*)(JNIEnv*,int,int, jobject (*)(JNIEnv*, jclass, jmethodID, ...)))(*v2)->CallStaticObjectMethod)(v2,cls_signed_query,method_signed_query_r,v3);v18=0;if( sub_3F70((int)v2) )v17=0;v32=v17;input_str=(const char*)((int(__fastcall*)(JNIEnv*,int, _DWORD))(*v2)->GetStringUTFChars)(v2, v17,0);if( v13 !=-1){((void (__fastcall*)(JNIEnv*,int, const char*))(*v2)->ReleaseStringUTFChars)(v2, v12, v30);v20=malloc(0x10u);if( v20 ){v31=v10;v21=global_key[v13];v22=&global_key[v13];v23=v22[5];v24=v22[10];v25=v22[15];*v20=v21;v20[1]=v23;v20[2]=v24;v20[3]=v25;_aeabi_memclr8(&v33,33);input_len=strlen(input_str);_aeabi_memclr8(v38,24);_aeabi_memclr8(&s,88);md5_init(&s);md5_update((int)&s, (int)input_str, input_len);sprintf(v38,"%08x", v21);md5_update((int)&s, (int)v38,8u);v27=1;do{sprintf(v38,"%08x", v20[v27]);md5_update((int)&s, (int)v38,8u);++v27;}while( v27 !=4);md5_final((int)v38, (int)&s);v28=&v33;v29=0;do{sprintf(v28,"%02x", (unsigned __int8)v38[v29++]);v28+=2;}while( v29 !=16);free(v20);v10=v31;v18=((int(__fastcall*)(JNIEnv*, char*))(*v2)->NewStringUTF)(v2, &v33);}else{v18=0;}}sub_41D0(v2, v10);v3=(*v2)->NewObject;result=((int(__fastcall*)(JNIEnv*,int,int,int,int))v3)(v2, cls_signed_query, signed_query_init, v32, v18);}}returnresult;}分析代码,发现关键在 check_app_key_get_type和global_key,一个是app key获取type,还有一个存着类似于secret key的 global key
整体看下来,结论就是 md5加密,按参数字典序排序,然后拼接SecretKey,再md5得到sign,取其中一个type来验证下:

和app请求生成的sign一样,secret key打码,如果想要,可以按步骤自己去试试,实测成功,关键地方都写清楚了

最后,写个python脚本试试,确实都能访问,这里脚本就不放了,求赞求加精,求账号升级,感谢