*班课开屏卡半天进不去导致签到没签上,好好好正好最近在推进jshookmcp的安卓逆向能力那么:
*班课 App 逆向分析:一个教育App能塞多少屎
一、基本信息
| 项目 |
值 |
| 包名 |
com.**.teach |
| 版本 |
5.4.51 |
| APK 大小 |
151 MB |
| targetSdk |
33 (Android 13) |
| 加壳 |
梆梆安全 SecNeo (com.secneo.apkwrapper) |
| Application |
com.**.teach.MTApp |
| 启动页 |
MainActivity → MainMenuActivity |
| 冷启动耗时 |
3042ms |
| 主线程最大阻塞 |
751ms(单次消息延迟) |
二、加壳分析
APK 使用梆梆安全加壳,壳代码位于 com.**.apkwrapper 包下:
壳特征
- Native 解壳库:
libDexHelper.so(ARM) / libDexHelper-x86.so
- AppComponentFactory 劫持:通过
AP.smali 拦截所有组件实例化
- 解壳时机:
H.load() 在 instantiateClassLoader 阶段触发,从 APK 中提取加密 DEX
- 加密 payload 位置:APK 内 assets 目录中(壳在
loadLib 方法中从 APK 的 lib/ 目录提取 SO)
反调试措施
// H.smali - Iii1Iii1IIIi1() 方法
// 读取 /proc/self/status 检查 TracerPid
new BufferedReader(new FileReader("/proc/self/status"))
// 匹配 "TracerPid:" 行,非零则表示被调试
// 另一个反调试
new FileInputStream("/system/bin/app_process")
// 检查 app_process 完整性
SystemClock.sleep(2000) // 延时反调试
启动日志也印证了反调试:
Not starting debugger since process cannot load the jdwp agent.
解壳耗时
从 logcat 时间线分析,梆梆解壳独占约 376ms:
T+0ms Init 进程
T+90ms ART SDK 初始化完成
T+466ms libDexHelper.so JNI_OnLoad success ← 解壳在这 376ms 内完成
三、SDK 清单:一个教育 App 集成了多少第三方
广告 SDK(5 个,开屏+信息流+激励视频)
| SDK |
用途 |
证据 |
| 倍孜 BeiZis |
开屏广告 |
BeiZis: request SplashAd adUnitId:119600 |
| 云帆 YFAds |
广告投放 |
YFAds: initWua end |
| 快手 Kwai Ad |
短视频广告 |
Manifest 中 com.kwad.sdk,6 个 Activity 壳 |
| 华为 Ads |
华为广告联盟 |
com.huawei.openalliance.ad |
| 穿山甲/字节 |
字节跳动广告 |
jad_click.json, jad_shake.json(穿山甲 Lottie 素材) |
推送 SDK(4 个,全家桶)
| SDK |
证据 |
| 极光推送 JPush |
cn.jpush.android.service,Manifest 中完整声明 |
| 华为 Push Kit |
com.huawei.hms.support.api.push.TransActivity |
| 魅族 Push |
cn.jpush.android.service.PluginMeizuPlatformsReceiver |
| vivo Push |
cn.jpush.android.service.PluginVivoMessageReceiver |
其他 SDK
| SDK |
用途 |
大小 |
| 声网 Agora |
音视频通话 |
17MB + 6.5MB×3 + 更多 |
| 百度语音 |
语音识别 |
1.2MB |
| Bugly |
崩溃上报 |
191KB |
| 友盟 Umeng |
数据统计 |
Manifest UMENG_APPKEY=55b0603be0f55a4dc0000a0f |
| 支付宝 |
支付 |
com.alipay.sdk.app |
| 腾讯 QQ/微信 |
社交分享 |
com.tencent.connect, WXEntryActivity |
| AGConnect |
华为服务 |
AGConnectInitializeProvider |
| Chromium WebView |
内置浏览器 |
HTTP 缓存 197MB (你要下片吗) |
四、原生库(SO)分析:83MB 的恐怖体积
仅 arm64-v8a 目录就塞了 83MB 的 SO 库:
| SO 文件 |
大小 |
所属 SDK |
libagora-rtc-sdk.so |
17 MB |
声网 RTC |
libagora-rtm-sdk-jni.so |
6.5 MB |
声网 RTM |
libAgoraMediaPlayer.so |
6.5 MB |
声网播放器 |
libagora-player-ffmpeg.so |
5.7 MB |
声网 FFmpeg |
libagora_fpa_sdk.so |
2.9 MB |
声网 FPA |
libagora-ffmpeg.so |
1.5 MB |
声网 FFmpeg |
libBaiduSpeechSDK.so |
1.2 MB |
百度语音 |
libDexHelper.so |
1.2 MB |
梆梆加壳 |
libDexHelper-x86.so |
1.2 MB |
梆梆加壳(x86) |
libRSSupport.so |
2.1 MB |
RenderScript |
libagora-core.so |
487 KB |
声网核心 |
libagora_fdkaac.so |
495 KB |
声网 AAC |
libagora-soundtouch.so |
179 KB |
声网音效 |
libagora_fpa_service.so |
911 KB |
声网 FPA |
libBugly_Native.so |
191 KB |
Bugly 崩溃 |
libPglbizssdk_ml.so |
1.1 MB |
未知 SDK |
libads-ac.so |
9.9 KB |
广告 SDK |
libads-c.so |
11 KB |
广告 SDK |
声网 SDK 一家就占了 ~33MB,一个教育 App 的音视频功能在启动时就要加载这些,浮木骨灰盒都打包进去了是吧。
五、启动流程分析
完整时间线(冷启动)
T+0ms 18:38:04.465 Init 进程
T+28ms 18:38:04.493 SecNeo 反调试检查,阻断 JDWP 调试器
T+90ms 18:38:04.555 ART SDK 版本检测 (API 33)
T+466ms 18:38:04.931 梆梆 libDexHelper JNI_OnLoad 完成 ← 解壳 ~376ms
T+542ms 18:38:05.007 MultiDex 安装
T+553ms 18:38:05.018 AGConnect 华为服务初始化
T+581ms 18:38:05.046 InitProvider 初始化(更多 SDK)
T+800ms 18:38:05.268 PerfMonitor 主线程延迟 548ms
T+800ms 18:38:05.268 PerfMonitor 主线程延迟 751ms ← 严重卡顿
T+800ms 18:38:05.269 PerfMonitor 主线程延迟 742ms
T+802ms 18:38:05.271 PerfMonitor 主线程延迟 741ms
...后续 SDK 继续在主线程初始化...
T+3042ms am start -W 返回 WaitTime
主线程阻塞证据
系统 PerfMonitor 捕获到连续 4 次严重的主线程消息延迟:
PerfMonitor: latency=548ms (BIND_APPLICATION, wall=533ms)
PerfMonitor: latency=751ms (EXECUTE_TRANSACTION, wall=204ms)
PerfMonitor: latency=742ms
PerfMonitor: latency=741ms
单次消息最高延迟 751ms,连续 4 次累计阻塞超过 2.7 秒。这直接导致冷启动 3 秒。
那么开屏流程是这样的:
MainActivity 启动
↓
请求开屏广告
↓
等待广告加载
↓ ↓
广告加载成功 广告加载失败/超时
↓ ↓
展示广告 N秒 等到超时才跳转
↓ ↓
跳转到 MainMenuActivity
如果你用去广告工具把广告请求 host 拦截了,BeiZis SDK 的 request SplashAd 就会一直等广告服务器响应,那么很抱歉同学你签不上到了。
六、广告 SDK 开屏行为分析
BeiZis 开屏广告请求
18:15:54.707 BeiZis: request SplashAd adUnitId:119600
18:15:54.825 BeiZis: spaceBean is null and return fail
18:15:54.825 BeiZis: startUpdateConfig
18:15:54.825 BeiZis: heartbeatTime:60000
倍孜 SDK 在启动时请求开屏广告位 119600,同时:
- 获取设备 OAID(
IdProviderImpl: getOAID)
- 通过 AdManager 管理广告标识(
AdManager: code sm Oaid:5d2a1f53331d09f6)
- 云帆广告 SDK 同步初始化(
YFAds: initWua end)
断网测试结果
使用 iptables DROP 掉 app 全部出站 TCP 流量后:
- 冷启动时间:3042ms(与正常网络一致)
- 结论:广告网络请求不是启动卡顿的主因,3 秒主要来自壳解密 + SO 加载 + 主线程 SDK 初始化
七、其他恶心发现
7.1 MIUI 反射调用被拦截
Accessing hidden method Landroid/view/Window;->setExtraFlags(II)V (blocked, reflection, denied)
Accessing hidden field Landroid/view/MiuiWindowManager$LayoutParams;->EXTRA_FLAG_NAVIGATION_BAR_DARK_MODE:I
App 试图通过反射调用 MIUI 系统隐藏 API 修改导航栏颜色,被 Android 13 的 hidden API 限制拦截。
7.2 HTTP 缓存 197MB
chromium: HTTP Cache size is: 206722859 (197 MB)
WebView 的 HTTP 缓存膨胀到 197MB,在启动时需要初始化,我问问簧片app启动缓存都没这么大你是要下片吗。
7.3 Bugly 广播接收器权限配置错误
BroadcastQueue: Permission Denial: broadcasting Intent { act=android.net.conn.CONNECTIVITY_CHANGE }
requires com.tencent.bugly.BuglyBroadcastReceiver.permission
Bugly 的网络变化广播接收器权限配置有误,每次网络变化都会触发权限拒绝警告。
7.4 极光推送连接失败
JIGUANG-JCore: [TcpRequestManager-main] [key-step]send error -tcp connect was invalid
极光推送在启动时 TCP 连接失败,每次启动都会重试,又不降级又不容灾赤白饭吗。
7.5 开屏请求摄像头服务
CameraManagerGlobal: Connecting to camera service
CameraExtStub: init android.hardware.camera2.impl.CameraExtImplXiaoMi
开屏阶段就连接摄像头服务,你是l聊app吗。
7.6 Manifest 泄露的密钥
<meta-data android:name="UMENG_APPKEY" android:value="55b**"/>
<meta-data android:name="MEIZU_APPKEY" android:value="MZ-**"/>
<meta-data android:name="MEIZU_APPID" android:value="MZ-**"/>
友盟 AppKey 和魅族推送密钥直接明文写在 Manifest 中啊对对对就喜欢露出。
八、assets 目录可疑文件
| 文件 |
说明 |
ijiami_1011.VData |
爱加密相关数据(疑似壳中壳或安全检测) |
libscorpion_bin.so |
Scorpion 二进制(调试检测) |
SCORPION.VERSION |
Scorpion 版本信息 |
aicontrol.bsg |
AI 控制相关 |
baidu_speech_grammar.bsg |
百度语音语法 |
info.y |
未知用途 |
na.czl |
未知用途 |
mimoVerCode |
版本验证码 |
ssl_red_ai/aj/ak.properties |
SSL 相关配置 |
九、总结
问题汇总
| 严重程度 |
问题 |
评价 |
| 🔴 严重 |
梆梆加壳导致冷启动增加 ~400ms |
拉完了 |
| 🔴 严重 |
主线程 SDK 初始化阻塞 750ms+ |
卡开屏 |
| 🔴 严重 |
5 个广告 SDK + 4 个推送 SDK 同时初始化 |
启动慢、耗电、耗流量 |
| 🟠 中等 |
83MB+ SO 库全量加载 |
内存占用高、启动慢 |
| 🟠 中等 |
声网 33MB SDK 常驻加载 |
想偷听 |
| 🟡 一般 |
WebView 缓存膨胀到 197MB |
在下片 |
| 🟡 一般 |
开屏连接摄像头服务 |
想l聊 |
| 🟡 一般 |
反射调用系统隐藏 API |
兼容性问题 |
| 🔵 信息 |
Manifest 明文密钥 |
喜欢露出 |
数据对比
| 指标 |
云班课 |
行业正常水平 |
| APK 大小 |
151 MB |
30-50 MB |
| 冷启动时间 |
3.0 秒 |
< 1.0 秒 |
| 广告 SDK 数量 |
5 个 |
0-1 个(教育类) |
| 推送 SDK 数量 |
4 个 |
1 个 |
| 原生库体积 |
83 MB |
10-20 MB |
| 主线程最大阻塞 |
751 ms |
< 16 ms |
| 第三方 SDK 总数 |
15+ |
5-8 个 |
十、运行时密钥提取
提取方法
梆梆安全加壳导致静态分析无法获取真实代码和密钥。Frida 被反调试(TracerPid 检测)拦截,frida-dexdump 失败。最终通过某神秘方法(后续上架jshookmcp)拿到密钥。
提取到的密钥
| # |
SDK |
密钥类型 |
值 |
来源 |
| 1 |
OPPO 推送 |
AppKey |
97ff** |
运行时内存 |
| 2 |
OPPO 推送 |
AppSecret |
8ac9** |
运行时内存 |
| 3 |
魅族推送 |
AppKey |
MZ-** |
Manifest + 内存验证 |
| 4 |
魅族推送 |
AppID |
MZ-** |
Manifest |
| 5 |
华为推送 |
AppID |
B737** |
运行时内存 |
| 6 |
高德地图 |
API Key |
d6f0** |
运行时内存 |
| 7 |
倍孜广告 |
开屏广告位 ID |
2067、20670 |
运行时内存 |
| 8 |
倍孜广告 |
SDK Host |
https://sdk.beizi.biz |
运行时内存 |
| 9 |
友盟 |
AppKey |
55b0** |
Manifest |
| 10 |
环信 IM |
加密 AppKey |
rzlD** |
SharedPreferences |
十一、赠礼
一个教育App把安全预算花在反逆向加壳上,开屏必须看广告不看不给进主界面,很好奇家里还有人吗。