替换 证书方法 破解软件:

# 某rust开发语音识别软件获取专业版授权
https://www.52pojie.cn/thread-2114894-1-1.html

  1. 前言
    目标是一个基于 Tauri 2.x 框架构建的桌面应用程序,后端使用 Rust 编写,前端 WebView 渲染。该应用具有用户登录认证和许可证激活系统,未登录或未激活状态下部分功能受限。

本文记录从零开始的完整本地破解过程,不涉及具体软件名称。

  1. 环境与工具
    工具 用途
    PowerShell 7+ 命令行分析、脚本执行
    Python 3 二进制解析
    010 Editor / HxD(可选) 十六进制查看和编辑
    Process Monitor 监控文件访问
    分析环境:Windows 11 x64

  2. 第一步:初步侦察
    3.1 安装目录分析
    复制代码 隐藏代码
    TargetApp/
    ├── TargetApp.exe # 主程序 (~52MB)
    ├── DirectML.dll
    ├── onnxruntime.dll
    ├── onnxruntime_providers_shared.dll
    ├── uninstall.exe
    3.2 判断技术栈
    Tauri 应用的典型特征:

单 EXE 约 40–60MB(Rust 原生代码 + 内嵌 Web 前端资源)
带有 .taubndl 节区(Tauri Bundle)
字符串中包含 tauri::、invoke、IPC 命令名等 Rust/Tauri 标记
复制代码 隐藏代码
$bytes = [System.IO.File]::ReadAllBytes(“TargetApp.exe”)
$text = [System.Text.Encoding]::UTF8.GetString($bytes)
$text.IndexOf(“tauri”) # 确认 Tauri 框架
$text.IndexOf(“.taubndl”) # Tauri bundle 节区
3.3 运行时探测
启动应用后,监控以下行为:

文件访问(关键):

%APPDATA%\TargetApp\signed_license.json — 许可证文件
%APPDATA%\TargetApp\user_cache.json — 用户信息缓存
%APPDATA%\TargetApp\user_token.json — 登录 Token 缓存
日志位置:

复制代码 隐藏代码
%LOCALAPPDATA%\TargetApp\logs\TargetApp.log
日志包含关键信息:

复制代码 隐藏代码
[SignedLicense Verifier] 开始验证签名
[SignedLicense Verifier] 签名验证成功,plan=PRO
[License] 初始化完成,计划: PRO
[Settings] 刷新(trigger=startup_license_init): plan=PRO, license_ready=true

  1. 第二步:二进制字符串分析
    4.1 提取 IPC 命令
    复制代码 隐藏代码
    import re
    with open(‘TargetApp.exe’, ‘rb’) as f:
    data = f.read()
    text = data.decode(‘utf-8’, errors=’replace’)
    pattern = r’\b\w+_command\b’
    commands = set(re.findall(pattern, text))
    for cmd in sorted(commands):
    print(cmd)
    输出:

    复制代码 隐藏代码
    ai_command
    ai_providers_command
    asr_model_command
    dataset_command
    file_transcribe_command
    hotword_command
    license_command
    mcp_command
    onboarding_command
    permission_command
    recording_command
    save_ui_settings_command
    ticket_command
    user_command
    window_command
    4.2 用户认证命令
    复制代码 隐藏代码
    send_email_code_command
    email_login_command
    get_current_user_command
    get_cached_user_command
    logout_command
    bind_ticket_command
    trigger_device_activation_command
    4.3 错误文案
    复制代码 隐藏代码
    error.auth.unauthorized: 未授权的请求,请先登录
    error.auth.login_failed: 登录失败,请重试
    error.ticket.login_required: 请先登录才能绑定 Ticket
    4.4 环境变量
    复制代码 隐藏代码
    VOCO_API_BASE_URL=https://targetapp.com/api/v1

  2. 第三步:映射认证流程
    复制代码 隐藏代码
    用户输入邮箱 → POST /users/send-email-code
    → POST /users/email-login (email + code)
    → 服务端返回 JWT Token
    → 保存 Token 到 user_token.json
    → GET /users/info (携带 Bearer Token)
    → 获取用户信息缓存到 user_cache.json
    启动时:

    复制代码 隐藏代码
    → get_cached_user_command(读取本地缓存)
    ├── 缓存存在、isLoggedIn=true → 显示已登录
    └── 不存在/无效 → 显示未登录
    → 后台 get_current_user_command(在线验证 Token)
    ├── Token 有效 → 更新缓存
    └── 无效/网络不通 → 根据 isOffline 决定行为

  3. 第四步:数据结构逆向
    通过搜索 struct Xxx with N elements 还原结构体:

    复制代码 隐藏代码

    在二进制中可找到:

    struct TokenStorage with 2 elements
    struct Jwt with 2 elements
    struct JwtPayload with 1 element
    struct UserInfoResponse with 4 elements
    struct UserCache with 5 elements
    struct SignedLicense with 6 elements
    struct SignedLicenseResponse with 2 elements
    还原的结构
    TokenStorage(2 字段):

    复制代码 隐藏代码
    struct TokenStorage {
    token: String, // JWT 字符串
    token_type: String, // “Bearer”
    }
    UserInfoResponse(4 字段):

    复制代码 隐藏代码
    struct UserInfoResponse {
    tokenType: String,
    email: String,
    expireAt: u64,
    plan: String, // FREE / PRO / ULTRA
    }
    UserCache(5 字段):

    复制代码 隐藏代码
    struct UserCache {
    userId: String,
    email: String,
    lastSyncTime: u64,
    isLoggedIn: bool,
    isOffline: bool,
    }
    SignedLicense(6 字段):

    复制代码 隐藏代码
    struct SignedLicense {
    issuedAt: String,
    machineId: String,
    motherboardUuid: String,
    cpuBrand: String,
    plan: String,
    expireAt: String,
    }
    磁盘存储格式:

    复制代码 隐藏代码
    struct SignedLicenseStorage {
    signedLicenseBase64: String, // SignedLicense JSON 的 Base64
    signature: String, // RSA-SHA256 签名的 Base64
    }
    关键发现:签名对象是 signedLicenseBase64 字符串本身的 UTF-8 字节,不是解码后的数据。

  4. 第五步:提取编译后的 RSA 公钥
    7.1 查找原理
    Rust 代码中,RSA 公钥以 PEM 格式的字符串字面量硬编码在二进制中。由于字符串字面量在编译后以明文形式保留,我们可以直接搜索 PEM 标记来定位。

PEM 格式标记:

复制代码 隐藏代码
——-BEGIN PUBLIC KEY——-
7.2 搜索方法
方法一:直接在二进制中搜索 PEM 标记

复制代码 隐藏代码
$bytes = [System.IO.File]::ReadAllBytes(“TargetApp.exe”)
$text = [System.Text.Encoding]::UTF8.GetString($bytes)
$idx = $text.IndexOf(“——-BEGIN PUBLIC KEY——-“)
Write-Host “公钥位置: 0x$(‘{0:X}’ -f $idx) ($idx)”
方法二:用 Python 搜索

复制代码 隐藏代码
with open(‘TargetApp.exe’, ‘rb’) as f:
data = f.read()

marker = b’——-BEGIN PUBLIC KEY——-‘
idx = data.find(marker)
print(f’公钥位置: 0x{idx:X} ({idx})’)

提取公钥全文 (450 字节固定长度)

