【IP相关】如何查看某个IP下载过什么文件?

IP相关教程

之前讲过很多IP相关的知识,无论是情报还是溯源、防止溯源,如果你对此感兴趣可以阅读往期文章。

通过IP寻找别人下载过什么种子文件

查询入口

查询效果

输入IP可以查询到这个IP近期的下载种子文件的动态,而且还可以看到分类,比方说下载了游戏?下载了电影?

有了这样的工具可以帮助我们更好的研究某个IP的近期状态及行为。

图片[1]-【IP相关】如何查看某个IP下载过什么文件?-FancyPig's blog

技术实现手段

主要是通过解析 torrent 站点和监听 DHT 网络 收集 torrent 文件

DHT网络相关知识

参考文章《趣谈网络协议 – 第17讲 | P2P协议:我下小电影,99%急死你》

谈到DHT网络,就必须要讲到P2P概念以及P2P所属的分布式的下载缓存体系。

传统的下载就是client和server端交互,这个server是固定的,就是存储资源的服务器,这时候不管网速如何,下载的速度严重依赖于服务器的服务稳定性,如果服务器不太稳定,接入就比较慢,下载速度就会受到很大的限制,这也是传统下载方式的速度优化有一个非常明显的天花板——难以解决单一服务器带来的带宽压力。鉴于这个瓶颈问题,我们想要是可以将服务器的压力分担出去就好了,不唯一依赖某一个或者某几个服务器,这样至少可以将下载速度的上限明显提高。

很显然,真的有这样的下载方式,就是P2P。P2P 就是 peer-to-peer。资源开始并不集中地存储在某些设备上,而是分散地存储在多台设备上。这些设备我们姑且称为 peer。想要下载一个文件的时候,你只要得到那些已经存在了文件的 peer,并和这些 peer 之间,建立点对点的连接,而不需要到中心服务器上,就可以就近下载文件。一旦下载了文件,你也就成为 peer 中的一员,你旁边的那些机器,也可能会选择从你这里下载文件,所以当你使用 P2P 软件的时候,例如 BitTorrent,往往能够看到,既有下载流量,也有上传的流量,也即你自己也加入了这个 P2P 的网络,自己从别人那里下载,同时也提供给其他人下载。可以想象,这种方式,参与的人越多,下载速度越快,一切完美。

种子文件解析

我们怎么知道有哪些客户端有资源文件?

就轮到“种子文件”登场了,种子文件的后缀是“.torrent”,这个相信大家并不陌生,PC时代有很多P2P的下载工具,其中著名的有迅雷、电驴、风行,还有一些使用P2P技术的播放器,比较著名的有快播。哈哈,言归正传,种子文件中记录中每一个节点中存放着什么数据,并且这个节点的一些基本信息,方便我们能连接上这个节点并且下载这个资源。

给大家介绍一个在线的torrent分析站点:http://tool.chacuo.net/commontorrentinfo,我现在想解析一个本地的torrent文件,结果如下:

图片[2]-【IP相关】如何查看某个IP下载过什么文件?-FancyPig's blog
图片[3]-【IP相关】如何查看某个IP下载过什么文件?-FancyPig's blog

.torrent 文件由两部分组成,分别是:announce(tracker URL)文件信息

下载时,BT 客户端首先解析.torrent 文件,得到 tracker 地址,然后连接 tracker 服务器。tracker 服务器回应下载者的请求,将其他下载者(包括发布者)的 IP 提供给下载者。下载者再连接其他下载者,根据.torrent 文件,两者分别对方告知自己已经有的块,然后交换对方没有的数据。此时不需要其他服务器参与,并分散了单个线路上的数据流量,因此减轻了服务器的负担。

下载者每得到一个块,需要算出下载块的 Hash 验证码,并与.torrent 文件中的对比。如果一样,则说明块正确,不一样则需要重新下载这个块。这种规定是为了解决下载内容的准确性问题。

从这个过程也可以看出,这种方式特别依赖 tracker。tracker 需要收集下载者信息的服务器,并将此信息提供给其他下载者,使下载者们相互连接起来,传输数据。虽然下载的过程是非中心化的,但是加入这个 P2P 网络的时候,都需要借助 tracker 中心服务器,这个服务器是用来登记有哪些用户在请求哪些资源。

所以,这种工作方式有一个弊端,一旦 tracker 服务器出现故障或者线路遭到屏蔽,BT 工具就无法正常工作了。

  • info 区:这里指定的是该种子有几个文件、文件有多长、目录结构,以及目录和文件的名字。
  • Name 字段:指定顶层目录名字。
  • 每个段的大小:BitTorrent(简称 BT)协议把一个文件分成很多个小段,然后分段下载。
  • 段哈希值:将整个种子中,每个段的 SHA-1 哈希值拼在一起。

