QQwry.dat格式分析和查詢IP位置的PHP程序
By Strongc http://strongc.51.net/d2x/ 轉(zhuǎn)載時(shí)不要去掉我的名字和我的主頁(yè)鏈接,謝謝!
以前的追捕數(shù)據(jù)庫(kù)太大,而且很久沒(méi)有更新了。 所以我想到利用QQwry.dat這個(gè)文件查詢IP所在位置,QQwry.dat 在很多地方都能找到,一般看IP地址的QQ壓縮包中都有。
但是沒(méi)有任何相關(guān)格式資料。
我分析了這個(gè)文件的格式,目前如下結(jié)論:
格式如下:
A。文件頭,共8字節(jié) B。若干條記錄的結(jié)束地址+國(guó)家和區(qū)域 C。按照從小到大排列的若干條起始地址+結(jié)束地址偏移,定長(zhǎng),7字節(jié) D。所有的IP都是用4字節(jié)整數(shù)記錄的,并且遵照Intel次序,高位在后,低位在前。 E。所有偏移量都是絕對(duì)偏移,就是從文件最開(kāi)頭計(jì)算。 F。除了文件頭用了兩個(gè)4字節(jié)偏移,其余偏移量都用3字節(jié)。 G。所有的偏移量也是低位在前,高位在后 H。采用了一些字符串壓縮技術(shù)
1。文件頭,共8字節(jié) FirstStartIpOffset:4 第一個(gè)起始IP的絕對(duì)偏移 LastStartIpOffset:4 最后一個(gè)起始IP的絕對(duì)偏移
2。起始地址+結(jié)束地址偏移記錄區(qū) 每條記錄7字節(jié),按照起始地址從小到大排列
StartIp:4 起始地址,整數(shù)形式的IP EndIpOffset:3 結(jié)束地址絕對(duì)偏移
3。結(jié)束地址+國(guó)家+區(qū)域記錄區(qū)
EndIP:4 國(guó)家+區(qū)域記錄:不定長(zhǎng)
4。國(guó)家+區(qū)域記錄,有幾種形式 4.1。 國(guó)家字符串,以 0x0 結(jié)束 區(qū)域字符串,以 0x0 結(jié)束
4.2。 Flag:1 標(biāo)識(shí)取值: 0x1,后面沒(méi)有Local記錄 0x2,后面還有Local記錄 sCountryOffset:3 實(shí)際的字符串要去這個(gè)偏移位置去找 LocalRec:不定長(zhǎng),可選 根據(jù)Flag取值而定。這個(gè)記錄也類似Country,可能采用壓縮
4.3 LocalRec結(jié)構(gòu)一 flag:1 還不是十分了解這個(gè)flag含義,取值 0x1 or 0x2 sLocalOffset:3
4.4 LocalRec結(jié)構(gòu)二 sLocal:不定長(zhǎng) 普通的C風(fēng)格字符串
注意:sCountryOffset指向的位置可能依然是4.2格式的,不知道為什么這樣設(shè)計(jì)。
Flag取0x1時(shí),sCountryOffset指向的位置可能是Flag為0x2,這時(shí),LocalRec也在這里尋找。
現(xiàn)在不明白當(dāng)記錄Local的位置遇到0x2的標(biāo)志意味著什么。
在qqwry.dat中,似乎存在一些錯(cuò)誤。 個(gè)別的記錄Local會(huì)被寫為: 0x2,0x0,0x0,0x0 根據(jù)規(guī)則,應(yīng)該到文件最開(kāi)頭去尋找,可是,文件最開(kāi)頭顯然不是記錄這些的。
我才學(xué)PHP不久,各位不要笑,你要能改進(jìn)當(dāng)然好,記得給我一份。 我參考了一些網(wǎng)上找到的代碼,就不一一寫出出處了。
說(shuō)老實(shí)話,我很頭疼PHP無(wú)法明確指定變量的類型。 比如,我想讓某個(gè)數(shù)是無(wú)符號(hào)的整形,它很不聽(tīng)話,非要是帶個(gè)負(fù)號(hào),我只好嘗試各種可能的寫法.......... 各位都是怎么處理類似的事情?
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; // 標(biāo)識(shí) Country位置 // 0x01,隨后3字節(jié)為Country偏移,沒(méi)有Local // 0x02,隨后3字節(jié)為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("(我們一定要解放臺(tái)灣!!!)", "", $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做一些事情,我是讓他自動(dòng)記錄未知IP到一個(gè)表,代碼就不寫了。 return $wry->Country.$wry->Local ; }
|