首页>>磁力资讯

BT背后的技术

2023-05-25 18:23:04 575
磁力搜索引擎链接直达

历程

初期的BT下载首要是通过开放的网站,进行种子资源的发布。在借用tracker核心服务器完结下载peer的互换,终极实行从下载客户的电脑中获得资源。

在这个流程中,存在2个危害较为大的点:

第1个发布种子的网站,种子文件含盖了所须要下载资源的整个数据,较容易被探测出种子内容能否合规,进而关闭种子发布资源站点。

第二个供应tracker的核心服务节点,这个也是较容易从种子中查找到的,较容易被封杀。造成P2P自由的分享环境被冲破。

在这个根基上,就显现代替tracker服务器和种子许多新方式,咱们简洁的简介下较为主流的去核心化tracker和种子分享网站的P2P分享网络。

首先从第一步获得种子开始,咱们通常想找一部片子/游戏或者许多其余资源,通常全是网盘搜索,或者去BT站,PT去找种子,或者论坛上去找链接。先简介下种子和磁链的关系,种子通常以.torrent结尾的索引文件。torrent

d8:announce62:https://xxxxx.im.xxx/88696dc48cbdad7518e4b111b83ee77c7:comment14:TorrenTGui.ORG10:created by13:uTorrent/221013:creation datei1523099215e8:encoding5:UTF-84:infod6:lengthi23715250176e4:name14:SKY HUNTER.iso12:piece lengthi4194304e6:pieces113100:xxx

这类被序列化的数据就是种子了,里面应用的是一类明文的序列化方式。bencode 第二节简洁简介。

反序列化里面首要含盖下列数据:announce:Tracker的主服务器announce-list:Tracker服务器列表comment:种子文件的注释comment.utf-8:种子文件注释的utf-8编码creation date:种子文件创建的时间,是从1970年1月1日00:00:00到如今的秒数。encoding:种子文件的默认编码,例如GB2312,Big5,utf-8等info:一切对于下载的文件的数据都在这个字段里,依据下载的是单个文件还是多个文件,子字段的项目会不同。files:表示文件的名称,大小,该字段含盖如下3个子字段:length:文件的大小,用byte计算path:文件的名称,在下载时不可更改path.utf-8:文件名的UTF-8编码,

多文件Torrent的构造的树形图为

├─comment.utf-8

│ │ └─path.utf-8

│ ├─name.utf-8

│ ├─publisher-url.utf-8

│ └─publisher.utf-8

单文件Torrent的构造的树形图为

├─comment.utf-8

│ ├─name.utf-8

│ ├─publisher-url.utf-8

│ └─publisher.utf-8

为了解决第一类子文件含盖的内容数据太多,容易被探测中此中的主要数据。第二种子文件过大,不太容易扩散分享。显现了一类代替种子文件的数据字符串就是磁力链接。形如:magnet:?xt=urn:btih:b7d9b9d9df8d7678af1f2542677e195fdbdb1674

此中首要字段是 btih,实则这里的值就是bt种子文件中info字段sha1值的base32编码后的字符串。

种子文件的bencode 含盖四品种型的编码:string型号

string型号的编码格式为[length]:[string]。以字符串的长度开头,加1个冒号,并以字符串内容完毕。

示例:"abc" => 3:abcint型号

int型号的编码格式为i[int]e。以i开头,加上数字,以e结尾。

示例:123 => i123eList<object>型号

List<object>型号的编码格式为l[object]e。以l开头,加上列表中逐个元素的编码(元素的型号一样为BEncoding支持的型号),以e结尾。

示例:List<"abc", 123> => l3:abci123eeDictionary<string, object>型号

Dictionary<string, object>型号的编码格式为d[Key-Value Pair]e。以d开头,加上字典中每个键值对的编码,以e结尾。

示例:Dictionary<{"name":"create chen"},{"age":23}> => d4:name11:create chen3:agei23ee

以上编码list和dictionary支持嵌套。bencode编码方法也被用在后期的BT查找新闻的建立上。

去核心化的peer互换网络 DHT

