本帖最后由 冥界3大法王 于 2026-11-6 13:15 编辑
某日法王神君巡游吾爱诸界。。发现某君在回复豆包输入法曰:“有个离线版本输入法” 遂让吾对其发生了兴趣,于是下载尝试。
咦。。竟要注册,果不出乎所料也。
随便点几个界面四处瞧瞧,发现了这。。。
于是法王想。。。要不用这个软件祭祭刀?试试朕自己用VS2022研发的“通天宝镜”的威力几何?
先让目标软件运行起来,附加进来
然后再在软件 注册相关的界面里随便点几下。。。
头一次运气十足,直接看到了关键的信息:
去重之后,就得到了下面的一手信息:
16:51:28.654 VocoType.exe+0xF261B5 <- transcribe.dll!transcribe_device_count+0x1 <- VocoType.exe+0x920CE0 transcribe.dll!transcribe_device_count VA:0x7FFC64BAA240 RVA:0x1A240
16:51:28.655 transcribe.dll+0xF483 <- ggml.dll!ggml_backend_dev_count+0x1 <- VocoType.exe+0xF261B5 ggml.dll!ggml_backend_dev_count VA:0x7FFC73CB7420 RVA:0x7420
16:51:28.656 VocoType.exe+0xF2621D <- transcribe.dll!transcribe_device_count+0x1 <- VocoType.exe+0x920CE0 transcribe.dll!transcribe_device_count VA:0x7FFC64BAA240 RVA:0x1A240
16:51:28.656 transcribe.dll+0xF483 <- ggml.dll!ggml_backend_dev_count+0x1 <- VocoType.exe+0xF2621D ggml.dll!ggml_backend_dev_count VA:0x7FFC73CB7420 RVA:0x7420
16:51:28.656 VocoType.exe+0xF2626F <- transcribe.dll!transcribe_device_get+0x1 <- VocoType.exe+0x920CE0 transcribe.dll!transcribe_device_get VA:0x7FFC64BAA260 RVA:0x1A260
16:51:28.656 transcribe.dll+0xF51F <- ggml.dll!ggml_backend_dev_count+0x1 <- transcribe.dll!transcribe_device_get+0x25 ggml.dll!ggml_backend_dev_count VA:0x7FFC73CB7420 RVA:0x7420
16:51:28.656 transcribe.dll+0xF52C <- ggml.dll!ggml_backend_dev_get+0x1 <- transcribe.dll!transcribe_device_get+0x25 ggml.dll!ggml_backend_dev_get VA:0x7FFC73CB7440 RVA:0x7440
16:51:28.656 VocoType.exe+0xF2628F <- transcribe.dll!transcribe_device_info_init+0x1 <- VocoType.exe+0x920CE0 transcribe.dll!transcribe_device_info_init VA:0x7FFC64BAA2D0 RVA:0x1A2D0
16:51:28.656 VocoType.exe+0xF2629A <- transcribe.dll!transcribe_device_get_info+0x1 <- VocoType.exe+0x920CE0 transcribe.dll!transcribe_device_get_info VA:0x7FFC64BAA290 RVA:0x1A290
16:51:28.656 transcribe.dll+0xEE4C <- ggml.dll!ggml_backend_dev_count+0x1 <- transcribe.dll!transcribe_device_get_info+0x2F ggml.dll!ggml_backend_dev_count VA:0x7FFC73CB7420 RVA:0x7420
16:51:28.656 transcribe.dll+0xEE5E <- ggml.dll!ggml_backend_dev_get+0x1 <- transcribe.dll!transcribe_device_get_info+0x2F ggml.dll!ggml_backend_dev_get VA:0x7FFC73CB7440 RVA:0x7440
16:51:28.656 transcribe.dll+0xEE91 <- ggml-base.dll!ggml_backend_dev_get_props+0x1 <- transcribe.dll!transcribe_device_get_info+0x2F ggml-base.dll!ggml_backend_dev_get_props VA:0x7FFC6E006E10 RVA:0x6E10
16:51:28.657 transcribe.dll!transcribe_was_truncated+0x1A0C6 <- ggml-base.dll!ggml_backend_dev_type+0x1 <- transcribe.dll+0xEE99 ggml-base.dll!ggml_backend_dev_type VA:0x7FFC6E007020 RVA:0x7020
16:51:28.657 transcribe.dll!transcribe_was_truncated+0x1A0C6 <- ggml-cpu-haswell.dll!ggml_cpu_has_riscv_v+0x1 <- transcribe.dll+0xEE99 ggml-cpu-haswell.dll!ggml_cpu_get_rvv_vlen VA:0x7FFC42EE7460 RVA:0x77460
16:51:28.657 transcribe.dll!transcribe_was_truncated+0x1A0D1 <- ggml-base.dll!ggml_backend_dev_backend_reg+0x1 <- transcribe.dll+0xEE99 ggml-base.dll!ggml_backend_dev_backend_reg VA:0x7FFC6E006D10 RVA:0x6D10
16:51:28.657 transcribe.dll!transcribe_was_truncated+0x1A0E1 <- ggml-base.dll!ggml_backend_reg_name+0x1 <- transcribe.dll+0xEE99 ggml-base.dll!ggml_backend_reg_name VA:0x7FFC6E007D50 RVA:0x7D50
16:51:28.657 transcribe.dll+0xEEAC <- ggml-base.dll!ggml_backend_dev_name+0x1 <- transcribe.dll!transcribe_device_get_info+0x2F ggml-base.dll!ggml_backend_dev_name VA:0x7FFC6E006F20 RVA:0x6F20
16:51:28.657 transcribe.dll+0xEED4 <- ggml-base.dll!ggml_backend_dev_description+0x1 <- transcribe.dll!transcribe_device_get_info+0x2F ggml-base.dll!ggml_backend_dev_description VA:0x7FFC6E006DD0 RVA:0x6DD0
16:51:28.657 transcribe.dll+0xEF82 <- VCRUNTIME140.dll!memcpy+0x1 <- transcribe.dll!transcribe_device_get_info+0x2F VCRUNTIME140.dll!memcpy VA:0x7FFC73B63010 RVA:0x13010
16:51:28.657 transcribe.dll+0xEF82 <- VCRUNTIME140.dll!memmove+0x1 <- transcribe.dll!transcribe_device_get_info+0x2F VCRUNTIME140.dll!memmove VA:0x7FFC73B620D0 RVA:0x120D0
16:52:00.134 combase.dll!CoWaitForMultipleHandles+0x4756 <- 0x7FFC65301B51 <- combase.dll!CoWaitForMultipleHandles+0x45AE tiptsf.dll!DllGetClassObject VA:0x7FFC65301B50 RVA:0x1B50
16:52:00.203 combase.dll!RoClearError+0xC61 <- 0x7FFC65301B51 <- combase.dll!CoCreateInstance+0x13B8 tiptsf.dll!DllGetClassObject VA:0x7FFC65301B50 RVA:0x1B50
我们用DIE查看
64位 Rust开发的
还是在Emeditor弄点色看的比较直观些。
通过打印api调用的堆栈日志,我们猜测:
VocoType.exe
transcribe.dll
ggml.dll
这三个里边有一个可能是与注册相关的核心判断
外部分析到此结束
![]()
来到x64dbg中附加程序
如图所示:
圈1: 注册设备的数量
圈2: 注册的名称
圈3: 好像是设备回调 注册的结果(这个只能先猜 和 蒙)具体需要动态调试时说话。
读取本地 *.json 授权失败时出来这个
然后用上面的这个字串,搜索 全部【用户模块】再全下断
再看堆栈
结论
不能。 从二进制和日志看,离线能不能永久用,只取决于官方签发的授权文件本身——自己没法“永久化”。
依据(静态特征 + 运行时日志)
VocoType.exe 是 Tauri/Rust 应用,授权模块是 vocotype_lib::license::*(license_activation / license_renewal / license_storage / signed_license_verifier / feature_gate / device_fingerprint)。两条激活路径:
- 在线激活:
/license/activate,需登录 + 设备指纹(主板 UUID / machine-uid、CPU、设备名),服务端下发 SignedLicense。
- 内网(离线)激活:
import_intranet_activation_file 导入激活文件(SignedLicense / SignedLicenseStorage,含 signedLicenseBase64 + signature),用 minisign / Ed25519 验签,并单独校验“内网激活文件已过期”。
关键点:
- 验签私钥在服务端,本地只有公钥。伪造或改写激活文件的签名必然失败(日志里有“SignedLicense 签名验证失败,可能被篡改或无效”)。
- 内网激活文件携带
expireAt,且代码里明确有 内网激活文件已过期 这一分支,所以“导入离线文件”不等于“永久”。
- 非终身套餐还有
[Renewal Scheduler]:每 24 小时检查、到期前几天必须联网续期,离线会走到 [License Init] 续期失败。
- 计划枚举只有
ULTRA_LIFETIME 属于终身类;FREE / CORE / PLUS / ORIGINAL / PRO / ULTRA / SPECIAL 都带期限。
你这台机器的当前状态
VocoType.log 显示:本地 signed_license.json / license.dat 不存在 → 自动激活失败“请先登录后才能激活设备” → 回落 plan=FREE(有 trial_exhausted / license_plan_restricted 状态,属受限模式);19:25:55 有一次 [SignedLicense Verifier] 开始验证签名,之后仍然回到“本地 SignedLicense 文件不存在”。
所以:
- 纯离线 + FREE:不是永久,是按试用额度受限使用。
- 离线永久的前提:拿到官方签发的
SignedLicense,其 expireAt 为终身/无到期,并且本机指纹与签发时一致。正规途径是走内网授权申请(企业离线激活),或购买终身版。
-
通过和Trae CN的对智对勇 我们得到 最关键的很多信息:
还有很多很多 ,可是我不知如何粘出全部给你们看,凑合看看吧。
先回答“为什么是 20”
不是启动代码里写死的常量,而是 plan 配额:替换词典这一项的上限由运行时配置决定(FREE = 20 条)。校验处只拿“当前条数”和“上限”做比较,常量在 plan/feature 配额表里,不在加载循环内。
判断与截断的分支
| 环节 |
地址 |
说明 |
| 取上限 |
0x1401A236A call 0x140917BD0(ecx=0,表示“替换词典”这一项) |
返回 rax = 当前 plan 的条数上限(FREE=20) |
| ≤ 上限直接放行 |
0x1401A237A cmp rcx, rax → 0x1401A237D jbe 0x1401A23EE |
rcx = 已加载条数;≤ 上限就跳过告警,全部保留 |
| 超限告警 |
0x1401A23AF(拼 替换词典超出上限: N 条(最多 M 条))→ 0x1401A23DE call 0x1415FC0C0 输出 |
只在 jbe 不成立时走到 |
| 截断告警(跳过后续) |
0x1401A334D |
持久化配置中的有效规则超过当前限制,跳过后续 N 条 |
| 截断告警(只取前 N) |
0x1401A3478 |
持久化配置包含 N 条规则,当前版本仅加载前 N 条有效规则 |
| 保存时再校验 |
0x14053DB10 |
替换词典超出上限: N 条(最多 M 条),阻止写入超额条目 |
完整执行顺序
读取 postprocess.json 的 replace_map
→ 过滤非法规则 / 去重(日志:跳过非法规则、重复规则)
→ 取 plan 上限(0x1401A236A)
→ 条数 > 上限? (0x1401A237A / jbe 0x1401A23EE)
是 → 告警 + 只保留前 N 条(0x1401A334D / 0x1401A3478)
否 → 全部进入下一步
→ 构建替换匹配器(失败分支 0x14053AED2 / 0x14053B7B5)
所以“启动只加载 20 条”本质是:加载完先按 plan 截断,再建匹配器;超额部分即便留在文件里,也不会进入运行时。
观察验证
- 断
0x1401A237A:rcx = 条数,rax = 上限,能直接看到 2 ≤ 20 这种比较;
- 断
0x1401A236A 返回处看 rax,就是 FREE 档的配额值;
- 想确认“20”从哪张表来,从
0x140917BD0 往上跟到 plan/feature 初始化即可。
按启动顺序,已确认的必经点如下(VA 都是 VocoType.exe,日志 emit 点,可下断):
| 阶段 |
日志锚点 |
VA |
| 数据库初始化 |
[数据库] 初始化数据库: |
0x140365C4C(2) |
| 连接初始化 |
PRAGMA foreign_keys = ON; |
0x140365E7D(3) |
| 迁移 |
[迁移] 执行迁移: |
0x14012E799 |
| 数据库就绪 |
[数据库] 初始化完成 |
0x14036627A(4) |
| 授权初始化 |
[License Init] |
0x140B798A1(另一处 0x1402F8B2D) |
| 本地授权缺失 |
本地 SignedLicense 文件不存在 |
0x140B79DE2(5) |
| 设备指纹 |
[DeviceFingerprint] |
0x140828E79 |
| 激活尝试 |
[License Activation] 准备调用激活 API |
0x140B84859 |
| 验签 |
[SignedLicense Verifier] 开始验证签名 |
0x14098F93F |
| ONNX Runtime |
[app_setup] 已初始化 CPU ONNX Runtime |
0x140915066(1) |
| 词典加载(有配置才走) |
持久化配置包含 N 条规则… |
0x1401A3478 |
| 词典匹配器构建 |
构建替换词典匹配器失败 |
0x14053AED2 / 0x14053B7B5 |
顺序
DB 初始化 → PRAGMA → 迁移 → 初始化完成
→ License Init(无文件则尝试激活 → 失败回落 FREE)
→ DeviceFingerprint → ONNX Runtime
→ 随包模型检测 → Paraformer / 标点 / VAD 初始化
→ 托盘 / 热键 / 单实例 → 就绪
未定位到的几项
随包模型目录(RVA 0x18EFC69)、Paraformer 中文引擎初始化成功(0x17FEE8C)、加载 VAD 模型、单实例、托盘 —— 这些字符串没有 lea 引用:Rust 把长日志拆成“前缀 + 变量 + 后缀”多段拼接,或者由 Tauri 插件内部使用,所以按整串搜不到代码点。
- 抓这些地方的办法:从相邻已知点(如
0x14036627A 之后)单步跟,或用前缀(如 找"中文引擎")搜。
断点建议:先断 0x140365C4C 确认流程,再依次断 0x140B798A1 → 0x140915066,就能串起完整启动链。注意先关掉已运行的实例(单实例会让新进程直接退出),并在 System Breakpoint 之后下断。
最终最终。。。
我发现
0000000140917C7B | BAFFFFFFFF | mov edx,FFFFFFFF 这可是我一步步跟出来的,观察右上角rax
修改为:
0000000140917C7B | BA14000000 | mov edx,14 | 找到了,这里!!!
我们本地化 替换的词典 就能有255条啦,哈哈。
C:\Users\你的用户名\AppData\Roaming\VocoType\postprocess.json 默认只有2条,随便扔给在线DeepSeek让它生成200条
我们成功用嘴 把多于2条以上 替换的 词汇说到了屏幕上,并替换成功。
|