import{_ as l,c as o,b as e,a as t,d as a,e as p,w as c,r,o as d}from"./app-Dgsdh8A6.js";const i={};function D(y,s){const n=r("RouteLink");return d(),o("div",null,[s[3]||(s[3]=e("h1",{id:"设备各类标识算法-app-端",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#设备各类标识算法-app-端"},[e("span",null,"设备各类标识算法(APP 端)")])],-1)),s[4]||(s[4]=e("h2",{id:"设备唯一标识-buvid",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#设备唯一标识-buvid"},[e("span",null,"设备唯一标识 BUVID")])],-1)),e("p",null,[s[1]||(s[1]=a("注意区分于 Web 端的 ")),p(n,{to:"/docs/misc/buvid3_4.html"},{default:c(()=>s[0]||(s[0]=[a("buvid3, buvid4")])),_:1,__:[0]}),s[2]||(s[2]=a("."))]),s[5]||(s[5]=t(`

BUVID 在 APP 首次安装于某设备, 且首次启动时生成.

APP 首次(即每次安装后)启动, 会向云端发送本机各类设备特征, 含 AndroidId, DrmId 等, 请求是否有匹配的 BUVID, 有就使用云端的, 否则使用本地生成的.

APP 请求是否有匹配的 BUVID 发送的本机各类设备特征包括(但不限于):

生成方法

  1. 选定设备特征码, 可以是 AndroidID, DrmId, 手机网卡 MAC 等. 记为 ID. 特别地, MAC 应当去掉 :, GUID(即 UUID) 应当去掉 -.

  2. 计算 ID 的 MD5. 记为 ID_MD5.

  3. ID_MD5 抽取第 3, 13, 23 位, 失败就默认为 000, 记为 ID_E.

  4. 根据选定的设备特征码类型确定 BUVID Prefix, 见附录. 记为 BUVID_Prefix.

  5. {BUVID_Prefix}{ID_E}{ID_MD5} 的顺序连接起来, 共37位(2+3+32). 结果应当为大写.

Demo

Rust

代码及测试样例见 Rust Playground.

设备指纹 fp (fp_local, fp_remote)

用于请求账户相关 REST API, 及 gRPC Metadata 生成.

在请求头中, fp_localfp_remote 设置为同一值即可, 暂不清楚区别.

生成方法

  1. 获取 BUVID. 此处一般使用 XU Prefix 的 BUVID.

  2. 获取设备 Model(Build.MODEL), 如 NOH-AN01.

  3. 获取手机无线电固件版本号(Build.getRadioVersion()), 失败则留空. 如 21C20B686S000C000,21C20B686S000C000.

  4. 按前述顺序拼接字符串, 计算得 MD5.

  5. 获取年月日, 格式 yyyyMMddhhmmss, 拼接到 4 得到的字符串后.

  6. 生成 16 位随机字符串, CharSet 为 0123456789abcdef, 拼接到 5 得到的字符串后, 记为 fp_raw.

  7. 计算得到一个特殊字符串, 拼接到 fp_raw 后, 即得到最终的 fp, 特殊字符串算法见下:

let mut veri_code = 0;
// 有点像 HEX 的操作
let fp_raw_sub_str = fp_raw
    .as_bytes() // 将字符串 fp_raw 转换为字节数组
    .chunks(2)  // 按每两个字节一组进行切分
    .map(|s| unsafe { ::std::str::from_utf8_unchecked(s) }) // 对每一组解析作为 UTF-8 字符串
    .collect::<Vec<_>>(); // 将结果收集到 Vec 中
// 如果 fp_raw 的长度小于 62, 则向下取偶数减半作为循环终止条件, 否则终止条件为31
for i in 0..({
    if fp_raw.len() < 62 {
        fp_raw.len() - fp_raw.len() % 2 // 取偶数
    } else {
        62
    }
} / 2)
{
    // 将每组字符串转换为对应的 16 进制整数, 将转换得到的整数加到 veri_code 上. 
    veri_code += i32::from_str_radix(fp_raw_sub_str[i], 16).unwrap_or(0);
}
// 最后将 veri_code 对 256 取余, 格式化为两位的 16 进制字符串
let veri_code = format!("{:0>2x}", veri_code % 256);

Demo

Rust

代码及测试样例见 Rust Playground.

附录

BUVID Prefix

设备特征码BUVID Prefix备注
AndroidIDXX
DrmIdXU
IMEIXZ已弃用
GUIDXW已弃用
MACXY
GoogleIdXG东南亚版本
FacebookIdXF东南亚版本
`,21))])}const m=l(i,[["render",D]]),C=JSON.parse('{"path":"/docs/misc/device_identity.html","title":"设备各类标识算法(APP 端)","lang":"zh-CN","frontmatter":{},"git":{"updatedTime":1721909032000,"contributors":[{"name":"cxw620","username":"cxw620","email":"70561268+cxw620@users.noreply.github.com","commits":1,"url":"https://github.com/cxw620"},{"name":"SessionHu","username":"SessionHu","email":"102411014+SessionHu@users.noreply.github.com","commits":1,"url":"https://github.com/SessionHu"}],"changelog":[{"hash":"18c1efbc102ae6b44c8f5314c90e5e64f0d926cd","time":1721909032000,"email":"102411014+SessionHu@users.noreply.github.com","author":"Session小胡","message":"feat: bili_ticket 算法 Java 实现 及 信息补充 及 错误修正 (#1061)"},{"hash":"f5263d04570c9dcd1d1554a0c67651c532fe1b6a","time":1689310001000,"email":"70561268+cxw620@users.noreply.github.com","author":"陈寒彤","message":"add grpc docs (#741)"}]},"filePathRelative":"docs/misc/device_identity.md"}');export{m as comp,C as data};