抖音a_bogus参数加密逆向

概述

本技术文档详细记录了针对抖音Web端反爬机制中a_bogus参数的逆向工程过程。该参数是抖音API请求中的关键加密参数,用于验证请求的合法性。通过完整的环境模拟和Hook技术,我们成功实现了该参数的本地生成。

网页分析

1755508179348-323045d3-880f-4f44-b4b9-5bd9c6bd0830.png

1755508278666-0ec1411e-ce6a-485c-89f3-0c2da9014b7a.png

1755508403830-dcd5921a-a7fc-446a-8781-874b593cb245.png

1755508479596-ce745007-5f71-4037-81bc-ddbbeffe1e2d.png

1755508554344-0b7eac26-56a5-4c2a-a661-45bc148f56e1.png

1755508605590-303d5343-258d-4570-bc9b-a197ccbf7fcc.png

1755508668143-0dcdf0ae-973e-4e07-9cf5-603191210c9f.png

1755508864406-90ec8435-690c-43d3-9283-707e715c886b.png

1755509036103-8f883536-918a-44ec-8af1-dc782d67493d.png

技术背景

2.1 抖音反爬机制特点

  • 基于浏览器指纹的深度检测
  • 动态加密参数a_bogus验证
  • 函数调用链混淆与保护
  • 环境完整性检查

2.2 破解技术路线

  1. 环境模拟:完整构建浏览器运行环境
  2. 函数Hook:拦截关键函数调用
  3. 原型链保护:防止检测代码识别环境差异
  4. 动态代理:监控对象属性访问

核心实现技术

3.1 环境模拟系统

3.1.1 全局对象初始化

window = global;
window.requestAnimationFrame = function(){}
window.HTMLSpanElement = function(){}

// 窗口属性配置
window.outerWidth = 1773
window.outerHeight = 293
window.innerWidth = 293
window.innerHeight = 293

3.1.2 SDK版本信息模拟

window._sdkGlueVersionMap = {
    "sdkGlueVersion": "1.0.0.64-fix.01",
    "bdmsVersion": "1.0.1.19-fix.01",
    "captchaVersion": "4.0.10"
}

3.2 函数保护机制

3.2.1 安全函数包装器

const $toString = Function.toString
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
    })
}

// 重写Function.prototype.toString
delete Function.prototype['toString']
set_native(Function.prototype, 'toString', myToString)

3.2.2 关键函数保护

globalThis.safefunction = (func) => {
    set_native(func, myFunction_toString_symbol, `function ${func.name || ''}() { [native code] }`)
}

// 保护定时器函数
setInterval = function setInterval(){}; safefunction(setInterval);
setTimeout = function setTimeout(){}; safefunction(setTimeout);

3.3 DOM环境模拟

3.3.1 文档对象模型

document = {
    createElement: function(tag) {
        if (tag == 'span') return span
        if (tag == 'canvas') return canvas
        return {}
    },
    documentElement: {},
    createEvent: function(){},
    addEventListener: function(){},
    all: {}
}

3.3.2 自定义元素实现

span = {classList: {}}
canvas = {
    getContext: function(){}
}
safefunction(canvas.getContext)

3.4 浏览器特性模拟

3.4.1 导航对象

navigator = {
    userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
    platform: 'Win32',
    storage: {}
}

3.4.2 屏幕属性

screen = {
    availWidth: 1920,
    availHeight: 1040,
    width: 1920,
    height: 1080,
    colorDepth: 24,
    availLeft: 0
}

反检测技术实现

4.1 Symbol.toStringTag 伪装

Object.defineProperty(document, Symbol.toStringTag, {
    value: 'HTMLDocument',
    configurable: true,
    writable: true
});

Object.defineProperty(navigator, Symbol.toStringTag, {
    value: 'Navigator',
    configurable: true,
    writable: true
});

4.2 第三方SDK模拟

window.CefSharp = {
    isMock: true,
    BindObjectAsync: function() { return Promise.resolve(); }
};

window.eoapi = {
    mock: true,
    invoke: function() { console.log('eoapi mock called'); }
};

动态代理监控系统

5.1 代理处理器实现

