前排提示!!!!!

论坛禁止留联系方式!!

禁止求成品,也没有成品!

写在前面的话

前段时间已经研究过这个平台网页和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模式)...相关推荐

  1. 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  2. 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  3. 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  4. c++builder启动了怎么停止_App 竟然是这样跑起来的 —— Android App/Activity 启动流程分析...

    在我的上一篇文章: AJie:按下电源键后竟然发生了这一幕 -- Android 系统启动流程分析​zhuanlan.zhihu.com 我们分析了系统在开机以后的一系列行为,其中最后一阶段 AMS( ...

  5. Android:Android NDK项目中C++文件中打印日志

    Android NDK项目中C++文件中打印日志 工作需要,在NDK项目中的C++文件中打印日志,还是费了点劲查找 才达到自己 想要的效果了. 步骤 添加头文件 添加头文件 定义宏函数 #includ ...

  6. android系统性能优化(63)---Android APP 卡顿问题分析及解决方案

    Android APP 卡顿问题分析及解决方案 用户对卡顿的感知, 主要来源于界面的刷新. 而界面的性能主要是依赖于设备的UI渲染性能. 如果我们的UI设计过于复杂, 或是实现不够友好,计算绘制算法不 ...

  7. 用Gradle打包出jar文件 前面我们说过,在Android Studio里面使用Gradle来打包应用程序,一般都是build出来一个apk文件。但是有的同学是做实现层的开发,不直接做View层

    用Gradle打包出jar文件 前面我们说过,在Android Studio里面使用Gradle来打包应用程序,一般都是build出来一个apk文件.但是有的同学是做实现层的开发,不直接做View层的 ...

  8. Android中app卡顿原因分析示例

    http://www.cnblogs.com/zhucai/p/weibo-graphics-performance-analyse.html 朱才 专注于Android图形动画 MIUI工程师 博客 ...

  9. android中app分享小程序卡片及跳转回app

    随着小程序的日渐火热,许多app都做了相应的小程序端,与之而来的两端交互是必不可少的,前几天我们的分享到微信的样式也要求改成了小程序卡片样式的了.其实微信的官方文档还算比较详细,我这就结合自己踩的坑给 ...

最新文章

  1. 几个不错的网页载入页面
  2. 大战设计模式【23】—— 原型模式
  3. IDEA2021.03 项目全部变红,但是可以正常编译运行
  4. linux telnet 权限,允许telnet 通过root用户进行访问
  5. 工信部推动云计算健康快速发展 催生巨大市场机会
  6. html边框塌陷怎么,你不知道的CSS(边框塌陷)?
  7. 设计模式第三篇-装饰者模式
  8. css预处理器_【第十一课】初尝CSS的预处理器
  9. 【汇编语言】通用数据处理指令——位操作类指令
  10. 注解(Annotation)自定义注解入门(转)
  11. 一个cp命令引发的mongodb大量慢查询
  12. 异常处理:java.lang.ClassNotFoundException: javax.xml.bind.JAXBContext
  13. cad转dwf格式怎么转换?
  14. PCBA方案开发设计—咖啡秤厨房电子秤PCBA方案
  15. 【Python】爬取TapTap原神评论并生成词云分析
  16. 南京大学计算机实验教程,南京大学 计算机系统基础 课程实验 2018(PA2)
  17. c++ cv转化灰度图_OpenCV C++如何使RGB图像变为灰度图像
  18. 网络安全[脚本小子] -- SSI注入
  19. current,present,recent 都是现在的,都是形容词
  20. 用计算机画函数,用计算机画函数图像 优质课教案设计

热门文章

  1. 高级程序员证书_过了而立之年的程序员应该何去何从?
  2. 如何部署 OB 社区版
  3. thymeleaf中的条件判断用法
  4. ViewPager子类与父类滑动冲突的情况
  5. 基于JAVA+SpringMVC+Mybatis+MYSQL的仓库管理系统
  6. python科学计算与可视化教程
  7. 51nod1812树的双直径(换根树DP)
  8. 11 二叉查找树中搜索区间
  9. CentOS 安装WildFly Jboss10
  10. 2016.08.30~2017.07.20