android tsclib.so,续 某哩某哩APP之m3u8解密分析之跳过so文件 从APP日志入手(AES/CBC模式)...
前排提示!!!!!
论坛禁止留联系方式!!
禁止求成品,也没有成品!
写在前面的话
前段时间已经研究过这个平台网页和APP的AES加解密方式了。
最近发现这个平台的APP的加解密方式更新了,所以又继续研究了下...
本贴用到的工具
1.反编译:jadx+NP管理器+Androidkiller(mt管理器的日志注入或Androidkiller的log/toast均可或别的工具均可)[jadx看java,NP管理器用来打印字符串APP,Androidkiller可以实时查看日志内容]
2.抓包:fiddler+安卓模拟器(这里用的是逍遥)
3.加解密网站(由于可能会...所以这里就不贴了)
抓包
1.打开APP抓包部分接口
设置好fd和模拟器的代{过}{滤}理端口之后,打开APP查看网络请求
1.0 fd.png (30.63 KB, 下载次数: 1)
2021-2-23 11:56 上传
接口
请求方式
提交内容
数据类型
(猜测)用途
host_XXXX.txt
GET
空
密文(暂不知道是什么加密)
可能是返回能正常访问的主机群
/v1/initial
GET
空
明文
新版本检测+首页弹窗+视频分类
/v1/register/token
POST
device_id/platform/key/universal_id/lang
明文
注册新用户,返回token和uid
/v1/user/info
POST
token/lang/download_amount
明文
查询并返回用户信息
/v1/firstpurchase
GET
空
明文
买VIP悬浮广告
2.随意点一个视频查看fd的数据
1.1 m3u8密文.png (128.34 KB, 下载次数: 0)
2021-2-23 12:10 上传
由图看出:
之前的m3u8处理方法
现在的m3u8处理方法
链接加密,m3u8明文
链接明文拼接,m3u8密文
(PS:图中能看到http://localhost:1500/video?mode=的请求,其实关闭模拟器的fd代{过}{滤}理之后,用浏览器打开这个网址.看到的就是解密后的m3u8的内容,不过这个m3u8文件是需要处理后才能用下载器下载的)
1.2 需要处理的本地m3u8.png (16.92 KB, 下载次数: 0)
2021-2-23 12:16 上传
(再PS:这个m3u8的headers中还有和网页版中的X-VTag参数)
1.3 vtag.png (15.81 KB, 下载次数: 1)
2021-2-23 13:08 上传
那我们就来用工具解密下m3u8
反编译app解密m3u8
把app拉入jadx
搜"X-VTag"
用jadx搜"X-VTag",发现只有一个a.b.i.a.h.a,点进去看看代码
1.4 X-VTag.png (15.14 KB, 下载次数: 0)
2021-2-23 12:23 上传
以下是a.b.i.a.h.a截取的部分代码
String a5 = tVar.a("X-VTag");/* ①.a5就是X-VTag的值,我测试的视频X-VTag是825497755 */
if (a5 == null || (i = a.b.f.o.f.i(a5)) == null) {/* ④.i=a.b.f.o.f.i,那么a.b.f.o.f.i是什么操作呢?我们用jadx跳到声明 */
str = null;/* ②.如果a5或i是null的那str=null */
} else {
str = i.substring(8, 24);/* ③.否则str= 从i的第9位取到第24位,i的长度为16 */
i.a((Object) str, "(this as java.lang.Strin…ing(startIndex, endIndex)");
}
h0 h0Var2 = a2.g;
w c = h0Var2 != null ? h0Var2.c() : null;
if (!(str == null || c == null)) {
h0 a6 = h0.a(c, a.b.j.v3.a.a(CipherClient.decodeKey(), str, a3));
g0.a aVar2 = new g0.a(a2);
aVar2.g = a6;
g0 a7 = aVar2.a();
i.a((Object) a7, "response.newBuilder().body(body).build()");
return a7;
}
a.b.f.o.f.i函数
public static final String i(String str) {
if (str != null) {
try {
MessageDigest instance = MessageDigest.getInstance(AESEncryptor.HASH_ALGORITHM);/* AESEncryptor.HASH_ALGORITHM跳转后是MD5 */
byte[] bytes = str.getBytes(z.z.a.f8176a);
i.a((Object) bytes, "(this as java.lang.String).getBytes(charset)");
instance.update(bytes);
byte[] digest = instance.digest();
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
String hexString = Integer.toHexString(b & 255);
while (hexString.length() < 2) {
hexString = '0' + hexString;
}
sb.append(hexString);
}
String sb2 = sb.toString();
i.a((Object) sb2, "hexString.toString()");
return sb2;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
} else {
i.a("$this$toMd5");
throw null;
}
}
看这个操作可以结案了,a.b.f.o.f.i函数就是把str参数md5处理了下
所以可知案例中的部分参数
变量
解析
值
a5
headers中的X-VTag的值
825497755
i
md5(a5)
9807a3e5370512bab61bee56200ee1d6
str
i.substring(8, 24)
370512bab61bee56
我们继续往下看a.b.i.a.h.a
h0 a6 = h0.a(c, a.b.j.v3.a.a(CipherClient.decodeKey(), str, a3));
先跳转看这个CipherClient.decodeKey()函数
public static final String decodeKey() {
String str = CipherCore.get("aa01bdd83d0f12833ddaea2f2af22865");
return str;
}
我们接着看CipherCore.get函数,跳转就跳到了net.idik.lib.cipher.so.CipherCore类中
package net.idik.lib.cipher.so;
public final class CipherCore {
static {
System.loadLibrary("cipher-lib");
init();
}
public CipherCore() throws IllegalAccessException {
throw new IllegalAccessException();
}
public static String get(String str) {
return getString(str);
}
public static native String getString(String str);
public static native void init();
}
我去,这样一看CipherCore.get的方法在cipher-lib.so中??
111.png (47.01 KB, 下载次数: 1)
2021-2-23 14:18 上传
这伪代码我也看不懂呀....不过不用怕.这个函数有返回值咱就不怕.
我们尝试用NP管理器的NPPrintFuncSrc打印出CipherClient.decodeKey()和其他函数的值(PS:也可以用Androidkiller的toast或者log,别的方法也均可)
smali代码是这样的:
.method public static final decodeKey()Ljava/lang/String;
.registers 1
const-string v0, "解密-decodekey"
invoke-static {v0}, Lnp/NPLogEncryptString;->NPPrintFuncSrc(Ljava/lang/String;)V #打印"解密-decodekey"
const-string v0, "aa01bdd83d0f12833ddaea2f2af22865"
.line 1
invoke-static {v0}, Lnet/idik/lib/cipher/so/CipherCore;->get(Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
invoke-static {v0}, Lnp/NPLogEncryptString;->NPPrintFuncSrc(Ljava/lang/String;)V #打印decodeKey返回的字符串
return-object v0
.end method
java代码是这样的:
/* 以上省略大部分代码 */
public static final String decodeKey() {
NPLogEncryptString.NPPrintFuncSrc("解密-decodekey");
String str = CipherCore.get("aa01bdd83d0f12833ddaea2f2af22865");
NPLogEncryptString.NPPrintFuncSrc(str);
return str;
}
/* 以下也省略大部分代码 */
在Androidkiller中显示是这样的
1.5 decodekey.png (20.28 KB, 下载次数: 0)
2021-2-23 12:47 上传
按类似的方法成功获取到部分常量如下:
函数
常量
获取到的结果
apiHashKey
d708e111b5db90af74ef84ff4d5e647b
666wInteriscommingyoUshouldNotpassmotherfuckeR=
ccToken
810d903a88254b27c643e0bc471d406a
空
decodeKey
aa01bdd83d0f12833ddaea2f2af22865
6e561ccd4aade2fed458d4da61e76770
encodeType
e82b6153b4ea2340333e2254c3553d03
空
everIv
c5b287eb7ee64e90ed015bac470f4b6b
BakinsodaIgotbakinsoda
everKey
0d7cb519eb483a597549f7f466b189bc
iaMiNloveWithtHecoCo
hostIv
6cf9e96524081ac264dc31982d0be319
5e8bf1f958f56644
hostKey
51cdba173d412fdecec3e78572cde731
f332ae8214fcbb0d98f8626f123459b4
imageDecodeIv
9e1add49a87568f90c43e418e7370287
E01EDE6331D37AFCC7BE05597D654D22
imageDecodeKey
a8ae2831cbea74111bc5116ba81ec191
B2F3842866F9583D1ECE61C4E055C255
registerKey
951eeb6b19b70177fd25706aa620edcf
空
我们再继续回去看a.b.i.a.h.a
h0 a6 = h0.a(c, a.b.j.v3.a.a('6e561ccd4aade2fed458d4da61e76770', '370512bab61bee56', a3)); /* decodeKey()的值为6e561ccd4aade2fed458d4da61e76770,这个a3我猜测是密文 */
那么a.b.j.v3.a.a是啥呢,我们继续跳声明
a.b.j.v3.a.a函数
public static String a(String str, String str2, String str3) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException {
return a(new IvParameterSpec(str2.getBytes("UTF-8")), new SecretKeySpec(a(str).getBytes("UTF-8"), "AES"), str3);
}/* str=6e561ccd4aade2fed458d4da61e76770,str2就是a.b.i.a.h.a中的str=370512bab61bee56 */
又return a(IvParameterSpec, SecretKeySpec(a(str).getBytes("UTF-8"), "AES"), str3) /str3=a3猜测应该是密文,加密方式是AES/
又出现两个未知数,我们一个一个的解
解析a(str)
跳转声明发现这个a函数(a.b.j.v3.a)和a.b.f.o.f.i的函数是一致的,均为md5算法,那么a(str)=key=md5('6e561ccd4aade2fed458d4da61e76770')=ae52f7ffd2dd66ba5743bb180188b991
解析return a(x,x,x)
public static String a(IvParameterSpec ivParameterSpec, SecretKeySpec secretKeySpec, String str) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher instance = Cipher.getInstance(AESEncryptor.AES_MODE);/* AESEncryptor.AES_MODE的值跳转后可得是加解密方法为"AES/CBC/PKCS5Padding" */
instance.init(2, secretKeySpec, ivParameterSpec);
return new String(instance.doFinal(Base64.decode(str, 2)));
}
套入a.b.i.a.h.a中,那么本案例中的iv就是'9807a3e5370512bab61bee56200ee1d6'.substring(8, 24)=370512bab61bee56
尝试用在线aes解密和下载
成功解密m3u8
1.6 成功解密m3u8.png (53.88 KB, 下载次数: 0)
2021-2-23 13:12 上传
我们新建一个txt文档,把解密后的明文粘贴进去,保存关闭后,文件重命名为1.m3u8,拖入下载器,成功下载
下载器.png (29.22 KB, 下载次数: 0)
2021-2-23 20:55 上传
最后结案
m3u8相关
貌似算法和网页版的几乎一样?
切记获取的时候要协议头要带上version/platform/time/userid/hash 不然会拒绝访问的..
关于hash的加密算法在a.b.a.v.f中,用到了CipherClient.apiHashKey()
和一开始不加密的那种m3u8格式一样,但是现在m3u8是加密的需要解密:
m3u8地址:#域名#/media/#分辨率#/#视频id#.m3u8?token=#token#
加解密模式
key
iv
AES/CBC/PKCS5Padding
固定值:ae52f7ffd2dd66ba5743bb180188b991
md5(#X-VTag#).substring(8, 24)
PS:我用易语言的e2ee先Base64解码密文即可解密出明文..在线解密也可以解出的
请求头中hash值的算法
发现在请求m3u8密文的时候会经常提示拒绝访问access denied
看一下请求头中的参数
参数
值
来源
version
2.3.1
app版本
platform
Android
安卓平台
time
1614062660
当前十位时间戳
userid
AiwrHWxoYtGd
可能是随机值
hash
625698fd47f50c2c026b9cfe543d56c5
待探索
如果随便改的话也会提示拒绝访问access denied,所以我们去jadx中搜以下这个hash。
用jadx搜"hash"
hash.png (18.23 KB, 下载次数: 0)
2021-2-23 20:38 上传
所以i=hash的值,往上翻,找到i的赋值
i.png (29.99 KB, 下载次数: 0)
2021-2-23 20:52 上传
String i = a.b.f.o.f.i(a2 + value + valueOf + str + str2);
之前我们已经得出a.b.f.o.f.i是md5的操作。
依次看a2 / value / valueOf / str / str2
a2 = version的值 = APP版本号
value = platform的值 = 平台Android
valueOf = time的值 = 时间戳
str = userId的值 = 从"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"取随机字符
str2 = CipherClient.apiHashKey() = ‘666wInteriscommingyoUshouldNotpassmotherfuckeR=’
所以i = md5(app版本 + 平台Android + 时间戳 + 随机userId + ‘666wInteriscommingyoUshouldNotpassmotherfuckeR=’)
上述案例中 i = md5(2.3.1Android1614062660AiwrHWxoYtGd666wInteriscommingyoUshouldNotpassmotherfuckeR=) = 625698fd47f50c2c026b9cfe543d56c5
hash算法结案
host相关
由于我们以及得到了hostIv和hostkey,所以弯回去看看host_XXXX.txt,在线解密结果:
1.7 host.png (37.4 KB, 下载次数: 0)
2021-2-23 13:22 上传
关于倍速播放
正常情况下倍速播放只能是vip才有的功能,那么我们可以通过固定/v1/user/info接口中返回的Expiry的值即可免费使用该功能
.method public final getExpiry()J
.registers 3
.line 1
const-wide v0,0x5af3107a3ff6L #0x5af3107a3ff6L
return-wide v0
.end method
1.8 倍速.png (39.59 KB, 下载次数: 1)
2021-2-23 13:29 上传
android tsclib.so,续 某哩某哩APP之m3u8解密分析之跳过so文件 从APP日志入手(AES/CBC模式)...相关推荐
- 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- c++builder启动了怎么停止_App 竟然是这样跑起来的 —— Android App/Activity 启动流程分析...
在我的上一篇文章: AJie:按下电源键后竟然发生了这一幕 -- Android 系统启动流程分析zhuanlan.zhihu.com 我们分析了系统在开机以后的一系列行为,其中最后一阶段 AMS( ...
- Android:Android NDK项目中C++文件中打印日志
Android NDK项目中C++文件中打印日志 工作需要,在NDK项目中的C++文件中打印日志,还是费了点劲查找 才达到自己 想要的效果了. 步骤 添加头文件 添加头文件 定义宏函数 #includ ...
- android系统性能优化(63)---Android APP 卡顿问题分析及解决方案
Android APP 卡顿问题分析及解决方案 用户对卡顿的感知, 主要来源于界面的刷新. 而界面的性能主要是依赖于设备的UI渲染性能. 如果我们的UI设计过于复杂, 或是实现不够友好,计算绘制算法不 ...
- 用Gradle打包出jar文件 前面我们说过,在Android Studio里面使用Gradle来打包应用程序,一般都是build出来一个apk文件。但是有的同学是做实现层的开发,不直接做View层
用Gradle打包出jar文件 前面我们说过,在Android Studio里面使用Gradle来打包应用程序,一般都是build出来一个apk文件.但是有的同学是做实现层的开发,不直接做View层的 ...
- Android中app卡顿原因分析示例
http://www.cnblogs.com/zhucai/p/weibo-graphics-performance-analyse.html 朱才 专注于Android图形动画 MIUI工程师 博客 ...
- android中app分享小程序卡片及跳转回app
随着小程序的日渐火热,许多app都做了相应的小程序端,与之而来的两端交互是必不可少的,前几天我们的分享到微信的样式也要求改成了小程序卡片样式的了.其实微信的官方文档还算比较详细,我这就结合自己踩的坑给 ...
最新文章
- 几个不错的网页载入页面
- 大战设计模式【23】—— 原型模式
- IDEA2021.03 项目全部变红,但是可以正常编译运行
- linux telnet 权限,允许telnet 通过root用户进行访问
- 工信部推动云计算健康快速发展 催生巨大市场机会
- html边框塌陷怎么,你不知道的CSS(边框塌陷)?
- 设计模式第三篇-装饰者模式
- css预处理器_【第十一课】初尝CSS的预处理器
- 【汇编语言】通用数据处理指令——位操作类指令
- 注解(Annotation)自定义注解入门(转)
- 一个cp命令引发的mongodb大量慢查询
- 异常处理:java.lang.ClassNotFoundException: javax.xml.bind.JAXBContext
- cad转dwf格式怎么转换?
- PCBA方案开发设计—咖啡秤厨房电子秤PCBA方案
- 【Python】爬取TapTap原神评论并生成词云分析
- 南京大学计算机实验教程,南京大学 计算机系统基础 课程实验 2018(PA2)
- c++ cv转化灰度图_OpenCV C++如何使RGB图像变为灰度图像
- 网络安全[脚本小子] -- SSI注入
- current,present,recent 都是现在的,都是形容词
- 用计算机画函数,用计算机画函数图像 优质课教案设计