function get_environment(proxy_array) {
    const createProxyHandler = (objName) => ({
        get(target, property, receiver) {
            const value = Reflect.get(target, property, receiver)
            console.log(`[GET] 对象: ${objName}, 属性: ${String(property)}`)
            return value
        },
        set(target, property, value, receiver) {
            console.log(`[SET] 对象: ${objName}, 属性: ${String(property)}`)
            return Reflect.set(target, property, value, receiver)
        }
    })
    
    proxy_array.forEach((objName) => {
        try {
            let obj = objName in window ? window[objName] : {}
            window[objName] = new Proxy(obj, createProxyHandler(objName))
        } catch (e) {
            console.error(`处理 ${objName} 时出错:`, e)
        }
    })
}

5.2 监控对象配置

proxy_array = [
    'window',
    'document',
    'location',
    'navigator',
    'history',
    'screen',
    'localStorage',
    'sessionStorage',
    'span',
    'canvas'
]
get_environment(proxy_array)

加密参数生成流程

6.1 bdms初始化

require('./bdms')
window.bdms.init({
    aid: 6383,
    pageId: 6241,
    paths: ['^/webcast/', '^/aweme/v1/'],
    boe: false,
    ddrt: 8.5,
    ic: 8.5
})

6.2 a_bogus生成函数

