本帖最后由 Barih 于 2025-11-28 22:41 编辑
这个APP之前版本是可以重写实现完整VIP功能的,可能是开发者加强了安全措施并在最新版本进行了服务端验证,不符合直接PASS到支付页面。
以下是我的分析:
这是一个Flutter应用,通过Blutter逆向后,PP.txt找到关键代码:
[pp+0x24638] AnonymousClosure: (0x78a86c), in [package:astarte/home/movie_page.dart] MoviePageState::_buildBody (0x77a040)
[pp+0x24640] List(7) [0, 0x4, 0x1, 0x3, "isFromMovie", 0x3, Null]
[pp+0x24648] String: "/api/v1/movies/%s/play"
[pp+0x24650] String: "operation"
[pp+0x24658] String: "play"
[pp+0x24660] AnonymousClosure: (0x783c4c), in [package:astarte/home/presenter/movie_presenter.dart] MoviePagePresenter::getM3U8Info (0x7837c8)
[pp+0x24668] AnonymousClosure: (0x783aac), in [package:astarte/home/presenter/movie_presenter.dart] MoviePagePresenter::getM3U8Info (0x7837c8)
[pp+0x24670] TypeArguments: <MovieM3u8Entity>
[pp+0x24678] String: "PermissionDenied"
[pp+0x24680] String: "PermissionDeniedToPayment"
[pp+0x24688] String: "PermissionDeniedToPromotion"
[pp+0x24690] TypeArguments: <SourceItem?>
[pp+0x24698] AnonymousClosure: (0x783290), in [package:astarte/home/movie_page.dart] MoviePageState::_showBottomSourceSheet (0x783200)
[pp+0x246a0] AnonymousClosure: (0x7834c0), in [package:astarte/home/movie_page.dart] MoviePageState::_showBottomSourceSheet (0x783200)
[pp+0x246a8] Null
[pp+0x246b0] String: "value"
[pp+0x246b8] AnonymousClosure: (0x7836e4), in [package:astarte/home/movie_page.dart] MoviePageState::_showBottomSourceSheet (0x783200)
[pp+0x246c0] String: "ConnectivityResult.mobile"
[pp+0x246c8] AnonymousClosure: (0x78b1f8), in [package:astarte/home/movie_page.dart] MoviePageState::setM3U8Info (0x783d28)
[pp+0x246d0] AnonymousClosure: (0x71b068), in [package:astarte/mine/recent_viewed_page.dart] RecentViewedPageState::_getAppBar (0x71b964)
[pp+0x246d8] AnonymousClosure: (0x78b6c0), in [package:astarte/home/movie_page.dart] MoviePageState::setM3U8Info (0x783d28)
[pp+0x246e0] AnonymousClosure: (0x78b63c), in [package:astarte/home/movie_page.dart] MoviePageState::setM3U8Info (0x783d28)
[pp+0x246e8] AnonymousClosure: (0x7184e8), in [package:astarte/mine/mine_page.dart] MinePageState::_getDownloadLine (0x718510)
[pp+0x246f0] AnonymousClosure: (0x78b018), in [package:astarte/home/movie_page.dart] MoviePageState::handleM3U8Entity (0x783e28)
[pp+0x246f8] AnonymousClosure: (0x78b018), in [package:astarte/home/movie_page.dart] MoviePageState::handleM3U8Entity (0x783e28)
[pp+0x24700] AnonymousClosure: (0x78b018), in [package:astarte/home/movie_page.dart] MoviePageState::handleM3U8Entity (0x783e28)
[pp+0x24708] AnonymousClosure: (0x78aff4), in [package:astarte/home/movie_page.dart] MoviePageState::handleM3U8Entity (0x783e28)
点击播放按钮后,请求/api/v1/movies/%s/play 获取视频播放URL,并在设置Mu38前检查用户权限
这三个字符串是关键的权限检查标识:
- PermissionDenied - 基本权限拒绝
- PermissionDeniedToPayment - 权限拒绝,引导至支付页面
- PermissionDeniedToPromotion - 权限拒绝,引导至推广页面
参考我之前的帖子:某DB去开屏广告+本地vip(ProxyPin重写)
这里我在ProxyPin接口虽然通过重写获取了VIP,但播放视频还是返回了PermissionDeniedToPayment,并直接跳转到支付页面,很有可能就是服务端验证失败
丢给AI分析:
视频播放流程逻辑
- 用户点击播放按钮 → 触发 _buildBody 中的播放逻辑
- 调用API获取播放信息 → 向 /api/v1/movies/%s/play 发送请求
- 处理API响应 → getM3U8Info 函数获取M3U8播放列表
- 权限检查 → 检查返回结果中是否包含 PermissionDenied* 字符串
- 根据权限结果处理 :
- 如果有权限: setM3U8Info → handleM3U8Entity → 开始播放
- 如果无权限:显示 PermissionDeniedToPayment → 跳转支付页面
权限检查流程:
-
第一次检查 :TBZ W0, #4, loc_783B50 调用权限检查函数,测试W0寄存器第4位
- 如果为0 → 正常播放流程
- 如果为1 → 继续VIP检查
- VIP字符串检查 :检查"PermissionDeniedToPayment"
-
第二次检查 : TBNZ W0, #4, loc_783B98 再次测试W0寄存器第4位
- 如果不为0 → 跳转到支付页面
- 如果为0 → 正常播放流程
打开IDA查找汇编
尝试修改汇编地址
-
将 TBZ W0, #4, loc_783B50 改为 B loc_783B50 - ✅ 强制 第一次检查后跳转到正常流程。
-
将 TBNZ W0, #4, loc_783B98 改为 NOP - ✅ 禁用 任何跳转到支付页面的可能性
然后令人抓狂的事情就来了:
- 无论怎么修改汇编,Patch后重新打包都会直接闪退。
- frida Hook也总是失败,各种错误,模拟器和真机都尝试过。
总结:我太菜了,对于Flutter是真没招了。
不知论坛大佬有没有解决播放过程中绕过权限检查的办法,还请不吝赐教。
|