目标分析

豆包(com.lemon.lv)是字节跳动的 AI 助手 App,用户名称(昵称)可以绑定一个唯一 ID。「抢 ID」的意义在于:豆包早期有大量简短、有意义的 ID 未被注册,可以批量检测并注册。

协议抓包

使用 mitmproxy + 证书固定绕过,抓到关键接口:

设备注册

POST https://passport.bytedance.com/api/v3/passport/user/login/new_device
aid=497858  (豆包的 AppID)

用户名检测

GET https://alice.doubao.com/alice/profile/check_username
?username=target_id&aid=497858

设置用户名

POST https://alice.doubao.com/alice/profile/update_profile
{
  "username": "target_id",
  "session_id": "<session>"
}

device_id 生成

字节系 device_id 是一个 18-19 位数字,通过设备注册接口获取:

import uuid, requests, time

DOUBAO_AID = 497858
DEVICE_REGISTER_URL = "https://passport.bytedance.com/api/v3/passport/user/login/new_device"

def gen_device_info() -> dict:
    return {
        "openudid": uuid.uuid4().hex,
        "device_type": "Pixel 7",
        "os_version": "14",
        "resolution": "1080*2400",
        "dpi": "420",
        "language": "zh",
        "timezone": "Asia/Shanghai",
    }

def register_device(aid: int = DOUBAO_AID) -> dict:
    info = gen_device_info()
    headers = {
        "User-Agent": f"com.lemon.lv/1.0.0 (Linux; Android 14)",
        "Content-Type": "application/x-www-form-urlencoded",
    }
    payload = {"aid": aid, **info}
    r = requests.post(DEVICE_REGISTER_URL, data=payload, headers=headers, timeout=10)
    return r.json()  # {"device_id": "...", "install_id": "..."}

唯一性检测

import requests

ALICE_CHECK_URL = "https://alice.doubao.com/alice/profile/check_username"

def check_username(username: str, session_token: str) -> bool:
    # 返回 True 表示该用户名可用
    r = requests.get(
        ALICE_CHECK_URL,
        params={"username": username, "aid": DOUBAO_AID},
        headers={
            "Cookie": f"sessionid={session_token}",
            "User-Agent": "doubao/1.0 (iPhone; iOS 17.0)",
        },
        timeout=5
    )
    data = r.json()
    return data.get("data", {}).get("available", False)

批量检测脚本

from concurrent.futures import ThreadPoolExecutor
import itertools, string

def gen_candidates(length: int = 4):
    # 生成所有 length 位纯字母+数字组合(演示)
    chars = string.ascii_lowercase + string.digits
    for combo in itertools.product(chars, repeat=length):
        yield "".join(combo)

def batch_check(session_token: str, candidates, max_workers: int = 20):
    available = []
    with ThreadPoolExecutor(max_workers=max_workers) as pool:
        futures = {
            pool.submit(check_username, uid, session_token): uid
            for uid in candidates
        }
        for f in futures:
            uid = futures[f]
            try:
                if f.result():
                    available.append(uid)
                    print(f"[+] 可用: {uid}")
            except Exception as e:
                print(f"[-] 检测 {uid} 失败: {e}")
    return available

if __name__ == "__main__":
    TOKEN = "your_session_token_here"
    # 检测 4 字母短 ID
    candidates = list(gen_candidates(4))[:1000]  # 只测试前1000个
    available = batch_check(TOKEN, candidates)
    print(f"
共找到 {len(available)} 个可用 ID")

注意事项

  1. 频率限制:豆包有接口调用频率限制,并发不宜过高(建议 ≤10)
  2. 账号封禁:批量注册违反服务条款,生产使用请评估合规性
  3. 验证码:注册流程可能触发图形验证码,需对接识别服务

免责声明:本文仅供安全学习研究使用,请勿用于违法目的。