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(`<p>BUVID 在 APP 首次安装于某设备, 且首次启动时生成.</p><p>APP 首次(即每次安装后)启动, 会向云端发送本机各类设备特征, 含 <code>AndroidId</code>, <code>DrmId</code> 等, 请求是否有匹配的 BUVID, 有就使用云端的, 否则使用本地生成的.</p><p>APP 请求是否有匹配的 BUVID 发送的本机各类设备特征包括(但不限于):</p><ul><li><code>AndroidID</code></li><li><code>DrmId</code></li><li><code>IMEI</code></li><li><code>OAID</code></li><li>手机网卡 <code>MAC</code></li><li>设备品牌</li><li>设备 Model</li><li>本地生成的 BUVID</li></ul><h3 id="生成方法" tabindex="-1"><a class="header-anchor" href="#生成方法"><span>生成方法</span></a></h3><ol><li><p>选定设备特征码, 可以是 <code>AndroidID</code>, <code>DrmId</code>, 手机网卡 <code>MAC</code> 等. 记为 <code>ID</code>. 特别地, <code>MAC</code> 应当去掉 <code>:</code>, <code>GUID</code>(即 UUID) 应当去掉 <code>-</code>.</p></li><li><p>计算 <code>ID</code> 的 MD5. 记为 <code>ID_MD5</code>.</p></li><li><p>从 <code>ID_MD5</code> 抽取第 3, 13, 23 位, 失败就默认为 000, 记为 <code>ID_E</code>.</p></li><li><p>根据选定的设备特征码类型确定 BUVID Prefix, 见附录. 记为 <code>BUVID_Prefix</code>.</p></li><li><p>按 <code>{BUVID_Prefix}{ID_E}{ID_MD5}</code> 的顺序连接起来, 共37位(2+3+32). 结果应当为大写.</p></li></ol><h3 id="demo" tabindex="-1"><a class="header-anchor" href="#demo"><span>Demo</span></a></h3><h4 id="rust" tabindex="-1"><a class="header-anchor" href="#rust"><span>Rust</span></a></h4><p>代码及测试样例见 <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=40b5906cf3838a60efa83fa368b15147" target="_blank" rel="noopener noreferrer">Rust Playground</a>.</p><h2 id="设备指纹-fp-fp-local-fp-remote" tabindex="-1"><a class="header-anchor" href="#设备指纹-fp-fp-local-fp-remote"><span>设备指纹 fp (fp_local, fp_remote)</span></a></h2><p>用于请求账户相关 REST API, 及 gRPC Metadata 生成.</p><p>在请求头中, <code>fp_local</code> 和 <code>fp_remote</code> 设置为同一值即可, 暂不清楚区别.</p><h3 id="生成方法-1" tabindex="-1"><a class="header-anchor" href="#生成方法-1"><span>生成方法</span></a></h3><ol><li><p>获取 BUVID. 此处一般使用 XU Prefix 的 BUVID.</p></li><li><p>获取设备 Model(<code>Build.MODEL</code>), 如 <code>NOH-AN01</code>.</p></li><li><p>获取手机无线电固件版本号(<code>Build.getRadioVersion()</code>), 失败则留空. 如 <code>21C20B686S000C000,21C20B686S000C000</code>.</p></li><li><p>按前述顺序拼接字符串, 计算得 MD5.</p></li><li><p>获取年月日, 格式 <code>yyyyMMddhhmmss</code>, 拼接到 4 得到的字符串后.</p></li><li><p>生成 16 位随机字符串, CharSet 为 <code>0123456789abcdef</code>, 拼接到 5 得到的字符串后, 记为 <code>fp_raw</code>.</p></li><li><p>计算得到一个特殊字符串, 拼接到 <code>fp_raw</code> 后, 即得到最终的 <code>fp</code>, 特殊字符串算法见下:</p></li></ol><div class="language-rust line-numbers-mode" data-highlighter="shiki" data-ext="rust" style="background-color:#1E1E1E;color:#D4D4D4;"><pre class="shiki dark-plus vp-code"><code class="language-rust"><span class="line"><span style="color:#569CD6;">let</span><span style="color:#569CD6;"> mut</span><span style="color:#9CDCFE;"> veri_code</span><span style="color:#D4D4D4;"> = </span><span style="color:#B5CEA8;">0</span><span style="color:#D4D4D4;">;</span></span>
<span class="line"><span style="color:#6A9955;">// 有点像 HEX 的操作</span></span>
<span class="line"><span style="color:#569CD6;">let</span><span style="color:#9CDCFE;"> fp_raw_sub_str</span><span style="color:#D4D4D4;"> = </span><span style="color:#9CDCFE;">fp_raw</span></span>
<span class="line"><span style="color:#D4D4D4;">    .</span><span style="color:#DCDCAA;">as_bytes</span><span style="color:#D4D4D4;">() </span><span style="color:#6A9955;">// 将字符串 fp_raw 转换为字节数组</span></span>
<span class="line"><span style="color:#D4D4D4;">    .</span><span style="color:#DCDCAA;">chunks</span><span style="color:#D4D4D4;">(</span><span style="color:#B5CEA8;">2</span><span style="color:#D4D4D4;">)  </span><span style="color:#6A9955;">// 按每两个字节一组进行切分</span></span>
<span class="line"><span style="color:#D4D4D4;">    .</span><span style="color:#DCDCAA;">map</span><span style="color:#D4D4D4;">(|</span><span style="color:#9CDCFE;">s</span><span style="color:#D4D4D4;">| </span><span style="color:#569CD6;">unsafe</span><span style="color:#D4D4D4;"> { ::</span><span style="color:#4EC9B0;">std</span><span style="color:#D4D4D4;">::</span><span style="color:#4EC9B0;">str</span><span style="color:#D4D4D4;">::</span><span style="color:#DCDCAA;">from_utf8_unchecked</span><span style="color:#D4D4D4;">(</span><span style="color:#9CDCFE;">s</span><span style="color:#D4D4D4;">) }) </span><span style="color:#6A9955;">// 对每一组解析作为 UTF-8 字符串</span></span>
<span class="line"><span style="color:#D4D4D4;">    .</span><span style="color:#DCDCAA;">collect</span><span style="color:#D4D4D4;">::&lt;</span><span style="color:#4EC9B0;">Vec</span><span style="color:#D4D4D4;">&lt;</span><span style="color:#9CDCFE;">_</span><span style="color:#D4D4D4;">&gt;&gt;(); </span><span style="color:#6A9955;">// 将结果收集到 Vec 中</span></span>
<span class="line"><span style="color:#6A9955;">// 如果 fp_raw 的长度小于 62, 则向下取偶数减半作为循环终止条件, 否则终止条件为31</span></span>
<span class="line"><span style="color:#C586C0;">for</span><span style="color:#9CDCFE;"> i</span><span style="color:#569CD6;"> in</span><span style="color:#B5CEA8;"> 0</span><span style="color:#D4D4D4;">..({</span></span>
<span class="line"><span style="color:#C586C0;">    if</span><span style="color:#9CDCFE;"> fp_raw</span><span style="color:#D4D4D4;">.</span><span style="color:#DCDCAA;">len</span><span style="color:#D4D4D4;">() &lt; </span><span style="color:#B5CEA8;">62</span><span style="color:#D4D4D4;"> {</span></span>
<span class="line"><span style="color:#9CDCFE;">        fp_raw</span><span style="color:#D4D4D4;">.</span><span style="color:#DCDCAA;">len</span><span style="color:#D4D4D4;">() - </span><span style="color:#9CDCFE;">fp_raw</span><span style="color:#D4D4D4;">.</span><span style="color:#DCDCAA;">len</span><span style="color:#D4D4D4;">() % </span><span style="color:#B5CEA8;">2</span><span style="color:#6A9955;"> // 取偶数</span></span>
<span class="line"><span style="color:#D4D4D4;">    } </span><span style="color:#C586C0;">else</span><span style="color:#D4D4D4;"> {</span></span>
<span class="line"><span style="color:#B5CEA8;">        62</span></span>
<span class="line"><span style="color:#D4D4D4;">    }</span></span>
<span class="line"><span style="color:#D4D4D4;">} / </span><span style="color:#B5CEA8;">2</span><span style="color:#D4D4D4;">)</span></span>
<span class="line"><span style="color:#D4D4D4;">{</span></span>
<span class="line"><span style="color:#6A9955;">    // 将每组字符串转换为对应的 16 进制整数, 将转换得到的整数加到 veri_code 上. </span></span>
<span class="line"><span style="color:#9CDCFE;">    veri_code</span><span style="color:#D4D4D4;"> += </span><span style="color:#4EC9B0;">i32</span><span style="color:#D4D4D4;">::</span><span style="color:#DCDCAA;">from_str_radix</span><span style="color:#D4D4D4;">(</span><span style="color:#9CDCFE;">fp_raw_sub_str</span><span style="color:#D4D4D4;">[</span><span style="color:#9CDCFE;">i</span><span style="color:#D4D4D4;">], </span><span style="color:#B5CEA8;">16</span><span style="color:#D4D4D4;">).</span><span style="color:#DCDCAA;">unwrap_or</span><span style="color:#D4D4D4;">(</span><span style="color:#B5CEA8;">0</span><span style="color:#D4D4D4;">);</span></span>
<span class="line"><span style="color:#D4D4D4;">}</span></span>
<span class="line"><span style="color:#6A9955;">// 最后将 veri_code 对 256 取余, 格式化为两位的 16 进制字符串</span></span>
<span class="line"><span style="color:#569CD6;">let</span><span style="color:#9CDCFE;"> veri_code</span><span style="color:#D4D4D4;"> = </span><span style="color:#DCDCAA;">format!</span><span style="color:#D4D4D4;">(</span><span style="color:#CE9178;">&quot;{:0&gt;2x}&quot;</span><span style="color:#D4D4D4;">, </span><span style="color:#9CDCFE;">veri_code</span><span style="color:#D4D4D4;"> % </span><span style="color:#B5CEA8;">256</span><span style="color:#D4D4D4;">);</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="demo-1" tabindex="-1"><a class="header-anchor" href="#demo-1"><span>Demo</span></a></h3><h4 id="rust-1" tabindex="-1"><a class="header-anchor" href="#rust-1"><span>Rust</span></a></h4><p>代码及测试样例见 <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=40b5906cf3838a60efa83fa368b15147" target="_blank" rel="noopener noreferrer">Rust Playground</a>.</p><h2 id="附录" tabindex="-1"><a class="header-anchor" href="#附录"><span>附录</span></a></h2><h3 id="buvid-prefix" tabindex="-1"><a class="header-anchor" href="#buvid-prefix"><span>BUVID Prefix</span></a></h3><table><thead><tr><th style="text-align:center;">设备特征码</th><th style="text-align:center;">BUVID Prefix</th><th style="text-align:center;">备注</th></tr></thead><tbody><tr><td style="text-align:center;"><code>AndroidID</code></td><td style="text-align:center;"><code>XX</code></td><td style="text-align:center;"></td></tr><tr><td style="text-align:center;"><code>DrmId</code></td><td style="text-align:center;"><code>XU</code></td><td style="text-align:center;"></td></tr><tr><td style="text-align:center;"><code>IMEI</code></td><td style="text-align:center;"><code>XZ</code></td><td style="text-align:center;">已弃用</td></tr><tr><td style="text-align:center;"><code>GUID</code></td><td style="text-align:center;"><code>XW</code></td><td style="text-align:center;">已弃用</td></tr><tr><td style="text-align:center;"><code>MAC</code></td><td style="text-align:center;"><code>XY</code></td><td style="text-align:center;"></td></tr><tr><td style="text-align:center;"><code>GoogleId</code></td><td style="text-align:center;"><code>XG</code></td><td style="text-align:center;">东南亚版本</td></tr><tr><td style="text-align:center;"><code>FacebookId</code></td><td style="text-align:center;"><code>XF</code></td><td style="text-align:center;">东南亚版本</td></tr></tbody></table>`,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};