DHT——去中心化

鉴于P2P下载的一些瑕疵,那能不能彻底去中心化呢?

为了解决P2P下载的这个问题,DHT(Distributed Hash Table)应运而生,这是一种完全的去中心化的网络。每个加入这个 DHT 网络的人,都要负责存储这个网络里的资源信息和其他成员的联系信息,相当于所有人一起构成了一个庞大的分布式存储数据库。

有一种著名的 DHT 协议,叫 Kademlia 协议。这个和区块链的概念一样,很抽象,我来详细讲一下这个协议。任何一个 BitTorrent 启动之后,它都有两个角色。一个是 peer,监听一个 TCP 端口,用来上传和下载文件,这个角色表明,我这里有某个文件。另一个角色 DHT node,监听一个 UDP 的端口,通过这个角色,这个节点加入了一个 DHT 的网络。

任何一个 BitTorrent 启动之后,它都有两个角色。一个是 peer,监听一个 TCP 端口,用来上传和下载文件,这个角色表明,我这里有某个文件。另一个角色 DHT node,监听一个 UDP 的端口,通过这个角色,这个节点加入了一个 DHT 的网络。

图片[4]-【IP相关】如何查看某个IP下载过什么文件?-FancyPig's blog

在 DHT 网络里面,每一个 DHT node 都有一个 ID。这个 ID 是一个很长的串。每个 DHT node 都有责任掌握一些知识,也就是文件索引,也即它应该知道某些文件是保存在哪些节点上。它只需要有这些知识就可以了,而它自己本身不一定就是保存这个文件的节点。

当然,每个 DHT node 不会有全局的知识,也即不知道所有的文件保存在哪里,它只需要知道一部分。那应该知道哪一部分呢?这就需要用哈希算法计算出来。

每个文件可以计算出一个哈希值,而 DHT node 的 ID 是和哈希值相同长度的串。DHT 算法是这样规定的:如果一个文件计算出一个哈希值,则和这个哈希值一样的那个 DHT node,就有责任知道从哪里下载这个文件,即便它自己没保存这个文件。当然不一定这么巧,总能找到和哈希值一模一样的,有可能一模一样的 DHT node 也下线了,所以 DHT 算法还规定:除了一模一样的那个 DHT node 应该知道,ID 和这个哈希值非常接近的 N 个 DHT node 也应该知道。

什么叫和哈希值接近呢?例如只修改了最后一位,就很接近;修改了倒数 2 位,也不远;修改了倒数 3 位,也可以接受。总之,凑齐了规定的 N 这个数就行。

刚才那个图里,文件 1 通过哈希运算,得到匹配 ID 的 DHT node 为 node C,当然还会有其他的节点。所以,node C 有责任知道文件 1 的存放地址,虽然 node C 本身没有存放文件 1。同理,文件 2 通过哈希运算,得到匹配 ID 的 DHT node 为 node E,但是 node D 和 E 的 ID 值很近,所以 node D 也知道。当然,文件 2 本身没有必要一定在 node D 和 E 里,但是碰巧这里就在 E 那有一份。

接下来一个新的节点 node new 上线了。如果想下载文件 1,它首先要加入 DHT 网络,如何加入呢?

在这种模式下,种子.torrent 文件里面就不再是 tracker 的地址了,而是一个 list 的 node 的地址,而所有这些 node 都是已经在 DHT 网络里面的。当然随着时间的推移,很可能有退出的,有下线的,但是我们假设,不会所有的都联系不上,总有一个能联系上。node new 只要在种子里面找到一个 DHT node,就加入了网络。

node new 会计算文件 1 的哈希值,并根据这个哈希值了解到,和这个哈希值匹配,或者很接近的 node 上知道如何下载这个文件,例如计算出来的哈希值就是 node C。

但是 node new 不知道怎么联系上 node C,因为种子里面的 node 列表里面很可能没有 node C,但是它可以问,DHT 网络特别像一个社交网络,node new 只有去它能联系上的 node 问,你们知道不知道 node C 的联系方式呀?

在 DHT 网络中,每个 node 都保存了一定的联系方式,但是肯定没有 node 的所有联系方式。DHT 网络中,节点之间通过互相通信,也会交流联系方式,也会删除联系方式。和人们的方式一样,你有你的朋友圈,你的朋友有它的朋友圈,你们互相加微信,就互相认识了,过一段时间不联系,就删除朋友关系。

