对于bittorrent协议的深入研究与探讨

最近下小电影的时候突发奇想,想到bit种子是否能够利用在ctf之中?进行一定的限制或者解密,但是再出题之前,要把协议深入研究才行,那么进入正题

一、bittorrent-tracker环境搭建

(1)、windows

非常简单,只需要安装好nodejs,直接执行

npm install -g bittorrent-tracker

即可安装,最后执行bittorrent-tracker即可

(2)、debian/ubuntu

通过该命令安装nodejs

curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -

sudo apt-get install -y nodejs

该过程较慢,请耐心等待

apt-get install npm

进行安装npm

npm install -g bittorrent-tracker

即可全局安装bittorrent-tracker,同理,命令行输入即可启动

访问http://localhost:8000/

如出现该内容,则代表成功

当然,访问http://localhost:8000/stats,可以查看状态

(3)、centos

稍微麻烦了一点,但是还好

先安装npm和nodejs,请参考这篇教程:https://www.cnblogs.com/zhi-leaf/p/10979629.html

安装好后直接输入npm install -g bittorrent-tracker

即可安装

关于启动这里有个小tips,对于一些云服务器来讲,直接启动后再关闭终端,会导致程序停止运行,这里输入

nohup bittorrent-tracker &

即可保存在后台运行且不会停止

二、做种

做种方式很多,这里只讲述常规的利用bittorrent做种以及transmission-create做种

bittorrent

点击制作种子后会显示如下窗口,我们挨个进行讲解

来源不必多说,单文件选择文件,多文件选择目录

trackers则为种子服务器,如果种子服务器未开启,则无法进行下载

可添加多个地址,直接回车间隔即可,此处对应你的上文创建的种子地址

网页速度:这个东西没啥用,如果做限速直接在服务器apache或者nginx写配置文件就行了

说明:说明

区块大小:选择的bite越小意味着分的区块就越多,对于多线程下载有着提升速度的作用

私用种子:具体科普请看:https://tieba.baidu.com/p/1190663772?red_tag=2652800582

Create Encrtpted:创建加密,没有卵用,建议关闭

相关:同样没有卵用的一些信息,建议空下

接下来创建种子即可

在你创建好种子后,会进入超长的做种期,期间电脑不能关闭且要保持网络通畅,否则其他用户无法下载种子文件

transmission-create

一句话:

./transmission-create -p -o /userdisk/data/a.torrent -t http://XX/announce.php -s 2048 /userdisk/data/a.7z &

# -p 表示这是私用的种子,这个必须要加上
# -o 生成的种子输出位置,不要忘记把名字打上
# -t tracker的地址
# -s 每个文件块的大小,单位是KB
# & 后台运行

三、下载种子

种子创建好接下来就是下载,下载不必多说,当利用软件下载后,所开启的服务器命令行会显示连接的ip

但是会因为无人做种而导致下载速度为0,毕竟你所做的种子只存在内网,如果放置在外网服务器,搭配外网下载用户,速度可提升很大。

以上是搭建种子服务器以及如何做种的教程,接下来详细解读torrent文件格式

四、torrent文件详解

为了方便演示,我们直接用文本编辑器打开一个种子文件

d8:announce33:udp://106.54.85.131:8000/announce10:created by17:BitTorrent/7.10.513:creation datei1599294904e8:encoding5:UTF-84:infod6:lengthi28e4:name8:flag.txt12:piece lengthi16384e6:pieces20:wS�E.�t�mGW����(rCee