在去核心化的互换网络上,每个客户(node)都会存储一部分种子数据和种子索引数据。这类索引数据里面含盖自己正在下载的资源(peer)、自己周围正在下载的资源数据(peer)、并且许多也许具有某种子资源的数据(node)。当咱们获得到某个种子或者磁链时,就会到这个网络中查找哪类客户在进行这个种子的资源下载,获得这类客户的peer数据(含盖ip port token),接着和这类peer进行连通,获得资源。在查找的流程中,首要借用krpc进行调用,krpc是一类基于udp的rpc调用服务。

根本构造如下:

       "t":"aa", --transcationID   2^16   two byte

       "y":"r",   --message type query->q response->r error->e

       "q":" ".   --ping find_node get_peers

       "r":{}

       "e"

       "a":           --query params

一条 KRPC 新闻由1个独立的字典构成,此中有 2 个主要字是一切的新闻都含盖的,其他的附带主要字取决于新闻型号。每1个新闻都含盖“t”主要字,它是1个代表了 transaction ID 的字符串型号。transaction ID由恳求节点形成,以及答复中要含盖回显该字段,因此答复也许相应1个节点的多个恳求。transaction ID应该被编码为1个短的二进制字符串,例如 2 个字节,如此就阔以相应 2^16 个恳求。另1个每个 KRPC 新闻都含盖的主要字是“y”,它由1个字节构成,标明这个新闻的型号。y 相应的值有三类状况:q 表示恳求,r 表示答复,e 表示错误。

首要含盖下列4个操控:ping

探测节点能否存活。

最根基的恳求是1个ping。"q"="ping",1个ping恳求有1个参数,"id",它的值是1个20字节的字符串,含盖网络字节排序的发送者的节点ID。1个ping的恰当答复有1个主要字"id",含盖发出答复的节点的节点ID。

arguments:   {"id" : "<querying nodes id>"}

response: {"id" : "<queried nodes id>"}

ping Query = {"t":"aa", "y":"q", "q":"ping", "a":{"id":"abcdefghij0123456789"}}

bencoded = d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe

Response = {"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}

bencoded = d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:refind_node

查询节点。

find_node 被拿来查询给定 ID 的节点的联络数据。此时 KPRC 协定中的 "q" == "find_node"。find_node恳求含盖 2 个参数,第1个参数是 id,含盖了恳求节点的ID。第二个参数是 target,含盖了恳求者正在查询的节点的ID。当1个节点接收到了 find_node 的恳求,他理应给出相应的答复,答复中含盖 2 个主要字 id 和 nodes,nodes 是1个字符串型号,含盖了被恳求节点的路由表中最靠近目的节点的 K(n) 个最靠近的节点的联络数据。在标准的DHT网络中,K(n)倡议值是8。

arguments:   {"id" : "<querying nodes id>", "target" : "<id of target node>"}

response: {"id" : "<queried nodes id>", "nodes" : "<compact node info>"}

find_node Query = {"t":"aa", "y":"q", "q":"find_node", "a": {"id":"abcdefghij0123456789", "target":"mnopqrstuvwxyz123456"}}

bencoded = d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:t2:aa1:y1:qe

Response = {"t":"aa", "y":"r", "r": {"id":"0123456789abcdefghij", "nodes": "def456..."}}

bencoded = d1:rd2:id20:0123456789abcdefghij5:nodes9:def456...e1:t2:aa1:y1:reget_peers

查询文件peer。

get_peers 与 torrent 文件的 infohash 相关。此时 KPRC 协定中的 "q"="get_peers"。get_peers 恳求含盖 2 个参数。第1个参数是 id,含盖了恳求节点的 ID。第二个参数是info_hash,它代表 torrent 文件的 infohash。假设被恳求的节点有相应 info_hash 的peers,他将返回1个主要字 values,这是1个列表型号的字符串。每1个字符串含盖了 "CompactIP-address/portinfo" 格式的 peers 数据。假设被恳求的节点没有这个 infohash 的peers,那么他将返回主要字 nodes,这个主要字含盖了被恳求节点的路由表中离 info_hash 近日的 K 个节点,

arguments:   {"id" : "<querying nodes id>", "info_hash" : "<20-byte infohash of target torrent>"}

response: {"id" : "<queried nodes id>", "token" :"<opaque write token>", "values" : ["<peer 1 info string>", "<peer 2 info string>"]}     

or: {"id" : "<queried nodes id>", "token" :"<opaque write token>", "nodes" : "<compact node info>"}

get_peers Query = {"t":"aa", "y":"q", "q":"get_peers", "a": {"id":"abcdefghij0123456789", "info_hash":"mnopqrstuvwxyz123456"}}

bencoded = d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz123456e1:q9:get_peers1:t2:aa1:y1:qe

Response with peers = {"t":"aa", "y":"r", "r": {"id":"abcdefghij0123456789", "token":"aoeusnth", "values": ["axje.u", "idhtnm"]}}

bencoded = d1:rd2:id20:abcdefghij01234567895:token8:aoeusnth6:valuesl6:axje.u6:idhtnmee1:t2:aa1:y1:re

Response with closest nodes = {"t":"aa", "y":"r", "r": {"id":"abcdefghij0123456789", "token":"aoeusnth", "nodes": "def456..."}}

bencoded = d1:rd2:id20:abcdefghij01234567895:nodes9:def456...5:token8:aoeusnthe1:t2:aa1:y1:reannounce_peer

发动恳求节点。

这个恳求拿来标明发出 announce_peer 恳求的节点,正在某个端口下载 torrent 文件。announce_peer 含盖 4个参数。第1个参数是 id,含盖了恳求节点的 ID;第二个参数是 info_hash,含盖了 torrent 文件的infohash;第3个参数是 port 含盖了整型的端口号,标明 peer 在哪个端口下载;第4个参数数是 token,这是在此前的get_peers 恳求中收到的答复中含盖的。收到 announce_peer 恳求的节点必要检验这个 token 与此前咱们答复给这个节点get_peers 的 token 能否相近。假设相近,那么被恳求的节点将记载发送 announce_peer 节点的 IP 和恳求中含盖的port 端口号在 peer 联络数据中相应的 infohash 下。

arguments:   {"id" : "<querying nodes id>",

   "implied_port": <0 or 1>,

   "info_hash" : "<20-byte infohash of target torrent>",

   "port" : <port number>,

   "token" : "<opaque token>"}

response: {"id" : "<queried nodes id>"}

announce_peers Query = {"t":"aa", "y":"q", "q":"announce_peer", "a": {"id":"abcdefghij0123456789", "implied_port": 1, "info_hash":"mnopqrstuvwxyz123456", "port": 6881, "token": "aoeusnth"}}

bencoded = d1:ad2:id20:abcdefghij012345678912:implied_porti1e9:info_hash20:mnopqrstuvwxyz1234564:porti6881e5:token8:aoeusnthe1:q13:announce_peer1:t2:aa1:y1:qe

Response = {"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}

bencoded = d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re

DHT (Distributed Hash Table)散布式哈希表,Kad网络是其的一类实行方法。Kademlia协定(下列简称Kad)是美国纽约大学的PetarP. Maymounkov和David Mazieres.在2002年公布的一项研发结果《Kademlia: A peerto -peer information system based onthe XOR metric》。nodeid

2^160位(bit)的标注id,阔以是本机mac的sha1值。在DHT分享网络中,资源的info-hash恰好也是160位(bit)。在借用kad网络,将info-hash存储于与其相同的多个node中,便利在查找流程中借用get_peer进行迅速迫近。在怎样判断能否与其相同,kad网络借用的是xor运算。


1.jpg


Kademlia 依据2个标示符之间的距离来把 <key , value> 对分派给特定的节点。针对2个 160 位标示符 x 和 y , Kademlia 把它们之间的距离定论为两者按位异或( XOR )结果的整数值, d(x,y)=x ⊕y 。

首先,XOR 是一类有效的度量方式(固然不是欧几里得几何意义上的)。很明显,d(x,x)=0 ;当x 不等于y 时,d(x,y)>0 ,以及针对任意的x,y 来说,d(x,y)=d(y,x) 。XOR 还拥有三角特征:d(x,y)+d(y,z) 大于等于d (x,z )。该三角特征阔以从如下事实得出:d(x,y) ⊕d(y,z)=d(x,z) ,以及针对任意大于等于0 的a 和b :a+b 都大于等于a ⊕b 。

同时,XOR 也描绘出了隐含在咱们基于二叉树刻画的体系中距离的概念。在160 位ID 的满二叉树中,2个IDs 间的距离大小就是含盖它们的最小子树的高度。当树不是满的时,距离ID x 近日的叶子就是其ID 和x 拥有最长的公共前缀的那个叶子。假设树中有空的分支,那么拥有最长公共前缀的叶子就会有多个。这时,咱们基于该树的空分支,把x 相应的位取反获得ID x’ ,那么距离x’ 近日的叶子即为距离x 近日的叶子。借用xor的距离测算,最坏O(n)便可获取到查找结果。


2.jpg

3.jpg


路由表是一颗二叉树,其叶子是 k-buckets 。每个 k-bucket 寄存着拥有某类公共 ID 前缀的节点。前缀就是该 k-bucket 在二叉树中的位子。了因而,每个 k-bucket 覆盖了 ID 空间的某个范畴,一切 k-bucket 合起来完整覆盖了全部 160 位 ID 空间。

节点是依据须要动态分派到路由树中的。一开始,节点A的路由树唯独1个节点——覆盖全部 ID 空间的单个 k-bucket 。当A通过find_node或者get_peer获得1个新节点的联络数据时,就会企图把其插入到对应的 k-bucket 中。假设该 bucket 不满,简洁将其插入便可。不然,假设该 k-bucket 的区间范畴含盖了A自己的节点 ID ,那么该 bucket 会分裂为2个新的 buckets ,原有的内容会被区分到这2个 buckets 中,然后反复插入流程。假设 k-bucket 已满且不含有A的节点 ID ,那么就直接抛弃这个新的联络数据。须要注重的是P2P节点在线时间是不安稳的,须要定时去ping探测每个k-bucket捅中的节点,假设死掉则剔除掉。根据beps005标准没有刷新的节点15分钟须要探测一次。每次krpc操控中阔以常态呼应的则认定是活泼节点。

在Kad网络中还存在一类高度不平衡的二叉树均衡的流程,在BT的DHT网络中,现在没有看见应用。

BT 爬虫实行要点

1.先伪装自己向公共节点进行find_node 查找,将自己添加网络。

"router.bittorrent.com:6881",

"router.utorrent.com:6881",

"dht.transmissionbt.com:6881",

2.在返回的find_node中,更换返回nodeid的某类n位,导致与返回node节点id不在区间内,持续开拓全部网络节点散布。

       msg["t"] = TCIDMNGR.GetTranscationID()

       msg["y"] = "q"

       msg["q"] = "find_node"

       msg["a"] = map[string]interface{}{"id": tarGET@[:15] + OwnNodeID.ToString()[15:], "target": target}

       return []byte(PackageMessage(msg)), msg["t"].(string)

3.通常BT爬虫被动接收announce_peer 从peer的数据中应用peer wire protocol 获得 metadata 即种子数据。接着解析torrent中的数据确定文件内容。

4.注重进行ip黑名单过滤,DHT网络中,存在许多如此爬虫,在实行流程中,发掘北美有个ip爬虫猖狂变更nodeid进行find_node 恳求,把内存塞满的状况(1G),注重操控本地DHT表的总节点数。

5.加快搜索流程,在获得get_peer恳求时进行递归get_peer恳求,注重操控深度。阔以加大K-bucket桶的大小,减低二叉树的深度。

6.在应用peer wire protocol流程中,由于经营商对P2P封锁,许多状况下下载种子落败,阔以应用有名种子库进行info-hash下载。

7.注重操控udp发包速率,避免被主机商防火墙误认为中毒。

额外提一点,对于如今的PT,PT网络是制止启动DHT,防范资源外泄。在PT站公布的种子都会含盖PT站的私有tracker服务地点,每个会员下载的种子也会含盖自己的私有token,当种子泄漏到BT上时,PT治理员较容易封禁掉该会员的数据。PT努力于打造高端高活泼的P2P分享网络。


4.jpg


考虑资料

作家:戚华威 



相关标签: