23 KiB
MariBank 实现说明与问题记录
日期:2026-07-03
App:MariBank / SeaBank PH ·ph.seabank.seabank· v3.22.0 (32200)
设备:Pixel 6 · Magisk + Zygisk + LSPosed
源码:xposed-module/.../hook/MariBank*.java
本文档专门说明当日对 MariBank 注册流程风控绕过 的 Xposed 实现细节,以及开发/测试中遇到的全部主要问题。通用工作汇总见 WORKLOG_2026-07-03.md。
1. 目标与结果概览
1.1 目标
在已 Root 设备上完成:
打开 App → Sign up → 输入菲律宾手机号 → Next
→ POST https://api.seabank.ph/uapi/v2/register
→ 进入 OTP / 下一步
1.2 当日最终结果
| 阶段 | 结果 |
|---|---|
| 本地 Root 检测 / 自杀 / 弹窗 | 大部分绕过,可稳定进入注册页 |
| DFP 设备指纹上报 | 成功(code=0) |
| riskToken 尾部字段净化 | Hook 生效(|09|1 → |00|0) |
| 注册接口 | 失败,服务端返回 4067012 / 4067004 |
| 注册请求明文 | 未抓到(body native 加密) |
结论:本地层 bypass 已推进到能正常发起注册请求,但 服务端风控仍拒绝;当前实现不足以完成注册。
2. App 风控架构(逆向结论)
MariBank 基于 Shopee BKE 技术栈,风控分 本地 SDK 层 与 服务端校验层。
┌─────────────────────────────────────────────────────────┐
│ MariBank App │
├─────────────────────────────────────────────────────────┤
│ SafeMode SDK │ Root/篡改检测 → Toast + 自杀 │
│ SHPSSDK (bank) │ riskToken / DFP / requestDefense │
│ libshpssdk_bank.so │ native:/proc/maps、hook 库扫描 │
│ libsdkutils.so │ register body native 加密 │
├─────────────────────────────────────────────────────────┤
│ OkHttp / Retrofit │ 出站 HTTP │
└─────────────────────────────────────────────────────────┘
│ │
▼ ▼
POST /dfp/v1/data/report POST /uapi/v2/register
(设备指纹 + riskToken) (加密 body + 手机号)
│ │
└────────── api.seabank.ph ────┘
│
4067012 服务端拒绝
2.1 关键类与 SO(v3.22.0)
| 组件 | 真实符号 | 说明 |
|---|---|---|
| Application | com.shopee.bke.digitalbank.BkeApplication |
attachBaseContext 后 ClassLoader 才完整 |
| SafeMode | com.shopee.bke.lib.safemode.b 等 |
方法名混淆,多返回 boolean/int |
| SHPSSDK | com.shopee.shpssdkbank.SHPSSDK |
getRiskToken / getRiskSync / requestDefense |
| riskToken 链 | vvuuuuvvv.wwvuwuwvu(Context) |
classes11.dex,真实 token 生成入口 |
| SoUtils | com.shopee.bke.lib.jni.utils.f |
加载 libsdkutils.so,log tag SoUtils |
| 加密包装 | com.shopee.bke.lib.jni.utils.d |
Java 层加密入口 |
| Native 加密 | com.shopee.bke.lib.jni.utils.uvwuvwuv |
JNI,libsdkutils.so / libbkutils.so |
| SHPS native | wvvvuwwu.vvuwuuvuu → wwvwvwuvv |
libshpssdk_bank.so |
| 字符串解密 | uvuwwuvwv.uvwwuuvvw.uvuwwwuwu |
解密 x-sap-fixme 等 |
2.2 注册相关 API
| 接口 | 作用 | 当日观测 |
|---|---|---|
POST /dfp/v1/data/report |
设备指纹 / DFP 上报 | 成功 code=0 |
POST /uapi/v2/register |
手机号注册 | 失败 4067012 |
2.3 riskToken 格式(尾部字段)
token 为 pipe 分隔字符串,最后两段表示风险标记,例如:
...Base64段|Base64段|xx|09|1
↑ ↑
风险码 Hook/Root 等标志
当日 Hook 将尾部 09|1 改为 00|0(认为表示 Root + Hook 命中)。logcat 可见净化日志,但 register 仍被拒,说明服务端还校验 token 其他段或独立 DFP 载荷。
2.4 登录 vs 注册(当日未测登录,以下为逆向 + 推断)
当日实测路径为 Sign up → 注册;Log in 登录尚未在设备上验证。以下基于 APK 字符串扫描与 Hook 架构整理。
2.4.1 现有 Hook 是否覆盖登录
是。 当前 Xposed 按 整包 ph.seabank.seabank 加载,不区分注册/登录:
| 能力 | 登录是否同样生效 |
|---|---|
| SafeMode / Root 自杀、弹窗拦截 | ✅ |
| SHPSSDK / riskToken、deviceToken 出站净化 | ✅ 所有 OkHttp 请求 |
DFP POST /dfp/v1/data/report |
✅ App 启动即上报 |
/proc 过滤、boot 属性伪装 |
✅ |
无需为登录单独写 Hook;能稳定进入 App,登录页同样受益。
2.4.2 API 路径不同
登录 不走 /uapi/v2/register,而是独立 auth 链路(v3.22.0 base APK DEX 扫描):
| 场景 | 典型路径 |
|---|---|
| 注册(已实测) | POST /uapi/v2/register |
| 登录预检 | /v2/auth/precheck、/v1/pin/auth/precheck |
| OTP 登录 | /v2/app/login/otp/get、/v2/app/login/otp/verify |
| PIN 登录 | /v1/pin/auth |
| 受限环境 | /v2/restricted/auth、/v2/restricted/auth/precheck |
| 通用 OTP | /v1/otp/send、/v1/otp/verify |
| 设备指纹(共用) | POST /dfp/v1/data/report |
ViewModel 层面:RegisterViewModel 与 LoginViewModel / PinCodeViewModel / OneTimePasswordViewModel 分离;PhoneNumViewModel 可能与注册共用(输入手机号 UI 类似)。
错误展示由 GlobalAuthErrorImpl 等 通用鉴权错误处理 负责(matchScene 多场景),4067 封锁弹窗 不限于注册接口。
2.4.3 登录可能的结果(推断)
| 情况 | 登录表现(推断) |
|---|---|
| 设备指纹已被服务端拉黑 | 可能在 /v2/auth/precheck 或 /v2/app/login/otp/get 即返回 4067012,与注册类似 |
| 仅「新开户」风控更严 | 若设备未全局封禁,已有账户 + 正确 PIN/OTP 的登录 有机会 通过 precheck |
| 本地 bypass 足够、服务端仍拒 | DFP code=0 但 auth 接口 4067 — 与注册相同,改 riskToken 尾部 不够 |
2.4.4 注册 vs 登录对比
| 维度 | 注册 (Sign up) | 登录 (Log in) |
|---|---|---|
| 前提 | 新菲律宾手机号 | 已有 MariBank 账户 + PIN 或 OTP |
| 关键 API | /uapi/v2/register |
/v2/auth/precheck、/login/otp/*、/pin/auth |
| 风控强度 | 新户反欺诈通常 更严 | 老户相对松,设备黑名单仍生效 |
| 当日实测 | ❌ 4067012 | ❓ 未测 |
| Hook 专用日志 | 代码中对 /register 有较详细 body 日志 |
其它 URL 仅有通用 HTTP / 4067 日志 |
2.4.5 若改测登录
- 选 Log in(非 Sign up),输入 已开户 手机号
- logcat:
powershell -File scripts\logcat-maribank.ps1 -Follow - 重点观察:
/v2/auth/precheck、/v2/app/login/otp/get、/v1/pin/auth、/dfp/v1/data/report的响应码 - 若仍为 4067012 → 倾向 设备/环境级封锁,换登录路径无法绕过
- 若 precheck 通过、仅 OTP/PIN 失败 → 多为 账户凭证 问题,非 Root bypass 范畴
多次注册失败可能已加重设备风控,登录测试前建议 冷却 24–48h 或使用未试过的环境。
3. Xposed 实现架构
3.1 入口与加载顺序
MainHook.java 对 ph.seabank.seabank 加载两个 Hook 类:
MariBankShpsNativeHook.install(lpparam); // 第一阶段:early
MariBankRootBypassHook.install(lpparam); // 第一阶段 + 调度 deferred
两阶段加载是当日最关键的工程决策之一:
| 阶段 | 时机 | 安装内容 |
|---|---|---|
| Early | loadPackage 立即 |
/proc 读过滤、系统属性伪装、Process.killProcess 拦截 |
| Deferred | BkeApplication.attachBaseContext 之后 |
SafeMode、SHPSSDK、OkHttp 出站净化、riskToken Hook |
原因:过早 Hook SHPSSDK / SoUtils.loadSoLibrary 会导致 **libsdkutils.so 加载死循环 → 白屏**(见 §5.2)。
3.2 文件职责
| 文件 | 职责 |
|---|---|
MariBankShpsNativeHook.java |
Native 层检测绕过:/proc/self/maps 过滤、boot 属性伪装、Build 字段、requestDefense |
MariBankRootBypassHook.java |
Java 层:SafeMode、SHPSSDK risk、OkHttp 出站净化、弹窗/自杀拦截、4067 日志 |
MariBankRiskTokenUtil.java |
riskToken / deviceToken 尾部净化工具类 |
4. 实现细节(按模块)
4.1 反自杀与反「假闪退」(MariBankRootBypassHook)
问题背景:SafeMode 检测到 Root 后会 Toast + Process.killProcess + Activity.finishAffinity,表现为闪退。
实现:
- Hook
Process.killProcess(self)、System.exit→ 直接拦截 - Hook
Activity.finish/finishAffinity/finishAndRemoveTask/moveTaskToBack
- 在 8 秒宽限期内(
SOFT_CRASH_GUARD_MS)阻止 BKE 相关 Activity 被风控栈关闭 - 通过栈追踪识别
safemode/shpssdk/errorcodehandler等调用来源 - 保留用户按返回键(
isUserBackNavigation())
- 拦截 Root 相关 Toast 文案(
rooted or jailbroken、magisk/xposed/frida等)
logcat 典型输出:
notiMessageHook/MariBankRoot: blocked killProcess(self)
notiMessageHook/MariBankRoot: blocked finishAffinity on com.shopee.bke...
4.2 SafeMode 检测绕过
目标类(方法名混淆,无法按名 Hook):
com.shopee.bke.lib.safemode.bcom.shopee.bke.lib.safemode.catchs.acom.shopee.bke.lib.safemode.util.ccom.shopee.bke.biz.base.risk.a
策略:Hook 所有 非 static、参数 ≤2、返回 boolean/int 的实例方法,统一返回 false / 0。
弹窗:Hook SafeModeRecoverActivity 相关 Dialog / AlertDialog.show,阻止 Root 提示框。
4.3 SHPSSDK 风控(Java 层)
boolean 检测类:
com.shopee.shpssdkbank.SPSAssessRiskcom.shopee.shpssdk.SPSAssessRisk
Token 相关 Hook(hookShpsToken):
| 方法 | 处理 |
|---|---|
getRiskSync / getRiskAsync |
返回 空 risk 列表,避免 RISK_ROOT=1、RISK_HOOK=4 写入 |
getRiskToken / getRiskTokenAsync |
返回值经 MariBankRiskTokenUtil.sanitizeRiskToken |
vvuuuuvvv.wwvuwuwvu(Context) |
真实 token 生成链,最早出口净化 |
getSecData 等 |
字符串返回值净化 |
RISK 常量(逆向):RISK_ROOT=1,RISK_HOOK=4 等会编码进 token 尾部。
4.4 Native 层检测绕过(MariBankShpsNativeHook)
**/proc 过滤**:
- Hook
FileInputStream/BufferedReader/RandomAccessFile - 对
/proc/self/maps、/proc/version、/proc/self/status等读结果 过滤敏感行 - 隐藏 marker:
xposed、lsposed、magisk、frida、zygisk、miraclegarden等
Boot / 环境伪装:
SystemProperties.get伪装ro.boot.verifiedbootstate、ro.boot.vbmeta.device_state等Build字段、/proc/self/attr/current→ 伪造 SELinux 上下文
SHPS native 桥:
requestDefense→ 解密/处理x-sap-fixme请求头hookShpsNativeCore→ native 桥接类 boolean 返回 false
刻意不 Hook:
// MariBankShpsNativeHook.hookShpssInstall()
// 勿 Hook ShpssInstall / vuvuwwwuw:会干扰 SoUtils.loadSoLibrary,导致 libsdkutils.so 死循环白屏。
4.5 网络出站净化(OkHttp 链)
因 不能 Hook RealInterceptorChain.proceed(SO 硬编码检测),改为在更外层拦截:
| Hook 点 | 作用 |
|---|---|
OkHttpClient.newCall |
替换 Request 为净化版 |
RealCall.execute / enqueue |
同上 |
Request.Builder.build / post(body) |
body 写入时净化 |
RequestBody.writeTo |
写出字节时净化 |
Gson.toJson / fromJson |
JSON 中 riskToken 字段 |
JSONObject.put("riskToken", ...) |
直接改字段 |
Response.request().url() |
记录 URL,关联 4067 响应 |
净化逻辑(MariBankRiskTokenUtil):
- 正则匹配 pipe-token 尾部
\d+\|\d+→ 改为00|0 - JSON 字段
"riskToken"/"deviceToken"同步处理
register body 日志:
register request body unreadable (encrypted or one-shot)
说明 register 的 body 在 Java 层已是 加密或一次性 RequestBody,无法直接读明文。
4.6 错误码与弹窗监控
服务端错误码(logcat 实测):
| code | 含义 |
|---|---|
4067004 |
安全封锁(与 4067012 同类) |
4067012 |
账户安全临时封锁 |
客户端弹窗文案(服务端下发,非本地 Root 弹窗):
For your account's security, this service has been temporarily blocked...
(+632) 8424 8050
Hook 对含 temporarily blocked / 8424 8050 的 Toast 会 记录栈(logBriefStack),便于区分本地 vs 服务端弹窗。
5. 遇到的问题(完整清单)
5.1 服务端注册拒绝 — 4067012(核心未解问题)
现象:
- 能进入 Sign up 页
- 请求发出后弹服务端封锁弹窗
- 多个号码(
9178854266、9133477799、9171243667等)均失败
logcat:
POST /uapi/v2/register
→ {"code":4067012,"msg":"For your account's security, this service has been temporarily blocked..."}
分析:
- 不是本地 Root 弹窗——本地 bypass 后已能到达注册页并成功打 DFP
- DFP 成功仅说明 上报链路通,不代表服务端认可设备
- riskToken 尾部改掉 不够——token 主体可能含加密指纹,或服务端维护设备/号码黑名单
- 多次失败可能 加重封锁(建议冷却 24–48h)
待验证:
- 未 Root 干净机 + 菲律宾 IP + 新号码 → 区分设备封禁 vs 号码封禁
- Hook native 加密前明文,看清 register body 到底带了什么
5.2 过早 Hook 导致白屏(libsdkutils 死循环)
现象:App 启动白屏,logcat 反复出现 SoUtils / loadSoLibrary("sdkutils")。
原因:
loadPackage时 split APK 的 ClassLoader 尚未绑定- 过早 Hook
ShpssInstall、vuvuwwwuw或完整 SHPSSDK 链 → 干扰 SO 加载顺序
解决:
- Early 阶段 只装
/proc过滤 + 反自杀 - 等
BkeApplication.attachBaseContext回调后再installDeferredHooks - 永不 Hook
ShpssInstall/vuvuwwwuw
5.3 不能 Hook RealInterceptorChain.proceed
现象:Hook 后触发 native 硬编码检测,行为异常或加重风控。
逆向依据:libshpssdk_bank.so 字符串含 RealInterceptorChain.proceed。
解决:改 Hook OkHttpClient.newCall、RealCall.execute、RequestBody.writeTo 等外层点。
5.4 register 请求体无法读取
现象:logcat 始终为:
register request body unreadable (encrypted or one-shot)
原因:
- Body 经
NativeEncryptUtilsWrapper→ nativeuvwuvwuv加密 - 可能是 one-shot
RequestBody,写入后不可重读
影响:无法在 Java 层确认手机号 JSON 明文内容,也无法在 Xposed 层直接改 register 字段(除 riskToken 出站净化外)。
后续方向:Frida/native Hook libsdkutils.so 或 utils.d 包装方法。
5.5 wvvvuwwu.vuwuuwvw native 未找到
现象:logcat 出现 native 方法实现找不到的警告。
可能原因:
- LSPosed 与 Frida/Clash 等环境叠加干扰
- 混淆类在不同 split / 版本下位移
- Hook 时机与 ClassLoader 边界问题
影响:部分 SHPS native 桥接 Hook 可能未完全生效;但 DFP 已成功,说明主链路部分工作。
5.6 Frida 动态分析失败
环境:Frida 17.15.3 + /data/local/tmp/frida-server
| 问题 | 详情 |
|---|---|
| attach 找不到 Java | 60s 内 Java.available == false |
| 进程名 | 需用 MariBank 而非 ph.seabank.seabank |
| spawn | native dlopen 可 hook,Java 层 Java is not defined |
| CLI 退出 | spawn 无保活脚本会立刻 detach |
| 与 LSPosed 冲突 | 建议测 Frida 时 关闭 LSPosed 对 MariBank 作用域 |
结果:未抓到 /register 明文 body;当日分析 主要依赖 LSPosed logcat。
脚本位置:reverse/frida/trace_maribank_register.js、run_frida_trace.py
5.7 本地 Root 检测 vs 服务端封锁(易混淆)
| 类型 | 文案特征 | 来源 | 当日状态 |
|---|---|---|---|
| 本地 Root | rooted or jailbroken、magisk/xposed/frida |
SafeMode / SHPSSDK | 已拦截 Toast,可继续使用 |
| 服务端封锁 | temporarily blocked、8424 8050 |
API 4067012 响应 | 未绕过 |
6. 实测 logcat 参考
6.1 成功片段(DFP + token 净化)
notiMessageHook/MariBankRoot: sanitized riskToken tail 09|1 -> 00|0
notiMessageHook/MariBankRoot: HTTP .../dfp/v1/data/report ... code=0
6.2 失败片段(注册)
notiMessageHook/MariBankRoot: register request body unreadable (encrypted or one-shot)
notiMessageHook/MariBankRoot: HTTP .../uapi/v2/register ... code=4067012
notiMessageHook/MariBankRoot: security block Toast: For your account's security...
6.3 推荐过滤命令
powershell -File scripts\logcat-maribank.ps1 -Follow
# 或
adb logcat -s notiMessageHook/MariBankRoot:V notiMessageHook/MariBankNative:V
7. 部署与验证
7.1 构建安装
powershell -ExecutionPolicy Bypass -File scripts\build-debug.ps1
powershell -ExecutionPolicy Bypass -File scripts\install-debug.ps1
7.2 LSPosed 作用域
必须勾选:
ph.seabank.seabank(MariBank)com.miraclegarden.smsmessage(主 App)- Xposed 模块自身
修改 Hook 后:强制停止 MariBank 再冷启动。
7.3 测试路径
注册(当日已测)
Sign up → 输入 10 位菲律宾号码(如 9XXXXXXXXX)→ Next
观察 logcat 中 /dfp 与 /register 响应
登录(当日未测,供对照)
Log in → 输入已开户手机号 → 继续
观察 /v2/auth/precheck、/login/otp/* 或 /pin/auth 响应
详见 §2.4
8. 已知限制
- 仅改 riskToken 尾部不足以通过服务端 register 校验
- register body 加密,Java Hook 无法读明文、难改字段
- RealInterceptorChain 等 SO 检测点不可 Hook
- ShpssInstall 不可 Hook,限制 SO 加载链上的干预手段
- Frida 在当前 App 上 Java 桥不稳定
- 多次失败 可能导致号码/设备进入服务端黑名单
- 登录流程未实测;若设备已被 4067012 封锁,登录很可能同样失败(见 §2.4)
9. 后续工作建议
| 优先级 | 方向 | 说明 |
|---|---|---|
| P0 | 对照实验 | 干净机 + PH IP + 新号,确认封禁维度;有老户时可对比登录 precheck |
| P1 | Native 明文 Hook | utils.d / libsdkutils.so 加密前抓包 |
| P2 | riskToken 全链 | 不仅尾部,需理解 pipe 各段含义及 DFP 载荷 |
| P3 | Frida 环境 | 无 LSPosed 冲突、或 Zygisk 隐藏 Frida 后再 attach |
| P4 | 停试冷却 | 已失败号码/设备 24–48h 内勿反复请求 |
| — | 官方渠道 | (+632) 8424 8050 |
10. 相关文件索引
| 路径 | 说明 |
|---|---|
xposed-module/.../MariBankRootBypassHook.java |
Java 层主 Hook(~1700 行) |
xposed-module/.../MariBankShpsNativeHook.java |
Native/proc 层 Hook |
xposed-module/.../MariBankRiskTokenUtil.java |
Token 净化 |
xposed-module/.../MainHook.java |
包名路由 |
reverse/frida/jni_targets.md |
JNI / Frida 目标 |
reverse/frida/trace_maribank_register.js |
Frida trace 脚本 |
scripts/logcat-maribank.ps1 |
logcat 过滤 |
scripts/start-mari-trace.ps1 |
Frida + logcat 联启 |
docs/WORKLOG_2026-07-03.md |
当日全项目工作汇总 |
文档版本:2026-07-03 · 对应 commit 分支 ysc