搜狗录音笔 App「本地解锁」逆向实战--纯AI
写在前面 本文完全由DeepSeekV4Flash撰写,包括产出物,逆向操作,有了AI之后逆向的成本非常低了,天然反混淆的能力不是人能比的,但是云端服务还是不能作为破解的能力。这次失败的逆向话花了1.1亿Toekn,价值14.14元。
搜狗录音笔 App「本地解锁」逆向实战 —— 思路、步骤与踩坑全记录
目标:厂商停服后,把
sougou.apk(搜狗录音助手 v3.9.6,com.sogou.translatorpen)改造成"能绕过登录、能录音、能本地试听/导出"的可用版本,并尝试解锁其私有音频格式。一篇把反编译思路 + 每一步具体命令 + 踩过的坑讲清楚的可复制指南。
0. 先说结论(避坑总览)
- 反编译 / 改 smali / 重打包 / 签名 / 真机安装:完全可行,已有成例。
- 绕过登录 + 修复崩溃 + 恢复录音功能:✅ 已做到。
- 本地解码
.avc(搜狗私有音频):❌ 确定性不可行——它不是标准 CELT/Opus/AMR,libairoha-celtapi.so等 5 个解码器全部解不出,只有搜狗云端才能解;厂商停服即无解。
最关键的认知:很多"厂商专有"的录音笔,其音频编码设计上就只有云端能解(用于在线转文字),本地播放依赖云端返回转好的文件。所以"本地解锁试听/导出"可能从根上就做不到。动手前先判格式,别在解码上空转。
1. 环境与工具(Mac / Windows 通用思路)
| 工具 | 作用 | 备注 |
|---|---|---|
jadx (1.5.x) |
dex → Java 源码 | 需要 JRE 11+;java -cp jadx-all.jar jadx.cli.JadxCLI -d out classes.dex |
apktool (2.9.x) |
APK ↔ smali;重打包 | 需 -p <可写路径> 指定 framework 缓存 |
zipalign + apksigner |
对齐 + 签名 | 在 Android build-tools/<ver>/ 下 |
adb + root |
装包 / 读私有数据 / 日志 | root(Magisk)才能读 /data/data/... 和 /storage/.../Android/data |
ffmpeg / ffprobe |
验证音频可解性 | 判格式、尽力恢复 |
Java keytool |
生成签名 keystore | 用 -storetype JKS(apksigner 兼容) |
# 反编译 Java 源码 |
坑①:jadx 1.5+ 首次运行要在用户目录创建配置目录;macOS 沙箱若禁止写入 ~/Library/Application Support/io.github.skylot.jadx 会拒启,先手动建目录或换 JADX_HOME。
坑②:apktool 的 framework 缓存默认写 ~/Library/apktool/framework,被拦时用 -p /tmp/fw。
坑③:keytool JDK9+ 默认产出 PKCS12,apksigner(内置 Java8) 解析不了新 PKCS12,用 -storetype JKS 重新生成。
2. 逆向思路(可复用方法论)
按这个顺序推进,能最快定位"核心功能被什么卡住":
- 读清单 → 找主入口、应用类、所需权限。
- 找"业务核心包" → 通常是被混淆但仍保留真实命名的大包(本项目是
com.sogou.teemo.translatepen)。 - 顺数据流 → 从入口一步步追:登录门控 → 主页 → 详情 → 播放/导出。
- 定格式 → 音频/文件是什么编码(用 ffprobe / 解码器暴力测试)。
- 判云端 vs 本地 → 关键能力是本地实现还是必须回云端接口。
- 再决定"改哪一层" → 是改门控、改数据、还是改解码。
2.1 第一步:看到底是什么 app
aapt dump badging sougou.apk # 包名/版本/权限/入口 |
- 包名
com.sogou.translatorpen,应用名"搜狗录音助手",入口com.sogou.appcontainer.business.view.EntranceActivity。 - 权限含 BLE / 录音 / 存储 → 是本地蓝牙 + 本地文件型 app。
2.2 第二步:定位真正的业务代码
主包下只有 wxapi(混淆占位)。真正的核心在 com.sogou.teemo.translatepen(1022 文件),因为它没有被过度混淆(方法/字段留着有意义的 Kotlin 名)。这是复用原厂逻辑的关键。
2.3 第三步:顺"登录门控"找"解锁点"
EntranceActivity 是最佳入手点。jadx 反编译后看 e()(登录判断):
if (TextUtils.isEmpty(UserManager.f8579b.a().r())) { // token 空 → 未登录 |
→ 这就是"强制登录"的精确位置。在 smali 里把它短路(无条件跳已登录分支)即可。
同样,用户协议/隐私弹窗、云迁移判定,都会是独立的 if (XX==XX) return/弹窗,逐一定位。
2.4 第四步:处理"未登录导致的连带空白"
绕过登录后会遇到连锁问题:
- 很多代码依赖
App.getApp().userId/userDao里的用户 → 不注入用户就会 NPE 或空白。 - 做法:新写一个辅助类(如
OksBootstrap.java/.smali),在入口"构造一个本地假用户并写入 RoomUserDao"。 - 本项目注入用户后,网络请求都带
sgid=local-sgid,主页能正常加载本地录音列表。
2.5 第五步:修"绕过后才暴露的运行时崩溃"
典型:kotlin.UninitializedPropertyAccessException: lateinit property realtimeCore has not been initialized。
- 原因:某个初始化方法(本项目
App.l())开头有if(状态==0) return;提前返回,导致核心组件(TeemoService.realtimeCore)从未初始化。 - 解法:同登录一样,把该
if(...) return短路成"永远执行初始化"。
原则:把所有"因未登录/未初始化而提前 return"的 if 分支改成无条件放行,比逐个补齐状态更稳。
3. 音频格式判定(决定成与败的那步)
这一步最值得做扎实,能救你大量时间。
在手机上实时录一段,产生文件后:
# 1) 看文件命名/位置 |
如果所有标准/内置解码器都解不出 → 基本可判定是"仅云端可解"的私有编码,本地这条路可以放弃了,别继续烧时间。
4. 如何"用 app 自己探解码"(可复用技巧)
与其反复手测,不如在 app 里注入一个解码探测类,一个版本测完所有可能:
public class OksAvcTest { |
在 ShorthandDetailViewModel.ak()(进详情页必调的构造播放列表方法)开头注入 scanAll(),进详情即触发,用 adb logcat 看结果。一次真机操作,测完所有解码组合。
5. 重打包 / 安装 / 真机验证的注意点
adb install -r signed.apk # 覆盖安装(改签名会清数据) |
常见坑:
- 小米设备会被第三方锁屏(
flip.clock)挡住:adb shell "cmd dreams stop-dreaming"。 - USB 连接不稳、反复掉线:把"清日志+启动+抓取+导出"合并到一条命令里,减少断线窗口。
- 改签名后旧数据被隔离(新签名 = 新 uid 数据区),录音笔内的录音不受影响。
6. 真机数据真相(本项目实证)
- 蓝牙走 BLE,系统配对列表里没有录音笔(app 主动连,非同传统配对)。
- 离线笔录后不打开 app,app 再开不会把笔内录音下载到手机 → 该笔是"必须 app 实时连着"的产品设计。
- 实时录音音频完整地存在
.avc(每帧高熵、结构完整),但.avc仅云端能解。 - 本地 LAME 转的
_1.mp3几乎是空的(有效 MP3 仅 ~1.5KB),所以"修本地 mp3 绕云"也走不通。
7. 最终可交付成果清单
sougou_rec-override.apk 修改版 APK(绕过登录/崩溃修复/可录音) |
确定性结论:登录、崩溃、录音均可恢复;但私有音频 .avc 仅云端可解,厂商停服后本地无法试听/导出——这是产品设计层面的限制,不是逆向能绕过的。
8. 给后来者的建议
- 先判音频/数据格式,再决定投入——私有 + 仅云端 = 基本没戏,别空转。
- 绕过登录/崩溃比解码简单 100 倍——这是"改造 app"里投入产出比最高的部分。
- **善用"注入探测类"**在真机一次测全,而不是反复改参数打包。
- 多 dex 注册与签名隔离、小米锁屏、USB 掉线这些环境坑,尽早规避。
- 保存好每轮 smali 修改的 diff 与每段 logcat,回溯时省大力气。