有个理论是,社交网络中,任何两个人直接的距离不超过六度,也即你想联系比尔盖茨,也就六个人就能够联系到了。

所以,node new 想联系 node C,就去万能的朋友圈去问,并且求转发,朋友再问朋友,很快就能找到。如果找不到 C,也能找到和 C 的 ID 很像的节点,它们也知道如何下载文件 1。

在 node C 上,告诉 node new,下载文件 1,要去 B、D、 F,于是 node new 选择和 node B 进行 peer 连接,开始下载,它一旦开始下载,自己本地也有文件 1 了,于是 node new 告诉 node C 以及和 node C 的 ID 很像的那些节点,我也有文件 1 了,可以加入那个文件拥有者列表了。

但是你会发现 node new 上没有文件索引,但是根据哈希算法,一定会有某些文件的哈希值是和 node new 的 ID 匹配上的。在 DHT 网络中,会有节点告诉它,你既然加入了咱们这个网络,你也有责任知道某些文件的下载地址。

这儿引出两个问题?

  • DHT网络是如何维护这么多node的?
  • DHT网络是如何查找需要的node?具体的原理算法是什么?

小结

  • 传统下载方式一般采用单一服务器方式,下载速度受到较大的制约。分布式的下载方式解决了传统下载方式的弊端。
  • 分布式下载方式也有两种:依赖tracker的“元数据集中,文件数据分散”的方式;另一种是基于分布式的哈希算法,保证元数据和文件数据完全分开。

分布式下载方式也有两种:依赖tracker的“元数据集中,文件数据分散”的方式;另一种是基于分布式的哈希算法,保证元数据和文件数据完全分开。

在 DHT 网络里面,每一个 DHT node 都有一个 ID。这个 ID 是一个很长的串。每个 DHT node 都有责任掌握一些知识,也就是文件索引,或者叫做文件Hash值。每一个DHT node都有一个ID,这个ID是一个160bits(20字节)的数据,它存储的文件标识也是一个160bits的Hash值。

上面文章我们也介绍了在DHT网络中我们知道需要联系的节点,但是有时候不一定能找到这个节点,但是也可以退而求其次,找到与其相似的节点,那么这个“相似”怎么定义?相似不是地理位置的接近,位置近不算近,ID相近或者相似才是近。这个相似的比较通过异或XOR来处理。举个例子,两个节点01010 与 01000 的距离,就是两个 ID 之间的异或值,为 00010,也即为 2。01010 与 00010 的距离为 01000,也即为 8,。01010 与 00011 的距离为 01001,也即 8+1=9 。以此类推,高位不同的,表示距离更远一些;低位不同的,表示距离更近一些,总的距离为所有的不同位的距离之和。

如何维护DHT网络节点

DHT网络中有很多节点,就像人的朋友圈网络一样。朋友圈中的朋友关系有远近之分,当然DHT中节点之间的关系也有远近之分,上面也讲了如何判断两个节点是否相近(或者叫相似)。

DHT网络的节点是按照层次来划分的,举个例子就清楚了。

假设某个节点的 ID 为 01010,如果一个节点的 ID,前面所有位数都与它相同,只有最后 1 位不同。这样的节点只有 1 个,为 01011。与基础节点的异或值为 00001,即距离为 1;对于 01010 而言,这样的节点归为“k-bucket 1”。

如果一个节点的 ID,前面所有位数都相同,从倒数第 2 位开始不同,这样的节点只有 2 个,即 01000 和 01001,与基础节点的异或值为 00010 和 00011,即距离范围为 2 和 3;对于 01010 而言,这样的节点归为“k-bucket 2”。我们上面也说了,高位不同的,表示距离更远一些;低位不同的,表示距离更近一些。

如果一个节点的 ID,前面所有位数相同,从倒数第 i 位开始不同,这样的节点只有 2^(i-1) 个,与基础节点的距离范围为[2^(i-1), 2^i);对于 01010 而言,这样的节点归为“k-bucket i”。

最终到从倒数 160 位就开始都不同。

你会发现,差距越大,陌生人越多,但是朋友圈不能都放下,所以每一层你可以放N个节点,N要在一定的范围之内即可。这就是DHT网络分层的由来。

通过这样的分层,我们可以将DHT中各个节点之间的远近关系建立起来,这样在查找的时候很清晰。那么问题来了,DHT网络如何查找节点?

DHT网络如何查找节点

