文章目录
1、文章仅供学习参考,严禁非法使用!如非法使用,而导致的一切后果,由使用者自负!
2、禁止将本文技术或者本文所关联的项目源码用于任何除学习外的目的。
3、文中案例应用均来自网络,若有侵权烦请告知,第一时间删除。
前言:五一假期在家无聊,看看有没有合适的APP学习一下。还真找到一个合适的付费视频app,【加密算法简单,未加壳】,利用两天时间将所有付费内容全部搞定。以下是详细思考分析过程。
一、目标应用
Y29tLnZpZGVvLmxpdmU=
二、环境
- PyCharm
- Android Studio
- Frida
- Reqable
- Jadx
- uiautomatorviewer
- 手机Android13
三、步骤
1、查壳
刚拿到安装包时,直接丢jadx中,发现没有加壳。可以直接开搞了。开始的时候有点懵,无从下手。
那就先看下要处理的界面吧。
2、定位付费界面布局
关键布局id:tv_open_vip_btn发现使用的ViewBinding进而找到对应的Activity

3、找到可疑方法

心想:这么简单的嘛,直接hook这个方法,不执行不就完了嘛。
hook代码:
let VideoDetailActivity = Java.use("[包名].VideoDetailActivity");
VideoDetailActivity["addOpenVipView"].implementation = function (a) {
console.log(`VideoDetailActivity.addOpenVipView is called: a=${a}`);
this["addOpenVipView"](a);
};
是我想多了,还是不可用,并没有什么作用。
4、那就看下请求信息吧,看下有没有思路

发现好多请求,都是加密的。通过后缀可以看出是AES的图片加密。我们的目标是视频,图片没啥用,都是封面啥的。
5、其他请求(列表,视频信息,获取播放url)

- getList:获取列表信息
- getInfo:获取某视频信息
- geturl:获取播放视频地址
- getpreurl:获取预看地址
1、看了下所有请求参数都有加密,响应数据明文。
2、着手搞参数加密就可以了。
- endata:请求参数
- ents:时间戳
6、请求参数加密算法

还算顺利,搜索关键字就可以查到。
测试:
def encrypt(data: str) -> str:
print(data)
key = "[key]"
iv = "[iv]"
key_bytes = key.encode('UTF-8')
iv_bytes = iv.encode('UTF-8')
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
plaintext_bytes = data.encode('UTF-8')
plaintext_bytes_padded = pad(plaintext_bytes, AES.block_size, 'pkcs7')
ciphertext_bytes = cipher.encrypt(plaintext_bytes_padded)
ciphertext_base64_bytes = base64.b64encode(ciphertext_bytes)
ciphertext = ciphertext_base64_bytes.decode('UTF-8')
return f"{str(ciphertext)}\n"
自测可用,可以上手请求了。
实际效果:
{"code":607,"msg":"非法请求","data":{},"traceId":""}
o(╥﹏╥)o o(╥﹏╥)o o(╥﹏╥)o
- 检查参数,反复测试没有问题。
- 请求头信息没有特别数据。
- 模拟请求,快速再次重试,过10s左右会请求失败。
7、图片信息解密
数据请求暂时告一段落,先看下图片处理吧。

关键代码反编译失败了,看smali吧。
拿到关键key,加解密方式。OK了~~~
8、数据请求关键点
请求参数是没有问题的,header也没有其他签名数据。为什么会请求失败了呢,关键还是时间戳的问题。
继续扣代码…

找到关键位置,该时间戳并不是用的现在时间。真想大白o(╥﹏╥)o…
测试:可以正常返回数据。
9、以上都是废话
是我太高估这个APP了,发现logcat里面有明文输出。oh,my zsh…
10、直接找关键hook点
- 短视频部分:

判断是登录用户,直接开始播放。
let AppExtendKt = Java.use("[包名].AppExtendKt");
AppExtendKt["isUserLogin"].implementation = function () {
console.log(`AppExtendKt.isUserLogin is called`);
let result = this["isUserLogin"]();
console.log(`AppExtendKt.isUserLogin result=${result}`);
return true;
};
- 成功之后,视频仅播放部分。
- 通过参数测试,可调整参数来返回完整数据。
- 在设置播放链接的部分调整播放时间。
let BaseVideoView = Java.use("[包名].BaseVideoView");
BaseVideoView["setUrl"].overload('java.lang.String').implementation = function (str) {
str = str.replace(/start=\d+&end=\d+&/g, '');
console.log(`BaseVideoView.setUrl is called: str=${str}`);
this["setUrl"](str);
};
(。◕ˇ∀ˇ◕)搞定,可正常播放。
- 正常视频部分:

- 通过获取用户信息,找到关键代码进行用户信息调整,发现APP so层会崩溃[pass]。
- 通过关键部分进行主动调用获取数据。
{"code":2002,"msg":"无权限获取","data":{},"traceId":""}
很遗憾,无权限。
let VideoDetailActivity$createObserver$4 = Java.use("[包名].VideoDetailActivity$createObserver$4");
VideoDetailActivity$createObserver$4["invoke"].overload('[包名].GatherData').implementation = function (gatherData2) {
console.log(`VideoDetailActivity$createObserver$4.invoke2 is called: gatherData2=${gatherData2}`);
// 内部类使用外部类对象引用
let videoDetailActivity = this.this$0.value
console.log(videoDetailActivity + " ,videoId:" + videoDetailActivity.videoId.value);
videoDetailActivity.getVideoPreUrl(videoDetailActivity.videoId.value);
this["invoke"](gatherData2);
};
- 使用短视频思路,请求预加载数据,然后对数据链接进行修改,请求完整数据。
(。◕ˇ∀ˇ◕)搞定,可正常播放。
四、总结
- 加密解密有时候没啥用。o(╥﹏╥)o不过可以用来练手。
- 找到需要处理的点,一招制敌才是关键。
- 对于低级一点的APP就不要太高估了。
五、相关源码
1、文章仅供学习参考,严禁非法使用,如非法使用,而导致的一切后果,由使用者自负。
2、禁止将本文技术或者本文所关联的项目源码用于任何除学习外的目的。
3、文中案例应用均来自网络,若有侵权烦请告知,第一时间删除。