- Add cos_upload.py pure-Python COS upload method (no browser needed) - Expand API endpoint table to include all discovered endpoints - Document dual-verification login detection (URL + DOM) - Add COS V1 signing algorithm explanation - Update file reference table to match current codebase - Remove obsolete file references (get_resource_id.py, etc.) - Sync Chinese and English versions Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
8.0 KiB
8.0 KiB
腾讯混元3D API 逆向工程文档
项目概述
本项目通过逆向工程破解了腾讯混元3D(https://3d.hunyuan.tencent.com/)的前端签名算法,实现了纯 Python HTTP 调用,无需浏览器环境即可使用图生3D、查询配额等功能。
核心成果
1. 签名算法破解
算法位置: webpack Chunk 3057, Module 47436
签名流程:
1. nonce 生成: 16次 Math.random(),从 62 字符集 (A-Za-z0-9) 选取
2. timestamp: Math.floor(Date.now() / 1000)(秒级时间戳)
3. 密钥派生: 硬编码字节数组 → XOR → 循环左移 → 置换 → 截断
4. 签名: HMAC-SHA256(排序后的查询参数字符串, 派生密钥) → Hex
关键常量:
C = bytes([122, 59, 92, 165, 30, 79, 166, 139, 142, 129, 139, 89, 219, 131, 101, 204])
D = bytes([122, 59, 92, 45, 30, 79, 106, 139, 156, 13, 46, 63, 74, 91, 108, 125])
U = [3, 5, 2, 7, 1, 4, 6, 2, 5, 3, 1, 4, 2, 6, 3, 5] # 左移位数
M = [14, 11, 13, 9, 15, 10, 12, 8, 6, 3, 5, 1, 7, 2, 4, 0] # 置换表
派生密钥: Hf6d6KFB3D(10字符)
2. 签名范围(重要发现)
⚠️ 签名只包含 URL 查询参数,不包含请求体数据
浏览器实际请求:
POST /api/3d/creations/generations?timestamp=xxx&nonce=yyy&sign=zzz
Body: {"sceneType":"playGround3D-2.0",...}
签名计算:
# 只签查询参数(timestamp + nonce)
param_str = "nonce=yyy×tamp=xxx"
sign = HMAC-SHA256(param_str, key="Hf6d6KFB3D")
3. 图片上传方式演进
方式一:浏览器自动化上传(原始方式)
通过 generator.py 在浏览器内完成上传,获取腾讯内部资源格式的 resourceId。
方式二:纯 Python COS 直传(新方式)
通过 cos_upload.py 直接调用腾讯 COS API 上传文件,无需浏览器:
- 调用
/resource/genUploadInfo获取临时 STS 凭证(encryptTmpSecretId/encryptTmpSecretKey/encryptToken) - 使用 COS V1 签名算法生成
Authorization请求头 - 直接
PUT上传到*.cos.accelerate.myqcloud.com - 获得
resourceUrl供后续图生3D API 使用
from hunyuan3dweb.cos_upload import upload_image
resource_url = upload_image("/path/to/image.png")
文件说明
| 文件 | 说明 |
|---|---|
hunyuan3dweb/sign.py |
签名算法纯 Python 实现 |
hunyuan3dweb/api.py |
基础 API 客户端(图生3D、文生3D) |
hunyuan3dweb/api_complete.py |
完整 API 客户端(所有生成模式) |
hunyuan3dweb/cos_upload.py |
纯 Python COS 上传工具(无需浏览器) |
hunyuan3dweb/config.py |
用户配置路径管理(cookie、profile) |
hunyuan3dweb/cli.py |
CLI 入口 |
hunyuan3dweb/browser/login.py |
浏览器自动化登录工具 |
hunyuan3dweb/browser/generator.py |
浏览器自动化图生3D |
hunyuan3dweb/browser/sniffer.py |
API 请求嗅探工具 |
使用流程
1. 登录获取 Cookie
hunyuan3dweb-login
# 按提示输入邮箱和验证码
# 登录状态自动保存到 ~/.config/hunyuan3dweb/profile
# Cookie 同时导出到 ~/.config/hunyuan3dweb/cookies.txt
登录状态检测逻辑(双重验证):
- 检查当前页面 URL 是否包含
"login" - 检查页面上是否存在"登录"按钮
- 只有 URL 不含 login 且 没有登录按钮时,才判定为已登录
2. 纯 Python 生成3D模型
from hunyuan3dweb import Hunyuan3DAPI
# 自动从 ~/.config/hunyuan3dweb/cookies.txt 加载 cookie
api = Hunyuan3DAPI()
# 查询配额
quota = api.get_quota_info()
# 文生3D
result = api.generate_text("一只可爱的熊猫", title="熊猫模型")
# 查询状态
status = api.get_generation_status(result["creationsId"])
3. 本地图片上传 + 图生3D(纯 Python)
from hunyuan3dweb.cos_upload import upload_image
from hunyuan3dweb import Hunyuan3DAPIComplete
# 上传本地图片到 COS
resource_url = upload_image("/path/to/image.png")
# 调用图生3D API
api = Hunyuan3DAPIComplete()
result = api.generate_from_image(resource_url, title="我的模型")
API 端点
| 功能 | 方法 | 端点 | 签名范围 |
|---|---|---|---|
| 用户信息 | GET | /getuserinfo |
查询参数 |
| 配额查询 | POST | /quotainfo |
查询参数 |
| 生成3D | POST | /creations/generations |
查询参数 |
| 查询状态 | GET | /creations/detail |
查询参数 |
| 作品列表 | POST | /creations/list |
查询参数 |
| 作品数量 | POST | /creations/count |
查询参数 |
| 取消生成 | POST | /creations/cancel |
查询参数 |
| 上传凭证 | POST | /resource/genUploadInfo |
查询参数 |
| 资源审核 | POST | /resource/review |
查询参数 |
| 创建分享 | POST | /share |
查询参数 |
| 全局配置 | GET | /config |
查询参数 |
| 动画模板 | GET | /workflow/action/templates |
查询参数 |
技术细节
签名算法实现
def derive_key(c: bytes) -> str:
"""从硬编码常量派生签名密钥"""
# 1. XOR with D
t = bytearray(16)
for i in range(16):
t[i] = c[i] ^ D[i]
# 2. 循环左移
o = bytearray(16)
for i in range(16):
n = U[i]
val = t[i]
o[i] = (val << n | val >> (8 - n)) & 0xFF
# 3. 置换
n = bytearray(16)
for i in range(16):
n[i] = o[M[i]]
# 4. 截断(找到第一个0字节)
try:
r = n.index(0)
except ValueError:
r = 16
return n[:r].decode('utf-8')
COS V1 签名(用于直传)
def _cos_v1_auth(method, pathname, query_params, headers, secret_id, secret_key, key_time):
sign_key = hmac.new(secret_key.encode("utf-8"), key_time.encode("utf-8"), hashlib.sha1).hexdigest()
query_str = _obj2str(query_params, True)
headers_str = _obj2str(headers, True)
format_string = f"{method}\n{pathname}\n{query_str}\n{headers_str}\n"
format_string_sha1 = hashlib.sha1(format_string.encode("utf-8")).hexdigest()
string_to_sign = f"sha1\n{key_time}\n{format_string_sha1}\n"
signature = hmac.new(sign_key.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1).hexdigest()
return "&".join([
"q-sign-algorithm=sha1",
f"q-ak={secret_id}",
f"q-sign-time={key_time}",
f"q-key-time={key_time}",
f"q-header-list={';'.join(sorted(headers.keys())).lower()}",
f"q-url-param-list={';'.join(sorted(query_params.keys())).lower()}",
f"q-signature={signature}",
])
请求签名
def sign(params: dict) -> dict:
result = dict(params)
result["timestamp"] = int(time.time())
result["nonce"] = generate_nonce(16)
# 排序并拼接(JSON格式用于列表/字典)
sorted_items = sort_params(result)
param_str = join_params(sorted_items)
# HMAC-SHA256
key = derive_key(C)
signature = hmac.new(
key.encode('utf-8'),
param_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
result["sign"] = signature
return result
验证结果
| 测试项 | 浏览器签名 | Python 签名 | 匹配 |
|---|---|---|---|
| 固定参数 | 2214e55a... |
2214e55a... |
✅ |
| 配额查询 | 可用 | 可用 | ✅ |
| 生成请求 | 可用 | 可用 | ✅ |
| 状态查询 | 可用 | 可用 | ✅ |
| COS 直传 | 浏览器上传 | Python 上传 | ✅ |
限制与注意事项
- Cookie 有效期: 登录状态会过期,需要定期重新登录
- 配额限制: 每个账号有生成配额限制(默认20次/天)
- 风控检测: 频繁调用可能触发风控
- 并发安全: 多个工具同时运行时,
login.py独享标准 profile 目录,sniffer.py/generator.py会自动复制 profile 到临时目录启动,避免 ChromiumSingletonLock冲突
依赖
requests
cloakbrowser (用于登录和浏览器自动化)
法律声明
本项目仅供学习和研究使用。使用本代码需遵守腾讯混元3D的服务条款。请勿用于商业用途或大规模自动化调用。