本帖最后由 spcspcspcspcspc 于 2020-12-28 07:59 编辑
感谢网友的提醒,下面两篇帖子确实早就分析过该APP,发帖之前没有仔细搜索,在此表示歉意!如有违规请管理员删除该贴,谢谢!
对某猫安卓端数据接口加密方式的简单分析
https://www.52pojie.cn/thread-1163182-1-1.html
(出处: 吾爱破解论坛)
分析并破解某猫网站js数据接口请求参数加密
https://www.52pojie.cn/thread-1154784-1-1.html
感谢某热心网友分享的APP,之前尝试破解会员的方法上失败了,这几天看完大神分析某哩的帖子深受启发,直接从视频地址下手!于是简单分析一下:
程序中所有地址及数据均经过修改,不作为真实数据!仅提供分析方法!。
程序分析第一步 抓包。请求内容类似为“api_token=&data=A11F0341C删除一大段&device=android&device_id=2273ce99efcb&device_type=MI999&device_version=99&version_code=9.9.9”,返回的全都是“589CF94EFBFAA858142064D……”这类的,一看就是很火的AES加密。假如你看不出来是什么加密方式,一步步来分析他。
JADX打开app,打开AndroidManifest.xml,找到第一个<activity所在的位置:
[Java] 纯文本查看 复制代码 <activity android:theme="@style/AppTheme_Splash" android:name="com.省略.省略.main.activity.SplashActivity">
按住CTRL再单击"com.省略.省略.main.activity.SplashActivity",就进入app初始化的第一个界面了。
先整体浏览一遍,有中文的先看中文!其次再看双引号里面的内容。例如这一行:
[Java] 纯文本查看 复制代码 splashActivity.m14963h((SplashActivity) (DefaultWebClient.HTTP_SCHEME + ApiUtils.m15997e() + ":8099" + "/api/domain/index"));
一看端口和地址就知道是请求某个网址,同样CTRL再单击m14963h,跟进去看看:
[Java] 纯文本查看 复制代码 private void m14963h(String str) {
RequestCall a = ((PostFormBuilder) ((PostFormBuilder) OkHttpUtils.m13452e().mo30123b("data", new SimpleRequest().toString()).mo30122b().mo30118a(str)).mo30117a((Object) SplashActivity.class.getName())).mo30121a();
a.mo30147b(3000);
a.mo30149c(3000);
a.mo30148b(new C3899c(str));
}
其中的"data"应该就是抓包请求中的"data",同样CTRL再单击mo30123b,跟进去看看:
[Java] 纯文本查看 复制代码 public PostFormBuilder mo30123b(String str, String str2) {
if (this.f8573d == null) {
this.f8573d = new TreeMap<>();
}
this.f8573d.put(str, AesEncryptionUtil.m11953b(str2, "x;j/6Sp})&{ZJD", "znbV%$JCpt<c"));
return this;
}
这两串乱码的字符是不是很特别?应该就是密码和偏移了,同样CTRL再单击m11953b,跟进去看看:
[Java] 纯文本查看 复制代码 public static String m11953b(String str, String str2, String str3) {
byte[] bArr;
try {
bArr = str.getBytes("UTF-8");
} catch (Exception e) {
e.printStackTrace();
bArr = null;
}
return m11950a(m11955b(bArr, str2, str3));
}
这一段是转化UTF-8格式的,String str2 和 String str3 原封不动给到了m11950a和m11955b函数,分别点进去看看:
[Java] 纯文本查看 复制代码 public static byte[] m11955b(byte[] bArr, String str, String str2) {
try {
SecretKeySpec b = m11954b(str);
Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
instance.init(1, b, m11951a(str2));
return instance.doFinal(bArr);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
不难发现果然就是AES/CBC/PKCS5Padding加密!这段其实是把请求的内容加密后给到data,然后发送请求。估计解密的密码也是一样吧。我们直接试试这套密码解密返回的数据。果然解密为明文信息。例如:
{"data":{"video_fee_limit":"1000~50000","video_hot_fee":1000,"service_url":"https:\/\/hhh.xxx.cn\/widget\/standalone.html?eid=e85ce5dcc79d5e1adcc&groupid=b786accb1554a12170","upload_notice":"一天只能发布2次非原创付费视频,原创付费视频一天发布3次","upload_limit":3,"discount_end_time":11110640},"code":200,"msg":"查询成功"}
采用JSON格式数据。
请求中data的数据同样解密出来,我们分析两个关键的请求:
POST
/***/video/index 获取首页播放列表
api_token=&data=D8957D0A806CB388A565中间省略一大堆BB9A5D49C37DF5ED3AABD
{"page":1,"perPage":10,"signature":"104AA9C95C8759999999F81B2B210660","type":3} 第一页,每页10条,签名,视频类型
POST
/***/video/info 获取视频信息
api_token=&data=55C73D6B1A9A7A78AAF0中间省略一大堆3DC886C865D6C4838118
{"signature":"4268FBC4650C7EBE9999999B48F17CE1","video_id":"9999"} 签名,视频ID
直接更改第二条中的“video_id”后的数据为888,然后加密,发送请求。测试发现返回 {"data":null,"code":401,"msg":"签名无效"} 看来signature中的签名也是随参数改变而改变的,继续分析。
JADX中点击-导航-搜索文本,输入signature发现太多条了,加引号后再搜,只有3条,后两条还在同一个地方,进去看看:
[Java] 纯文本查看 复制代码 for (Map.Entry<String, Object> entry : getParams$app_officialRelease().entrySet()) {
sb.append(HttpUtils.PARAMETERS_SEPARATOR);
sb.append(entry.getKey());
sb.append(HttpUtils.EQUAL_SIGN);
sb.append(EncodeUtils.m11968a(entry.getValue().toString()));
}
String sb2 = sb.toString();
C4842f.m16976a((Object) sb2, "sb.toString()");
if (sb2 != null) {
String substring = sb2.substring(1);
C4842f.m16976a((Object) substring, "(this as java.lang.String).substring(startIndex)");
String a = EncryptUtils.m11970a(substring + "ohI}-bD*z8)W7~REuYKQ=[C1&tevxBL/HS,Pqr0Z_{Xq5$z*");
C4842f.m16976a((Object) a, "EncryptUtils.encryptMD5ToString + Api.URL_SIG_KEY)");
if (a != null) {
String upperCase = a.toUpperCase();
C4842f.m16976a((Object) upperCase, "(this as java.lang.String).toUpperCase()");
add("signature", upperCase);
其实"EncryptUtils.encryptMD5ToString这里已经暴露了是MD5加密,看前面PARAMETERS_SEPARATOR是“&”,EQUAL_SIGN是“=”,前面就是把JSON转换为URL格式,sb2.substring(1)就是去掉第一个字符“&”。
举例:{"a":1,"b":2}转换后就是"a=1&b=2"。然后再加上一段乱码字符"ohI}-bD*z8)W7~REuYKQ=[C1&tevxBL/HS,Pqr0Z_{Xq5$z*",之后采用MD5加密。
MD5加密是没法解密的(一般情况下),不过我们已经知道了加密方法就无所谓了。加密一个试一下。
例如“video_id=888”加上乱码字符"ohI}-bD*z8)W7~REuYKQ=[C1&tevxBL/HS,Pqr0Z_{Xq5$z*",MD5加密后为“20C503275FE3222489F6E728BD41AB5A”,JSON数据就是{"signature":"20C503275FE3222489F6E728BD41AB5A","video_id":"888"},再经过AES加密后为“533B3BC中间省略一大堆95FECDCB1”,最后得到请求的数据为“data=533B3BC中间省略一大堆95FECDCB1”,其他参数可以不加的。然后就可以POST得到任意ID的视频信息了。
关于收费(需要金币)视频,请求后返回“该视频收费”,并且不会返回视频的地址等信息。继续往下看。
使用第一个请求,并修改perPage参数为50,就是获取首页列表50条(再多了会比较慢,也许是我电脑和网络卡的原因)。然后分析其中一条如下:
{"id":999,"user_id":888,"title":"鍝ュ摜佹垜鍢?,"gold":1000,"fee_num":0,"praise_num":165,"width":320,"height":568,"cover":"https:\/\/www.52.com\/20201106\/1650802\/48822e9bd02bbce52c283.jpg","is_vip":0,"is_original":0,"type":1,"nickname":"楠眰鎻?,"is_certification":0,"is_pay":0,"base64_txt":"https:\/\/www.52.com\/4882e52c283.txt"}
cover中就是图片地址,其实仔细观察视频的图片地址,再对比获取的播放地址不难发现,中间部分是一样的。{:301_997:} 掐头去尾。。。移花接木。。。利用中间部分就可以拼接出视频地址,所有视频都可以看了。gold中是需要的金币,利用他可以筛选出付费视频,只看付费的,{:1_918:}
最后放一个易语言源代码 https://wwx.lanzoux.com/iMtvyjopq1a 里面集成了加密、解密、转换、提取等程序,关键数据已经修改,仅作为学习使用。
libeay32.dll出错的可以试试这个: https://wwx.lanzoux.com/iu9Y1joq1ej
|