«
QQwry.dat格式分析和查询IP位置的PHP程序

时间:2008-5-31    作者:Deri    分类: 分享


   <p>  以前的追捕数据库太大,而且很久没有更新了。</p><p>  所以我想到利用QQwry.dat这个文件查询IP所在位置,QQwry.dat 在很多地方都能找到,一般看IP地址的QQ压缩包中都有。 </p><p>  但是没有任何相关格式资料。 </p><p>  我分析了这个文件的格式,目前如下结论: </p><p>  格式如下: </p><p>  A。文件头,共8字节</p><p>  B。若干条记录的结束地址+国家和区域</p><p>  C。按照从小到大排列的若干条起始地址+结束地址偏移,定长,7字节</p><p>  D。所有的IP都是用4字节整数记录的,并且遵照Intel次序,高位在后,低位在前。</p><p>  E。所有偏移量都是绝对偏移,就是从文件最开头计算。</p><p>  F。除了文件头用了两个4字节偏移,其余偏移量都用3字节。</p><p>  G。所有的偏移量也是低位在前,高位在后</p><p>  H。采用了一些字符串压缩技术 </p><p>  1。文件头,共8字节</p><p>  FirstStartIpOffset:4 第一个起始IP的绝对偏移</p><p>  LastStartIpOffset:4 最后一个起始IP的绝对偏移 </p><p>  2。起始地址+结束地址偏移记录区</p><p>  每条记录7字节,按照起始地址从小到大排列 </p><p>  StartIp:4 起始地址,整数形式的IP</p><p>  EndIpOffset:3 结束地址绝对偏移 </p><p>  3。结束地址+国家+区域记录区 </p><p>  EndIP:4</p><p>  国家+区域记录:不定长 </p><p>  4。国家+区域记录,有几种形式</p><p>  4.1。</p><p>  国家字符串,以 0x0 结束</p><p>  区域字符串,以 0x0 结束 </p><p>  4.2。</p><p>  Flag:1 标识取值: 0x1,后面没有Local记录</p><p>  0x2,后面还有Local记录</p><p>  sCountryOffset:3 实际的字符串要去这个偏移位置去找</p><p>  LocalRec:不定长,可选 根据Flag取值而定。这个记录也类似Country,可能采用压缩 </p><p>  4.3 LocalRec结构一</p><p>  flag:1 还不是十分了解这个flag含义,取值 0x1 or 0x2</p>