在逐一讲解之前,来讲一下编码类型(这玩意有点像php序列化

首先种子文件的主要编码格式为B编码,其分为四种存储类型,分别为:字符型、数值型、列表型、字典型

字符型:

主要格式为:数字:字符串 例如表示flag,则为4:flag 数字以十进制指出字符串的长度

数值型:

主要格式:i数字e,其中数字依旧是十进制数,例如:

i0e:表示数字零

列表:

主要格式:l数据e,例如:

l10:snowywar’s4:blog2:is17:z.mofalongmao.xyze

上述列表数据的内容为

[‘snowywar’s’,’blog’,’url’,’z.mofalongmao.xyz’]

li2020e5:suckse

则为

[‘2020′,’sucks’]

字典:

顾名思义,是为了让具体的name与具体的数据类型表示关联,上述的三种类型仅仅是表示数据本身,而必须要用字典将其连接起来,同时字典支持嵌套使用

字典格式:d数据e

d4:Name:4:Flag7:springsl4:shit5:shite6:sshittee

而这上述这个字典的内容则为:

’name‘='flag',
'springs'=['shit','shite','sshitt']

了解了核心的B编码,让我们再来了解一下bittorrent规范

在bittorrent规范中,torrent文件又称作metainfo files,其中有announce部分与info部分组成

announce部分的关键字:

关键字含义
announce(必选)该关键字的值为Tracker的URL。
announce-list(可选)它的值存放的是备用Tracker的URL。
creation-date(可选)该关键字对应的值存放的是种子文件创建的时间。
comment(可选)它的值存放的是种子文件制作者的备注信息。
created by(可选)值存放生成种子文件的BT客户端软件的信息,如客户端名、版本号。
encoding(必选)指出info中pieces部分的编码类型,一般为UTF-8

让我们来看一下刚才的种子文件的announce部分吧

d8:announce33:udp://106.54.85.131:8000/announce10:created by17:BitTorrent/7.10.513:creation datei1599294904e8:encoding5:UTF-8

即可理解为

announce=['udp://106.54.85.131:8000/announce'] //announce地址
createby=['BitTorrent/7.10.5']   //制作工具
creation date='1599294904'       //时间戳格式
encoding = UTF-8                 //指出info部分编码

接下来继续来看看info部分的关键字:

不过值得注意,单文件传输与多文件传输的不同:单文件传输是指torrent文件只存储了单个文件下载信息;多文件传输指torrent中存储了一个以上的文件下载信息

关键字含义
piece length每个piece的长度,值是B编码类型,单位为字节,此处为你做种时所设置的区块大小,例如i262144e,即为256K
pieces对应一个字符串,此字符串长度是20的倍数。它可以再分成每20字节一段的多个字符串,分别对应块在相应索引中的SHA1校验码(hash)。
private值如果为1,则表明客户端必须通过连接Tracker来获取其他下载者信息,即peer的IP地址和端口号;如果为0,则表明客户端还可以通过其它方式获取peer的IP地址和端口号,如DHT方式,DHT(Distribute Hash Tabel)即分布式哈希表,它是一种以分布式的方式来获取peer的方法,现在许多BT客户端既支持通过Tracker来获取peer,也支持通过DHT来获取peer,如果种子文件中没有private关键字,则表明不限制一定要通过连接tracker来获取peer。
单文件:
name共享文件的文件名,也就是要下载的文件的文件名。
length共享文件的长度,以byte为单位。
md5sum可选,是共享文件的md5值,这个值在bt协议中不适用
多文件:
name存放共享文件的文件夹名字。
file它的值是一个列表,含有多个字典,每个共享文件为一个字典。每个字典中含有三个关键字:length、md5sum、path
files字典:
length共享文件的长度,以byte为单位。
md5sum可选,同上。
path存放共享文件的路径和文件名。

我们继续使用开头所展示的文件进行讲解,

下面是上面种子的info部分

4:infod6:lengthi28e4:name8:flag.txt12:piece lengthi16384e6:pieces20:wS�E.�t�mGW����(rCee

即可理解为

info{
length=28
name=flag.txt
piece length =16384 //此处单位依旧是字节=16kb
preces20://此处省略20个字节的hash值,每个picec的hash值战用20个字节,即20/20=1个piece
}

为了加深理解,在此举例一个多文件的torrent内容

这是一个我制作的存在一堆gif的多文件种子,看的出来其量还是非常大的

d8:announce33:udp://106.54.85.131:8000/announce10:created by17:BitTorrent/7.10.513:creation datei1599293877e8:encoding5:UTF-84:infod5:filesld6:lengthi3418e4:pathl13:打击三.gifeed6:lengthi3779e4:pathl10:打击.gifeed6:lengthi3867e4:pathl13:打击二.gifeed6:lengthi5386e4:pathl10:格挡.gifeed6:lengthi64784e4:pathl10:爆炸.gifeed6:lengthi74872e4:pathl16:火柱爆破.gifeed6:lengthi463514e4:pathl13:超必杀.gifeed6:lengthi738113e4:pathl10:必杀.gifeed6:lengthi2208900e4:pathl10:地裂.gifeee4:name12:火焰素材12:piece lengthi16384e6:pieces4360:f3D��5�����������643Ƴ��=�U��*�����L��h]tCC)s�}蝋�q���ΧzG�AQ���ޡ;�Yӝ�#1U�z[=»�ITm�m+'���ved���a(z��Ɩ2�;,K�Z�/��9D���+��3�c뉬1_��:U��dJ�H%��$���"�g�	��l�3w���u�!|g1
/��P�N*�k�z��K4���z��D��r����n�g�4�:
�f�d�z^+�b�Rq������^��VP ��kh�a�k*|���pX����!�r�k��~`��)����LJ�v�۞d�W��	'g�ooʖp�2eÆ�q�d�	=*�J�@��%n��L� g�W1i��]V�	(p���trC9�to�h?I��8d�{+�����s��z�mD��3s,�+�S�����x�S���FKm�@������1?y��e*(}d_��M��Z+�u��\����I�hy���VOaJXr�uZԎ^

我们还是先进行announce部分的拆解

d8:announce33:udp://106.54.85.131:8000/announce10:created by17:BitTorrent/7.10.513:creation datei1599293877e8:encoding5:UTF-8

可以拆解为

announce=['udp://106.54.85.131:8000/announce'] //announce地址
createby=['BitTorrent/7.10.5']   //制作工具
creation date='1599293877'       //时间戳格式
encoding = UTF-8                 //指出info部分编码

看起来差别不大,继续info部分

4:infod5:filesld6:lengthi3418e4:pathl13:打击三.gifeed6:lengthi3779e4:pathl10:打击.gifeed6:lengthi3867e4:pathl13:打击二.gifeed6:lengthi5386e4:pathl10:格挡.gifeed6:lengthi64784e4:pathl10:爆炸.gifeed6:lengthi74872e4:pathl16:火柱爆破.gifeed6:lengthi463514e4:pathl13:超必杀.gifeed6:lengthi738113e4:pathl10:必杀.gifeed6:lengthi2208900e4:pathl10:地裂.gifeee4:name12:火焰素材12:piece lengthi16384e6:pieces4360:(此处省略)

同理,拆解为

info{
 files={
       length=3418
       path=打击三.gif
       length=3779
       path=打击.gif    //后续同理,此处不做过多赘述
       ......
       ......
       pieces4360      //依旧省略后续hash值,总共省略了4360/20=218个pieces
}
}

至此,整个文件的编码原理讲解完毕,下面来讲解一些其余的内容

五、Bittorrent通信协议

首先,BT通信中由如下几部分组成:torrent文件,种子提供站点,tracker服务器,内容发布者/下载者

关系图如下(灵魂画师)

当我们通过软件进行bittorrent进行下载资源时,会进行三个阶段,分别为请求阶段、应答阶段、关闭交互阶段

在请求阶段,包含但不限于如下内容

  • info_hash:.torrent文件中的info部分的Shal校验码,共20 byte。tracker服务器通过它在发布列表中找到对应的记录。
  • peer_id:BT客户机的惟一性标志,在客户机启动时产生,共20 bit。在BT V1.0中没有规定产生peer_id的算法,只要求能够保证惟一性即可。
  • port:提供上传的端口号,亦即常说的监控端口,这里是6641(可自行设定)。
  • key:可选。一个扩展的惟一性标志,即使改变了IP地址,也可以使用该字段标志该BT客户机。
  • uploaded/downloaded:上传/下载的字节数(从客户机向tracker服务器发送“started”开始计算),服务器可以用它来做流量分析。
  • left:还需要下载的字节数。
  • compact:压缩标志。如果值为1表示接受压缩格式的对等方列表,即用6 byte表示一个对等方(前4 byte表示IP地址,后2 byte表示端口号);值为0表示不接受。
  • event:表明客户机的状态,只能是started、completed、stopped等3种中的一种。
  • 除了上面这些例子中包含的参数外,可选的参数还有:
  • ip:可选。IP地址,没有的话服务器会自己找到。
  • numwant:可选。客户机希望从tracker服务器得到的对等方的数目。
  • trackerid:可选。如果在之前的announce中包含了trackerid,将其值设置在该处。

(不同软件所发起的请求并不相同,但是其元素基本一致)

核心参数:

infohash、peer_id、ip、port

在这里多讲一下infohash是如何运算的,首先infohash就是我们俗称的特征值,这个值是随着info值进行SHA1的加密获得的,这里我通过开源项目列了一个计算hash值得脚本,仅供参考

#!/usr/bin/python
 
import sys, os, hashlib, StringIO
import bencode

def main():
    torrent_file = open(sys.argv[1], "rb")
    metainfo = bencode.bdecode(torrent_file.read())
    info = metainfo['info']
    print hashlib.sha1(bencode.bencode(info)).hexdigest()    

if __name__ == "__main__":
    main()

Peerwire协议

对等方通信过程

建立TCP链接后,与对等方之间的交互过程包括如下几步:

  1. 握手
  2. 互换所拥有的资源的情况
  3. 互通对资源的意愿情况
  4. 互相请求资源
  5. 断开连接

Peer to Peer

简称p2p,就是从其他下载用户里获取数据,这也就是bittorrent下载得核心特点,客户端从Trakcer服务器获取到若干其他下载者(peer)的ip和port信息,会进行请求并维持跟每一个peer的连接状态

一个客户端和一个peer有下列状态信息:

  • choked:远程客户端拒绝响应任何本客户端的请求。
  • interested:远程客户端对本客户端的数据感兴趣,当本客户端unchoked远程客户端后,远程客户端会请求数据。

当然这不是重点,就不过多介绍了。

六、PT协议

与做种时的私密种子有着密切的关系

PT全称Private Tracker,与BT最大的不同点分别为可进行私密范围下载,及可统计每个用户的上载及下载量。从技术上可以简单的看作有一个Tracker服务器会对用户的下载上传进行统计,分享率不够就禁止用户下载,在一定程度上可以防止只下载而不上传的用户存在。PT最大的问题是需要账号导致小众,资源容易断档,有些PT网站会用各种激励的错失尽可能减少文件断档,例如即使没有其他用户在下载,开启BT客户端将资源挂着做种也可以得到一些积分奖励。

上图为上海交大的pt站

七、DHT结构

最后讲一下这给东西吧,这也是沿用至今的结构

DHT = Distributed Hash Table 中文分布式哈希表。
人话:代替Tracker服务器,通过分布式存储的方式将原本存储在Tracker中的信息存储到各个用户中。

有了DHT以后,下载种子文件的过程变成了:首先,从DHT中查询种子对应的活跃用户。然后,根据获得的用户信息开始p2p下载。和原始BT协议不同的地方只是在于改变了获取活跃用户的方式。

因而DHT可以说是一个分布式数据库。网络中的所有用户构成的DHT网络是一个大的数据库。每个用户都存储一小部分数据。

理解DHT需要理解一下几个重要的问题:
1、如何分配数据的存储位置?就是说如何知道某个数据应该存储在哪个节点(node)上?
2、如何设计路由算法?路由表的更新?
3、数据的查询、添加。

DHT中数据的分配方式

DHT中的数据是按键值对(key value pairs)的方式存储的。在BT协议的应用中,key是种子文件的ID,value是该种子文件的活跃用户(peers)信息。这里种子的ID是160bit的infohash,就是每个种子文件hash生成的独一无二的字符串。

DHT中的每个存储单元,其实也是用户,叫做节点(node),注意和peer区分。每个node也有一个ID,是一个随机生成的160bit 字符串。如此以来,数据ID和nodeID都是160bit字符串了。

接下来再引入一个概念做为铺垫——距离。node和node之间,node和数据之间都有距离,距离的计算方法,是做两个160bit ID的异或运算。注意,node ID是随机生成的,因而这里的距离是与实际网络连接情况无关的。

DHT中数据存储便是把数据存放在离它最近的node中

DHT中的路由

知道DHT中数据和node的距离计算和存储原则,我们可以知道只要查找和数据ID最接近的几个node,就能够找到目标数据了。但是有个问题,一个node中,可以存储部分其他node的ID以及对应的IP地址端口号。但是无法存储所有的节点的信息。要连接到目标node,需要询问其他节点,慢慢的接近目标节点。

1、bucket

存储node ID、ip地址、端口号列表叫做路由表。

bucket结构是路由表中其他节点信息的集合,一个bucket中最多只能存放8个node。node初始情况下只有一个bucket,范围是160bit ID的整个空间。如果一个bucket满了,而且这个bucket的范围包含了自己的node ID,那么就允许将这个bucket平均一分为二。如果bucket的范围不包括自己的nodeID,那么就不能够分裂。可以得出结论,持续分裂,会导致新的bucket范围会越来越小,而且离nodeID越来越近。由于每个bucket都只能存8个node,这么以来就确保了距离近的node信息多,距离远的node信息少。

2、查询过程

DHT中数据的查询过程是:发起查询的节点(node)首先计算自己路由表中的节点到目标数据的距离,然后对最近的k个节点发起数据查询。收到数据查询请求的节点如果有数据直接返回,没有则在自己的路由表中查询并返回距离数据最近的节点。发起查询的节点收到了返回的节点,则继续查询这些节点同时返回数据

这个持续的查询过程会一步步接近存在数据的节点。

3、路由表的更新

DHT中用户在持续的变化,所以需要一些机制保证路由表信息不会过时。

路由表中的node有三种状态:good、questionable、bad。

good就是活跃的节点。如果长期没有一个节点的信息,这个节点就变成了questionable,如果对某个节点多次发送请求都没有回复,那么这个节点就是bad的,会从路由表中去除。

questionable节点暂时不会被去除,只有当加入新节点时但bucket已经满了,才会对该bucket中的questionable节点进行测试,并删除没有回应的节点。

另外,每个bucket中,至少需要维持一个good节点,如果一个bucket里的所有点都长期没有动静,就需要对整个bucket进行刷新

4、新节点初始化路由表

新节点进入DHT网络时,必须至少知道一个节点。然后对该节点发起查询(查询自己的id)。这样以来,会越来越接近自己的Node信息。同时在查询的过程中存储这些node信息。查询结束后,就完成了路由表的初始化。

以上便是对bittorrent的通信原理及协议进行的深度理论研究,实际等一些列操作会在未来进行撰写,感谢阅读

参考文章:

http://www.bittorrent.org/beps/bep_0005.html

[论文]Kademlia: A peer-to-peer information system based on the xor metric

https://wiki.theory.org/index.php/BitTorrentSpecification

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享