# 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`](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 分隔字符串,**最后两段**表示风险标记,例如: ```text ...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 若改测登录 1. 选 **Log in**(非 Sign up),输入 **已开户** 手机号 2. logcat:`powershell -File scripts\logcat-maribank.ps1 -Follow` 3. 重点观察:`/v2/auth/precheck`、`/v2/app/login/otp/get`、`/v1/pin/auth`、`/dfp/v1/data/report` 的响应码 4. 若仍为 **4067012** → 倾向 **设备/环境级封锁**,换登录路径无法绕过 5. 若 precheck 通过、仅 OTP/PIN 失败 → 多为 **账户凭证** 问题,非 Root bypass 范畴 多次注册失败可能已加重设备风控,登录测试前建议 **冷却 24–48h** 或使用未试过的环境。 --- ## 3. Xposed 实现架构 ### 3.1 入口与加载顺序 `MainHook.java` 对 `ph.seabank.seabank` 加载两个 Hook 类: ```java 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`,表现为闪退。 **实现**: 1. Hook `Process.killProcess(self)`、`System.exit` → 直接拦截 2. Hook `Activity.finish` / `finishAffinity` / `finishAndRemoveTask` / `moveTaskToBack` - 在 **8 秒宽限期**内(`SOFT_CRASH_GUARD_MS`)阻止 BKE 相关 Activity 被风控栈关闭 - 通过栈追踪识别 `safemode` / `shpssdk` / `errorcodehandler` 等调用来源 - **保留用户按返回键**(`isUserBackNavigation()`) 3. 拦截 Root 相关 Toast 文案(`rooted or jailbroken`、`magisk/xposed/frida` 等) **logcat 典型输出**: ```text notiMessageHook/MariBankRoot: blocked killProcess(self) notiMessageHook/MariBankRoot: blocked finishAffinity on com.shopee.bke... ``` ### 4.2 SafeMode 检测绕过 **目标类**(方法名混淆,无法按名 Hook): - `com.shopee.bke.lib.safemode.b` - `com.shopee.bke.lib.safemode.catchs.a` - `com.shopee.bke.lib.safemode.util.c` - `com.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.SPSAssessRisk` - `com.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**: ```java // 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 日志**: ```text register request body unreadable (encrypted or one-shot) ``` 说明 register 的 body 在 Java 层已是 **加密或一次性 RequestBody**,无法直接读明文。 ### 4.6 错误码与弹窗监控 **服务端错误码**(logcat 实测): | code | 含义 | | --------- | ------------------ | | `4067004` | 安全封锁(与 4067012 同类) | | `4067012` | 账户安全临时封锁 | **客户端弹窗文案**(服务端下发,非本地 Root 弹窗): ```text 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**: ```text POST /uapi/v2/register → {"code":4067012,"msg":"For your account's security, this service has been temporarily blocked..."} ``` **分析**: 1. **不是本地 Root 弹窗**——本地 bypass 后已能到达注册页并成功打 DFP 2. DFP 成功仅说明 **上报链路通**,不代表服务端认可设备 3. riskToken 尾部改掉 **不够**——token 主体可能含加密指纹,或服务端维护设备/号码黑名单 4. 多次失败可能 **加重封锁**(建议冷却 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 始终为: ```text register request body unreadable (encrypted or one-shot) ``` **原因**: - Body 经 `NativeEncryptUtilsWrapper` → native `uvwuvwuv` 加密 - 可能是 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 净化) ```text notiMessageHook/MariBankRoot: sanitized riskToken tail 09|1 -> 00|0 notiMessageHook/MariBankRoot: HTTP .../dfp/v1/data/report ... code=0 ``` ### 6.2 失败片段(注册) ```text 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 powershell -File scripts\logcat-maribank.ps1 -Follow # 或 adb logcat -s notiMessageHook/MariBankRoot:V notiMessageHook/MariBankNative:V ``` --- ## 7. 部署与验证 ### 7.1 构建安装 ```powershell 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. 已知限制 1. **仅改 riskToken 尾部**不足以通过服务端 register 校验 2. **register body 加密**,Java Hook 无法读明文、难改字段 3. **RealInterceptorChain** 等 SO 检测点不可 Hook 4. **ShpssInstall** 不可 Hook,限制 SO 加载链上的干预手段 5. **Frida** 在当前 App 上 Java 桥不稳定 6. **多次失败** 可能导致号码/设备进入服务端黑名单 7. **登录流程未实测**;若设备已被 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`*