function get_ab(params) {
    xhr = new XMLHttpRequest()
    xhr.bdmsInvokeList = [
        { args: ['GET', params, 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
}

完整代码


import requests
import execjs
from urllib.parse import urlencode

headers = {
    "accept": "application/json, text/plain, */*",
    "accept-language": "zh-CN,zh;q=0.9",
    "cache-control": "no-cache",
    "pragma": "no-cache",
    "priority": "u=1, i",
    "referer": "https://www.douyin.com/user/MS4wLjABAAAAPs96_XYppoAye-VK57HhjucNZfR_mTyR4KKqDBHdt5k?from_tab_name=main&vid=7521300075331456256",
    "sec-ch-ua": "\"Google Chrome\";v=\"137\", \"Chromium\";v=\"137\", \"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-origin",
    "uifid": "2eb4f745f9fe6544447c1d68cb43a44931f67e23b1951fd9ca8b76ce94a622365d5383cfffac86a6f5d9f4a996c6515de06c474d5bc145dbf8585b9de15cac9a709982e71e6779405f6a51c415bdcdb57c7efe5d11d9657d7e4f163276f1e3a551eb7337a5493c4c969bd7434a32cd5b5a9f5c0db12d2e64408a381b39018f7ae415d4210d1f59164ebb59fe54ad4412f2860dfd832192820486bf3a1e82c9b4",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
}
cookies = {
    "UIFID_TEMP": "2eb4f745f9fe6544447c1d68cb43a44931f67e23b1951fd9ca8b76ce94a622364c348c7d9a916f9d339d7226513c0350e401a1f04298a7806a492f0514d7a280aefc4e2f7ed9420e360d6daea7d56025",
    "hevc_supported": "true",
    "fpk1": "U2FsdGVkX19LuXWdPdoJUwJDGa9OvlhMpTQMqy5yGra6L9p9CAjJlysDiTli1bQFd62TBekPMQJ+G3CSFdDWyw==",
    "fpk2": "3fa31b52dd6ebc517e5492d43d77e61c",
    "bd_ticket_guard_client_web_domain": "2",
    "UIFID": "2eb4f745f9fe6544447c1d68cb43a44931f67e23b1951fd9ca8b76ce94a622365d5383cfffac86a6f5d9f4a996c6515de06c474d5bc145dbf8585b9de15cac9a709982e71e6779405f6a51c415bdcdb57c7efe5d11d9657d7e4f163276f1e3a551eb7337a5493c4c969bd7434a32cd5b5a9f5c0db12d2e64408a381b39018f7ae415d4210d1f59164ebb59fe54ad4412f2860dfd832192820486bf3a1e82c9b4",
    "SearchMultiColumnLandingAbVer": "2",
    "SEARCH_RESULT_LIST_TYPE": "%22multi%22",
    "xgplayer_device_id": "32342470011",
    "xgplayer_user_id": "990979576172",
    "live_use_vvc": "%22false%22",
    "passport_mfa_token": "CjfZsPUesQxijJk50MyNG2G5kRbK2Sqj%2BwRXLRcPX%2B8HkPG31O2%2FfMnENwQ%2B5ncNCODjbEAXdyJhGkoKPOHQKWNZxI8TUlFBC0qlRyGDldo0IwYm80lswgkKk%2F3yxskR4MWHEZpuvf%2Bv9zBMBmT2quATvI2g5%2F7y5hChlvENGPax0WwgAiIBA78fYR4%3D",
    "d_ticket": "f2640363d320cf41c637d73d8ed24baac436c",
    "n_mh": "9-mIeuD4wZnlYrrOvfzG3MuT6aQmCUtmr8FxV8Kl8xY",
    "SelfTabRedDotControl": "%5B%5D",
    "__security_mc_1_s_sdk_cert_key": "8acbdf1b-4c3b-8b22",
    "__security_server_data_status": "1",
    "my_rd": "2",
    "__security_mc_1_s_sdk_crypt_sdk": "6f3b610c-4cb2-ae51",
    "s_v_web_id": "verify_mb67kwnm_HcU0pVSA_nv07_4Q2k_BD6N_5GthzfviapYA",
    "passport_csrf_token": "9d8eadc10a526d72a353a20d2cd7b202",
    "passport_csrf_token_default": "9d8eadc10a526d72a353a20d2cd7b202",
    "volume_info": "%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Afalse%2C%22volume%22%3A0.591%7D",
    "enter_pc_once": "1",
    "dy_swidth": "1920",
    "dy_sheight": "1080",
    "is_dash_user": "1",
    "publish_badge_show_info": "%220%2C0%2C0%2C1751375097755%22",
    "h265ErrorNum": "-1",
    "stream_player_status_params": "%22%7B%5C%22is_auto_play%5C%22%3A0%2C%5C%22is_full_screen%5C%22%3A0%2C%5C%22is_full_webscreen%5C%22%3A0%2C%5C%22is_mute%5C%22%3A0%2C%5C%22is_speed%5C%22%3A1%2C%5C%22is_visible%5C%22%3A0%7D%22",
    "__live_version__": "%221.1.3.5120%22",
    "__security_mc_1_s_sdk_sign_data_key_sso": "38985a08-417a-89e5",
    "passport_auth_status": "9311c93531036fc76d78cbb011b7b817%2C",
    "passport_auth_status_ss": "9311c93531036fc76d78cbb011b7b817%2C",
    "session_tlb_tag": "sttt%7C5%7ClNh4zgz_nEZz5hkc3NEbL__________HQVGGG2mTTjFRYRf0RURdgymoaJudXvRNcyg6RKd0EaI%3D",
    "_bd_ticket_crypt_doamin": "2",
    "_bd_ticket_crypt_cookie": "d6b6f0072e8c691c4f816d25127d1981",
    "__security_mc_1_s_sdk_sign_data_key_web_protect": "a5bdc8a1-4c79-904d",
    "FOLLOW_LIVE_POINT_INFO": "%22MS4wLjABAAAAAVg2eDB7F5WcELvRy7KwgfZCCZzeoxk0YXunQ7vgEa1UpFHvLMeQnebGco8NAh-8%2F1751644800000%2F0%2F0%2F1751639923707%22",
    "FOLLOW_NUMBER_YELLOW_POINT_INFO": "%22MS4wLjABAAAAAVg2eDB7F5WcELvRy7KwgfZCCZzeoxk0YXunQ7vgEa1UpFHvLMeQnebGco8NAh-8%2F1751817600000%2F0%2F1751778336164%2F0%22",
    "login_time": "1751778820338",
    "odin_tt": "0966ab51948e2b573ad57290c70f9b4aa6290deedbc3029a45e4c2a2c4d96d58575b79ddf77f3a3a251a1955359176ac7948557567a5a8797c9801994555de52b741c97387f5d5135d70006841214837",
    "pwa2": "%220%7C0%7C2%7C0%22",
    "download_guide": "%223%2F20250704%2F1%22",
    "WallpaperGuide": "%7B%22showTime%22%3A1751779112350%2C%22closeTime%22%3A0%2C%22showCount%22%3A1%2C%22cursor1%22%3A64%2C%22cursor2%22%3A20%2C%22hoverTime%22%3A1751548108048%7D",
    "live_can_add_dy_2_desktop": "%221%22",
    "strategyABtestKey": "%221751868942.104%22",
    "biz_trace_id": "83ad859c",
    "ttwid": "1%7CGwV7OJwoKkpTbMetbdx2z4VlKSlkI1BsZlPtvBdsiKQ%7C1751868972%7Cdfd602d3d7530e48172eccf839f9a378e759a74fc7c278f93074bf5af54b249e",
    "bd_ticket_guard_client_data": "eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtcmVlLXB1YmxpYy1rZXkiOiJCS0JRR2NFemxKeTNnQmJJek9YaFdkTjA3bDh2NnVVTytadmdlUWNJSWp2STVFYUVOc3BIVmdjN2JtNk8yT2VZcW8xK2psMHVucFFGQTJ6RzRYbS9VeU09IiwiYmQtdGlja2V0LWd1YXJkLXdlYi12ZXJzaW9uIjoyfQ%3D%3D",
    "__ac_nonce": "0686b6f48004aeb3f1fd9",
    "__ac_signature": "_02B4Z6wo00f01W0ZveAAAIDBWj15vpURIyVtOblAADMwe7",
    "IsDouyinActive": "true",
    "home_can_add_dy_2_desktop": "%220%22",
    "stream_recommend_feed_params": "%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1920%2C%5C%22screen_height%5C%22%3A1080%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A16%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A100%7D%22"
}
url = "https://www.douyin.com/aweme/v1/web/aweme/post/"
params = {
    "device_platform": "webapp",
    "aid": "6383",
    "channel": "channel_pc_web",
    "sec_user_id": "MS4wLjABAAAAPs96_XYppoAye-VK57HhjucNZfR_mTyR4KKqDBHdt5k",
    "max_cursor": "0",
    "locate_item_id": "7521300075331456256",
    "locate_query": "false",
    "show_live_replay_strategy": "1",
    "need_time_list": "1",
    "time_list_query": "0",
    "whale_cut_token": "",
    "cut_version": "1",
    "count": "18",
    "publish_video_strategy_type": "2",
    "from_user_page": "1",
    "update_version_code": "170400",
    "pc_client_type": "1",
    "pc_libra_divert": "Windows",
    "support_h265": "1",
    "support_dash": "1",
    "cpu_core_num": "16",
    "version_code": "290100",
    "version_name": "29.1.0",
    "cookie_enabled": "true",
    "screen_width": "1920",
    "screen_height": "1080",
    "browser_language": "zh-CN",
    "browser_platform": "Win32",
    "browser_name": "Chrome",
    "browser_version": "137.0.0.0",
    "browser_online": "true",
    "engine_name": "Blink",
    "engine_version": "137.0.0.0",
    "os_name": "Windows",
    "os_version": "10",
    "device_memory": "8",
    "platform": "PC",
    "downlink": "10",
    "effective_type": "4g",
    "round_trip_time": "100",
    "webid": "7486005262718158363",
    "uifid": "2eb4f745f9fe6544447c1d68cb43a44931f67e23b1951fd9ca8b76ce94a622365d5383cfffac86a6f5d9f4a996c6515de06c474d5bc145dbf8585b9de15cac9a709982e71e6779405f6a51c415bdcdb57c7efe5d11d9657d7e4f163276f1e3a551eb7337a5493c4c969bd7434a32cd5b5a9f5c0db12d2e64408a381b39018f7ae415d4210d1f59164ebb59fe54ad4412f2860dfd832192820486bf3a1e82c9b4",
    "msToken": "fW2gW_DXSzbPKlMKipT8DBHbFIKeS94CZOZeAj0sFqmVavAISD9ZhcHefhK7IWiyKYswFy12SW3VAD-wby1eNVWYszNMnpQ0ARAxDR7cjoZDfAfcne3xZvbqbLVt4jR_Z8Ki_liwIW4OQdSboTeBHghHMQkzieN6hlbss-k1PumR81rQOX8nba0=",
    "verifyFp": "verify_mb67kwnm_HcU0pVSA_nv07_4Q2k_BD6N_5GthzfviapYA",
    "fp": "verify_mb67kwnm_HcU0pVSA_nv07_4Q2k_BD6N_5GthzfviapYA"
}
url_params = url + "?" + urlencode(params)
# print(url_params)
ctx = execjs.compile(open('env.js', 'r', encoding='utf-8').read()).call("get_ab", url_params)
# print(ctx)
params['a_bogus'] = ctx
response = requests.get(url, headers=headers, cookies=cookies, params=params)

print(response.text)
print(response)
window = global;

console_log = console.log
;(() => {
  '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
    })
  }

  // 删除原型链上的toString
  delete Function.prototype['toString']

  // 自定义一个getter方法,其实就是一个hook
  set_native(Function.prototype, 'toString', myToString)

  // 套个娃,保护一下我们定义的toString:避免对toString再次toString
  set_native(
    Function.prototype.toString,
    myFunction_toString_symbol,
    'function toString() { [native code] }'
  )

  globalThis.safefunction = (func) => {
    set_native(
      func,
      myFunction_toString_symbol,
      `function ${func.name || ''}() { [native code] }`
    )
  }

  // 导出函数到globalThis,更改原型上的toString为自己的toString
  // 这个方法相当于过掉func的toString检测点
}).call(globalThis)
setInterval = function setInterval(){};safefunction(setInterval);
setTimeout = function setTimeout(){};safefunction(setTimeout);

