## 概要
本文仅作为技术分享交流,切勿用于非法用途。附完整签名算法还原实例代码!
为避免文中代码过多,导致文章不方便阅读,所以请求代码全换成了curl请求格式,请自行粘贴后使用工具进行转换成python代码进行调用!
#### 获取资源上传授权
curl 请求格式
curl --location 'https://creator.douyin.com/web/api/media/upload/auth/v5/' \
--header 'Cookie:{这儿是你自己账号的登录cooies}'
响应体
{
"ak": "AKLTYjdjYTUwZWQwZDU0NDRmMmEwNWU5NmE1MTdiYzUyZTg",
"auth": "{\"AccessKeyID\":\"AKTPNzAyMGQ2YTgxYj*******YjY\",\"SecretAccessKey\":\"tr3i*****4x\",\"SessionToken\":\"STS2eyJMVEFj*********WJhNiJ9\",\"ExpiredTime\":\"2024-03-20T18:18:36+08:00\",\"CurrentTime\":\"2024-03-18T18:18:36+08:00\"}",
"extra": {
"logid": "2024031818*********0",
"now": 1710757116000
},
"status_code": 0
}
// 这儿需要保存ak与auth字段,这两个参数会在后续加密和获取资源分配请求中用到
#### 获取图文资源上传分配地址
curl --location 'https://imagex.bytedanceapi.com/?Action=ApplyImageUpload&Version=2018-08-01&ServiceId=jm8ajry58r&app_id=2906&user_id=&s=cbdzimvyprv' \
--header 'accept: */*' \
--header 'authorization: {通过加密算法生成,会做加密分析还原}' \
--header 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0' \
--header 'x-amz-date: 20240318T095316Z' \
--header 'x-amz-security-token: STS2eyJMVEFj*********WJhNiJ9'
响应体
{
"ResponseMetadata": {
"RequestId": "202403181********FCC",
"Action": "ApplyImageUpload",
"Version": "2018-08-01",
"Service": "imagex",
"Region": "cn-north-1"
},
// 如果签名请求成功,会返回Result对象,失败则返回Error对象
"Result": {
// 内容太多,此处省略。主要取SessionKey, UploadHosts, StoreUri, Auth参数
}
}
该段请求主要针对
headers
中的authorization
参数加密,x-amz-date
为当前ios8601格式的时间,x-amz-security-token
参数为上文获取资源上传授权请求中获取 3. authorization 参数逆向分析authorization
参数为AWS4-HMAC-SHA256进行加密 格式:AWS4-HMAC-SHA256 Credential={上文获取的ak参数}/{ios8601格式的时间前8位}/{资源分配桶地址}/{资源分配服务名}/aws4_request, SignedHeaders=x-amz-date;x-amz-security-token, Signature={加密参数}通过上述加密格式可分析出主要需要获取并还原出
资源分配桶地址
,资源分配服务名
,以及最后的Signature
生成规则。 全局搜索AWS4-HMAC-SHA256
得到资源分配桶地址
和资源分配服务名
分别为固定值cn-north-1
和imagex
Signature
参数为: this.signature(e, t), 继续深入发现一直再调用a函数,a函数为AWS4-sha256标准加密
python 加密过程还原代码
import datetime, hashlib, hmac
from urllib.parse import urlencode
def getSignatureKey(key, date_stamp, regionName, serviceName):
kDate = hmac.new(('AWS4' + key).encode('utf-8'), date_stamp.encode("utf-8"), hashlib.sha256).digest()
kRegion = hmac.new(kDate, regionName.encode("utf-8"), hashlib.sha256).digest()
kService = hmac.new(kRegion, serviceName.encode("utf-8"), hashlib.sha256).digest()
kSigning = hmac.new(kService, 'aws4_request'.encode("utf-8"), hashlib.sha256).digest()
return kSigning
def buildStrParams(params):
url = ""
for key, value in params.items():
url += key + "=" + str(value) + "&"
return url[:-1] # 去除最后一个多余的"&"
def random_s():
digits = '0123456789'
ascii_letters = 'abcdefghigklmnopqrstuvwxyz'
l = digits + ascii_letters
str_list = [random.choice(l) for i in range(11)]
random_str = ''.join(str_list)
return random_str
def generateAuthorization(secretAccessKey, region, service, canonical_querystring, amz_date, SessionToken, date_stamp, AccessKeyID):
signing_key = getSignatureKey(secretAccessKey, date_stamp, region, service)
canonical_headers = 'x-amz-date:' + amz_date + '\n' + 'x-amz-security-token:' + SessionToken + '\n'
canonical_request = "GET" + '\n' + "/" + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + 'x-amz-date;x-amz-security-token' + '\n' + hashlib.sha256("".encode("utf-8")).hexdigest()
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = date_stamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode("utf-8")).hexdigest()
Signature = hmac.new(signing_key, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
data = f"AWS4-HMAC-SHA256 Credential={AccessKeyID}/{date_stamp}/{region}/{service}/aws4_request, SignedHeaders=x-amz-date;x-amz-security-token, Signature={Signature}"
return data
if __name__ == "__main__":
auth = {
"accessKeyId": "AKTPZjAzMDR******NmNlMDE",
"secretAccessKey": "AuISVb8*******PlyYEK3L2",
"sessionToken": "STS2eyJ*******ZCJ9"
}
times = datetime.datetime.utcnow()
amz_date = times.strftime('%Y%m%dT%H%M%SZ')
date_stamp = times.strftime('%Y%m%d')
region = 'cn-north-1'
service = 'imagex'
params = {
"Action": "ApplyImageUpload",
"ServiceId": "jm8ajry58r",
"Version": "2018-08-01",
"app_id": "2906",
"s": random_s,
"user_id": ""
}
canonical_querystring = buildStrParams(params)
SessionToken = auth.get('sessionToken')
AccessKeyID = auth.get('accessKeyId')
secretAccessKey = auth.get('secretAccessKey')
Authorization = generateAuthorization(secretAccessKey, region, service, canonical_querystring, amz_date, SessionToken, date_stamp, AccessKeyID)
print(Authorization)
至此
Authorization
参数还原分析完成.加密都规则都是比较规范,没什么好继续说的.拼接headers请求头后拿到响应体中的SessionKey
,UploadHosts
,StoreUri
,Auth
参数,其中上传地址为:https://{UploadHosts}/upload/v1/{storeUri}
,需要对上传地址进行动态拼接.
#### 上传图片
curl --location 'https://tos-lf-x.snssdk.com/upload/v1/*****' \
--header 'Authorization: SpaceKey/jm*******' \
--header 'Content-CRC32: ff3ceced' \
--header 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0'
--data '{你要上传的文件内容}'
其中
url
为上文拼接的url地址,Authorization
为上文请求获取得到的Auth
参数,Content-CRC32
通过计算文件内容获取.python读取文件内容并且计算crc32代码如下.
with open(filePath,"rb")as f:
data = f.read()
crc32 = hex(zlib.crc32(data) & 0xFFFFFFFF)[2:]
响应体
{
"code":2000, // 如果返回2000则上传成功
"apiversion":"v1",
"message":"Success",
"data":{
"crc32":"ff3ceced"
}
}
#### 发布图文作品
curl --location 'https://creator.douyin.com/web/api/media/aweme/create/' \
--header 'cookie: {你的登录授权cookie信息}' \
--header 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0' \
--header 'Content-Type: text/plain' \
--data 'text={标题}&text_extra=[]&activity=[]&challenges=[]&hashtag_source=""&mentions=[]&ifLongTitle=true&hot_sentence=&visibility_type=0&download=1&poster={storeUri}&timing=-1&images=[{"uri":"{storeUri}"]&creation_id=1hz6lvsn1710767057933'
主要修改--data中的参数,text为标题,images里面是你要发布的图文
storeUri
地址的数组,poster是封面图片的storeUri
地址
#### 视频切片上传
视频资源上传规则和图文资源上传规则大致相同,唯一的区别在于获取分配地址的params参数以及加密过程中的
service
参数需要做修改,变动如下.
service = 'vod'
params = {
'Action': 'ApplyUploadInner',
'FileSize': fileSize, ## 文件总大小
'FileType': 'video',
'IsInner': '1',
'SpaceName': 'aweme',
'Version': '2020-11-19',
'app_id': '2906',
's': random_s,
'user_id': ''
}
最后post进行请求即可完成视频上传,视频切片python代码实现如下:
def videoSplit(filePath, chunk_size=3):
chunk_size_bytes = chunk_size * 1024 * 1024
with open(filePath, 'rb') as file:
file_data = file.read()
total_slices = (len(file_data) + chunk_size_bytes - 1) // chunk_size_bytes
slices = []
chunks = {}
uploadid = str(uuid.uuid4())
for i in tqdm(range(total_slices), desc='切片'):
start = i * chunk_size_bytes
end = min((i + 1) * chunk_size_bytes, len(file_data))
data = file_data[start:end]
# 每一段视频的请求参数信息
slice_info = {
"uploadid": uploadid, # 上传uuid,一个完整的视频切片后的uuid需要一致
'part_number': i + 1, # 当前视频切片的顺序
'part_offset': start, # 上传文件的写入地址
"phase": "transfer",
}
chunks[start] = data
slices.append(slice_info)
return slices, chunks
至此,视频切片上传结束,至于视频发布我这儿就不做继续分析了,换汤不换药都是一个流程.还原过程中如果如遇到什么问题或文中有什么错误欢迎评论区留言补充,看到就回复.
#### 最终效果
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。