SocialSisterYi / bilibili-API-collect

哔哩哔哩-API收集整理【不断更新中....】
https://socialsisteryi.github.io/bilibili-API-collect/
Other
15.27k stars 1.73k forks source link

`bili_ticket` related discovery (#903 Extended) #940

Open cxw620 opened 10 months ago

cxw620 commented 10 months ago

Since the way getting web bili_ticket was found by @aynuarance in https://github.com/SocialSisterYi/bilibili-API-collect/issues/903, I guess that the way getting app bili_ticket is similar and also makes use of HS256, meaning that what we need to do is finding the HMAC key. After a day of hard work REing of libbili.so(OLLVM obfuscation, f**k you), I successfully did so.

Encryption algorithm: HMAC-SHA256

HMAC KEY INFO:

Details:

Progress:

cxw620 commented 10 months ago

x-exbadbasket seems not a must so we can leave it empty.

Here's example of x-exbadbasket (already converted into json string and formatted) with explain (may be wrong) of each param. Not familiar with reverse engineering native codes and I need more help.

{
    "b00e":"tv.danmaku.bili", // pn => process name
    "a0c6":"7.57.2", // vn => version name
    "c94e":"3.2.43", // sdk_version => ?
    "cd5e":"android", // os
    "b59e":"", // serial, leave it empty
    "dd3b":0, // root?
    "a769":0, // root?
    "fd49":"11", // osv => os version
    "c203":"", // mac, default empty
    "b935":458243454, // apk_sign => **Not know how `libbili.so` gets such value**
    "ed96":"", // mid
    "f438":"XU0D0580A80C82276D9DF33B4D20665C42E33", // buvid
    "e57c":"Dalvik/2.1.0 (Linux; U; Android 11; Pixel 2 XL Build/RP1A.201005.004.A1) 7.57.2 os/android model/Pixel 2 XL mobi_app/android build/7572100 channel/master innerVer/7572110 osVer/11 network/2", // ua
    "aff2":1, // app_id
    "edc2":1705589660, // ctime
    "e24d":7572110, // vc => version code
    "e701":"13566853", // build => build sn
    "e29f":"0", // ptrace
    "e58c":"", // frida => **Not know how `libbili.so` gets such value**
    "fd4c":"", // xposed => **Not know how `libbili.so` gets such value**
    "d7be":"", // magisk => **Not know how `libbili.so` gets such value**
    "e7fa":1, // net
    "debc":"google", // brand
    "adf0":"Pixel 2 XL", // model
    "ccd6":1705677891, // fts
    "ada0":"a3811c3af294c9ff045bf24c9bb0545b2024011923245159b5fa061488ab5b05" // fp => see `fp_local`
}

I'm more than curious about the relation between hashcode and real name(ahh, pure characters seen from the register) like b00e and pn. MD5 or any else? I don't know...

SessionHu commented 2 months ago

I successfully generate hexsign of APP platform with the help of your code. But I do not know how to get bili_ticket with hexsign of APP platform from API. I tried to add these extra fields to request headers or URL querys but failed. To be honest, Rust is too abstract to understand. So I use TypeScript to test.

Here is my code.

import crypto from 'crypto';

function app_sign(device_info: Uint8Array, x_exbadbasket: Uint8Array, x_fingerprint: Uint8Array): string {
    const hmac = crypto.createHmac('sha256', 'Ezlc3tgtl');
    hmac.update(device_info);
    hmac.update('x-exbadbasket').update(x_exbadbasket);
    hmac.update('x-fingerprint').update(x_fingerprint);
    return hmac.digest('hex');
}

function web_sign(timestamp: number): string {
    const hmac = crypto.createHmac('sha256', 'XgwSnGZ1p');
    hmac.update(`ts${timestamp}`);
    return hmac.digest('hex');
}

async function gen_bili_ticket(mark: 'ec01' | 'ec02', hexsign: string, timestamp: number, csrf: string | null): Promise<any> {
    const url: string = 'https://api.bilibili.com/bapis/bilibili.api.ticket.v1.Ticket/GenWebTicket';
    const params: URLSearchParams = new URLSearchParams({
        key_id: mark,
        hexsign: hexsign,
        'context[ts]': timestamp.toString(),
        csrf: csrf || ''
    });
    try {
        const response = await fetch(`${url}?${params.toString()}`, {
            method: 'POST',
            headers: {
                'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0'
            }
        });
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        const data = await response.json();
        return data;
    } catch (e) {
        throw e;
    }
}

(async () => {
    try {
        const timestamp: number = Math.floor(Date.now() / 1000);
        const apphexsign: string = app_sign(
            new Uint8Array([0x1, 0x2, 0x3, 0x4]),
            new Uint8Array([0x5, 0x6, 0x7, 0x8]),
            new Uint8Array([0x5, 0x6, 0x7, 0x8]),
        );
        const webhexsign: string = web_sign(timestamp);
        console.log('AppHexSign:', apphexsign);
        console.log('WebHexSign:', webhexsign);
        const appbiliticket = await gen_bili_ticket('ec01', apphexsign, timestamp, null);
        const webbiliticket = await gen_bili_ticket('ec02', webhexsign, timestamp, null);
        console.log('AppBiliTicket:', appbiliticket);
        console.log('WebBiliTicket:', webbiliticket);
    } catch (e) {
        console.error('Failed to get BiliTicket:', e);
    }
})();

Here is output.

❯ ts-node bili_ticket_all.ts
AppHexSign: 4b7790ee464223540731d50cbf71d8a8622943aea679ad126189f6b41767c5aa
WebHexSign: 8d88ea3fa5bdd525c9ad64d13d422930a49f97c935f0762fbb1f1657869fb885
AppBiliTicket: { code: -400, message: 'invalid sign', ttl: 1 }
WebBiliTicket: {
  code: 0,
  message: 'OK',
  data: {
    ticket: 'eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjUyNTUxMzIsImlhdCI6MTcyNDk5NTg3MiwicGx0IjotMX0.WPO-oWCbNmLkl3wiuYsZOm5EFdFk5nF6IXXkdm5Qaus',
    created_at: 1724995872,
    ttl: 259200,
    context: {},
    nav: {
      img: 'https://i0.hdslb.com/bfs/wbi/7cd084941338484aae1ad9425b84077c.png',
      sub: 'https://i0.hdslb.com/bfs/wbi/4932caff0ff746eab6f01bf08b70ac45.png'
    }
  },
  ttl: 1
}
cxw620 commented 2 months ago

I successfully generate hexsign of APP platform with the help of your code. But I do not know how to get bili_ticket with hexsign of APP platform from API. I tried to add these extra fields to request headers or URL querys but failed. To be honest, Rust is too abstract to understand. So I use TypeScript to test.

Not REST api https://api.bilibili.com/bapis/bilibili.api.ticket.v1.Ticket/GenWebTicket but gRPC for app.