window.requestAnimationFrame = function requestAnimationFrame(){};safefunction(window.requestAnimationFrame)
window.EventSource = function EventSource(){};safefunction(window.EventSource)
window.addEventListener = function addEventListener(){};safefunction(window.addEventListener)
window.OfflineAudioContext = function OfflineAudioContext(){};safefunction(window.OfflineAudioContext)
window.outerWidth = 1773
window.outerHeight = 293
window.innerWidth = 293
window.innerHeight = 293

window._sdkGlueVersionMap = {
    "sdkGlueVersion": "1.0.0.64-fix.01",
    "bdmsVersion": "1.0.1.19-fix.01",
    "captchaVersion": "4.0.10"
}
window.indexedDB = {

}

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('createElement:::', tag)
    return {}
  },
  documentElement: {},
  createEvent: function createEvent() {},
  addEventListener: function addEventListener(){},
}
// 模拟这些属性避免检测
window.CefSharp = {
  isMock: true,
  BindObjectAsync: function() { return Promise.resolve(); }
};

window.eoapi = {
  mock: true,
  invoke: function() { console.log('eoapi mock called'); }
};

window.eoWebBrowserDispatcher = {
  mock: true,
  dispatch: function() {}
};

safefunction(document.createElement)
safefunction(document.createEvent)
safefunction(document.addEventListener)
document.all = {}
document.location = {
    "ancestorOrigins": {},
    "href": "https://www.douyin.com/user/MS4wLjABAAAAPs96_XYppoAye-VK57HhjucNZfR_mTyR4KKqDBHdt5k?from_tab_name=main&vid=7521300075331456256",
    "origin": "https://www.douyin.com",
    "protocol": "https:",
    "host": "www.douyin.com",
    "hostname": "www.douyin.com",
    "port": "",
    "pathname": "/user/MS4wLjABAAAAPs96_XYppoAye-VK57HhjucNZfR_mTyR4KKqDBHdt5k",
    "search": "?from_tab_name=main&vid=7521300075331456256",
    "hash": ""
}

