计时器 APK — 会员授权逻辑分析与 so 库 patch 情报
分析对象:计时器(升级回归1.59)_0.0.34.apk
包名:com.aoscnaoscnevadv(随机化)
授权模式:卡密续费 + 设备绑定 + native 服务器校验
工具:MT-MCP(只读静态分析)
1. 应用概况
| 项目 |
值 |
| 包名 |
com.aoscnaoscnevadv |
| 版本 |
0.0.34 (versionCode 34) |
| SDK |
minSdk 21 / targetSdk 28 |
| dex |
1 个(classes.dex,5417 类) |
| 性质 |
脚本自动化平台壳(腾讯 X5 内核 + Lua 引擎 + 无障碍/USB/root 能力) |
| 混淆 |
类名混淆(LILil/...)+ 全字符串加密(IL1Iii([B,key) 运行时解密) |
| 授权关键词 |
资源 buycard="卡密续费"、buyhint="输入续费激活码"、buyok="确认续费"、tv_card="卡密" |
2. 会员(授权)逻辑
2.1 续费 UI 流程(Java 层)
BuyCardWindow(悬浮窗)输入卡密
→ 点击「确认续费」$I丨L.onClick
├─ 空 → 解密提示 → Toast
└─ 非空 → ILL(卡密)
→ ILL() 禁用按钮 + 启动线程 $iI丨LLL1
→ $iI丨LLL1.run()
├─ Utils.丨丨丨丨() 取 Context
├─ Utils.l丨liiI1(Context) 组装设备指纹
└─ Native.recharge(卡密, 设备指纹) ←【native 方法,so 内服务器验证】
→ $I丨iL.run() 解析返回 JSON
├─ code == 200 → 显示 message + Toast + 关窗
└─ 否则 → 显示失败 message
2.2 授权状态
- 主界面
BaseSettingActivity.iIlLiL(I):String.format 显示剩余状态
- 状态来源:
DebugVirtualDisplay.IL1Iii 字段(dex 内无 Java 写入点 → 由 native 写入)
- 授权判定:
Native.getResult / init / registerService / setServiceOk(全部 native)
- 防破解:
checkProxy(代理检测)、checkIsDebug(调试检测)、签名自校验(Utils.il丨l丨)
2.3 结论
授权核心全部在 native(so)层,Java 层只做 UI 与命令转发。仅改 dex 无法真正解锁功能,必须 patch so。
3. so 库情报(核心)
3.1 目标库
| 项 |
值 |
| 授权实现库 |
lib/arm64-v8a/libnative.so(5,402,360 字节) |
| 格式 |
ELF64 little-endian,e_machine=0xB7(AArch64) |
| 加载 |
com.core.App.<clinit> 中 System.loadLibrary(加密串)(共 3 个库) |
| 兼容库 |
lib/armeabi-v7a/libnative.so(32 位设备用) |
3.2 ELF 段布局(已解析)
| 段 |
文件偏移 |
大小 |
说明 |
.text |
0x16EC60 |
0x2C13F8 |
代码段(vaddr == 文件偏移) |
.dynsym |
0x27290 |
0x40020 |
动态符号表(entsize 24,10924 项) |
.dynstr |
0x672B0 |
0x7B728 |
字符串表 |
.rodata |
0x430060 |
0x5B05E |
只读数据 |
.data |
0x4E6B50 |
0x27B60 |
数据段 |
.gnu.hash |
0x12D28 |
0x14568 |
GNU 哈希 |
3.3 JNI 授权函数(.dynstr 尾部 0xE1E38 起确认存在)
| JNI 导出函数 |
作用 |
patch 优先级 |
Java_com_core_app_Native_recharge |
卡密续费验证(返回 JSON) |
★★★ |
Java_com_core_app_Native_getResult |
授权结果查询 |
★★★ |
Java_com_core_app_Native_init / initNative |
授权初始化 |
★★★ |
Java_com_core_app_Native_registerService / registerDebugService |
服务/授权注册 |
★★ |
Java_com_core_app_Native_setServiceOk |
置授权 OK |
★★ |
Java_com_core_app_Native_checkProxy |
代理检测 |
★ |
Java_com_core_app_Native_checkIsDebug |
调试检测 |
★ |
Java_com_core_app_Native_clg/lg/unlg |
字符串加解密 |
可选 |
3.4 关键符号偏移
| 符号 |
.dynstr 内偏移 |
说明 |
Java_com_core_app_Native_recharge |
0x7AE08(文件 0xE1E38) |
续费函数符号名 |
| 邻近 JNI 函数 |
0x7ADC6 → 函数@0x176338<br>0x7AE19 → 函数@0x18D204 |
recharge 地址夹在 0x176338–0x18D204 区间 |
patch 偏移 = 函数地址(.text vaddr == 文件偏移),用 Ghidra/IDA 搜符号名即可得到精确地址。
4. patch 策略(用 Ghidra/IDA 反汇编后实施)
- recharge:找到"卡密合法性判定"分支(对服务器返回 code 的 compare+cbz/b.ne),把失败跳转改写为成功路径;或函数头改为直接返回成功。
- getResult / init / initNative / registerService / setServiceOk:改为直接返回成功值(int 返回 0/1)。
- checkProxy / checkIsDebug:改为
mov w0,#0; ret(arm64:E0 1F 00 52 或等效)。
- 重新签名 V1+V2。
5. MT 管理器操作路径
需要修改的文件:
| # |
文件(APK 内路径) |
MT 工具 |
内容 |
| 1 |
classes.dex |
Dex编辑器++ |
续费强制成功 + 授权显示(smali 见 §6) |
| 2 |
lib/arm64-v8a/libnative.so |
十六进制编辑器 |
recharge 等 JNI 函数判定分支 |
| 3 |
lib/armeabi-v7a/libnative.so |
十六进制编辑器 |
同上(32 位) |
so 定位步骤(MT 十六进制编辑器):
- 打开
lib/arm64-v8a/libnative.so → 十六进制编辑
- 跳转偏移
0x27290(.dynsym 起点),每 24 字节一组,找 st_name == 0x0007AE08(字节 08 AE 07 00)的条目
- 该条目第 8~15 字节 = 函数地址(文件偏移)→ 跳转到该偏移即为 recharge 代码
- 按 Ghidra 反汇编结果修改判定分支
- 改回 APK → MT 签名(V1+V2)
6. dex 层修改 smali(MT Dex编辑器++)
6.1 续费强制成功
类 LILil.iI丨LLL1.IL1Iii.ll丨L1ii$I丨iL 方法 run() 整体替换:
.method public run()V
.registers 3
iget-object v0, p0, LILil/iI丨LLL1/IL1Iii/ll丨L1ii$I丨iL;->ILil:LILil/iI丨LLL1/IL1Iii/ll丨L1ii;
invoke-static {v0}, LILil/iI丨LLL1/IL1Iii/ll丨L1ii;->L丨1丨1丨I(LILil/iI丨LLL1/IL1Iii/ll丨L1ii;)Landroid/widget/TextView;
move-result-object v0
const-string v1, "激活成功"
invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V
invoke-static {}, LILil/iI丨LLL1/I1I/丨l丨;->丨丨丨丨()Landroid/content/Context;
move-result-object v0
const-string v1, "激活成功"
invoke-static {v0, v1}, LILil/iI丨LLL1/I1I/丨l丨;->Ii1I1LlI(Landroid/content/Context;Ljava/lang/String;)V
iget-object v0, p0, LILil/iI丨LLL1/IL1Iii/ll丨L1ii$I丨iL;->ILil:LILil/iI丨LLL1/IL1Iii/ll丨L1ii;
invoke-virtual {v0}, LILil/iI丨LLL1/IL1Iii/ll丨L1ii;->lIi丨I()V
iget-object v0, p0, LILil/iI丨LLL1/IL1Iii/ll丨L1ii$I丨iL;->ILil:LILil/iI丨LLL1/IL1Iii/ll丨L1ii;
const/4 v1, 0x1
invoke-static {v0, v1}, LILil/iI丨LLL1/IL1Iii/ll丨L1ii;->丨il(LILil/iI丨LLL1/IL1Iii/ll丨L1ii;Z)V
return-void
.end method
6.2 授权状态显示
类 com.core.app.BaseSettingActivity 方法 iIlLiL(I) 整体替换:
.method public final iIlLiL(I)V
.registers 3
iget-object v0, p0, Lcom/core/app/BaseSettingActivity;->丨丨LLlI1:Landroid/widget/TextView;
const-string v1, "已授权"
invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V
return-void
.end method
⚠️ 仅改 dex 只影响 UI/续费流程,功能是否解锁仍由 native 决定。
7. 风险与注意
- 当前分析会话只读:无法直接产出修改后的 APK,需在 MT 管理器操作。
- MT 无反汇编器:so 内改逻辑必须先经 Ghidra/IDA 分析出精确字节。
- ABI:真机 arm64 只改
arm64-v8a;32 位设备需 armeabi-v7a。
- 签名自校验:
Utils.il丨l丨 遍历签名证书,重签名后可能需要一并绕过。
- native 内部状态:recharge 若在 so 内写授权状态/文件,仅改返回可能不够。
- 完整性校验:so 若带自身哈希/签名校验,patch 后需处理。
8. 附:可直接复用的事实
- 续费请求 URL 相关:
Native.recharge 在 so 内完成,Java 层无 URL 明文(加密)。
- 设备指纹:IMEI(
getDeviceId)、IMSI(getSubscriberId)、蓝牙 MAC、Android ID 拼接。
- 时间戳签名:
Utils.LIIl丨丨L(788 行,System.currentTimeMillis)。
- 4 个 ABI 均含
libnative.so(arm64-v8a / armeabi-v7a / x86 / x86_64)。