key_bytes = data[idx:idx + 450]
pubkey_pem = key_bytes.decode(‘ascii’, errors=’replace’).rstrip(‘\x00 ‘)
print(pubkey_pem)
7.3 公钥的关键特征
属性 值
偏移量 0x23F8C91(十进制 37731857)
格式 PEM(——-BEGIN PUBLIC KEY——- … ——-END PUBLIC KEY——-)
固定长度 450 字节(不足部分用空格 0x20 填充)
算法 RSA 2048 位
换行 每行 64 个 Base64 字符,\n 换行
7.4 出厂内置公钥
复制代码 隐藏代码
——-BEGIN PUBLIC KEY——-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsFVgzHidHKm3n87riq+c
c4weMZA9TIPmO9bCx7lFw7pkSsO/8GahZgR0U+0PvokhWSuFlF6uwSFTG5kJ4SF9
+bLgyw7atgsFVbaM9DxcrKqcgt5gc3w8FPiV9Pc9ChqZZp4cmFKAhTQt33lNzGZm
gvCT2wwL8GEeGIbTU1pjmXmJswjwiYEIhHQ8Tb9YOSaFKjgPF6SxB7LGLoawfgpJ
9cCCbN7iFc4hUuRSupnYajCGaiKszSah/5WI7cSbHijSY4VTgEz6YEcZwy7r2aFI
7BUUKvh9qCpLu61AFxxXKFQ1p5v94oTYCX1kc31hRhXgECJ07uhSJ3LUFfTCC5wr
nwIDAQAB
——-END PUBLIC KEY——-

  1. 第六步:替换公钥与生成许可证
    8.1 核心原理
    应用启动时读取 signed_license.json
    提取 signedLicenseBase64 和 signature 字段
    用内嵌 RSA 公钥验证签名 (PKCS1v1.5 SHA256)
    验证通过 → 读取 plan 字段决定功能级别
    验证失败 → 回退到 FREE
    破解方法:用自己生成的 RSA 密钥对替换内嵌公钥,然后用私钥签发许可证。

8.2 签名验证流程详解
复制代码 隐藏代码
Step 1 - Base64 解码 signature
signature_bytes = Base64Decode(signature)
失败 → “Signature Base64 解码失败”

Step 2 - RSA 验证
用内嵌公钥验证:
VerifyData(
data = UTF8Bytes(signedLicenseBase64), ← 关键:签名对象是 Base64 字符串的 UTF-8 字节
signature = signature_bytes,
hashAlgorithm = SHA256,
padding = PKCS1v1.5
)
失败 → “SignedLicense 签名验证失败: verification error”

Step 3 - Base64 解码许可证数据
license_json_bytes = Base64Decode(signedLicenseBase64)
license_data = JSONParse(license_json_bytes)
失败 → “SignedLicense JSON 解析失败”

Step 4 - 设备匹配验证
比较 license_data.machineId == 本机 machineId
比较 license_data.motherboardUuid == 本机 motherboardUuid
比较 license_data.cpuBrand == 本机 cpuBrand
不匹配 → “设备不匹配”

Step 5 - 有效期检查
比较 license_data.expireAt > 当前时间
已过期 → “License 已过期”

Step 6 - 通过
输出 plan = license_data.plan
注意点:

签名对象是 Base64 编码字符串本身的 UTF-8 字节,不是 JSON 对象,也不是 Base64 解码后的数据
许可证中的设备信息必须与当前机器匹配
8.3 生成密钥对
复制代码 隐藏代码
Add-Type -AssemblyName System.Security.Cryptography

$rsa = [System.Security.Cryptography.RSA]::Create(2048)

导出公钥 PEM(450 字节)

$pubKeyRaw = $rsa.ExportSubjectPublicKeyInfo()
$pubKeyB64 = [Convert]::ToBase64String($pubKeyRaw)
$pubKeyPem = “——-BEGIN PUBLIC KEY——-n" for ($i = 0; $i -lt $pubKeyB64.Length; $i += 64) { $rem = [Math]::Min(64, $pubKeyB64.Length - $i) $pubKeyPem += $pubKeyB64.Substring($i, $rem) + "n”
}
$pubKeyPem += “——-END PUBLIC KEY——-“

导出私钥

