Articles

QQWry.dat文件结构分析

最近写一个程序,其中一部分功能是关于IP地址查询的,上网找了很久IP数据库, 得到的结果却不太理想。唯一找到比较全的数据库就是从QQWry.dat导出的纯文本文件,比较大。 所以我觉得不如索性就直接用QQWry.dat,这个文件每一个能显示IP的QQ里面都有。

以下内容参考了两篇文章,感谢这两篇文章的作者。一位是自称是设计这种格式的人, 一位则是自己研究出这种格式的人。但上述两篇文章写得都不够具体,前者因为是作者, 所以具体细节写得不够清楚;后者由于是自己研究的,所以有些东西分析不够透彻。

我则借鉴了这两篇文章,下面我详细介绍一下 QQWry.day的文件格式,有不正确之处还请大家指正。

总体上说,QQWry.dat有3部分组成:

  (文件头)
+ (结束IP + 地区1 + 地区2)(m)
+ (开始IP + 结束IP偏移)(n)

先说明一下以上每个部分指的具体是什么。在QQWry.dat中每个记录描述如下:

202.113.16.0 – 202.113.16.255
南开大学 网络中心

其中202.113.16.0就是(开始IP),202.113.16.255就是(结束IP), (开始IP) – (结束IP)组成一个IP段,所有IP段是按照从小到大排列的。

南开大学就是(地区1),网络中心就是(地区2)。一般来说(地区1)比较大,例如美国; (地区2)比较精确,例如纽约。(文件头)以及每一个(开始IP + 结束IP偏移)大小是固定的, 但每一个(结束IP + 地区1 + 地区2)的大小是不定的,并且所有偏移都是绝对偏移。

下面具体分析各部分:

(文件头)

文件头总共有8个字节,结构如下:

typedef struct
{
  //指向[开始IP + 结束IP偏移][0]
  unsigned long first_start_ip_offset;

  //指向[开始IP + 结束IP偏移][n - 1]
  unsigned long last_start_ip_offset;
 } header;

所以用[文件头]的这两个指针就可以遍历所有的[开始IP + 结束IP偏移]

(开始IP + 结束IP偏移)

每一个(开始IP + 结束IP偏移)结构有7个字节,

typedef struct
{
  //具体的[开始IP]
  unsigned long start_ip;

  //指向对应的[结束IP + 地区1 + 地区2]
  unsigned char end_ip_offset[3];
} start_ip;

例如记录的(开始IP)是202.113.16.0,则start_ip.start_ip = 0xCA711000 但注意,保存在文件中的数据是倒过来的,即0x001071CA。 通过start_ip.end_ip_offset可以定位到相应的(结束IP+地区1+地区2)

[结束IP + 地区1 + 地区2]

这部分相对复杂一些,他的长度是不定的,将前面出现过的重复的地区字符串删掉,而只保留一个指针,这样可以起到适当的数据压缩作用。[结束IP]同样是 4个字节,与start_ip.start_ip保存方式相同。下面具体介绍地区的保存方式,保存方式总共有 4种:

1) [地区1] + 0x00 + [地区2] + 0x00

其中[地区1][地区2]都是字符串,例如:”美国”+ 00+ “纽约”+ 00

2) 0x01 + [地区偏移]

表示[地区1 + 地区2] 前面出现过,通过 3字节的[地区偏移]就可以找到,值得注意的是利用这个[地区偏移]找到的[地区1+ 地区2]不一定就是1),可能是下面要讲的 3) 4),但一定不是2),也就是说2)不会递归的出现,至少我遍历了所有150000条记录,没有出现这样的情况。

3) 0x02 + [地区偏移] + [地区2] + 0x00

表示[地区1] 前面出现过,利用 3字节的[地区偏移]即可找到,并且找到的[地区1][0] != 0x01 && [地区1][0] != 0x02,也就说不会再出现2),并且3)不递归,所以找到的数据一定是[地区1] + 0x00这种情形。

4) 0x02 + [地区偏移] + 0x02 + [地区偏移]

表示[地区1][地区2]分别在前面出现过,分别利用这两个偏移就可以找到[地区] + 0x00这样的字符串。找到的数据不会再是2) 3)中的那样,也不会是4)本身。

现在网上有几种QQ IP数据格式,例如:Phoenix版、纯真版、梧州版,我以上分析的数据是纯真网络2004年9月5日的IP数据。

此外,大家可以参考以下两篇文章:QQwry格式关于QQWry格式