【贴图】trae数据库解密(2026/7/18)
仅贴图,内存扫描key的方式可参考:https://github.com/Oh-My-Trae/trae-db-decrypt
后续验证密钥固定的话,会公开分析
C:\Users\xxx\AppData\Roaming\Trae CN\ModularData\ai-agent\database.db

目录
ai_agent.dll 数据库密钥生成算法分析与数据库解析文档
本文档记录通过 IDA Pro 逆向分析 ai_agent.dll 还原的密钥生成算法、SQLCipher 4 数据库加密参数与页面解密逻辑,供后续版本适配与独立工具开发参考。
1. 总体架构
ai_agent.dll (Rust 编译)
└─ 数据库初始化主函数 sub_18139BFA9
├─ 1. 查找配置键 "mics_sj10gy"
├─ 2. 多表 XOR 解密 → 明文密码
├─ 3. PBKDF2-HMAC-SHA256 派生 → 32 字节加密密钥
└─ 4. SQLCipher 4 打开数据库
密钥是从固定密码通过 PBKDF2 派生的,不是随机数。密码本身通过多表 XOR 解密从硬编码加密数据中获得。
2. 密钥生成算法
2.1 配置键查找
- 配置键名:
mics_sj10gy(字符串地址 0x1881CBE08) - 加密数据地址:0x1880D98B4,长度 32 字节
- 加密数据(hex):
45 2A 17 35 1D 19 1E 13 36 09 14 01 2D 4E 2E 00 17 5B 24 1D 0F 0A 38 09 1F 21 21 0E 24 18 12 53
2.2 多表 XOR 解密
使用三个固定字符串表循环异或:
| 表序号 | 内容 | 长度 | 周期 | DLL 地址 |
|---|---|---|---|---|
| 表0 | rust |
4 字节 | 4 | 0x18AC4B962 |
| 表1 | cpp |
3 字节 | 3 | 0x18AC2F69B |
| 表2 | electron |
8 字节 | 8 | 0x188942CE8 |
算法(总周期 = 4 × 3 × 8 = 96 字节):
对每个字节 byte[i]:
v = byte[i]
v ^= table0[i % 4] # "rust"
v ^= table1[i % 3] # "cpp"
v ^= table2[i % 8] # "electron"
output[i] = v
解密结果:1CqAknayQsrfH9Byp2QzynTckHGzRom9(32 字节 ASCII)
2.3 PBKDF2 密钥派生
| 参数 | 值 | 来源 |
|---|---|---|
| 算法 | PBKDF2-HMAC-SHA256 | sub_1860790DF |
| 密码 | 1CqAknayQsrfH9Byp2QzynTckHGzRom9 |
XOR 解密结果 |
| Salt | 123456789abcdef01122334455667788(16 字节 hex) |
硬编码 0x18ADFB8D0 |
| 迭代次数 | 100000 | — |
| 输出长度 | 32 字节 | — |
最终密钥(hex):
3605f6691095a993f03d5009c918352ef5be31ae31e8f000212b81ff058da773
2.4 其他配置键(sub_1802420DA 键值映射)
| 键名 | 加密数据地址 | 长度 | 用途 |
|---|---|---|---|
mics_sj10gy |
0x1880D98B4 | 32 | ai-agent 数据库密码 |
mics_sj11ky |
0x1880D98D4 | 64 | 其他数据库密码 |
mics_sj3gy |
0x1880D9914 | 64 | 其他数据库密码 |
mics_EXAMPLE |
0x1880D9879 | 59 | 示例配置 |
agent-dev-builder |
0x188030890 | 17 | 开发构建配置 |
agent-reader |
0x188030CFC | 12 | Reader 配置 |
agent-solo-builder |
0x18803143C | 27 | Solo 构建配置 |
3. 关键函数地址索引
(版本不同,函数地址不同!)
| 函数地址 | 作用 | 关键细节 |
|---|---|---|
| sub_18139BFA9 | 数据库初始化主函数 | 75KB,加载配置并初始化数据库 |
| sub_180241E08 | 配置值查找 + XOR 解密 | 查找键名并 XOR 解密返回 String |
| sub_1802420DA | 键值查找函数 | 26KB,映射键名到加密数据地址 |
| sub_185E2F3E1 | 字符串比较 | 比较两个 &str 是否相等 |
| sub_1854CA010 | String::from_slice | 从 slice 创建 String |
| sub_1813B7CA9 | 数据库初始化 | 22KB,调用密钥生成 |
| sub_1813E87C5 | 密钥生成分发器 | switch/case 结构 |
| sub_1813E8E59 | 密钥生成核心 | 25KB,调用 PBKDF2 |
| sub_1860790DF | PBKDF2-HMAC-SHA256 | 100000 次迭代,32 字节输出 |
关键内存布局(Rust 结构体)
- 密码 String:
rbx+0x290={cap:[rbx+0x290], ptr:[rbx+0x298], len:[rbx+0x2A0]} - 配置结构体:
[rbx+0x90]→ r14,密码 String 存于r14+0x320 - PBKDF2 参数: 密码 ptr=
[r14+0xF0], 密码 len=[r14+0xF8], 输出=[r14+0x100]
4. SQLCipher 4 数据库参数
| 参数 | 值 |
|---|---|
| 加密算法 | AES-256-CBC |
| HMAC 算法 | SHA-512 |
| 页面大小 | 4096 字节 |
| Salt 长度 | 16 字节(每个数据库独立,存储在文件头前 16 字节) |
| 密钥长度 | 32 字节 |
| IV 长度 | 16 字节 |
| HMAC 长度 | 64 字节 |
| 预留区域 | 80 字节(16 IV + 64 HMAC) |
| 密钥派生 | PBKDF2-HMAC-SHA256, 100000 次迭代 |
| HMAC 密钥派生 | PBKDF2-HMAC-SHA512, 2 次迭代 |
5. 数据库页面布局
5.1 第 1 页(含 salt)
偏移 内容 长度
0x0000 Salt 16 字节
0x0010 加密数据 4096 - 80 - 16 = 4000 字节
0x0F80 IV(reserve 区域开头) 16 字节
0x0FC0 HMAC-SHA512 64 字节
5.2 其他页
偏移 内容 长度
0x0000 加密数据 4096 - 80 = 4016 字节
0x0FB0 IV(reserve 区域开头) 16 字节
0x0FC0 HMAC-SHA512 64 字节
6. HMAC-SHA512 密钥验证
# 1. 派生 HMAC 密钥
mac_salt = bytes(b ^ 0x3A for b in salt) # salt 每字节 XOR 0x3A
mac_key = hashlib.pbkdf2_hmac("sha512", enc_key, mac_salt, 2, dklen=32)
# 2. 计算 HMAC
hmac_data = page1[16 : 4096-80+16] # salt 之后到 reserve+IV
stored_hmac = page1[4096-64 : 4096] # 页面最后 64 字节
computed = hmac.new(mac_key, hmac_data + struct.pack('<I', 1), hashlib.sha512).digest()
# 3. 验证
验证: computed == stored_hmac
7. 页面解密逻辑(AES-256-CBC)
IV = page[4096-80 : 4096-80+16] # reserve 区域开头 16 字节
# 第 1 页:跳过 salt
encrypted = page[16 : 4096-80]
decrypted = AES.new(enc_key, AES.MODE_CBC, iv).decrypt(encrypted)
# 重建: SQLite 文件头 "SQLite format 3\x00" + 解密数据 + reserve 填充
# 其他页
encrypted = page[0 : 4096-80]
decrypted = AES.new(enc_key, AES.MODE_CBC, iv).decrypt(encrypted)
# 重建: 解密数据 + reserve 填充
8. 数据库路径
| 产品 | 数据库路径 |
|---|---|
| Trae CN | C:/Users/{user}/AppData/Roaming/Trae CN/ModularData/ai-agent/database.db |
| Trae Solo CN | C:/Users/{user}/AppData/Roaming/TRAE SOLO CN/ModularData/ai-agent/database.db |
ai_agent.dll 路径:<安装目录>/resources/app/modules/ai-agent/ai_agent.dll
9. 版本适配策略
不同版本的 ai_agent.dll 可能存在以下差异:
- 加密数据变化:
mics_sj10gy对应的加密数据内容改变 - XOR 表变化:
rust/cpp/electron表被替换或修改 - Salt 变化:PBKDF2 salt 常量改变
- 迭代次数变化:PBKDF2 迭代次数调整
- 配置键名变化:
mics_sj10gy被重命名
DLL 验证检查项
通过在 DLL 中搜索以下关键数据,判断密钥算法是否变更:
| 检查项 | 搜索模式 | 含义 |
|---|---|---|
| config_key | mics_sj10gy |
配置键名字符串 |
| encrypted_password | 45 2A 17 35 1D 19 1E 13 |
加密数据前 8 字节 |
| xor_table_rust | rust |
XOR 表 0 |
| xor_table_cpp | cpp |
XOR 表 1 |
| xor_table_electron | electron |
XOR 表 2 |
| pbkdf2_salt | 123456789abcdef01122334455667788 |
PBKDF2 salt |
| decrypted_password | 1CqAknayQsrfH9Byp2QzynTckHGzRom9 |
解密后的密码 |
如果所有检查项都找到,说明密钥算法未变更;如果缺失,说明版本更新后算法可能已改变。
10. 完整调用链
(版本不同,函数地址不同!)
sub_18139BFA9 (数据库初始化主函数, 0x18139BFA9, 75KB)
├─ 0x18139F7E0: r14 = [rbx+0x90] (加载配置结构体指针)
├─ 0x18139F809: lea rdx, "mics_sj10gy" (配置键名)
├─ 0x18139F81B: call sub_180241E08 (查找+解密配置值)
│ ├─ sub_1802420DA(key_ptr, key_len) (键值查找)
│ │ └─ 返回 &unk_1880D98B4, 长度 32 (加密数据)
│ └─ 多表 XOR 解密 (table0="rust", table1="cpp", table2="electron")
│ └─ 返回解密后的 String 到 rbx+0xC80
├─ 0x18139F820: lea rcx, [r14+0x320] (密码 String 目标地址)
├─ 0x18139F827: mov rdx, [rbx+0xC88] (解密后数据 ptr)
├─ 0x18139F82E: mov r8, [rbx+0xC90] (解密后数据 len)
├─ 0x18139F839: call sub_1854CA010 (String::from_slice)
├─ 0x18139F880: lea rcx, [rbx+0x290] (另一个密码 String 目标)
├─ 0x18139F887: call sub_1854CA010 (String::from_slice)
└─ 0x18139FB25: call sub_1813B7CA9 (数据库初始化)
└─ sub_1813E87C5 (密钥生成分发器, switch/case)
└─ sub_1813E8E59 (密钥生成核心)
├─ 0x1813E958C: mov rdx, [r14+0xF0] (密码 ptr)
├─ 0x1813E9593: mov r8, [r14+0xF8] (密码 len)
├─ 0x1813E959A: lea rcx, [r14+0x100] (输出缓冲区)
└─ 0x1813E95B6: call sub_1860790DF (PBKDF2-HMAC-SHA256)
py
#!/usr/bin/env python3
"""
ai_agent.dll 数据库密钥生成独立脚本
功能:
1. 自动查找 ai_agent.dll
2. 从 DLL 中验证/提取密钥生成所需的关键常量
3. 根据常量计算数据库加密密钥(XOR 解密 → PBKDF2 派生)
4. 可选验证密钥是否匹配指定数据库
5. 输出密钥信息(hex 格式,可直接用于 SQLCipher PRAGMA key)
算法流程(详见 docs/AI_AGENT_DLL_KEY_ANALYSIS.md):
1. 配置键 "mics_sj10gy" → 加密数据(32 字节)
2. 多表 XOR 解密("rust"/"cpp"/"electron")→ 明文密码
3. PBKDF2-HMAC-SHA256(密码, salt, 100000, 32) → 32 字节密钥
使用方式:
# 自动查找 DLL 并计算密钥
python tools/ai_agent_keygen.py
# 指定 DLL 路径
python tools/ai_agent_keygen.py --dll "D:/tools/Trae CN/resources/app/modules/ai-agent/ai_agent.dll"
# 指定 DLL 并验证密钥是否匹配数据库
python tools/ai_agent_keygen.py --dll <path> --db <database.db>
# 仅使用已知常量计算密钥(不读取 DLL)
python tools/ai_agent_keygen.py --no-dll
# 从 DLL 动态提取常量后计算密钥(适配新版本)
python tools/ai_agent_keygen.py --extract
# 仅输出 DLL 版本信息(方便测试时确认版本)
python tools/ai_agent_keygen.py --version
# 指定 DLL 并输出版本信息
python tools/ai_agent_keygen.py --version --dll <path>
依赖: Python 3.8+, 无第三方依赖(仅使用标准库 hashlib/hmac/struct/pathlib)
"""
from __future__ import annotations
import argparse
import hashlib
import hmac as hmac_mod
import json
import os
import struct
import subprocess
import sys
from pathlib import Path
from typing import Dict, List, Optional, Tuple
# ============================================================================
# 已知常量(当前版本 ai_agent.dll 逆向确认,见 docs/AI_AGENT_DLL_KEY_ANALYSIS.md)
# ============================================================================
# 配置键名(用于在 DLL 中查找加密数据)
CONFIG_KEY = b"mics_sj10gy"
# 加密的密码数据(32 字节,位于 DLL 偏移 0x1880D98B4)
ENCRYPTED_PASSWORD_BYTES = bytes([
0x45, 0x2A, 0x17, 0x35, 0x1D, 0x19, 0x1E, 0x13,
0x36, 0x09, 0x14, 0x01, 0x2D, 0x4E, 0x2E, 0x00,
0x17, 0x5B, 0x24, 0x1D, 0x0F, 0x0A, 0x38, 0x09,
0x1F, 0x21, 0x21, 0x0E, 0x24, 0x18, 0x12, 0x53,
])
# XOR 解密表
XOR_TABLE_0 = b"rust" # 4 字节, 周期 4
XOR_TABLE_1 = b"cpp" # 3 字节, 周期 3
XOR_TABLE_2 = b"electron" # 8 字节, 周期 8
# PBKDF2 参数
PBKDF2_SALT = bytes.fromhex('123456789abcdef01122334455667788') # 16 字节
PBKDF2_ITERATIONS = 100000
PBKDF2_KEY_LENGTH = 32
# 解密后的密码(XOR 解密结果,用于交叉验证)
DECRYPTED_PASSWORD = b"1CqAknayQsrfH9Byp2QzynTckHGzRom9"
# 已知密钥(PBKDF2 派生结果)
DERIVED_KEY_HEX = "3605f6691095a993f03d5009c918352ef5be31ae31e8f000212b81ff058da773"
# SQLCipher 4 页面布局常量
PAGE_SZ = 4096
SALT_SZ = 16
IV_SZ = 16
HMAC_SZ = 64
RESERVE_SZ = 80
# ============================================================================
# 密钥生成算法
# ============================================================================
def xor_decrypt(encrypted_data: bytes,
table0: bytes = XOR_TABLE_0,
table1: bytes = XOR_TABLE_1,
table2: bytes = XOR_TABLE_2) -> bytes:
"""多表 XOR 解密。
每字节与 3 个表的当前值异或,计数器循环递增。
总周期 = 4 × 3 × 8 = 96 字节。
Args:
encrypted_data: 加密数据
table0: XOR 表 0(默认 "rust", 周期 4)
table1: XOR 表 1(默认 "cpp", 周期 3)
table2: XOR 表 2(默认 "electron", 周期 8)
Returns:
bytes: 解密后的数据
"""
decrypted = bytearray()
c0, c1, c2 = 0, 0, 0
for byte in encrypted_data:
v = byte
v ^= table0[c0]
c0 = (c0 + 1) % len(table0)
v ^= table1[c1]
c1 = (c1 + 1) % len(table1)
v ^= table2[c2]
c2 = (c2 + 1) % len(table2)
decrypted.append(v)
return bytes(decrypted)
def derive_key(password: bytes,
salt: bytes = PBKDF2_SALT,
iterations: int = PBKDF2_ITERATIONS,
key_length: int = PBKDF2_KEY_LENGTH) -> bytes:
"""PBKDF2-HMAC-SHA256 密钥派生。
Args:
password: 密码字节
salt: salt 字节(16 字节)
iterations: 迭代次数(默认 100000)
key_length: 输出密钥长度(默认 32 字节)
Returns:
bytes: 派生的 32 字节密钥
"""
return hashlib.pbkdf2_hmac('sha256', password, salt, iterations, key_length)
def generate_key(encrypted_password: bytes = ENCRYPTED_PASSWORD_BYTES,
table0: bytes = XOR_TABLE_0,
table1: bytes = XOR_TABLE_1,
table2: bytes = XOR_TABLE_2,
salt: bytes = PBKDF2_SALT,
iterations: int = PBKDF2_ITERATIONS) -> Tuple[bytes, bytes]:
"""完整的密钥生成算法。
算法步骤:
1. 多表 XOR 解密加密数据 → 明文密码
2. PBKDF2-HMAC-SHA256(密码, salt, iterations, 32) → 32 字节密钥
Args:
encrypted_password: 加密的密码数据
table0/table1/table2: XOR 解密表
salt: PBKDF2 salt
iterations: PBKDF2 迭代次数
Returns:
Tuple[password, key]: (明文密码, 32 字节加密密钥)
"""
# Step 1: XOR 解密
password = xor_decrypt(encrypted_password, table0, table1, table2)
# Step 2: PBKDF2 密钥派生
key = derive_key(password, salt, iterations)
return password, key
# ============================================================================
# DLL 常量提取(适配不同版本)
# ============================================================================
def find_ai_agent_dll() -> Optional[Path]:
"""自动查找 ai_agent.dll 路径。
查找策略:
1. 从运行中的 Trae 进程路径推导
2. 常见安装目录扫描
Returns:
找到的 dll Path,或 None
"""
import subprocess
# 策略1: 从运行中的 Trae 进程路径推导
try:
result = subprocess.run(
["powershell", "-Command",
"Get-Process | Where-Object { $_.Name -like '*Trae*' } | "
"Select-Object -ExpandProperty Path -Unique"],
capture_output=True, text=True, timeout=5
)
if result.returncode == 0 and result.stdout.strip():
for line in result.stdout.strip().splitlines():
exe_path = Path(line.strip())
if not exe_path.exists():
continue
dll_candidate = (exe_path.parent / "resources" / "app" /
"modules" / "ai-agent" / "ai_agent.dll")
if dll_candidate.exists():
return dll_candidate
except Exception:
pass
# 策略2: 常见安装目录
common_roots = [
Path("D:/tools"),
Path("C:/Program Files"),
Path("C:/Program Files (x86)"),
Path(os.environ.get("LOCALAPPDATA", "")) / "Programs",
Path(os.environ.get("USERPROFILE", "")),
]
product_names = ["Trae CN", "Trae", "TRAE SOLO CN", "TRAE CN"]
for root in common_roots:
if not root or not root.exists():
continue
for product in product_names:
dll_candidate = root / product / "resources" / "app" / "modules" / "ai-agent" / "ai_agent.dll"
if dll_candidate.exists():
return dll_candidate
return None
def get_dll_version_info(dll_path: Path) -> Dict[str, str]:
"""获取 DLL 的版本信息。
ai_agent.dll 是 Rust 编译的,通常没有 PE 版本资源(FileVersion 等字段为空),
因此从以下途径获取版本标识信息:
1. PE 头部 TimeDateStamp(编译时间戳)
2. 文件 SHA256 哈希(唯一标识版本)
3. PE 版本资源(如果有)
4. 文件大小和修改时间
Args:
dll_path: ai_agent.dll 路径
Returns:
Dict: 版本信息字典
"""
info: Dict[str, str] = {}
try:
dll_data = dll_path.read_bytes()
except Exception as e:
return {"error": f"读取 DLL 失败: {e}"}
# 1. 从 PE 头部读取编译时间戳
try:
pe_offset = struct.unpack_from("<I", dll_data, 0x3C)[0]
if pe_offset + 8 < len(dll_data):
# PE 签名 + COFF 头部: TimeDateStamp 在 PE 签名后偏移 4
timestamp = struct.unpack_from("<I", dll_data, pe_offset + 8)[0]
if timestamp > 0:
from datetime import datetime, timezone
compile_time = datetime.fromtimestamp(timestamp, tz=timezone.utc)
info["compile_timestamp"] = str(timestamp)
info["compile_time_utc"] = compile_time.strftime("%Y-%m-%d %H:%M:%S UTC")
except Exception:
pass
# 2. 计算 SHA256 哈希(用于唯一标识 DLL 版本)
try:
info["sha256"] = hashlib.sha256(dll_data).hexdigest()
except Exception:
pass
# 3. 尝试从 PE 版本资源获取信息(Rust DLL 通常没有,但尝试一下)
try:
result = subprocess.run(
["powershell", "-Command",
f"(Get-Item -LiteralPath '{dll_path}').VersionInfo | "
f"Select-Object FileVersion, ProductVersion, FileDescription, "
f"ProductName, CompanyName | ConvertTo-Json -Compress"],
capture_output=True, text=True, timeout=10
)
if result.returncode == 0 and result.stdout.strip():
data = json.loads(result.stdout.strip())
for ps_key, dict_key in [("FileVersion", "file_version"),
("ProductVersion", "product_version"),
("FileDescription", "file_description"),
("ProductName", "product_name"),
("CompanyName", "company_name")]:
val = str(data.get(ps_key, "") or "").strip()
if val:
info[dict_key] = val
except Exception:
pass
# 4. 文件大小和修改时间
try:
stat = dll_path.stat()
info["file_size"] = str(stat.st_size)
from datetime import datetime
info["last_modified"] = datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M:%S")
except Exception:
pass
return info
def format_version_info(version_info: Dict[str, str]) -> str:
"""格式化版本信息为可读字符串。
Args:
version_info: get_dll_version_info 返回的字典
Returns:
str: 格式化后的多行字符串
"""
if not version_info:
return " (无法获取版本信息)"
if "error" in version_info:
return f" (获取版本信息失败: {version_info['error']})"
lines = []
field_map = [
("file_version", "文件版本"),
("product_version", "产品版本"),
("product_name", "产品名称"),
("file_description", "文件描述"),
("company_name", "公司名称"),
("compile_time_utc", "编译时间"),
("compile_timestamp", "编译时间戳"),
("sha256", "SHA256"),
("file_size", "文件大小"),
("last_modified", "最后修改"),
]
for key, label in field_map:
val = version_info.get(key, "")
if val:
# 文件大小转换为可读格式
if key == "file_size":
try:
size = int(val)
val = f"{size:,} 字节 ({size / 1024 / 1024:.1f} MB)"
except ValueError:
pass
lines.append(f" {label:12s}: {val}")
return "\n".join(lines) if lines else " (版本信息为空)"
def verify_dll_constants(dll_data: bytes) -> Dict[str, Dict]:
"""在 DLL 二进制数据中搜索已知常量,判断算法是否变更。
Args:
dll_data: DLL 文件的完整二进制数据
Returns:
Dict: 各检查项的详细结果
"""
checks = [
("config_key", CONFIG_KEY, "配置键名字符串"),
("encrypted_password", ENCRYPTED_PASSWORD_BYTES[:8], "加密密码前 8 字节"),
("xor_table_rust", XOR_TABLE_0, "XOR 表 0 (rust)"),
("xor_table_cpp", XOR_TABLE_1, "XOR 表 1 (cpp)"),
("xor_table_electron", XOR_TABLE_2, "XOR 表 2 (electron)"),
("pbkdf2_salt", PBKDF2_SALT, "PBKDF2 salt"),
("decrypted_password", DECRYPTED_PASSWORD, "解密后的密码"),
]
results = {}
for name, pattern, desc in checks:
pos = dll_data.find(pattern)
results[name] = {
"description": desc,
"found": pos >= 0,
"offset": pos if pos >= 0 else -1,
}
return results
def extract_encrypted_password_from_dll(dll_data: bytes) -> Optional[bytes]:
"""从 DLL 中动态提取加密的密码数据。
提取策略:
1. 搜索配置键 "mics_sj10gy" 的位置
2. 在其附近搜索已知的加密数据前 8 字节模式
3. 如果找到,提取完整 32 字节
Args:
dll_data: DLL 文件的完整二进制数据
Returns:
32 字节加密密码,或 None(提取失败)
"""
# 方法1: 搜索已知的加密数据前 8 字节
pattern = ENCRYPTED_PASSWORD_BYTES[:8]
pos = dll_data.find(pattern)
if pos >= 0:
extracted = dll_data[pos:pos + 32]
if len(extracted) == 32:
return extracted
# 方法2: 搜索配置键后查找附近的数据引用(更复杂,暂不实现)
# 新版本如果更改了加密数据,需要通过 IDA 重新分析
return None
def extract_constants_from_dll(dll_path: Path) -> Dict:
"""从 DLL 中提取/验证所有密钥生成所需常量。
Args:
dll_path: ai_agent.dll 路径
Returns:
Dict: 提取结果,包含:
- dll_path, dll_size
- constants_verified: 已知常量是否在 DLL 中找到
- checks: 各检查项详情
- encrypted_password: 提取的加密密码(32 字节)
- all_found: 是否全部找到
- summary: 摘要文本
"""
try:
dll_data = dll_path.read_bytes()
except Exception as e:
return {
"dll_path": str(dll_path),
"dll_size": 0,
"error": f"读取 DLL 失败: {e}",
"all_found": False,
}
checks = verify_dll_constants(dll_data)
all_found = all(c["found"] for c in checks.values())
# 尝试提取加密密码
extracted_password = extract_encrypted_password_from_dll(dll_data)
found_count = sum(1 for c in checks.values() if c["found"])
total = len(checks)
if all_found:
summary = f"全部 {total} 项关键数据均在 DLL 中找到,密钥算法未变更"
else:
missing = [name for name, c in checks.items() if not c["found"]]
summary = f"{found_count}/{total} 项找到,缺失: {', '.join(missing)}"
return {
"dll_path": str(dll_path),
"dll_size": len(dll_data),
"checks": checks,
"extracted_password": extracted_password,
"all_found": all_found,
"summary": summary,
}
# ============================================================================
# 密钥验证
# ============================================================================
def verify_key_with_db(enc_key: bytes, db_path: Path) -> bool:
"""使用 HMAC-SHA512 验证密钥是否匹配数据库。
Args:
enc_key: 32 字节加密密钥
db_path: 数据库文件路径
Returns:
bool: 密钥是否正确
"""
try:
with open(db_path, 'rb') as f:
page1 = f.read(PAGE_SZ)
except Exception as e:
print(f"读取数据库失败: {e}")
return False
if len(page1) < PAGE_SZ:
print(f"数据库页面不足 {PAGE_SZ} 字节")
return False
salt = page1[:SALT_SZ]
# 派生 HMAC 密钥
mac_salt = bytes(b ^ 0x3A for b in salt)
mac_key = hashlib.pbkdf2_hmac("sha512", enc_key, mac_salt, 2, dklen=32)
# 计算 HMAC
hmac_data = page1[SALT_SZ: PAGE_SZ - RESERVE_SZ + IV_SZ]
stored_hmac = page1[PAGE_SZ - HMAC_SZ: PAGE_SZ]
hm = hmac_mod.new(mac_key, hmac_data, hashlib.sha512)
hm.update(struct.pack("<I", 1)) # 页号 1,小端
return hm.digest() == stored_hmac
# ============================================================================
# 主程序
# ============================================================================
def main():
parser = argparse.ArgumentParser(
description="ai_agent.dll 数据库密钥生成工具",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
# 自动查找 DLL 并计算密钥
python tools/ai_agent_keygen.py
# 指定 DLL 路径
python tools/ai_agent_keygen.py --dll "D:/tools/Trae CN/resources/app/modules/ai-agent/ai_agent.dll"
# 指定 DLL 并验证密钥是否匹配数据库
python tools/ai_agent_keygen.py --dll <path> --db <database.db>
# 仅使用已知常量计算密钥(不读取 DLL)
python tools/ai_agent_keygen.py --no-dll
# 从 DLL 动态提取常量后计算密钥(适配新版本)
python tools/ai_agent_keygen.py --extract
# 仅输出 DLL 版本信息(方便测试时确认版本)
python tools/ai_agent_keygen.py --version
""")
parser.add_argument("--dll", type=str, help="ai_agent.dll 路径(不指定则自动查找)")
parser.add_argument("--db", type=str, help="数据库路径(用于验证密钥)")
parser.add_argument("--no-dll", action="store_true",
help="不读取 DLL,仅使用已知常量计算密钥")
parser.add_argument("--extract", action="store_true",
help="从 DLL 动态提取常量后计算密钥(适配新版本)")
parser.add_argument("--quiet", action="store_true",
help="仅输出密钥 hex,不输出详细信息")
parser.add_argument("--version", action="store_true",
help="仅输出 DLL 版本信息后退出")
args = parser.parse_args()
# ==================== 仅输出版本信息模式 ====================
if args.version:
dll_path = Path(args.dll) if args.dll else find_ai_agent_dll()
if dll_path is None or not dll_path.exists():
print("❌ 未找到 ai_agent.dll")
sys.exit(1)
print(f"📁 DLL 路径: {dll_path}")
version_info = get_dll_version_info(dll_path)
print(format_version_info(version_info))
return 0
# ==================== 计算密钥 ====================
encrypted_password = ENCRYPTED_PASSWORD_BYTES
table0, table1, table2 = XOR_TABLE_0, XOR_TABLE_1, XOR_TABLE_2
salt = PBKDF2_SALT
iterations = PBKDF2_ITERATIONS
dll_info = None
if not args.no_dll:
# 查找或使用指定的 DLL
dll_path = Path(args.dll) if args.dll else find_ai_agent_dll()
if dll_path is None or not dll_path.exists():
if not args.quiet:
print("⚠️ 未找到 ai_agent.dll,使用已知常量计算密钥")
else:
if not args.quiet:
print(f"📁 DLL 路径: {dll_path}")
print(f" 大小: {dll_path.stat().st_size / 1024 / 1024:.1f} MB")
# 输出 DLL 版本信息(方便测试时确认版本)
version_info = get_dll_version_info(dll_path)
print(f"\n📋 DLL 版本信息:")
print(format_version_info(version_info))
# 读取 DLL 数据
dll_data = dll_path.read_bytes()
if args.extract:
# 动态提取模式:从 DLL 中提取常量
if not args.quiet:
print("\n🔍 从 DLL 动态提取常量...")
extracted = extract_encrypted_password_from_dll(dll_data)
if extracted:
encrypted_password = extracted
if not args.quiet:
print(f" ✅ 提取加密密码: {extracted.hex()}")
else:
if not args.quiet:
print(" ⚠️ 无法从 DLL 提取加密密码,使用已知常量")
else:
# 验证模式:检查 DLL 中的常量是否与已知值匹配
checks = verify_dll_constants(dll_data)
all_found = all(c["found"] for c in checks.values())
dll_info = checks
if not args.quiet:
print("\n🔍 DLL 常量验证:")
for name, result in checks.items():
status = "✅" if result["found"] else "❌"
offset = f"0x{result['offset']:08X}" if result["offset"] >= 0 else "未找到"
print(f" {status} {name:25s} @ {offset}")
if all_found:
print("\n ✅ 所有常量匹配,密钥算法未变更")
else:
print("\n ⚠️ 部分常量缺失,密钥可能已变更")
print(" 建议使用 --extract 模式尝试动态提取")
# 生成密钥
if not args.quiet:
print("\n" + "=" * 60)
print("🔐 密钥生成")
print("=" * 60)
password, key = generate_key(
encrypted_password, table0, table1, table2, salt, iterations
)
if not args.quiet:
print(f"\n Step 1: XOR 解密")
print(f" 加密数据: {encrypted_password.hex()}")
print(f" XOR 表: {table0.decode()} / {table1.decode()} / {table2.decode()}")
print(f" 解密密码: {password.decode('ascii', errors='replace')}")
print(f"\n Step 2: PBKDF2-HMAC-SHA256")
print(f" Salt: {salt.hex()}")
print(f" 迭代次数: {iterations}")
print(f" 输出长度: {len(key)} 字节")
print(f"\n ✅ 加密密钥 (hex):")
print(f" {key.hex()}")
# 交叉验证
if key.hex() == DERIVED_KEY_HEX:
print(f"\n ✅ 密钥与已知值匹配")
else:
print(f"\n ⚠️ 密钥与已知值不同")
print(f" 已知值: {DERIVED_KEY_HEX}")
print(f" 计算值: {key.hex()}")
else:
print(key.hex())
# ==================== 验证密钥 ====================
if args.db:
db_path = Path(args.db)
if not db_path.exists():
print(f"\n❌ 数据库文件不存在: {db_path}")
sys.exit(1)
if not args.quiet:
print(f"\n" + "=" * 60)
print(f"🔎 验证密钥")
print(f"=" * 60)
print(f" 数据库: {db_path}")
print(f" 大小: {db_path.stat().st_size / 1024 / 1024:.1f} MB")
if verify_key_with_db(key, db_path):
if not args.quiet:
print(f"\n ✅ HMAC 验证通过,密钥正确!")
else:
print("VALID")
else:
if not args.quiet:
print(f"\n ❌ HMAC 验证失败,密钥不匹配")
else:
print("INVALID")
sys.exit(1)
# ==================== 使用说明 ====================
if not args.quiet:
print(f"\n" + "=" * 60)
print(f"📋 使用说明")
print(f"=" * 60)
print(f"""
密钥已生成,可通过以下方式使用:
1. SQLCipher 命令行:
sqlcipher database.db
PRAGMA key = "x'{key.hex()}'";
PRAGMA cipher_compatibility = 4;
SELECT count(*) FROM sqlite_master;
2. Python (sqlcipher3):
import sqlcipher3
conn = sqlcipher3.connect("database.db")
conn.execute(f"PRAGMA key = \\"x'{key.hex()}'\\"")
conn.execute("PRAGMA cipher_compatibility = 4")
3. DB Browser for SQLite (SQLCipher):
打开数据库时选择 SQLCipher 4,密钥格式: {key.hex()}
4. 保存到 trae_converter 配置:
将密钥写入 %APPDATA%/TraeMigrationTool/db_key.txt:
key_hex={key.hex()}
""")
return 0
if __name__ == "__main__":
sys.exit(main())
usage: ai_agent_keygen.py [-h] [--dll DLL] [--db DB] [--no-dll] [--extract] [--quiet] [--version]
ai_agent.dll 数据库密钥生成工具
options:
-h, --help show this help message and exit
--dll DLL ai_agent.dll 路径(不指定则自动查找)
--db DB 数据库路径(用于验证密钥)
--no-dll 不读取 DLL,仅使用已知常量计算密钥
--extract 从 DLL 动态提取常量后计算密钥(适配新版本)
--quiet 仅输出密钥 hex,不输出详细信息
--version 仅输出 DLL 版本信息后退出
示例:
# 自动查找 DLL 并计算密钥
python tools/ai_agent_keygen.py
# 指定 DLL 路径
python tools/ai_agent_keygen.py --dll "D:/tools/Trae CN/resources/app/modules/ai-agent/ai_agent.dll"
# 指定 DLL 并验证密钥是否匹配数据库
python tools/ai_agent_keygen.py --dll <path> --db <database.db>
# 仅使用已知常量计算密钥(不读取 DLL)
python tools/ai_agent_keygen.py --no-dll
# 从 DLL 动态提取常量后计算密钥(适配新版本)
python tools/ai_agent_keygen.py --extract
# 仅输出 DLL 版本信息(方便测试时确认版本)
python tools/ai_agent_keygen.py --version
python ai_agent_keygen.py
📁 DLL 路径: D:\IDE\Trae CN\resources\app\modules\ai-agent\ai_agent.dll
大小: 224.4 MB
📋 DLL 版本信息:
编译时间 : 2026-07-14 10:51:19 UTC
编译时间戳 : 1784026279
SHA256 : 96febd51092cc92e2dcda982722ec8a7763ce22c4adce791c3c58c233dc80ef3
文件大小 : 235,337,104 字节 (224.4 MB)
最后修改 : 2026-07-15 23:17:46
🔍 DLL 常量验证:
✅ config_key @ 0x083534E8
✅ encrypted_password @ 0x08261364
✅ xor_table_rust @ 0x0832A7A6
✅ xor_table_cpp @ 0x083C956F
✅ xor_table_electron @ 0x08A52F80
✅ pbkdf2_salt @ 0x0AFDFDD0
✅ decrypted_password @ 0x08353C68
✅ 所有常量匹配,密钥算法未变更
============================================================
🔐 密钥生成
============================================================
Step 1: XOR 解密
加密数据: 452a17351d191e13360914012d4e2e00175b241d0f0a38091f21210e24181253
XOR 表: rust / cpp / electron
解密密码: 1CqAknayQsrfH9Byp2QzynTckHGzRom9
Step 2: PBKDF2-HMAC-SHA256
Salt: 123456789abcdef01122334455667788
迭代次数: 100000
输出长度: 32 字节
✅ 加密密钥 (hex):
3605f6691095a993f03d5009c918352ef5be31ae31e8f000212b81ff058da773
✅ 密钥与已知值匹配
============================================================
📋 使用说明
============================================================
密钥已生成,可通过以下方式使用:
1. SQLCipher 命令行:
sqlcipher database.db
PRAGMA key = "x'3605f6691095a993f03d5009c918352ef5be31ae31e8f000212b81ff058da773'";
PRAGMA cipher_compatibility = 4;
SELECT count(*) FROM sqlite_master;
2. Python (sqlcipher3):
import sqlcipher3
conn = sqlcipher3.connect("database.db")
conn.execute(f"PRAGMA key = \"x'3605f6691095a993f03d5009c918352ef5be31ae31e8f000212b81ff058da773'\"")
conn.execute("PRAGMA cipher_compatibility = 4")
3. DB Browser for SQLite (SQLCipher):
打开数据库时选择 SQLCipher 4,密钥格式: 3605f6691095a993f03d5009c918352ef5be31ae31e8f000212b81ff058da773
4. 保存到 trae_converter 配置:
将密钥写入 %APPDATA%/TraeMigrationTool/db_key.txt:
key_hex=3605f6691095a993f03d5009c918352ef5be31ae31e8f000212b81ff058da773
PS
内存扫描可参考:https://forum.trae.cn/t/topic/18248

从分析来看,数据库密钥是固定的,各位可以测试一下;

浙公网安备 33010602011771号