夜间模式暗黑模式
字体
阴影
滤镜
圆角

Category: CTF

1 篇文章

thumbnail
【MiniLCTF 2020】浅析Bilibili的BV号算法
因为要给miniLCTF出题,正好趁机沉下心来研究一下B站的BV号是怎么一回事。顺便把这篇文章当做BilibiliDV题目的官方wp。在看这篇文章之前还是非常建议读者参考知乎的高赞回答。个人感觉在刚公布BV号的事实一天之后就写出了转换脚本,还是挺厉害的!这也是我出题的动机。 参考链接: 如何看待 2020 年 3 月 23 日哔哩哔哩将稿件的「av 号」变更为「BV 号」?- mcfx的回答 题目给的样本文件有一点大,为了方便一点,我就直接给出样本文件的生成脚本,可以自己跑一遍来生成一下: baseTable = 'd59nD71EcAt38aT24eCN06' baseArray = {} for i in range(22): baseArray[baseTable[i]] = i xor = 50790 inc = 114514 startNum = 1000000 endNum = 4000000 dvStaticStr = 'DV t ACD Ne ' dynIndex = [12, 11, 8, 4, 2] def encrypt(x): x = (x ^ xor) + inc dvNum = list(dvStaticStr) for i in range(5): dvNum[dynIndex[i]] = baseTable[x // 22 ** i % 22] return ''.join(dvNum) def decrypt(x): r = 0 for i in range(5): r += baseArray[x[dynIndex[i]]] * 22 ** i return (r - inc) ^ xor file = open('av1000000To3999999.txt', 'w') for num in range(startNum, endNum): file.write('av'+str(num)+' - '+encrypt(num)+'\n') 提前看到生成脚本的编码逻辑其实也没啥关系,这题重要的地方在于怎么去把这个逻辑给找出来,这是最关键的。 0x01 准备工作 一开始的工作是“观察”,找一些表面规律。一般来说,这个阶段应该找到这些事实: 算上开头的DV标识,DV号总长度是13个字符,固定不变。某些固定位上的字符固定不变,动态位用X表示出来就是这样的:DVXtXACDXNeXX 其实你把固定位上的字符拿出来看一下,是个有意义的一串字符:tACDNe。这样子可能看不出来,倒过来试试:eNDCAt。是不是发现了什么!在之后的工作中其实也有很多这样“有意思的地方”。 其实再观察的仔细一点你会发现,av号每增加一位的时候,对应dv号其实不是那种“翻天覆地”的变化,我把它亲切的称呼为“类似进位的变化规律”: 应当想到这可能是一种单表替换的编码方式,而且这个单表的顺序看起来也被打乱了。而且某些dv号在av递增时改变的位数不止一位,这样的变化情况也不是“等间距”发生的事件,事情慢慢变得麻烦起来了。这个阶段我觉得应该发现的事实有: 可能是数值转换为某种形式后进行了列表替换随着av号的递增,dv号的改变是从最末位的变化位开始变化,随后往更高位变化 0x02 base? 其实在比赛的过程中我发现已经很少有人能够做到这步了。对于替换表这样的编码方式,频率分析其实是非常重要的一个方法。我们针对样本的最后一位进行分析,并给出分析脚本: file = open('av1000000To3999999.txt', 'r') charDict = {} while True: line = file.readline() if not line: break endChar = line[-2] if endChar in charDict: charDict[endChar] = charDict[endChar] + 1 else: charDict[endChar] = 1 print(charDict) # result: # {'8': 136363, 'a': 136363, 't': 136363, '3': 136363, # 'c': 136363, 'A': 136363, '1':…