<p> </p>

   <p>  sLocalOffset:3 </p><p>  4.4 LocalRec结构二</p><p>  sLocal:不定长 普通的C风格字符串 </p><p>  注意:sCountryOffset指向的位置可能依然是4.2格式的,不知道为什么这样设计。 </p><p>  Flag取0x1时,sCountryOffset指向的位置可能是Flag为0x2,这时,LocalRec也在这里寻找。 </p><p>  现在不明白当记录Local的位置遇到0x2的标志意味着什么。 </p><p>  在qqwry.dat中,似乎存在一些错误。</p><p>  个别的记录Local会被写为:</p><p>  0x2,0x0,0x0,0x0</p><p>  根据规则,应该到文件最开头去寻找,可是,文件最开头显然不是记录这些的。 </p><p>  我才学PHP不久,各位不要笑,你要能改进当然好,记得给我一份。</p><p>  我参考了一些网上找到的代码,就不一一写出出处了。 </p><p>  说老实话,我很头疼PHP无法明确指定变量的类型。</p><p>  比如,我想让某个数是无符号的整形,它很不听话,非要是带个负号,我只好尝试各种可能的写法..........</p><p>  各位都是怎么处理类似的事情? </p><p>  <code>define('QQWRY' , $qqwry_root_path . 'QQwry.dat' ) ;<br />function IpToInt($Ip) {<br />  
    $array=explode('.',$Ip);<br />  
    $Int=($array[0] * 256*256*256) + ($array[1]*256*256) + ($array[2]*256) + $array[3];<br />  
    return $Int;<br />}<br />function IntToIp($Int) {<br />  
      $b1=($Int & 0xff000000)>>24;<br />  
      if ($b1<0) $b1+=0x100;<br />  
      $b2=($Int & 0x00ff0000)>>16;<br />  
      if ($b2<0) $b2+=0x100;<br />  
      $b3=($Int & 0x0000ff00)>>8;<br />  
      if ($b3<0) $b3+=0x100;<br />  
      $b4= $Int & 0x000000ff;<br />  
      if ($b4<0) $b4+=0x100;<br />  
      $Ip=$b1.'.'.$b2.'.'.$b3.'.'.$b4;<br />  
      return $Ip;<br />}<br />class TQQwry<br />{<br />  
        var $StartIP = 0;<br />  
        var $EndIP  = 0;<br />  
        var $Country = '';<br />  
        var $Local  = '';<br />var $CountryFlag = 0; // 标识 Country位置<br />             
          // 0x01,随后3字节为Country偏移,没有Local<br />             
          // 0x02,随后3字节为Country偏移,接着是Local<br />             
          // 其他,Country,Local,Local有类似的压缩。可能多重引用。<br />  
          var $fp;<br />var $FirstStartIp = 0;<br />  
            var $LastStartIp = 0;<br />  
            var $EndIpOff = 0 ;<br />function getStartIp ( $RecNo ) {<br />    
              $offset = $this->FirstStartIp + $RecNo * 7 ;<br />    
              @fseek ( $this->fp , $offset , SEEK_SET ) ;<br />    
              $buf = fread ( $this->fp , 7 ) ;<br />    
              $this->EndIpOff = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])* 256*256);<br />    
              $this->StartIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);<br />    
              return $this->StartIp ;<br />  
              }<br />function getEndIp ( ) {<br />    
                @fseek ( $this->fp , $this->EndIpOff , SEEK_SET ) ;<br />    
                $buf = fread ( $this->fp , 5 ) ;<br />    
                $this->EndIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);<br />    
                $this->CountryFlag = ord ( $buf[4] ) ;<br />    
                return $this->EndIp ;<br />  
                }<br />function getCountry ( ) {<br />switch ( $this->CountryFlag ) {<br />      
                  case 1:<br />      
                  case 2:<br />        
                  $this->Country = $this->getFlagStr ( $this->EndIpOff+4) ;<br />        
                  //echo sprintf('EndIpOffset=(%x)',$this->EndIpOff );<br />        
                  $this->Local = ( 1 == $this->CountryFlag )? '' : $this->getFlagStr ( $this->EndIpOff+8);<br />        
                  break ;<br />      
                  default :<br />        
                  $this->Country = $this->getFlagStr ($this->EndIpOff+4) ;<br />        
                  $this->Local =  $this->getFlagStr ( ftell ( $this->fp )) ;<br />}<br />  
                    }<br />  
                      function getFlagStr ( $offset )<br />  
                      {<br />$flag = 0 ;<br />    
                        while ( 1 ){<br />      
                        @fseek ( $this->fp , $offset , SEEK_SET ) ;<br />      
                        $flag = ord ( fgetc ( $this->fp ) ) ;<br />      
                        if ( $flag == 1 || $flag == 2 ) {<br />        
                        $buf = fread ($this->fp , 3 ) ;<br />        
                        if ($flag == 2 ){<br />          
                        $this->CountryFlag = 2 ;<br />          
                        $this->EndIpOff = $offset - 4 ;<br />        
                        }<br />        
                        $offset = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])* 256*256);<br />      
                        }else{<br />        
                        break ;<br />      
                        }<br />}<br />    
                          if ( $offset < 12 )<br />      
                          return '';<br />    
                          @fseek($this->fp , $offset , SEEK_SET ) ;<br />    
                          return $this->getStr();<br />  
                          }<br />  
                          function getStr ( )<br />  
                          {<br />    
                          $str = '' ;<br />    
                          while ( 1 ) {<br />      
                          $c = fgetc ( $this->fp ) ;<br />      
                          if ( ord ( $c[0] ) == 0 )<br />        
                          break ;<br />      
                          $str .= $c ;<br />    
                          }<br />    
                          return $str ;<br />  
                          }<br />  
                            function qqwry ($dotip) {<br />$nRet;<br />    
                              $ip = IpToInt ( $dotip );<br />$this->fp= @fopen(QQWRY, "rb");<br />    
                                if ($this->fp == NULL) {<br />       
                                $szLocal= "OpenFileError";<br />      
                                return 1;<br />}<br />     
                                  @fseek ( $this->fp , 0 , SEEK_SET ) ;<br />    
                                  $buf = fread ( $this->fp , 8 ) ;<br />    
                                  $this->FirstStartIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);<br />    
                                  $this->LastStartIp = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])*256*256) + (ord($buf[7])*256*256*256);<br />$RecordCount= floor( ( $this->LastStartIp - $this->FirstStartIp ) / 7);<br />    
                                    if ($RecordCount <= 1){<br />      
                                    $this->Country = "FileDataError";<br />      
                                    fclose ( $this->fp ) ;<br />      
                                    return 2 ;<br />    
                                    }<br />$RangB= 0;<br />    
                                      $RangE= $RecordCount;<br />    
                                      // Match ...<br />    
                                      while ($RangB < $RangE-1)<br />    
                                      {<br />     
                                      $RecNo= floor(($RangB + $RangE) / 2);<br />     
                                      $this->getStartIp ( $RecNo ) ;<br />if ( $ip == $this->StartIp )<br />      
                                        {<br />        
                                        $RangB = $RecNo ;<br />        
                                        break ;<br />      
                                        }<br />     
                                        if ( $ip > $this->StartIp)<br />      
                                        $RangB= $RecNo;<br />     
                                        else<br />      
                                        $RangE= $RecNo;<br />    
                                        }<br />    
                                        $this->getStartIp ( $RangB ) ;<br />    
                                        $this->getEndIp ( ) ;<br />if ( ( $this->StartIp <= $ip ) && ( $this->EndIp >= $ip ) ){<br />      
                                          $nRet = 0 ;<br />      
                                          $this->getCountry ( ) ;<br />      
                                          //这样不太好..............所以..........<br />      
                                          $this->Local = str_replace("(我们一定要解放台湾!!!)", "", $this->Local);<br />}else {<br />      
                                            $nRet = 3 ;<br />      
                                            $this->Country = '未知' ;<br />      
                                            $this->Local = '' ;<br />    
                                            }<br />    
                                            fclose ( $this->fp ) ;<br />    
                                            return $nRet ;<br />  
                                            }<br />}<br />function ip2location ( $ip )<br />{<br />  
                                              $wry = new TQQwry ;<br />  
                                              $nRet = $wry->qqwry ( $ip );<br />  
                                              //可以利用 $nRet做一些事情,我是让他自动记录未知IP到一个表,代码就不写了。<br /> 
                                              return $wry->Country.$wry->Local ;<br />}</code></p>