wasm加密,荔枝网x-itouchtv-ca-key、x-itouchtv-ca-signature 参数加密分析
一、声明
本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!
而
二、逆向目标
-
网站:pc端荔枝网加密逆向
-
网址:aHR0cHM6Ly93d3cuZ2R0di5jbi8=
-
解决方案:
补环境
三、加密参数定位分析
浏览器抓包请求分析
搜索接口:请求url中包含api/search/v1/news

右键包含api/search/v1/news的目标请求,Copy - > Copy as cURL(bash),spidertoos转化成python代码

运行报错,看请求参数中x-itouchtv-ca-timestamp 毫秒时间戳 参与了时间运算,对及时性要求比较高
有5个相同格式字符串的加密参数
x-itouchtv-ca-key : 待分析
x-itouchtv-ca-signature: 待分析
x-itouchtv-ca-timestamp:毫秒时间戳
x-itouchtv-client: WEB_PC(定值)
x-itouchtv-device-id: WEB_ec1082b0-d58f-11f0-930f-e74627bdcb69 (定值)
文章用hook WebAssembly.instantiateStreaming 和 堆栈追踪两种方式定位目标加密参数
hook方式定位加密参数
重新刷新浏览器,目标请求拦截,导航栏定位到wasm,发现请求获取wasm文件,基本确定了wasm加密

WebAssembly介绍,简单了解一下WebAssembly是什么,如何加载的,如何通过 WebAssembly 的 JavaScript API 来使用 WebAssembly,如何导出WebAssembly 函数

浏览器 下一个Script断点,刷新网站,断点断住

此时打开hook_wasm脚本,运行。脚本主要作用 hook WebAssembly 所有的导出函数。
// 保存原始方法
const originalInstantiateStreaming = WebAssembly.instantiateStreaming;
// 重写 WebAssembly.instantiateStreaming 方法
WebAssembly.instantiateStreaming = async function(resp, imports) {
console.log('WASM instantiateStreaming called:', resp, imports);
// 调用原始方法
const result = await originalInstantiateStreaming.call(this, resp, imports);
// 获取所有导出
const exports = result.instance.exports;
console.log('WASM exports:', Object.keys(exports));
// 拦截每个导出函数
Object.keys(exports).forEach(key => {
if (typeof exports[key] === 'function') {
const orig = exports[key];
exports[key] = function(...args) {
console.log('WASM export called:', key, args);
return orig.apply(this, args);
};
}
});
return result;
};

放开Script断点,运行,此时控制打印出所有的WebAssembly 导出函数

用导出的函数名全局搜索,将搜索结果都打上断点,观察返回值

Hook方式定位加密参数生成位置分析完毕,接下来看看堆栈回溯的方式
堆栈回溯
-
浏览器 XHR/fetch Breakpoints 下载一个条件断点:api/search/v1/news,案例中是搜索接口,刷新浏览器,沿着堆栈向上追踪

取消xhr断点,在s.request 下一个断点,发现加密结果已经存在

继续追踪,参数t 已经出现加密的值了

分析这段代码,t是从哪儿来的?
return (0,
c.default)(n, e, s).then((function(t) {
return new Promise((function(r, i) {
(0,
u.default)(f(f({
method: n || "POST",
url: e,
data: s,
timeout: 2e4
}, !v && {
responseType: y || "json"
}), {}, {
headers: t
})).then((function(t) {
d && d.log("fetch success:", p, t.config.method.toUpperCase(), "status ".concat(t.status), e),
r(t.data || g && t)
}
)).catch((function(t) {
if (t.response) {
var n = t.response || {}
, r = n.data || {}
, u = r.errorCode
, c = r.errorMessage;
return "object" == ("undefined" == typeof window ? "undefined" : (0,
o.default)(window)) && u && (a.Modal.destroyAll(),
a.Modal.error({
title: c
})),
d && d.error("fetch error:", p, n.config.method.toUpperCase(), "status ".concat(n.status), e),
i(n.data)
}
return d && d.error(t),
i(t)
}
))
}
))
}
)).catch((function(t) {
throw t
}
))
t值的来源:
(0,c.default)(n, e, s) 就是promise异步函数的resolve 的值,所以加密值是由该函数生成

在(0,c.default)(n, e, s) 下断点进入函数内部

继续进入,在所有控制流返回值出打断点(调试过程中如果有其它断点干扰,先取消其它断点,只关注返回值处断点)

发现return e.t3 = e.sent 出现我们加密值 , e.sent 是上一步return 的返回值,也就是m函数的返回值
m(n, o, d || "", "WEB_PC", a, (null === (u = r) || void 0 === u || null === (c = u.env) || void 0 === c ? void 0 : c.SSR_API_SCOPE) || void 0);
断点m函数,进入函数内部

进入之后,又是一个switch-case控制流,在所有分支返回值处打上断点,观察返回值

t.abrupt("return", a.apply(void 0, u)); 函数生成加密值,进入函数内部,加密值是由G 函数产生的,很多请求都会走B.a这个函数,为了方便查询我们目前请求,可以下一个条件断点

进入w.a函数内部,是wasm

通过hook wasm导出函数 和堆栈回溯两种方式定位加密函数分析完毕,接下来完成补环境环节
四、补环境
加密结果是B.a函数的返回值

抠出加密函数B.a 重命名为encrypt,运行代码:

将vendor_w_e0e1da0d.js 加密js 复制到vscode->lz_source.js 文件中,同时按住Ctrl+k + 0,搜索所有的代码

搜索B.a定位到加密位置:

鼠标悬浮在M,跳转到M定义的位置

M 抠出来,重新运行

接下来的值都按照这种方式去抠。
w的值说一下:

return w = A.exports, 下一个断点

向上追踪

w = A.exports 中的A 由参数C得到,C = Q.instance,
case 13:
return Q = A.sent,
C = Q.instance,
E = Q.module,
A.abrupt("return", O(C, E));
Q 是case 9 的返回值(0, A.t0)(A.t1, A.t2) 得到
case 9:
return A.t1 = A.sent,
A.t2 = B,
A.next = 13,
(0,
A.t0)(A.t1, A.t2);
参数A.t1, A.t2 由case2 代码得到
case 2:
B = t(),
("string" == typeof I || "function" == typeof Request && I instanceof Request || "function" == typeof URL && I instanceof URL) && (I = fetch(I)),
A.t0 = a,
A.next = 9,
I;
A.t2 = B, B =t()函数 (可以直接抠)
A.t1 = A.sent , A.sent 是case2 返回值I 得到,I= fetch(I) ,fetch(I) 是用于发送wasm 请求的。

w的值分析完毕

接下来继续抠缺少的变量和函数

补完整之后,继续运行:

将try catch 中catch 分支日志打印,一共也就2处

补上剩下几个缺少的变量函数,运行代码,RuntimeError 错误消失

剩下的就是不环境部分了
环境部分基本不需要补什么,evn.js代码如下:
catvm = {}
;(() => {
'use strict';
// 取原型链上的toString
const $toString = Function.toString;
// 取方法名 reload
const myFunction_toString_symbol = Symbol('('.concat('', ')_', (Math.random() + '').toString(36)));
const myToString = function () {
return typeof this == 'function' && this[myFunction_toString_symbol] || $toString.call(this);
};
function set_native(func, key, value) {
Object.defineProperty(func, key, {
"enumerable": false, // 不可枚举
"configurable": true, // 可配置
"writable": true, // 可写
"value": value
})
}
delete Function.prototype['toString'];// 删除原型链上的toString
set_native(Function.prototype, "toString", myToString); // 自定义一个getter方法,其实就是一个hook
//套个娃,保护一下我们定义的toString,避免js对toString再次toString,如:location.reload.toString.toString() 否则就暴露了
set_native(Function.prototype.toString, myFunction_toString_symbol, "function toString() { [native code] }");
catvm.safefunction = (func) => {
set_native(func, myFunction_toString_symbol, `function ${myFunction_toString_symbol,func.name || ''}() { [native code] }`);
}; //导出函数到globalThis,更改原型上的toSting为自己的toString。这个方法相当于过掉func的toString检测点
}).call(this);
//原型名称
catvm.rename = function rename(obj,proValue)
{
//定义对象名称
Object.defineProperties(obj,{
[Symbol.toStringTag]:{
value:proValue,
configurable:true
}
});
}
catvm.proxy = function (obj,objName) {
// Proxy 可以多层代理,即 a = new proxy(a); a = new proxy(a);第二次代理
// 后代理的检测不到先代理的
return new Proxy(obj, {
set(target, property, value) {
// catvm.memory.log.push({"类型":"set-->","调用者":target,"调用属性":property,"设置值":value});
console.table([{"类型":"set-->","调用者":objName,"调用属性":property,"设置值":value}]);
return Reflect.set(...arguments); //这是一种反射语句,这种不会产生死循环问题
},
get(target, property, receiver) {
if(target.name!=='toString' && property !=='Math' && property !== 'isNaN' && property !=='undefined'){
// catvm.memory.log.push({"类型":"get<--","调用者":target,"调用属性":property,"获取值":target[property]});
console.table([{"类型":"get<--","调用者":objName,"调用属性":property,"获取值":target[property]}]);
}
return target[property]; // target中访问属性不会再被proxy拦截,所以不会死循环
}
});
}
delete globalThis.global
delete globalThis.Buffer
// delete globalThis.process
window = globalThis;
self = window
self.self = window
window.self = window
Window = function Window(){}
Object.setPrototypeOf(window,Window.prototype)
catvm.rename(window,'Window')
catvm.rename(Window,'Window')
catvm.safefunction(Window)
Navigator = function Navigator(){}
Navigator.prototype.userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
navigator = {}
Object.setPrototypeOf(navigator,Navigator.prototype)
catvm.rename(Navigator,'Navigator')
catvm.safefunction(Navigator)
Location = function Location(){}
location = {
host:'www.gdtv.cn',
origin:'https://www.gdtv.cn',
href:'https://www.gdtv.cn',
}
Object.setPrototypeOf(location,Location.prototype)
catvm.rename(Location,'Location')
catvm.safefunction(Location)
HTMLDocument= function HTMLDocument(){}
document = {location:location}
Object.setPrototypeOf(document,HTMLDocument.prototype)
catvm.rename(HTMLDocument,'HTMLDocument')
catvm.safefunction(HTMLDocument)
// window = catvm.proxy(window,'window')
// document = catvm.proxy(document,'document')
// location = catvm.proxy(location,'location')
// navigator = catvm.proxy(navigator,'navigator')
抠代码部分lizhi.js
const fs = require('fs');
const path = require('path');
require('./env');
// 方法1:拼接完整路径
var wasmPath = path.join(__dirname, 'lizhi.wasm');
var wasmBuffer = fs.readFileSync(wasmPath);
var P = Function
x = eval
function E(A, g, I) {
return E = function () {
if ("undefined" == typeof Reflect || !Reflect.construct)
return !1;
if (Reflect.construct.sham)
return !1;
if ("function" == typeof Proxy)
return !0;
try {
return Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], (function () {}))),
!0
} catch (A) {
return !1
}
}() ? Reflect.construct.bind() : function (A, g, I) {
var B = [null];
B.push.apply(B, g);
var Q = new(Function.bind.apply(A, B));
return I && D(Q, I.prototype),
Q
},
E.apply(null, arguments)
}
N = new TextDecoder("utf-8", {
ignoreBOM: !0,
fatal: !0
})
function K(A, g) {
return A >>>= 0,
N.decode(k().subarray(A, A + g))
}
function S() {
for (var A = arguments.length, g = new Array(A), I = 0; I < A; I++)
g[I] = arguments[I];
var B = function () {
try {
return E(P, g)
} catch (A) {
console.log("S=A",A)
return function () {
return null
}
}
}();
return B.toString = function () {
return ""
},
B
}
function R(A, g) {
try {
return A.apply(this, g)
} catch (A) {
console.log("R==A",A)
w.__wbindgen_export_3(Y(A))
}
}
function Y(A) {
o === M.length && M.push(M.length + 1);
var g = o;
return o = M[g],
M[g] = A,
g
}
var c = null;
function s() {
return null !== c && 0 !== c.byteLength || (c = new Int32Array(w.memory.buffer)),
c
}
function U(A) {
return null == A
}
function I(A) {
return I = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(A) {
return typeof A
}
: function(A) {
return A && "function" == typeof Symbol && A.constructor === Symbol && A !== Symbol.prototype ? "symbol" : typeof A
}
,
I(A)
}
function i(A) {
return M[A]
}
function G(A) {
var g = i(A);
return function (A) {
A < 132 || (M[A] = o,
o = A)
}(A),
g
}
var h = 0,
y = "undefined" != typeof TextEncoder ? new TextEncoder("utf-8") : {
encode: function () {
throw Error("TextEncoder not available")
}
}
function F(A, g) {
return y.encodeInto(A, g)
}
var J = null;
function k() {
return null !== J && 0 !== J.byteLength || (J = new Uint8Array(w.memory.buffer)),
J
}
function t() {
var g = {
wbg: {}
};
return g.wbg.__wbindgen_object_drop_ref = function (A) {
G(A)
},
g.wbg.__wbg_self_1ff1d729e9aae938 = function () {
return R((function () {
return Y(self.self)
}), arguments)
},
g.wbg.__wbg_window_5f4faef6c12b79ec = function () {
return R((function () {
return Y(window.window)
}), arguments)
},
g.wbg.__wbg_globalThis_1d39714405582d3c = function () {
return R((function () {
return Y(globalThis.globalThis)
}), arguments)
},
g.wbg.__wbg_global_651f05c6a0944d1c = function () {
return R((function () {
return Y(A.global)
}), arguments)
},
g.wbg.__wbindgen_is_undefined = function (A) {
return void 0 === i(A)
},
g.wbg.__wbg_newnoargs_581967eacc0e2604 = function (A, g) {
return Y(S(K(A, g)))
},
g.wbg.__wbg_call_cb65541d95d71282 = function () {
return R((function (A, g) {
return Y(i(A).call(i(g)))
}), arguments)
},
g.wbg.__wbindgen_object_clone_ref = function (A) {
return Y(i(A))
},
g.wbg.__wbg_instanceof_Window_9029196b662bc42a = function (A) {
var g;
try {
g = i(A) instanceof Window
} catch (A) {
console.log("t==A",A)
g = !1
}
return g
},
g.wbg.__wbg_document_f7ace2b956f30a4f = function (A) {
var g = i(A).document;
return U(g) ? 0 : Y(g)
},
g.wbg.__wbg_location_56243dba507f472d = function (A) {
return Y(i(A).location)
},
g.wbg.__wbg_host_15090f3de0544fea = function () {
return R((function (A, g) {
var I = L(i(g).host, w.__wbindgen_export_0, w.__wbindgen_export_1),
B = h;
s()[A / 4 + 1] = B,
s()[A / 4 + 0] = I
}), arguments)
},
g.wbg.__wbg_origin_50aa482fa6784a0a = function () {
return R((function (A, g) {
var I = L(i(g).origin, w.__wbindgen_export_0, w.__wbindgen_export_1),
B = h;
s()[A / 4 + 1] = B,
s()[A / 4 + 0] = I
}), arguments)
},
g.wbg.__wbg_href_d62a28e4fc1ab948 = function () {
return R((function (A, g) {
var I = L(i(g).href, w.__wbindgen_export_0, w.__wbindgen_export_1),
B = h;
s()[A / 4 + 1] = B,
s()[A / 4 + 0] = I
}), arguments)
},
g.wbg.__wbg_newwithargs_a0432b7780c1dfa1 = function (A, g, I, B) {
return Y(S(K(A, g), K(I, B)))
},
g.wbg.__wbindgen_string_new = function (A, g) {
return Y(K(A, g))
},
g.wbg.__wbg_call_01734de55d61e11d = function () {
return R((function (A, g, I) {
return Y(i(A).call(i(g), i(I)))
}), arguments)
},
g.wbg.__wbindgen_string_get = function (A, g) {
var I = i(g),
B = "string" == typeof I ? I : void 0,
Q = U(B) ? 0 : L(B, w.__wbindgen_export_0, w.__wbindgen_export_1),
C = h;
s()[A / 4 + 1] = C,
s()[A / 4 + 0] = Q
},
g.wbg.__wbg_eval_8c72ad5eafe427f2 = function () {
return R((function (A, g) {
return Y(x(K(A, g)))
}), arguments)
},
g.wbg.__wbindgen_typeof = function (A) {
return Y(I(i(A)))
},
g.wbg.__wbindgen_boolean_get = function (A) {
var g = i(A);
return "boolean" == typeof g ? g ? 1 : 0 : 2
},
g.wbg.__wbg_new_56693dbed0c32988 = function () {
return Y(new Map)
},
g.wbg.__wbg_set_bedc3d02d0f05eb0 = function (A, g, I) {
return Y(i(A).set(i(g), i(I)))
},
g.wbg.__wbindgen_number_new = function (A) {
return Y(A)
},
g.wbg.__wbg_new0_c0be7df4b6bd481f = function () {
return Y(new Date)
},
g.wbg.__wbg_getTime_5e2054f832d82ec9 = function (A) {
return i(A).getTime()
},
g.wbg.__wbg_new_cd59bfc8881f487b = function (A) {
return Y(new Date(i(A)))
},
g.wbg.__wbg_getTimezoneOffset_8aee3445f323973e = function (A) {
return i(A).getTimezoneOffset()
},
g.wbg.__wbindgen_throw = function (A, g) {
throw new Error(K(A, g))
},
g
}
function L(A, g, I) {
if (void 0 === I) {
var B = y.encode(A),
Q = g(B.length, 1) >>> 0;
return k().subarray(Q, Q + B.length).set(B),
h = B.length,
Q
}
for (var C = A.length, E = g(C, 1) >>> 0, D = k(), w = 0; w < C; w++) {
var M = A.charCodeAt(w);
if (M > 127)
break;
D[E + w] = M
}
if (w !== C) {
0 !== w && (A = A.slice(w)),
E = I(E, C, C = w + 3 * A.length, 1) >>> 0;
var i = k().subarray(E + w, E + C);
E = I(E, C, w += F(A, i).written, 1) >>> 0
}
return h = w,
E
}
var H = 128;
var M = new Array(128).fill(void 0);
M.push(void 0, null, !0, !1);
var o = M.length;
function encrypt(A, g, I, B, Q, C) {
try {
var E = L(A, w.__wbindgen_export_0, w.__wbindgen_export_1),
D = h,
i = L(g, w.__wbindgen_export_0, w.__wbindgen_export_1),
o = h,
Y = L(I, w.__wbindgen_export_0, w.__wbindgen_export_1),
N = h,
J = L(B, w.__wbindgen_export_0, w.__wbindgen_export_1),
k = h,
K = L(Q, w.__wbindgen_export_0, w.__wbindgen_export_1),
y = h;
return G(w.a(E, D, i, o, Y, N, J, k, K, y, function (A) {
if (1 == H)
throw new Error("out of js stack");
return M[--H] = A,
H
}(C)))
} finally {
M[H++] = void 0
}
}
/*
错误的情况(不使用 async):
function call_encrypt() {
WebAssembly.instantiate(buffer).then(res => {
console.log("结果打印在这里"); // 这一步在未来才会发生
});
// Node.js 执行到这里发现后面没代码了,会直接“关门下班”
// 此时上面的 console.log 还没来得及跑,进程就没了
}
-------------------------------------------
function call_encrypt(A, g, I, B, Q, C){
WebAssembly.instantiate(wasmBuffer, t()).then(
(results) => {
w = results.instance.exports;
// console.log("w=", w)
const result = encrypt(A, g, I, B, Q, C)
console.log('result', result)
return result
}
);
}
-----------------
async function call_encrypt() {
// await 的意思是:主程序在这里停一下,等 WASM 加载完了再往下走
const res = await WebAssembly.instantiate(buffer);
const result = encrypt(...);
console.log(result); // 确保结果出来了再打印
}
*/
async function get_result(A, g, I, B, Q, C) {
const results = await WebAssembly.instantiate(wasmBuffer, t());
w = results.instance.exports;
return encrypt(A, g, I, B, Q, C);
}
// 测试 res Map(6) { 'Content-Type' => 'application/json', 'X-ITOUCHTV-Ca-Timestamp' => 1767001066145, 'X-ITOUCHTV-Ca-Signature' => 'boAgv1Qvydkbh9bFp2ub2Q5ZF6qj8DJV+f1wykQc+PA=', 'X-ITOUCHTV-Ca-Key' => '89541943007407288657755311868534', 'X-ITOUCHTV-CLIENT' => 'WEB_PC', 'X-ITOUCHTV-DEVICE-ID' => 'WEB_52a60410-d643-11f0-a6fe-4fcb7e8bd931' }
// A = "POST"
// g = "https://gdtv-api.gdtv.cn/api/search/v1/news"
// I = "WEB_52a60410-d643-11f0-a6fe-4fcb7e8bd931"
// B = "WEB_PC"
// Q = '{"keyword":"纪录片","pageNum":1,"type":-1,"pageSize":15}'
// C = undefined
// result = get_result(A, g, I, B, Q, C)
// console.log('result', result.then(res => console.log('res', res)))
// 接收命令行参数并输出结果,python调用
const args = process.argv.slice(2);
get_result(args[0], args[1], args[2], args[3], args[4], undefined).then(res => {
if (res instanceof Map) {
// 如果是 Map 对象,先转成普通 Object 再转 JSON 字符串
const obj = Object.fromEntries(res);
process.stdout.write(JSON.stringify(obj));
} else if (typeof res === 'object') {
// 如果已经是普通对象,直接转 JSON
process.stdout.write(JSON.stringify(res));
} else {
// 如果是字符串或其他,强制转字符串输出
process.stdout.write(String(res));
}
});
五、代码实现
Python 代码调用gdtv_keyword_search.py
import subprocess
import json
import requests
def get_wasm_encryption(method, url, device_id, platform, body):
js_path = 'lizhi.js' # 你的 JS 文件名
args = ['node', js_path, method, url, device_id, platform, body]
try:
# 运行并捕获输出
response = subprocess.check_output(args, stderr=subprocess.STDOUT).decode('utf-8')
# 将 JS 返回的 JSON 字符串转为 Python 字典
try:
encrypt_data = json.loads(response)
return encrypt_data
except json.JSONDecodeError:
return response # 如果不是 JSON,返回原始字符串
except subprocess.CalledProcessError as e:
print(f"Node 调用崩溃: {e.output.decode('utf-8')}")
return None
headers = {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"cache-control": "no-cache",
"content-type": "application/json",
"origin": "https://www.gdtv.cn",
"pragma": "no-cache",
"priority": "u=1, i",
"referer": "https://www.gdtv.cn/",
"sec-ch-ua": "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"x-itouchtv-ca-key": "89541943007407288657755311868534",
"x-itouchtv-ca-signature": "w0xkhOEYlm9Qtga1jbS/foXg6kEAwCHezrDssv/PBbE=",
"x-itouchtv-ca-timestamp": "1766988096294",
"x-itouchtv-client": "WEB_PC",
"x-itouchtv-device-id": "WEB_52a60410-d643-11f0-a6fe-4fcb7e8bd931"
}
url = "https://gdtv-api.gdtv.cn/api/search/v1/news"
data = {
"keyword": "纪录片",
"pageNum": 1,
"type": -1,
"pageSize": 15
}
data = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
A = "POST"
g = "https://gdtv-api.gdtv.cn/api/search/v1/news"
I = "WEB_52a60410-d643-11f0-a6fe-4fcb7e8bd931"
B = "WEB_PC"
# 调用示例
enc_res = get_wasm_encryption(A, g, I, B, data)
print('enc_res', enc_res)
headers['x-itouchtv-ca-key'] = enc_res['X-ITOUCHTV-Ca-Key']
headers['x-itouchtv-ca-signature'] = enc_res['X-ITOUCHTV-Ca-Signature']
headers['x-itouchtv-ca-timestamp'] = str(enc_res['X-ITOUCHTV-Ca-Timestamp'])
response = requests.post(url, headers=headers, data=data)
print(response.status_code, response.text)
# 运行结果:200 {"pageNum":0,"pageSize":0,"series":null,"tvColumns":{"total":3,"list":[{"pk":983,"tvChannelPk":46,"tvChannelName":"大湾区卫视(海外版)","name":"南派纪录片","desc":null,"coverUrl":"https://img.gdtv.cn/image/202012/0.63134060122414762554df517bd3bd0f8OSS1609401331.png","updatedAt":1609401371000,"category":0,"newsList":[]},{"pk":919,"tvChannelPk":94,"tvChannelName":"纪录片","name":"纪录片精选","desc":null,"coverUrl":"https://img.gdtv.cn/image/202007/0.723672065024739220ee4d8aed36d5934OSS1593758786.jpg","updatedAt":1593758801000,"category":0,"newsList":[{"id":"22f5c2717116e5e5dbd2348d8351d9ad","title":"2025-01-22 龙舟新传","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2025/01/a1163788b681a7bf2fd250c0b621a0bb.png","contentType":3,"releasedAt":1737531371000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202502/173755881488.m3u8\"}","timeLength":1379,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"b80291bcdffcbae7a5a686a8892c8e60","title":"2025-01-16 榜样","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2025/01/f7ea9cf2c5bc07bbbf6b95e7b9138def.jpg","contentType":3,"releasedAt":1736989200000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202502/173704212150.m3u8\"}","timeLength":5440,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"20a9e96a0e9afa631147de89922f72a0","title":"赤坎华侨古镇:第二集 钟声依旧","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/11/d109b25bfe8cada19aba6514eed0d3af.png","contentType":3,"releasedAt":1730691400000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202411/173069140080.m3u8\"}","timeLength":1800,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"70491cdbddbaa397b7be35cb7f080812","title":"赤坎华侨古镇:第一集 风云再起","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/11/a0848091f5350aed68fbbe8ea526a86e.png","contentType":3,"releasedAt":1730691294000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202411/173069129423.m3u8\"}","timeLength":1800,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"8df88f7cd76a5c6800d70480203b3022","title":"2024-10-03 跨越山海 共赴未来——广东对口援藏30周年 第一集《巨变》","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/10/06a28fbaefd06b9db0df917c0fe3a8e7.png","contentType":3,"releasedAt":1727960700000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202410/172804766443.m3u8\"}","timeLength":2084,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"2b0734135fd415ba0455a3be1a5ffee4","title":"2024-10-17 跨越山海 共赴未来——广东对口援藏30周年 第三集《奔赴》","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/10/1769ca909695f1a0659cc30bda496859.png","contentType":3,"releasedAt":1729170300000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202410/172924652246.m3u8\"}","timeLength":2105,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"18ee4449f53654023d00673e08ae8b30","title":"2024-10-10 跨越山海 共赴未来——广东对口援藏30周年 第二集《相依》","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/10/472d72bfd7858bc86559ef4bbdf8d1d6.png","contentType":3,"releasedAt":1729242277000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202410/172924227792.m3u8\"}","timeLength":2060,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"e61d7327922cdecfb5cd71728e8aa0ab","title":"2024-09-27 广东省全民国防教育主题宣讲比赛","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/09/fec35c433d0e0a1290c2788f81b971a8.jpg","contentType":3,"releasedAt":1727448686000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202410/172749114560.m3u8\"}","timeLength":6574,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"ef74b7c4ccd0500f35b5753fe839c579","title":"2024-09-17 凯文·罗兰的龙舟奇遇","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/09/a8085bfc564e7e18416a775193c1620f.png","contentType":3,"releasedAt":1727073508000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202409/172707350827.m3u8\"}","timeLength":1410,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"9b05e75a737ffde1a48be67745dfea82","title":"2024-07-19 奋斗者的幸福:藤县狮舞的前世今生","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/07/3e00bcc3e7767ddc3bb053a04f085962.jpg","contentType":3,"releasedAt":1721394300000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202407/172147525028.m3u8\"}","timeLength":2392,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"1e69753db3886cf2ef9074edac1ae4e0","title":"2024-07-12 奋斗者的幸福:水围村,从1368年走来","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/07/cdc78c39493f8c4cb3f1a02176691888.png","contentType":3,"releasedAt":1720789500000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202407/172105500092.m3u8\"}","timeLength":2910,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"3d557df061023a7ed644990c93d45f87","title":"2024-05-25 中华白海豚(下集)","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/mediaserver/img/2024/05/e00aa95ba751c43e1f0e23e71a571cf2.jpg","contentType":3,"releasedAt":1716642300000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202502/171668948534.m3u8\"}","timeLength":3000,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"8324375f272e2afb66831c4e43caec89","title":"2024-05-24 中华白海豚(上集)","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/mediaserver/img/2024/05/7a66024707c172de71be0b0f252da7a8.jpg","contentType":3,"releasedAt":1716555900000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202502/171663458639.m3u8\"}","timeLength":3000,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"2bdfb4057039642efb19e4d0f8c10494","title":"2023-12-30 大道之行","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2024/01/0cc873fecc5a87f869ebf5e93e327653.png","contentType":3,"releasedAt":1704445969000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202401/170444596984.m3u8\"}","timeLength":918,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"effdbf1a4d0c2c1c7e4f723c5064ab7f","title":"2023-12-30 海“斯”十年","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/mediaserver/img/2024/01/71910a4c33e0f1730ad20096cd8d01f4.jpg","contentType":3,"releasedAt":1703870530000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202401/170418707172.m3u8\"}","timeLength":316,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null}]},{"pk":984,"tvChannelPk":94,"tvChannelName":"纪录片","name":"“决战脱贫攻坚 决胜全面小康”主题纪录片","desc":null,"coverUrl":"https://img.gdtv.cn/image/202012/0.16967900569574423710c159d9466e0e3dOSS1609418596.png","updatedAt":1612775573000,"category":0,"newsList":[]}]},"topics":{"total":3,"list":[{"id":"7508f77979d7afe407cc4f1effd841f5","title":"纪录片频道","coverUrl":"https://img.gdtv.cn/image/202210/0.8110101250543261292b673d7f200722faOSS1665827377.jpg","summary":"","type":1,"memberNum":0,"newsList":null},{"id":"22c4dfd9d381cf00820dde45a68a1b5c","title":"微纪录片《拾·获》","coverUrl":"https://img.gdtv.cn/image/202312/0.33787050533916063326bff7c765fdb452OSS1703149646.jpg","summary":"","type":1,"memberNum":0,"newsList":null},{"id":"79535144c9376bcac2e4173aef3bc05b","title":"微纪录片《“国宝”时刻》","coverUrl":"https://img.gdtv.cn/image/202510/0.024823453176867405218d4b3e41d3bbd69OSS1761899745.png","summary":"文物超高清影像记录工程,用视听语言讲述文物故事,用超高清技术赋能文化传承。","type":1,"memberNum":0,"newsList":null}]},"newsItems":{"total":556,"list":[{"id":"0b3dd4dcd1265869b41caa2bec6b898a","title":"2025残特奥纪录片《怒放》今日上线","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/mediaserver/img/2025/12/0788a44c1a7baa04bece4c00760483ca.jpg","contentType":3,"releasedAt":1767017760000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202512/176701816225.m3u8\"}","timeLength":31,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"7884224f199ffd133d5b5786db3f4fd0","title":"2025残特奥纪录片《怒放》今日上线","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/mediaserver/img/2025/12/9d2b8ab3487c4d02e9f58a7b24ab819f.jpg","contentType":3,"releasedAt":1767015600000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202512/176701625933.m3u8\"}","timeLength":33,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"7716f12c247beead311e84e2449308f2","title":"2025残特奥纪录片《怒放》今日上线","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/livmedia/img/2025/12/65ee5bf49aa94ff04024236119f7251d.png","contentType":3,"releasedAt":1767012600000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202512/176701310864.m3u8\"}","timeLength":33,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"3d7f27070f78a9ca9be063f10ef6065d","title":"2025残特奥会纪录片《怒放》今天上线 致敬不屈的生命之光","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/mediaserver/img/2025/12/694ad1aee6ac4291f2edd0fbc6ec2d14.jpg","contentType":3,"releasedAt":1767006900000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202512/176706022426.m3u8\"}","timeLength":100,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"7ea7f5057ba1ab00d5d315e0e1930b85","title":"2025残特奥纪录片《怒放》今日上线","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img2-cloud.itouchtv.cn/tv/program/cover/b3a31b97063592f7a7163238a02c8772.jpg","contentType":3,"releasedAt":1767005700000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vfile2.grtn.cn/2025/1767/0061/1554/176700611554.ssm/176700611554.mp4\"}","timeLength":33,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"d683f3471401dac41a5c742834579b45","title":"微纪录片|一网兜住的安心","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img.gdtv.cn/image/202512/0.94045148020821132152_32uko5qlr15.jpg","contentType":0,"releasedAt":1766979650000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":null,"timeLength":0,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"1bdc0df0c1ae98523d6e4ba9a9a754bf","title":"致敬不屈的生命之光!残特奥会纪录片《怒放》,12月29日、12月30日重磅上线","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img.gdtv.cn/image/202512/0.702262377152849310dwxdqoEaxQIxrkKjAZzu.jpeg","contentType":0,"releasedAt":1766934965000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":null,"timeLength":0,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"4c80718f25230317cda1795e3dcf63ca","title":"央视重磅推出!残特奥会纪录片《怒放》:致敬不屈的生命之光","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img.gdtv.cn/image/202512/0.04236040693436482561.jpg","contentType":1,"releasedAt":1766933546000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/video/202512/28da7de860f8ea0fOSS1766933503__hd.mp4\",\"sd\":\"https://vod.gdtv.cn/video/202512/28da7de860f8ea0fOSS1766933503__sd.mp4\",\"ud\":\"https://vod.gdtv.cn/video/202512/28da7de860f8ea0fOSS1766933503__ud.mp4\"}","timeLength":60,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"fd2fc3b302a5da75cbe2352e7c08a40b","title":"央视重磅推出!残特奥会纪录片《怒放》:致敬不屈的生命之光","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img.gdtv.cn/image/202512/0.480693340973551241.jpg","contentType":0,"releasedAt":1766932920000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":null,"timeLength":0,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"04b76b45154ef01326ff26f73e620d01","title":"微纪录片|一路守望的安宁","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img.gdtv.cn/image/202512/0.393541164010459626QHHCfjMbJIdYLgqeBXz2.jpeg","contentType":0,"releasedAt":1766740948000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":null,"timeLength":0,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"32929b2d95e76942458d80df34221a52","title":"岭南经纬织就文明对话,《大地云纱》纪录片全球首播开启佛山文化国际传播新叙事\n","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img.gdtv.cn/image/202512/0.86977589349698921.jpg","contentType":0,"releasedAt":1766470958000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":null,"timeLength":0,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"3a461f73b52cd687acd8a3aeb4e2df57","title":"见证广东百县千镇万村精彩蝶变!纪录片《山海之变》今起播出","titleStyle":null,"titleColor":null,"subTitle":"","preSubTitle":null,"coverUrl":"https://img.grtn.cn/material/mediaserver/img/2025/12/d23eb3bf13266f7e15848645e0c298ba.jpg","contentType":3,"releasedAt":1766410680000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vod.gdtv.cn/m3u8/202512/176641141511.m3u8\"}","timeLength":35,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"001538fbb44902d26212648f6f41c0f7","title":"见证广东百县千镇万村精彩蝶变!纪录片《山海之变》12月22日起广东卫视播出","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img2-cloud.itouchtv.cn/tv/program/cover/9c8d13dbebe6327792731b89487c8fee.jpg","contentType":3,"releasedAt":1766400840000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":"{\"hd\":\"https://vfile2.grtn.cn/2025/1766/4013/3376/176640133376.ssm/176640133376.mp4\"}","timeLength":34,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"60369cc5c6d4ecc335081753b7a0409b","title":"岭南经纬织就文明对话《大地云纱》纪录片全球首播 开启佛山文化国际传播新叙事","titleStyle":null,"titleColor":null,"subTitle":"——佛山传媒文旅集团视频中心倾力打造非遗影像力作,以香云纱为媒书写中国故事","preSubTitle":"\n","coverUrl":"https://img.gdtv.cn/image/202512/0.0892360377057305275770ca422f37f8b2OSS1766398389.jpg","contentType":0,"releasedAt":1766398447000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":null,"timeLength":0,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null},{"id":"9a581da4d11ad9ab45580bc3ac4d6a1f","title":"重磅纪录片|此心系文脉","titleStyle":null,"titleColor":null,"subTitle":null,"preSubTitle":null,"coverUrl":"https://img.gdtv.cn/image/202512/0.229026139712824261612_4p7p1gcva3j.png","contentType":0,"releasedAt":1766128749000,"readCount":0,"author":null,"objectType":0,"memberNum":0,"videoUrl":null,"timeLength":0,"imageCount":0,"recentVisitTime":0,"keyword":null,"serialsInfo":null,"crawlUrl":null,"newsList":null}]}}
更多推荐




所有评论(0)