正文

QQwry.dat格式分析和查询IP位置的PHP及VB程序2005-10-23 22:31:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/tcmcz/6293.html

分享到:

QQwry.dat格式分析和查询IP位置的PHP程序 By Strongc http://strongc.51.net/d2x/ 转载时不要去掉我的名字和我的主页链接,谢谢! 以前的追捕数据库太大,而且很久没有更新了。所以我想到利用QQwry.dat这个文件查询IP所在位置,QQwry.dat在很多地方都能找到,一般看IP地址的QQ压缩包中都有。但是没有任何相关格式资料。 我分析了这个文件的格式,目前如下结论。 格式如下: ①、文件头,共8字节;②、若干条记录的结束地址+国家和区域;③、按照从小到大排列的若干条起始地址+结束地址偏移,定长,7字节;④、所有的IP都是用4字节整数记录的,并且遵照Intel次序,高位在后,低位在前;⑤、所有偏移量都是绝对偏移,就是从文件最开头计算;⑥、除了文件头用了两个4字节偏移,其余偏移量都用3字节;⑦、所有的偏移量也是低位在前,高位在后;⑧、采用了一些字符串压缩技术; ①、文件头,共8字节。    FirstStartIpOffset:4 第一个起始IP的绝对偏移    LastStartIpOffset:4  最后一个起始IP的绝对偏移 ②、起始地址+结束地址偏移记录区    每条记录7字节,按照起始地址从小到大排列    StartIp:     4  起始地址,整数形式的IP    EndIpOffset: 3  结束地址绝对偏移 ③、结束地址+国家+区域记录区    EndIP:       4  国家+区域记录:不定长 ④、国家+区域记录,有几种形式4.1    国家字符串,以 0x0 结束    区域字符串,以 0x0 结束 4.2    Flag:1 标识取值    0x1,后面没有Local记录    0x2,后面还有Local记录    sCountryOffset:3 实际的字符串要去这个偏移位置去找    LocalRec:不定长,可选 根据Flag取值而定。这个记录也类似Country,可能采用压缩 4.3 LocalRec结构一     flag:1 还不是十分了解这个flag含义,取值 0x1 or 0x2    sLocalOffset:3 4.4 LocalRec结构二    sLocal:不定长 普通的C风格字符串     注意:sCountryOffset指向的位置可能依然是4.2格式的,不知道为什么这样设计。     Flag取0x1时,sCountryOffset指向的位置可能是Flag为0x2,这时,LocalRec也在这里寻找。     现在不明白当记录Local的位置遇到0x2的标志意味着什么。     在qqwry.dat中,似乎存在一些错误。个别的记录Local会被写为:0x2,0x0,0x0,0x0    根据规则,应该到文件最开头去寻找,可是,文件最开头显然不是记录这些的。     我才学PHP不久,各位不要笑,你要能改进当然好,记得给我一份。我参考了一些网上找到的代码,就不一一写出出处了。     说老实话,我很头疼PHP无法明确指定变量的类型。比如,我想让某个数是无符号的整形,它很不听话,非要是带个负号,我只好尝试各种    可能的写法……各位都是怎么处理类似的事情? define('QQWRY' , $qqwry_root_path . 'QQwry.dat' ) ; function IpToInt($Ip) {    $array=explode('.',$Ip);    $Int=($array[0] * 256*256*256) + ($array[1]*256*256) + ($array[2]*256) + $array[3];    return $Int; } function IntToIp($Int) {    $b1=($Int & 0xff000000)>>24;    if ($b1<0) $b1+=0x100;    $b2=($Int & 0x00ff0000)>>16;    if ($b2<0) $b2+=0x100;    $b3=($Int & 0x0000ff00)>>8;    if ($b3<0) $b3+=0x100;    $b4= $Int & 0x000000ff;    if ($b4<0) $b4+=0x100;    $Ip=$b1.'.'.$b2.'.'.$b3.'.'.$b4;    return $Ip; } class TQQwry {    var $StartIP = 0;    var $EndIP = 0;    var $Country = '';    var $Local = '';    var $CountryFlag = 0; // 标识 Country位置                          // 0x01,随后3字节为Country偏移,没有Local                          // 0x02,随后3字节为Country偏移,接着是Local                          // 其他,Country,Local,Local有类似的压缩。可能多重引用。    var $fp;    var $FirstStartIp = 0;    var $LastStartIp = 0;    var $EndIpOff = 0 ;    function getStartIp ( $RecNo ) {        $offset = $this->FirstStartIp + $RecNo * 7 ;        @fseek ( $this->fp , $offset , SEEK_SET ) ;        $buf = fread ( $this->fp , 7 ) ;        $this->EndIpOff = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])* 256*256);        $this->StartIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);        return $this->StartIp ;    }    function getEndIp ( ) {        @fseek ( $this->fp , $this->EndIpOff , SEEK_SET ) ;        $buf = fread ( $this->fp , 5 ) ;        $this->EndIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);        $this->CountryFlag = ord ( $buf[4] ) ;        return $this->EndIp ;    }    function getCountry ( ) {        switch ( $this->CountryFlag ) {            case 1:            case 2:                $this->Country = $this->getFlagStr ( $this->EndIpOff+4) ;                //echo sprintf('EndIpOffset=(%x)',$this->EndIpOff );                $this->Local = ( 1 == $this->CountryFlag )? '' : $this->getFlagStr ( $this->EndIpOff+8);                break ;            default :                $this->Country = $this->getFlagStr ($this->EndIpOff+4) ;                $this->Local = $this->getFlagStr ( ftell ( $this->fp )) ;        }    }    function getFlagStr ( $offset )    {        $flag = 0 ;        while ( 1 ){            @fseek ( $this->fp , $offset , SEEK_SET ) ;            $flag = ord ( fgetc ( $this->fp ) ) ;            if ( $flag == 1 || $flag == 2 ) {                $buf = fread ($this->fp , 3 ) ;                if ($flag == 2 ){                    $this->CountryFlag = 2 ;                    $this->EndIpOff = $offset - 4 ;                }                $offset = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])* 256*256);            }else{                break ;            }        }        if ( $offset < 12 )            return '';        @fseek($this->fp , $offset , SEEK_SET ) ;        return $this->getStr();    }    function getStr ( )    {        $str = '' ;        while ( 1 ) {            $c = fgetc ( $this->fp ) ;            if ( ord ( $c[0] ) == 0 )               break ;            $str .= $c ;        }        return $str ;    }    function qqwry ($dotip) {        $nRet;        $ip = IpToInt ( $dotip );        $this->fp= @fopen(QQWRY, "rb");        if ($this->fp == NULL) {              $szLocal= "OpenFileError";            return 1;          }          @fseek ( $this->fp , 0 , SEEK_SET ) ;        $buf = fread ( $this->fp , 8 ) ;        $this->FirstStartIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);        $this->LastStartIp = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])*256*256) + (ord($buf[7])*256*256*256);        $RecordCount= floor( ( $this->LastStartIp - $this->FirstStartIp ) / 7);        if ($RecordCount <= 1){            $this->Country = "FileDataError";            fclose ( $this->fp ) ;            return 2 ;        }          $RangB= 0;        $RangE= $RecordCount;        // Match ...        while ($RangB < $RangE-1)        {          $RecNo= floor(($RangB + $RangE) / 2);          $this->getStartIp ( $RecNo ) ;            if ( $ip == $this->StartIp )            {                $RangB = $RecNo ;                break ;            }          if ( $ip > $this->StartIp)            $RangB= $RecNo;          else            $RangE= $RecNo;        }        $this->getStartIp ( $RangB ) ;        $this->getEndIp ( ) ;        if ( ( $this->StartIp <= $ip ) && ( $this->EndIp >= $ip ) ){            $nRet = 0 ;            $this->getCountry ( ) ;            //这样不太好..............所以..........            $this->Local = str_replace("(我们一定要解放台湾!!!)", "", $this->Local);        }else {            $nRet = 3 ;            $this->Country = '未知' ;            $this->Local = '' ;        }        fclose ( $this->fp ) ;        return $nRet ;    } } function ip2location ( $ip ) {    $wry = new TQQwry ;    $nRet = $wry->qqwry ( $ip );    //可以利用 $nRet做一些事情,我是让他自动记录未知IP到一个表,代码就不写了。  return $wry->Country.$wry->Local ; } -------------雷傲的cgi的做法 sub ipwhere { my $ipbegin,$ipend,$ipData1,$ipData2,$DataSeek,$ipFlag;  my $ip=shift; my @ip=split(/\./,$ip); my $ipNum = $ip[0]*16777216+$ip[1]*65536+$ip[2]*256+$ip[3];  my $ipfile="${lbdir}data/QQWry.Dat"; open(FILE,"$ipfile"); binmode(FILE); sysread(FILE,$ipbegin,4); sysread(FILE,$ipend,4);  $ipbegin=unpack("L",$ipbegin);  $ipend=unpack("L",$ipend); my $ipAllNum = ($ipend-$ipbegin)/7+1;  my $BeginNum=0; my $EndNum=$ipAllNum;  Bgn: my $Middle= int(($EndNum+$BeginNum)/2);  seek(FILE,$ipbegin+7*$Middle,0); read(FILE,$ipData1,4); my $ip1num=unpack("L",$ipData1); if ($ip1num > $ipNum) {  $EndNum=$Middle;  goto Bgn; }  read(FILE,$DataSeek,3); $DataSeek=unpack("L",$DataSeek."\0"); seek(FILE,$DataSeek,0); read(FILE,$ipData2,4); my $ip2num=unpack("L",$ipData2); if ($ip2num < $ipNum) {  goto nd if ($Middle==$BeginNum);  $BeginNum=$Middle;  goto Bgn; }  $/="\0"; read(FILE,$ipFlag,1); if ($ipFlag eq "\1") {  my $ipSeek;  read(FILE,$ipSeek,3);  $ipSeek = unpack("L",$ipSeek."\0");  seek(FILE,$ipSeek,0);  read(FILE,$ipFlag,1); } if ($ipFlag eq "\2") {  my $AddrSeek;  read(FILE,$AddrSeek,3);  read(FILE,$ipFlag,1);  if($ipFlag eq "\2") {   my $AddrSeek2;   read(FILE,$AddrSeek2,3);   $AddrSeek2 = unpack("L",$AddrSeek2."\0");   seek(FILE,$AddrSeek2,0);  }  else {   seek(FILE,-1,1);  }  $ipAddr2=;  $AddrSeek = unpack("L",$AddrSeek."\0");  seek(FILE,$AddrSeek,0);  $ipAddr1=; } else {  seek(FILE,-1,1);  $ipAddr1=;  read(FILE,$ipFlag,1);  if($ipFlag eq "\2") {   my $AddrSeek2;   read(FILE,$AddrSeek2,3);   $AddrSeek2 = unpack("L",$AddrSeek2."\0");   seek(FILE,$AddrSeek2,0);  }  else {   seek(FILE,-1,1);  }  $ipAddr2=; }  nd: chomp($ipAddr1,$ipAddr2); $/="\n"; close(FILE);  $ipAddr2="" if($ipAddr2=~/http/i); my $ipaddr="$ipAddr1 $ipAddr2"; $ipaddr =~ s/CZ88\.NET//isg; $ipaddr="未知地区" if ($ipaddr=~/未知|http/i || $ipaddr eq ""); return $ipaddr;}   Get和Put语句也可以读写多个字节,可以把每次读写的内容放在一个字节变量数组中,以提高程序速度。例如:       Dim DSX() As Byte      '为字节数组,用来存储读写内容       Dim ReadFileNo, WriteFileNo As Integer     '读写文件号       Const Unit = 100000       '读写块的大小       Open SourceFileName For Binary Access Read As 1       WriteFileNo = FreeFile       Open TargetFileName For Binary Access Write As WriteFileNo       ReDim DSX(Unit) As Byte '     设置存储字节数组的大小       Get #ReadFileNo, 100, DSX()       Put #WriteFileNo, 1, DSX()       Close WriteFileNo, ReadFileNo VB没有提供移位操作的指令和函数,只提供and(与)、or(或)、xor(异或)、eqv(同或)、not(非)等几个运算符,而编程时有时需要对一个字节进行移位操作(如进行加密),怎么办?其实只用and、or二个运算符即可搞掂。例如要将变量byte1的第八位置1(假设byte1的二进制值为01001101),则只需byte1 or &h80(即01001101 or 10000000),如要将第八位置0,则只需byte1 and &h7f。请看下面程序段是如何实现循环左移的:    Public Function byteleft(byte1 As Byte, n As Integer) As Byte   `将byte1左移n位  Dim intem As Byte         `临时变量  Dim intem1 As Byte         `临时变量  Dim x, y As Integer  intem1 = byte1  For x = 1 To n         `移多少位就循环多少次  For y = 8 To 1 Step -1        `从第八位(左边第一位)开始循环左移  Select Case y  Case 8  If (intem1 And &H80) = &H80 Then       `如果临时变量intem1的第八位是1,  intem = &H1         `则将临时变量intem置1,  Else  intem = &H0         `反之置0  End If  Case 7  If (intem1 And &H40) = &H40 Then       `如果临时变量intem1的第七位是1,  intem1 = intem1 Or &H80        `则将其第八位置1(其它位不变),  Else  intem1 = intem1 And &H7F        `反之将第八位置0(其它位不变)  End If  Case 6  If (intem1 And &H20) = &H20 Then       `操作与上面相同  intem1 = intem1 Or &H40  Else  intem1 = intem1 And &HBF  End If  Case 5  If (intem1 And &H10) = &H10 Then  intem1 = intem1 Or &H20  Else  intem1 = intem1 And &HDF  End If  Case 4  If (intem1 And &H8) = &H8 Then  intem1 = intem1 Or &H10  Else  intem1 = intem1 And &HEF  End If  Case 3  If (intem1 And &H4) = &H4 Then  intem1 = intem1 Or &H8  Else  intem1 = intem1 And &HF7  End If  Case 2  If (intem1 And &H2) = &H2 Then  intem1 = intem1 Or &H4  Else  intem1 = intem1 And &HFB  End If  Case 1  If (intem1 And &H1) = &H1 Then  intem1 = intem1 Or &H2  Else  intem1 = intem1 And &HFD  End If  If intem = &H1 Then        `移完第一位后,如果intem是1(即第八位是1)  intem1 = intem1 Or &H1        `则将intem1的第一位置1  Else  intem1 = intem1 And &HFE        `反之置0  End If  End Select  Next y  Next x  byteleft = intem1         `将intem1的值返回给函数名  End Function  参照此程序段,不难实现循环右移。(此程序段在VB5上调试通过。) Public Function ipwhere(ipAddress As String)'Dim ipbegin, ipend, ipData1, ipData2, DataSeek, ipFlagDim ipip = Split(ipAddress, ".")Dim ipNum '当前要查询的ipipNum = ip(0) * 16777216 + ip(1) * 65536 + ip(2) * 256 + ip(3)'Debug.Print ipNumDim ipfileipfile = App.Path & "\QQwry.dat" ''''''''''''''''''''''''vb的文件读取方式Dim fn As Integerfn = FreeFileOpen ipfile For Binary Access Read As #fnDim ipbegin, ipend'Dim ipbegin As Long'Dim ipend As Long'Get #fn, , ipbegin '读文件头4个字节 第一个起始IP的绝对偏移'Get #fn, , ipend '再读文件头4个字节 最后一个起始IP的绝对偏移Dim ipbeginArray(1 To 4) As ByteDim ipendArray(1 To 4) As ByteGet #fn, , ipbeginArray()Get #fn, , ipendArray()ipbegin = unPack(ipbeginArray)ipend = unPack(ipendArray) Dim ipAllNum'每组ip段占7个字节 计算有多少组ip段ipAllNum = (ipend - ipbegin) / 7 + 1Dim BeginNum, EndNumBeginNum = 0EndNum = ipAllNumDim MiddleDim ipData1Dim ipData1Array(1 To 4) As ByteDim ip1NumBgn: Middle = CLng((EndNum + BeginNum) / 2) Get #fn, ipbegin + 7 * Middle, ipData1Array() ipData1 = unPack(ipData1Array) Debug.Print Loc(fn) ip1Num = ipData1 If ip1Num > ipNum Then  EndNum = Middle  GoTo Bgn End IfDim DataSeekDim DataSeekArray(1 To 3) As Byte'Debug.Print Loc(fn)Get #fn, , DataSeekArray()DataSeek = unPack(DataSeekArray)Dim ipData2, ip2NumDim ipData2Array(1 To 4) As ByteGet #fn, DataSeek, ipData2Array()ipData2 = unPack(ipData2Array)ip2Num = ipData2If ip2Num < ipNum Then  If Middle = BeginNum Then GoTo nd  BeginNum = Middle  GoTo BgnEnd IfDim ipFlag As ByteGet #fn, , ipFlagDim ipAddr2 As StringDim ipAddr1 As StringIf ipFlag = 1 Then Dim ipSeek Dim ipSeekArray(1 To 3) As Byte Get #fn, , ipSeekArray() ipSeek = unPack(ipSeekArray) Get #fn, ipSeek, ipFlagEnd IfIf ipFlag = 2 Then Dim AddrSeek Dim AddrSeekArray(1 To 3) As Byte Dim AddrSeek2 Dim AddrSeek2Array(1 To 3) As Byte Get #fn, , AddrSeekArray() Get #fn, , ipFlag If ipFlag = 2 Then  Get #fn, , AddrSeek2Array()  AddrSeek2 = unPack(AddrSeek2Array)  Seek #fn, AddrSeek2 Else  Seek #fn, Loc(fn) - 1 End If  Dim temp1(1024) As Byte '1k 字节空间  Dim curr1 As Integer  curr1 = 0  Do   Get #fn, , temp1(curr1)   curr1 = curr1 + 1  Loop Until temp1(curr1 - 1) = 0  ipAddr2 = temp1  'ipaddr2  AddrSeek = unPack(AddrSeekArray)  Seek #fn, AddrSeek  curr1 = 0  Do   Get #fn, , temp1(curr1)   curr1 = curr1 + 1  Loop Until temp1(curr1 - 1) = 0  ipAddr1 = temp1  'ipaddr1Else Seek #fn, Loc(fn) - 1  Dim temp2(1024) As Byte '1k 字节空间  Dim curr2 As Integer  curr2 = 0  Do   Get #fn, , temp2(curr2)   curr2 = curr2 + 1  Loop Until temp2(curr2 - 1) = 0  ipAddr1 = temp2 'ipaddr1  Get #fn, , ipFlag  If ipFlag = 2 Then   Get #fn, , AddrSeek2Array()   AddrSeek2 = unPack(AddrSeek2Array)   Seek #fn, AddrSeek2  Else   Seek #fn, Loc(fn) - 1  End If  curr2 = 0  Do   Get #fn, , temp2(curr2)   curr2 = curr2 + 1  Loop Until temp2(curr2 - 1) = 0  ipAddr2 = temp2  'ipaddr2End Ifnd: Close #fn…………………………………………vb的文件读取方式 ipwhere = ipAddr1 & " " & ipAddr2 End Function 作者:Strongc整理:LAWRENCE备注:VB例子程序请到我的网站下载。 http://lawrence.ys168.com

阅读(256) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册