在DHT网络分层的基础上,我们来查找节点,还是举例子清晰表达一下。

假设,node A 的 ID 为 00110,要找 node B ID 为 10000,异或距离为 10110,距离范围在[2^4, 2^5),所以这个目标节点可能在“k-bucket 5”中,这就说明 B 的 ID 与 A 的 ID 从第 5 位开始不同,所以 B 可能在“k-bucket 5”中。

然后,A 看看自己的 k-bucket 5 有没有 B。如果有,太好了,找到你了;如果没有,在 k-bucket 5 里随便找一个 C。因为是二进制,C、B 都和 A 的第 5 位不同,那么 C 的 ID 第 5 位肯定与 B 相同,即它与 B 的距离会小于 2^4,相当于比 A、B 之间的距离缩短了一半以上。

再请求 C,在它自己的通讯录里,按同样的查找方式找一下 B。如果 C 知道 B,就告诉 A;如果 C 也不知道 B,那 C 按同样的搜索方法,可以在自己的通讯录里找到一个离 B 更近的 D 朋友(D、B 之间距离小于 2^3),把 D 推荐给 A,A 请求 D 进行下一步查找。

Kademlia 的这种查询机制,是通过折半查找的方式来收缩范围,对于总的节点数目为 N,最多只需要查询 log2(N) 次,就能够找到。

图片[5]-【IP相关】如何查看某个IP下载过什么文件?-FancyPig's blog

上面的图示就是最坏的一种情况,即使这样,也还是很快。

A 和 B 每一位都不一样,所以相差 31,A 找到的朋友 C,不巧正好在中间。和 A 的距离是 16,和 B 距离为 15,于是 C 去自己朋友圈找的时候,不巧找到 D,正好又在中间,距离 C 为 8,距离 B 为 7。于是 D 去自己朋友圈找的时候,不巧找到 E,正好又在中间,距离 D 为 4,距离 B 为 3,E 在朋友圈找到 F,距离 E 为 2,距离 B 为 1,最终在 F 的朋友圈距离 1 的地方找到 B。当然这是最最不巧的情况,每次找到的朋友都不远不近,正好在中间。

如果碰巧了,在 A 的朋友圈里面有 G,距离 B 只有 3,然后在 G 的朋友圈里面一下子就找到了 B,两次就找到了。

查找到了节点,那么如何沟通?

DHT网络中节点如何沟通?

Kademlia 算法中,每个节点只有 4 个指令:

  • PING:测试一个节点是否在线,还活着没,相当于打个电话,看还能打通不。
  • STORE:要求一个节点存储一份数据,既然加入了组织,有义务保存一份数据。
  • FIND_NODE:根据节点 ID 查找一个节点,就是给一个 160 位的 ID,通过上面朋友圈的方式找到那个节点。
  • FIND_VALUE:根据 KEY 查找一个数据,实则上跟 FIND_NODE 非常类似。KEY 就是文件对应的 160 位的 ID,就是要找到保存了文件的节点。

DHT之所以是一个高效的分布式网络,说明它是一个动态更新的网络,网络节点之间的远近是动态更新的,如何更新节点信息?

  • 每个 bucket 里的节点,都按最后一次接触的时间倒序排列,这就相当于,朋友圈里面最近联系过的人往往是最熟的。
  • 每次执行四个指令中的任意一个都会触发更新。
  • 当一个节点与自己接触时,检查它是否已经在 k-bucket 中,也就是说是否已经在朋友圈。如果在,那么将它挪到 k-bucket 列表的最底,也就是最新的位置,刚联系过,就置顶一下,方便以后多联系;如果不在,新的联系人要不要加到通讯录里面呢?假设通讯录已满的情况,PING 一下列表最上面,也即最旧的一个节点。如果 PING 通了,将旧节点挪到列表最底,并丢弃新节点,老朋友还是留一下;如果 PING 不通,删除旧节点,并将新节点加入列表,这人联系不上了,删了吧。

一个优秀的分布式网络,任何节点的加入和离开都不会随便影响整体网络的稳定性,这样才是一个健壮的分布式网络。

小结

  • DHT采用异或来区分远近,高位不同的,表示距离更远一些;低位不同的,表示距离更近一些。
  • DHT采用分层的方式将远近关系层次化。
  • DHT层次的划分依据,是方便查找节点。最快的利用层次关系来查找节点,查询的效率是log2(N)
  • DHT更新节点的原则基本上遵照着LRU的方式。
© 版权声明
THE END
喜欢就支持一下吧
点赞36 分享
评论 共230条

请登录后发表评论