$privKeyRaw = $rsa.ExportPkcs8PrivateKey()
$privKeyB64 = [Convert]::ToBase64String($privKeyRaw)
$privKeyPem = “——-BEGIN PRIVATE KEY——-n" for ($i = 0; $i -lt $privKeyB64.Length; $i += 64) { $rem = [Math]::Min(64, $privKeyB64.Length - $i) $privKeyPem += $privKeyB64.Substring($i, $rem) + "n”
}
$privKeyPem += “——-END PRIVATE KEY——-“

Write-Host $pubKeyPem
8.4 替换 EXE 中的公钥
复制代码 隐藏代码
$exeBytes = [System.IO.File]::ReadAllBytes(“TargetApp.exe”)

搜索 PEM 起始标记

$marker = [byte[]]@(0x2D,0x2D,0x2D,0x2D,0x2D,0x42,0x45,0x47,0x49,0x4E,0x20,0x50,0x55,0x42,0x4C,0x49,0x43,0x20,0x4B,0x45,0x59,0x2D,0x2D,0x2D,0x2D,0x2D)

ASCII: “——-BEGIN PUBLIC KEY——-“

$pkOffset = -1
for ($i = 0; $i -lt $exeBytes.Length - $marker.Length; $i++) {
$match = $true
for ($j = 0; $j -lt $marker.Length; $j++) {
if ($exeBytes[$i + $j] -ne $marker[$j]) { $match = $false; break }
}
if ($match) { $pkOffset = $i; break }
}

Write-Host “公钥偏移: 0x$(‘{0:X}’ -f $pkOffset)”

新公钥字节

$pubKeyPemBytes = [System.Text.Encoding]::ASCII.GetBytes($pubKeyPem)

替换(450 字节固定)

for ($j = 0; $j -lt $pubKeyPemBytes.Length; $j++) {
$exeBytes[$pkOffset + $j] = $pubKeyPemBytes[$j]
}
for ($j = $pubKeyPemBytes.Length; $j -lt 450; $j++) {
$exeBytes[$pkOffset + $j] = 0x20 # 空格填充
}

[System.IO.File]::WriteAllBytes(“TargetApp.exe”, $exeBytes)
Write-Host “公钥替换完成!”
8.5 采集本机设备指纹
复制代码 隐藏代码
$machineId = (Get-CimInstance Win32_ComputerSystemProduct).UUID
$motherboardUuid = (Get-CimInstance Win32_BaseBoard).SerialNumber
$cpuBrand = (Get-CimInstance Win32_Processor).Name

Write-Host “machineId = $machineId”
Write-Host “motherboardUuid = $motherboardUuid”
Write-Host “cpuBrand = $cpuBrand”
8.6 生成签名许可证
复制代码 隐藏代码

构建 SignedLicense JSON(字段顺序必须严格匹配 Rust struct)

$licenseObj = [PSCustomObject][ordered]@{
issuedAt = (Get-Date -Format “yyyy-MM-ddTHH:mm:ssZ”)
machineId = $machineId
motherboardUuid = $motherboardUuid
cpuBrand = $cpuBrand
plan = “PRO”
expireAt = “2099-12-31T23:59:59Z”
}
$licenseJson = $licenseObj | ConvertTo-Json -Compress

Base64 编码

$signedLicenseBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($licenseJson))

签名(签名对象是 Base64 字符串的 UTF-8 字节,不是 JSON 也不是解码后数据)

$base64StringBytes = [System.Text.Encoding]::UTF8.GetBytes($signedLicenseBase64)
$signature = $rsa.SignData(
$base64StringBytes,

[System.Security.Cryptography.HashAlgorithmName]::SHA256,
[System.Security.Cryptography.RSASignaturePadding]::Pkcs1

)
$signatureBase64 = [Convert]::ToBase64String($signature)

构造 SignedLicenseStorage

$storageObj = [PSCustomObject][ordered]@{
signedLicenseBase64 = $signedLicenseBase64
signature = $signatureBase64
}
$storageJson = $storageObj | ConvertTo-Json -Compress

部署到 %APPDATA%

Set-Content -Path “$env:APPDATA\TargetApp\signed_license.json” -Value $storageJson -Encoding UTF8

Write-Host “许可证已部署!”
Write-Host “内容: $storageJson”

  1. 第七步:伪造本地登录缓存
    9.1 为什么要伪造登录
    即使激活了 PRO 许可,部分 AI 后处理功能可能仍要求登录状态。前端的登录状态由 user_cache.json 中的 isLoggedIn 字段决定。

9.2 伪造 user_cache.json
复制代码 隐藏代码
{
“userId”: “local_user”,
“email”: “admin@localhost”,
“lastSyncTime”: 9999999999999,
“isLoggedIn”: true,
“isOffline”: true
}
关键:

isLoggedIn: true — 前端据此显示已登录
isOffline: true — 即使在线验证失败也不退出登录
9.3 伪造 user_token.json
复制代码 隐藏代码
{
“token”: “any_jwt_token_string”,
“token_type”: “Bearer”
}
9.4 部署命令
复制代码 隐藏代码
$dir = “$env:APPDATA\TargetApp”

Set-Content “$dir\user_cache.json” -Value ‘{“userId”:”local_user”,”email”:”admin@localhost”,”lastSyncTime”:9999999999999,”isLoggedIn”:true,”isOffline”:true}’ -Encoding UTF8

Set-Content “$dir\user_token.json” -Value ‘{“token”:”any_jwt_token_string”,”token_type”:”Bearer”}’ -Encoding UTF8

Write-Host “登录缓存已部署,重启应用后生效”

  1. 完整激活脚本
    以下一键脚本完成全部操作:生成密钥、替换公钥、采集设备指纹、生成签名许可证、部署登录缓存。

    复制代码 隐藏代码

    activate_all.ps1 - 一键激活脚本

    以管理员身份运行

param(
[string]$ExePath = “C:\Program Files\TargetApp\TargetApp.exe”,
[string]$Plan = “PRO”,
[string]$ExpireAt = “2099-12-31T23:59:59Z”
)

Add-Type -AssemblyName System.Security.Cryptography

1. 停止进程

Get-Process -Name TargetApp -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 2

2. 生成 RSA 密钥对

$rsa = [System.Security.Cryptography.RSA]::Create(2048)

$pubKeyRaw = $rsa.ExportSubjectPublicKeyInfo()
$pubKeyB64 = [Convert]::ToBase64String($pubKeyRaw)
$pubKeyPem = “——-BEGIN PUBLIC KEY——-n" for ($i = 0; $i -lt $pubKeyB64.Length; $i += 64) { $rem = [Math]::Min(64, $pubKeyB64.Length - $i) $pubKeyPem += $pubKeyB64.Substring($i, $rem) + "n”
}
$pubKeyPem += “——-END PUBLIC KEY——-“
$pubKeyPemBytes = [System.Text.Encoding]::ASCII.GetBytes($pubKeyPem)

3. 替换 EXE 公钥

$exeBytes = [System.IO.File]::ReadAllBytes($ExePath)
$marker = [byte[]]@(0x2D,0x2D,0x2D,0x2D,0x2D,0x42,0x45,0x47,0x49,0x4E,0x20,0x50,0x55,0x42,0x4C,0x49,0x43,0x20,0x4B,0x45,0x59,0x2D,0x2D,0x2D,0x2D,0x2D)
$pkOffset = -1
for ($i = 0; $i -lt $exeBytes.Length - $marker.Length; $i++) {
$match = $true
for ($j = 0; $j -lt $marker.Length; $j++) {
if ($exeBytes[$i + $j] -ne $marker[$j]) { $match = $false; break }
}
if ($match) { $pkOffset = $i; break }
}
for ($j = 0; $j -lt $pubKeyPemBytes.Length; $j++) { $exeBytes[$pkOffset + $j] = $pubKeyPemBytes[$j] }
for ($j = $pubKeyPemBytes.Length; $j -lt 450; $j++) { $exeBytes[$pkOffset + $j] = 0x20 }
[System.IO.File]::WriteAllBytes($ExePath, $exeBytes)

4. 采集设备指纹

$machineId = (Get-CimInstance Win32_ComputerSystemProduct).UUID
$motherboardUuid = (Get-CimInstance Win32_BaseBoard).SerialNumber
$cpuBrand = (Get-CimInstance Win32_Processor).Name

5. 生成签名许可证

$licenseObj = [PSCustomObject][ordered]@{
issuedAt = (Get-Date -Format “yyyy-MM-ddTHH:mm:ssZ”)
machineId = $machineId
motherboardUuid = $motherboardUuid
cpuBrand = $cpuBrand
plan = $Plan
expireAt = $ExpireAt
}
$licenseJson = $licenseObj | ConvertTo-Json -Compress
$signedLicenseBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($licenseJson))
$base64StringBytes = [System.Text.Encoding]::UTF8.GetBytes($signedLicenseBase64)
$signature = $rsa.SignData($base64StringBytes, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
$signatureBase64 = [Convert]::ToBase64String($signature)
$storageObj = [PSCustomObject][ordered]@{ signedLicenseBase64 = $signedLicenseBase64; signature = $signatureBase64 }
$storageJson = $storageObj | ConvertTo-Json -Compress

$appDataDir = “$env:APPDATA\TargetApp”
if (-not (Test-Path $appDataDir)) { New-Item -ItemType Directory -Path $appDataDir -Force | Out-Null }
Set-Content -Path “$appDataDir\signed_license.json” -Value $storageJson -Encoding UTF8

6. 伪造登录缓存

Set-Content “$appDataDir\user_cache.json” -Value ‘{“userId”:”local_user”,”email”:”admin@localhost”,”lastSyncTime”:9999999999999,”isLoggedIn”:true,”isOffline”:true}’ -Encoding UTF8
Set-Content “$appDataDir\user_token.json” -Value ‘{“token”:”any_jwt_token_string”,”token_type”:”Bearer”}’ -Encoding UTF8

7. 保存密钥

$keyDir = “$env:USERPROFILE\targetapp_keys”
if (-not (Test-Path $keyDir)) { New-Item -ItemType Directory -Path $keyDir -Force | Out-Null }
Set-Content “$keyDir\private.pem” -Value $privKeyPem
Set-Content “$keyDir\public.pem” -Value $pubKeyPem

Write-Host “==========================================”
Write-Host “ 激活完成!”
Write-Host “ 计划: $Plan”
Write-Host “ 过期: $ExpireAt”
Write-Host “ 设备: $machineId”
Write-Host “==========================================”
Write-Host “请启动 TargetApp 并查看日志验证。”

  1. 总结
    关键突破口
    突破口 说明
    PEM 公钥明文嵌入 直接在二进制中搜索 ——-BEGIN PUBLIC KEY——- 定位,450 字节固定长度
    RSA 签名算法 PKCS1v1.5 SHA256,签名对象是 Base64 字符串的 UTF-8 字节
    设备指纹可控 许可证需包含本机 machineId / motherboardUuid / cpuBrand
    客户端登录状态 user_cache.json 的 isLoggedIn + isOffline 直接控制前端显示
    公钥提取方法论
    在 EXE 中搜索 ——-BEGIN PUBLIC KEY——- 定位公钥起始
    确认固定长度 450 字节(——-END PUBLIC KEY——- 后可能跟空格填充)
    验证提取的公钥:用 [System.Security.Cryptography.RSA]::ImportSubjectPublicKeyInfo() 导入测试
    用自己的 RSA 2048 密钥对替换
    用新私钥签发许可证