location = {
    "ancestorOrigins": {},
    "href": "https://www.douyin.com/user/MS4wLjABAAAAPs96_XYppoAye-VK57HhjucNZfR_mTyR4KKqDBHdt5k?from_tab_name=main",
    "origin": "https://www.douyin.com",
    "protocol": "https:",
    "host": "www.douyin.com",
    "hostname": "www.douyin.com",
    "port": "",
    "pathname": "/user/MS4wLjABAAAAPs96_XYppoAye-VK57HhjucNZfR_mTyR4KKqDBHdt5k",
    "search": "?from_tab_name=main",
    "hash": ""
}

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"}
navigator.storage = {}
navigator.platform = 'Win32'
history = {}
screen = {availWidth: 1920, availHeight: 1040, width: 1920, height: 1080, colorDepth: 24, availLeft: 0}
localStorage = {}
localStorage.getItem = function getItem(){};safefunction(localStorage.getItem)


sessionStorage = {}

Object.defineProperty(document, Symbol.toStringTag, {
  value: 'HTMLDocument',
  configurable: true,
  writable: true
});
Object.defineProperty(location, Symbol.toStringTag, {
  value: 'Location',
  configurable: true,
  writable: true
});
Object.defineProperty(navigator, Symbol.toStringTag, {
  value: 'Navigator',
  configurable: true,
  writable: true
});
Object.defineProperty(history, Symbol.toStringTag, {
  value: 'History',
  configurable: true,
  writable: true
});
Object.defineProperty(screen, Symbol.toStringTag, {
  value: 'Screen',
  configurable: true,
  writable: true
});
Object.defineProperty(localStorage, Symbol.toStringTag, {
  value: 'Storage',
  configurable: true,
  writable: true
});
Object.defineProperty(sessionStorage, Symbol.toStringTag, {
  value: 'Storage',
  configurable: true,
  writable: true
});

XMLHttpRequest = function XMLHttpRequest() {}
safefunction(XMLHttpRequest)
XMLHttpRequest.prototype.send = function send() {}
safefunction(XMLHttpRequest.prototype.send)

