上個月有人讓我幫忙投票,我想要不寫個程序給他多刷點得了,雖然這事情有悖原則,就當娛樂了。。
先上圖
1.分析
既然是網頁投票,那肯定可以偽造HTTP請求來實現刷票。需要分析的就是該網站到底采用了哪些防止刷票的措施。原投票已經停了,我給大家描述一下。
(1).首先,這是公開給所有人的投票,沒有必須是其用戶的限制。
(2).先按F12查看網絡情況,抓取投票的HTTP請求。點投票,截取,發現是Get方式,QuerySting值有一個隨機數,一個候選人的編號,還有一個不清楚的值,初步猜測是用Cookie限制的。
(3).投完票再點投票,顯示“你已經參與過,每天只能投一次”。
(4).在Chrome瀏覽器,設置,隱私設置,查看Cookie里,找該網站的Cookies,發現多了一個不清楚的,還有一個存着候選人編號的Cookie。把這倆都刪了,再點投票,顯示“投票失敗”。雖然沒投票成功,但是返回信息不一樣了,說明肯定和Cookie有關。
(5).直接禁用該網站的Cookie,點投票還是顯示“投票失敗”。
(6).這時候仔細看了看QueryString那一長傳不清楚的值,和Cookie里那個不清楚的值是相同的,投票按鈕的事件應是不會變的,那估計頁面加載的時候這倆就一起加載了。然后刷新頁面,果然這個值變了。
(7).然后只刪除存候選人編號的Cookie,保留這個Cookie,再點投票,成功了!
這個網站防刷票的措施比較簡陋,只要仔細看看就能發現這個問題。然后我做了第一版。
2.構造HTTP請求
(1).獲取SID。先把上面我說的那個一長串不清楚的值叫做SID吧,應該是為了驗證Cookie是否起作用。
private string GetSid() { string url = "http://www.xxxxxx.com/xxxx/list-510-1.html"; HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); var res = req.GetResponse() as HttpWebResponse; string sid= res.Headers.GetValues(5)[0].Split('=')[1];//把Response里第一個Cookie的的值從Headers里取出來。
return sid;
}
(2).根據瀏覽器抓取的HTTP請求模擬一個HTTP請求
private HttpWebRequest CreatHttp() { Random rnd = new Random(); string rndstr = rnd.NextDouble().ToString();//這是模仿QueryString里那個隨機數的,沒有估計也不影響結果 string url = "http://www.xxxxxx.com/xxxx/api.php?op=qgtp&id=32&sid=" + sid + "&r=" + rndstr; HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); CookieCollection cookies = new CookieCollection(); //添加Cookie cookies.Add(new Cookie("xxxxaction", sid, "/rail", "www.peoplerail.com")); cookies.Add(new Cookie("xxxxxxxrand", rndstr, "/rail", "www.peoplerail.com")); //存的也是隨機數,生命周期只有2秒,所以分析的時候沒發現,為了仿的逼真就也寫上了 req.CookieContainer = new CookieContainer(); req.CookieContainer.Add(cookies); req.Method = "GET"; req.UserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"; req.Timeout = 10000; req.Referer = "http://www.xxxxxx.com/xxxxxx/index.php?m=content&c=index&a=lists&catid=517&page=6"; req.KeepAlive = true; req.Headers.Add("x-requested-with", "XMLHttpRequest"); //以上全部按照瀏覽器里抓取的結果一一對應 return req; }
(3).刷票,並觀察刷票狀況。就是將返回的內容里找到票數,更新到TextBox上。
private void Go() { HttpWebRequest req = CreatHttp(); var res = req.GetResponse() as HttpWebResponse; Stream st = res.GetResponseStream();//讀取Response StreamReader sr = new StreamReader(st); string result = sr.ReadToEnd().ToString(); try { textBox1.Text = result.Split(':')[1].Split('<')[0];//從返回的內容里找出票數 } catch { timer1.Stop(); label2.Text = "刷票暫停"; } }
(4).設置循環。用For循環發請求的話,嗖一下幾百次循環完了,而那邊還沒反應過來。中間用Sleep()的話,程序有時會陷入假死的狀態,不能實時觀察到刷票的情況。所以我們就用Winform自帶的控件Timer,在頁面加載的時候先停了,設置時間間隔,然后在Timer的事件里加上上面的方法就可以了。
private void Form1_Load(object sender, EventArgs e) { timer1.Stop(); timer1.Interval = 1000; }
3.改進
上面幾步完成后試着刷了一下,發現刷太快系統會返回“投票失敗,疑似刷票”的提示,又調整了一下間隔時間。可過了兩天,突然又不好使了,幾番分析,發現是加了IP限制。找代理比較麻煩,所以就用寬帶重連的方法不停的換IP吧。每刷一票斷線重連一次就可以了。
public static string Connect(string UserS,string PwdS) { string arg = @"rasdial.exe 寬帶連接" + " " + UserS + " " + PwdS; return InvokeCmd(arg); } public static string Disconnect() { string arg = string.Format("rasdial \"{0}\" /disconnect", "寬帶連接"); return InvokeCmd(arg); } private static string InvokeCmd(string cmdArgs) { string Tstr = ""; Process p = new Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardInput = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.CreateNoWindow = true; p.Start(); p.StandardInput.WriteLine(cmdArgs); p.StandardInput.WriteLine("exit"); Tstr = p.StandardOutput.ReadToEnd(); p.WaitForExit(); p.Close(); return Tstr; }
網頁投票沒有完善的防止刷票的措施,只能說是防君子不防小人,要想刷了總能找到空子。