自学内容网 自学内容网

揭秘:b站可以通过弹幕查询到发送者吗?答案是:不可行

查找发送者

发弹幕被找到

最近,我的一个好兄弟遇到了这样一个问题:他在b站发弹幕,结果被人找到了。他对此很困惑:“发送弹幕不是匿名的吗?只有评论才能看到用户名啊,难道发弹幕也可以被找到吗?“

事实上,b站的弹幕确实是匿名的,但是为什么好兄弟却会因为发弹幕被找到呢?发送弹幕的人总是可以被找到吗?下面,我们就来揭秘一下这个问题。

使用工具

如果,你去网上搜索,“怎么样查询弹幕的发送者”,可能最常见到的,是教程告诉你使用这样一个工具,查b站弹幕发送者工具

 首先,按照要求,输入一个视频地址:

此时,就查询到了很多弹幕,然后再点击查询,就可以看到弹幕的发送者了。看起来,非常厉害,但是,这是怎么做到的呢?

 弹幕查询

获取视频历史弹幕

首先,我们可以通过一个视频,查询到其历史弹幕,例如,再这个函数中,只需要提供一个bv号,就可以得到视频的历史弹幕,以及发送者。

不过需要注意的是,此处的发送者并非明文的发送者,而是通过加密校验的发送者,因此,是不能够直接得到对应用户的。

import requests
from parsel import Selector


def get_danmaku_sender(bvid, agent="bilibili ganbei"):
    headers = {"user-agent": agent}
    url = "https://api.bilibili.com/x/web-interface/view"
    r = requests.get(url, headers=headers, params={"bvid": bvid})
    oid = r.json().get("data").get("cid")

    url2 = "https://api.bilibili.com/x/v1/dm/list.so"
    r2 = requests.get(url2, headers=headers, params={"oid": oid})
    s = Selector(r2.content.decode())

    sender_list = []
    d = s.xpath("//d")
    for i in d:
        p = i.xpath("./@p").get()
        sender = p.split(",")[6]
        text = i.xpath("./text()").get()
        sender_list.append((text, sender))
        
    return sender_list

crc32校验

b站的用户匿名,使用的是crc32校验,因此,我们也可以通过python编程,将用户的uid,通过crc32,得到其匿名的值。

可以使用binascii

import binascii

data = "1000000000".encode()
crc32_value = binascii.crc32(data)
print(f"CRC32校验码: {crc32_value:08x}")

也可以使用zlib

import binascii

data = "1000000000".encode()
crc32_value = zlib.crc32(data)
print(f"CRC32校验码: {crc32_value:08x}")

实际上,crc32的所有输出可能约是42.95亿。与哈希类似,是有“碰撞”的可能的,也就是,多个输入会对应到同一个输出。换句话说,就是多个不同的用户,可能有相同的匿名结果。

那么,用户的uid是怎么样的呢?这个在用户的个人页面可以看到,是纯数字的。理论上,用户的编号11位就够了。因为11位就是100亿了,如果11位可以以9开头的话,那么足足有近1000亿的账号。即使地球上所有人都要用b站,而且,每个人都要注册10个账号也是足够用的。在这个范围内,平均会碰撞2-3次。

但是很显然,问题并没有那么容易。虽然理论上11位的uid足够使用了,但是,近年来,b站可能出于安全考虑,已经将新用户的uid增加到了16位了,这是一个非常庞大的数字。比如,某位大会员,就有16位的uid,例如:3546672061745379

那么,在这么大的范围内,crc32的碰撞又是怎么样的呢?假如,16位的所有账号全都被启用,那么会发生数十万次的碰撞(当然,实际上不可能有那么多账号在用,大多是空号)。在这个数量下,通过匿名的crc32值进行反查是不可能的。

传统位数uid查询

根据之前的分析,16位uid是无法通过常规方式解析出来的。假设,我们不考虑新的16位uid,只考虑传统的6位-11位uid,那么,这是一个有限的范围,因此,可以将所有的结果提前计算好,然后进行存储。在需要的情况下,就可以进行“反查”,类似于md5值的爆破。

这是一个简单的例子,首先将所有计算结果存入redis数据库中(需要注意的是,即使是11位数的有限范围,也包含了数百亿的结果,因此,需要消耗大量的性能与存储空间)

import redis
import zlib


r = redis.Redis(host="localhost", port=6379, db=0)

def store_crc32_values():
    for i in range(100000, 20000000000):
        data = str(i).encode("utf-8")
        crc_value = zlib.crc32(data)
        
        crc_key = f"{crc_value:08x}"
        r.sadd(crc_key, i)
        
        if i % 1000000 == 0:
            print(f"当前的进度为: {i}")

store_crc32_values()

当我们已经存储了所有情况以后,就可以查询所有可能结果了,类似于之前的查询工具。

import redis


r = redis.Redis(host="localhost", port=6379, db=0)

def query_crc32(crc_value):
    crc_key = f"{crc_value}"
    result = r.smembers(crc_key)
    
    if result:
        return [int(x.decode("utf-8")) for x in result]
    else:
        return None

    
crc_value_to_query = "efa013c8"
results = query_crc32(crc_value_to_query)

if results:
    print(f"CRC32值{crc_value_to_query},对应的原始输入为:{results}")
else:
    print("未找到对应的原始输入!")

此时,找到的值,就是一个可能的弹幕发送者。如果对应了多个值,那么就说明是这几位用户中的一个。后续还要依次查看所有用户,来进一步分析可能是哪位用户。

最终结论

现在,我们知道了,根据弹幕来查找用户是可能的,但不总是准确的,也不总是能找到。

其中,如果是一个老用户,是普通的11位以下的uid,那么是可以找到发送者列表的,他可能包含了几位可能的发送者,通过进一步分析,大概可以找到发送的用户(需要注意的是,由于我们不确定是老用户发送的,还是新用户发送的,该方法已经不准确了,因为,可能是未包含在发送者列表的新用户发送的,且恰好与老用户有相同匿名)。

但是,如果是一位新用户,用的是16位的uid,那么几乎是不可能找到对应的发送者的(特别情况下,根据b站新用户uid生成规则分析,可能可以锁定一个小的范围,例如,16位数其实只有包含了明确规则的一部分被启用。这样,可以大大缩小需要分析的范围)。因为这个范围太过广泛,包含了数十万可能性,根本无从分析。

因此,最终结论是,现在通过弹幕来查找用户变得不可能了,你可以找到“嫌疑人”,但并不总是准确。

 


原文地址:https://blog.csdn.net/sagegrass/article/details/143725757

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!