window = global;
console_log = console.log;
// ==================== 第一步:函数 Native 伪装 ====================
(() => {
'use strict';
const $toString = Function.toString;
// 使用随机 Symbol 作为 native 标签,避免被检测到固定属性名
const fakeNativeTag = Symbol('('.concat('', ')_', (Math.random() + '').toString(36)));
// 自定义 toString:如果函数已经打上标签,就返回伪造的 native 代码,否则沿用原始行为
const myToString = function () {
return (typeof this === 'function' && this[fakeNativeTag]) || $toString.call(this);
};
function set_native(obj, key, value) {
Object.defineProperty(obj, key, {
enumerable: false,
configurable: true,
writable: true,
value: value
});
}
// 替换 Function.prototype.toString
delete Function.prototype['toString'];
set_native(Function.prototype, 'toString', myToString);
// 保护自定义 toString 本身,使其看起来像原生函数
set_native(Function.prototype.toString, fakeNativeTag, 'function toString() { [native code] }');
// 导出一个工具函数,用来给任意函数打上“原生”标记
globalThis.safefunction = (func) => {
set_native(func, fakeNativeTag, `function ${func.name || ''}() { [native code] }`);
};
}).call(globalThis);
// ==================== 第二步:补全基础全局 API ====================
// 定时器相关(这些在 bdms 中经常被检测)
setInterval = function setInterval(){}; safefunction(setInterval);
setTimeout = function setTimeout(){}; safefunction(setTimeout);
window.requestAnimationFrame = function requestAnimationFrame(){}; safefunction(window.requestAnimationFrame);
window.addEventListener = function addEventListener(){}; safefunction(window.addEventListener);
// 窗口及屏幕尺寸(与你提取 bdms 时的浏览器保持一致)
window.outerWidth = 1920;
window.outerHeight = 1040;
window.innerWidth = 1920;
window.innerHeight = 937;
// 抖音安全 SDK 的版本标记(从页面环境中提取得到)
window._sdkGlueVersionMap = {
"sdkGlueVersion": "1.0.0.64-fix.01",
"bdmsVersion": "1.0.1.19-fix.01",
"captchaVersion": "4.0.10"
};
// ==================== 第三步:补全 DOM 相关对象与第三方 SDK ====================
// 极简 DOM 对象实现
span = {classList: {}};
canvas = {getContext: function getContext(){}}; safefunction(canvas.getContext);
document = {
createElement: function (tag) {
if (tag === 'span') return span;
if (tag === 'canvas') return canvas;
console_log('未处理的 DOM 元素创建:', tag);
return {};
},
documentElement: {},
createEvent: function createEvent(){}, safefunction(document.createEvent),
addEventListener: function addEventListener(){}, safefunction(document.addEventListener),
all: {},
location: {
"ancestorOrigins": {},
"href": "https://www.douyin.com/",
"origin": "https://www.douyin.com",
"protocol": "https:",
"host": "www.douyin.com"
}
};
safefunction(document.createElement);
// 第三方 SDK 存根(抖音可能会检测浏览器插件或开发环境)
window.CefSharp = { isMock: true, BindObjectAsync: () => Promise.resolve() };
window.eoapi = { mock: true, invoke: () => {} };
// ==================== 第四步:补全其他浏览器对象并伪装 toStringTag ====================
location = document.location;
navigator = {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
"platform": "Win32",
"storage": {}
};
history = {};
screen = {availWidth: 1920, availHeight: 1040, width: 1920, height: 1080, colorDepth: 24};
localStorage = {getItem: () => {}, setItem: () => {}}; safefunction(localStorage.getItem);
sessionStorage = {};
// 伪装 Symbol.toStringTag,绕过 Object.prototype.toString.call 的环境检测
const fakeToStringTag = (obj, tag) => {
Object.defineProperty(obj, Symbol.toStringTag, {
value: tag,
configurable: true,
writable: true
});
};
fakeToStringTag(document, 'HTMLDocument');
fakeToStringTag(location, 'Location');
fakeToStringTag(navigator, 'Navigator');
fakeToStringTag(history, 'History');
fakeToStringTag(screen, 'Screen');
fakeToStringTag(localStorage, 'Storage');
fakeToStringTag(sessionStorage, 'Storage');
// ==================== 第五步:动态代理兜底 ====================
function createProxy(objName) {
return new Proxy(window[objName] || {}, {
get(target, prop) {
console_log(`[GET] 对象: ${objName}, 属性: ${String(prop)}`);
return Reflect.get(target, prop);
},
set(target, prop, val) {
console_log(`[SET] 对象: ${objName}, 属性: ${String(prop)}, 值: ${JSON.stringify(val)}`);
return Reflect.set(target, prop, val);
}
});
}
// 只对核心对象挂代理,既能监控缺失属性,又不会严重影响性能
['window', 'document', 'location', 'navigator', 'screen'].forEach(name => {
window[name] = createProxy(name);
});
// ==================== 第六步:注入 bdms 并初始化 ====================
require('./bdms'); // 将你从抖音提取的 bdms 代码放在同目录下
window.bdms.init({
aid: 6383, // 抖音 Web 应用 ID
pageId: 6241, // 页面 ID
paths: ['^/webcast/', '^/aweme/v1/'], // 需要签名的接口路径(正则)
boe: false,
ddrt: 8.5,
ic: 8.5
});
// ==================== 第七步:模拟 XHR 触发 a_bogus 生成 ====================
XMLHttpRequest = function XMLHttpRequest() {};
safefunction(XMLHttpRequest);
XMLHttpRequest.prototype.send = function send() {
console_log('触发 XHR.send,等待加密...');
};
safefunction(XMLHttpRequest.prototype.send);
function get_ab(fullUrlWithParams) {
const xhr = new XMLHttpRequest();
// bdms 会通过这两个数组读取请求信息来构造 a_bogus
xhr.bdmsInvokeList = [
{ args: ['GET', fullUrlWithParams, true] },
{ args: ['Accept', 'application/json, text/plain, */*'] }
];
xhr.invokeList = [
{ name: 'addEventListener', args: ['load', null] },
{ name: 'addEventListener', args: ['error', null] }
];
xhr.send(null);
return window.a_bogus;
}