function get_environment(proxy_array) {
  if (!Array.isArray(proxy_array)) {
    console.error('Expected an array of property names')
    return
  }

  const createProxyHandler = (objName) => ({
    get(target, property, receiver) {
      const value = Reflect.get(target, property, receiver)
      console.log(
        `[GET] 对象: ${objName}, 属性: ${String(property)}, ` +
          `类型: ${typeof property}, 值类型: ${typeof value}, ` +
          `值: ${typeof value === 'function' ? 'function' : JSON.stringify(value)}`
      )
      return value
    },
    set(target, property, value, receiver) {
      console.log(
        `[SET] 对象: ${objName}, 属性: ${String(property)}, ` +
          `类型: ${typeof property}, 值类型: ${typeof value}, ` +
          `值: ${typeof value === 'function' ? 'function' : JSON.stringify(value)}`
      )
      return Reflect.set(target, property, value, receiver)
    },
    apply(target, thisArg, argumentsList) {
      console.log(
        `[CALL] 函数: ${objName}, ` + `参数: ${JSON.stringify(argumentsList)}`
      )
      return Reflect.apply(target, thisArg, argumentsList)
    }
  })

  proxy_array.forEach((objName) => {
    try {
      // 尝试获取全局对象
      let obj
      if (objName in window) {
        obj = window[objName]
      } else {
        obj = {}
        // 如果是特殊对象需要初始化
        if (objName === 'localStorage' || objName === 'sessionStorage') {
          obj = {
            getItem: () => {},
            setItem: () => {},
            removeItem: () => {},
            clear: () => {},
            length: 0,
            key: () => null
          }
        }
        window[objName] = obj
      }

      // 创建代理
      window[objName] = new Proxy(obj, createProxyHandler(objName))
    } catch (e) {
      console.error(`处理 ${objName} 时出错:`, e)
      window[objName] = new Proxy({}, createProxyHandler(objName))
    }
  })
}

proxy_array = [
  'window',
  'document',
  'location',
  'navigator',
  'history',
  'screen',
  'localStorage',
  'sessionStorage',
    'span',
    'canvas'
]
get_environment(proxy_array)


require('./bdms')
t = {
  aid: 6383,
  pageId: 6241,
  paths: [
    '^/webcast/',
    '^/aweme/v1/',
    '^/aweme/v2/',
    '/v1/message/send',
    '^/live/',
    '^/captcha/',
    '^/ecom/'
  ],
  boe: false,
  ddrt: 8.5,
  ic: 8.5
}
window.bdms.init(t)

function get_ab(params) {
  xhr = new XMLHttpRequest()
  xhr.bdmsInvokeList = [
    {
      args: [
        'GET',
        params,
        true
      ]
    },
    {
      args: ['Accept', 'application/json, text/plain, */*']
    },
    {
      args: [
        'uifid',
        '2eb4f745f9fe6544447c1d68cb43a44931f67e23b1951fd9ca8b76ce94a622365d5383cfffac86a6f5d9f4a996c6515de06c474d5bc145dbf8585b9de15cac9a709982e71e6779405f6a51c415bdcdb57c7efe5d11d9657d7e4f163276f1e3a551eb7337a5493c4c969bd7434a32cd5b5a9f5c0db12d2e64408a381b39018f7ae415d4210d1f59164ebb59fe54ad4412f2860dfd832192820486bf3a1e82c9b4'
      ]
    }
  ]
  xhr.invokeList = [
    {
      name: 'addEventListener',
      args: ['load', null]
    },
    {
      name: 'addEventListener',
      args: ['error', null]
    }
  ]
  xhr.send(null)
  return window.a_bogus
}
get_ab()

完整工作流程

  1. 环境初始化:构建完整的浏览器运行环境
  2. 函数保护:Hook关键函数防止检测
  3. 代理监控:设置属性访问监控
  4. 加密模块加载:引入bdms加密模块
  5. 参数生成:通过模拟XHR调用触发加密流程
  6. 结果获取:从全局变量提取生成的a_bogus

技术难点与解决方案

难点解决方案实现效果
函数toString检测重写Function.prototype.toString完美绕过函数类型检测
环境完整性检查完整模拟浏览器对象结构通过抖音的环境验证
动态加密逻辑代理监控+XHR调用模拟准确生成有效参数
第三方SDK依赖模拟CefSharp等对象消除SDK检测异常