Initial commit
This commit is contained in:
222
README_REVERSE_ENGINEERING_CN.md
Normal file
222
README_REVERSE_ENGINEERING_CN.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# 腾讯混元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
|
||||
```
|
||||
|
||||
**关键常量**:
|
||||
```python
|
||||
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",...}
|
||||
```
|
||||
|
||||
签名计算:
|
||||
```python
|
||||
# 只签查询参数(timestamp + nonce)
|
||||
param_str = "nonce=yyy×tamp=xxx"
|
||||
sign = HMAC-SHA256(param_str, key="Hf6d6KFB3D")
|
||||
```
|
||||
|
||||
### 3. 图片URL格式
|
||||
|
||||
必须使用腾讯内部资源格式:
|
||||
```
|
||||
https://3d.hunyuan.tencent.com/api/3d/resource/download?resourceId=<32位十六进制>
|
||||
```
|
||||
|
||||
不能直接上传 COS URL,需要通过浏览器上传获取 resourceId。
|
||||
|
||||
---
|
||||
|
||||
## 文件说明
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `hunyuan3d_sign.py` | 签名算法纯 Python 实现 |
|
||||
| `hunyuan3d_api.py` | 纯 Python API 客户端 |
|
||||
| `hunyuan3d_login.py` | 浏览器自动化登录工具 |
|
||||
| `get_resource_id.py` | 获取图片 resourceId 工具 |
|
||||
| `cookies.txt` | Cookie 存储文件 |
|
||||
| `uploaded_image_url.txt` | 上传后的图片 URL |
|
||||
|
||||
---
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 1. 登录获取 Cookie
|
||||
|
||||
```bash
|
||||
python hunyuan3d_login.py
|
||||
# 按提示输入邮箱和验证码
|
||||
# 登录状态自动保存到 ./hunyuan3d_profile
|
||||
```
|
||||
|
||||
### 2. 提取 Cookie
|
||||
|
||||
```bash
|
||||
python get_cookie_persistent.py
|
||||
# 生成 cookies.txt
|
||||
```
|
||||
|
||||
### 3. 上传图片获取 resourceId
|
||||
|
||||
```bash
|
||||
python get_resource_id_v2.py
|
||||
# 生成 uploaded_image_url.txt
|
||||
```
|
||||
|
||||
### 4. 纯 Python 生成3D模型
|
||||
|
||||
```python
|
||||
from hunyuan3d_api import Hunyuan3DAPI
|
||||
|
||||
api = Hunyuan3DAPI("hunyuan_user=xxx; hunyuan_token=yyy")
|
||||
|
||||
# 查询配额
|
||||
quota = api.get_quota_info()
|
||||
|
||||
# 生成3D模型
|
||||
result = api.generate_3d(
|
||||
image_url="https://3d.hunyuan.tencent.com/api/3d/resource/download?resourceId=...",
|
||||
scene_type="playGround3D-2.0",
|
||||
model_type="image2ModelV3.1"
|
||||
)
|
||||
|
||||
# 查询状态
|
||||
status = api.get_generation_status(result["creationsId"])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API 端点
|
||||
|
||||
| 功能 | 方法 | 端点 | 签名范围 |
|
||||
|------|------|------|----------|
|
||||
| 用户信息 | GET | `/getuserinfo` | 查询参数 |
|
||||
| 配额查询 | POST | `/quotainfo` | 查询参数 |
|
||||
| 生成3D | POST | `/creations/generations` | 查询参数 |
|
||||
| 查询状态 | GET | `/creations/detail` | 查询参数 |
|
||||
|
||||
---
|
||||
|
||||
## 技术细节
|
||||
|
||||
### 签名算法实现
|
||||
|
||||
```python
|
||||
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')
|
||||
```
|
||||
|
||||
### 请求签名
|
||||
|
||||
```python
|
||||
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...` | ✅ |
|
||||
| 配额查询 | 可用 | 可用 | ✅ |
|
||||
| 生成请求 | 可用 | 可用 | ✅ |
|
||||
| 状态查询 | 可用 | 可用 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 限制与注意事项
|
||||
|
||||
1. **图片上传**: 仍需浏览器环境上传图片获取 resourceId(腾讯 COS 需要临时签名)
|
||||
2. **Cookie 有效期**: 登录状态会过期,需要定期重新登录
|
||||
3. **配额限制**: 每个账号有生成配额限制(默认20次/天)
|
||||
4. **风控检测**: 频繁调用可能触发风控
|
||||
|
||||
---
|
||||
|
||||
## 依赖
|
||||
|
||||
```
|
||||
requests
|
||||
cloakbrowser (用于登录和上传)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 法律声明
|
||||
|
||||
本项目仅供学习和研究使用。使用本代码需遵守腾讯混元3D的服务条款。请勿用于商业用途或大规模自动化调用。
|
||||
Reference in New Issue
Block a user