前几天,有个亲戚打电话给我,在X市有个农特产品的投票,投票的流程概况是这样的:无需注册,每个小时可以投10票,经测试发现是IP限制的。更换代理之后可以继续投票。
本来是让我找人给X产品投票的,但是我觉得这样挺麻烦的。是不是可以伪造一个POST发送给服务器,模拟是成投票呢?反正不用登陆,只需要发送到服务器就行了。(初步设想)。
这里我想注明一下,高手请一笑了之。我只是菜鸟。
发送到服务器的包是什么呢?用smsniff抓个包看看。抓下来的包如下:(只注明重要的部分)
POST /vote.asp HTTP/1.1
Host: vote.cdyee.com
Connection: keep-alive
Content-Length: 51
Cache-Control: max-age=0
Origin: http://vote.cdyee.com
User-Agent: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.12 (KHTML, like Gecko) Maxthon/3.3.8.3000 Chrome/18.0.966.0 Safari/535.12
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
DNT: 1
Referer: http://vote.cdyee.com/plus_vote.asp?id=444
Accept-Encoding: gzip,deflate
Accept-Language: zh-CN
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Cookie: mobi=; mail=; ASPSESSIONIDCAATQBTS=MPKHCGCAGGJIIDEBBFCPGIEJ; ASPSESSIONIDCCBTQBST=LKAGCCPAMFBOBFEGBDIBMGNH
code=5ILA&id=444&t=&imageField.x=37&imageField.y=27
代码可以如下:
WebClient wc = new WebClient();
try
{
wc.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
wc.Headers.Add("Accept-Language", "zh-CN");
wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.12 (KHTML, like Gecko) Maxthon/3.3.8.3000 Chrome/18.0.966.0 Safari/535.12");
wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
wc.Headers.Add("Accept-Encoding", "gzip, deflate");
string result;
result = wc.UploadString("http://vote.cdyee.com/vote.asp", "POST", "code=5ILA&id=444&t=&imageField.x=37&imageField.y=27");
Console.WriteLine(result);
}
这样伪装的POST包发送到数据库,是不是就可以实现投票了呢?事实证明,还不行。
我觉得应该是验证码问题,返回的这句“code=5ILA&id=444&t=&imageField.x=37&imageField.y=27”,这个code后面的属性“5ILA”就是验证码,发回去的验证码和请求的时候服务器那边的验证码要一致才能完成投票过程。
不可避免的来到了验证码识别这里。
验证码怎么识别呢?验证码的目的主要就是为了防止自动注册、登陆之类大量消耗服务器资源的客户端的,因此识别起来肯定有一定的难度,关键是怎么解决这个的思路。
百度了一下,大部分是商业的验证码识别程序,都是收费的,功能也比较完善,这时我看到有一个有意思的东西--“验证码字库”,这个东西我一下就有了一点思路:将验证码采集下来,然后用采集的数据和要识别的验证码进行匹配,在约80%的匹配的情况下可以认为是同一个验证码字符。
好了,思路有了,实现就可以了。
该验证码是4位,1-9,A-N,P-Z这些字符构成,先将图片从网上请求后载入到C#的picturebox控件中,然后将该bmp图片(大小为200*60,3位保存一个像素),去掉bmp文件头54字节,将该文件读入到流中,找出除了底色之外颜色最多的一种颜色A,改颜色的点构成了验证码,其余的颜色都是噪点,这样把不是A颜色的点全部去掉,就得到了验证码的矩阵。这样的一个验证码矩阵,利用各种方法写入到txt文本文件中,保存足够的验证码字符,然后验证的时候,将格式化之后的矩阵和保存的文本文件中的数据进行匹配,相似度在80%的将该文件的文件名当做读取出的匹配字符输出。
大概思想就是这样,具体呢,我贴一点代码上来,截几张图。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
namespace votes
{
public partial class found : Form
{
public found()
{
InitializeComponent();
}
private string path = @"C:\Documents and Settings\Administrator\My Documents\Visual Studio 2010\Projects\votes\votes\bin\Debug\";
public char next(char c)
{
if ((c >= 49 && c <= 56) || (c >= 65 && c <= 77) || c >= 80 && c <= 89)
{
c++;
return c;
}
else
if (c == '9')
return 'A';
else
if (c == 'N')
return 'P';
else
return '#';
}
private void found_Load(object sender, EventArgs e)
{
pb_check.ImageLocation = @"http://vote.cdyee.com/code.asp";
StreamReader sr = new StreamReader(path+"count.txt");
{
String line;
// Read and display lines from the file until the end of
// the file is reached.
if ((line = sr.ReadLine()) != null)
{
lbl_count.Text = line.Trim();
}
}
sr.Close();
}
private void btn_get_Click(object sender, EventArgs e)//验证码识别
{
lbl_answer.Text = "";
// string s = "";
pb_check.Image.Save("a.bmp");
FileStream fileStream = new FileStream("a.bmp", FileMode.Open, FileAccess.Read);
BinaryReader binaryReader = new BinaryReader(fileStream);
fileStream.Position = 54;
byte[] buffer = binaryReader.ReadBytes((int)fileStream.Length - 54);
binaryReader.Close();
fileStream.Close();
File.Delete("a.bmp");
int[,] temp = new int[200, 4];
for (int i = 54; i < buffer.Length; )
{
if (buffer[i] != 255 || buffer[i + 1] != 255 || buffer[i + 2] != 255)//不是白色,可能是杂色或者验证码的颜色
{
for (int k = 0; k < 200; k++)
{
if (temp[k, 0] == (int)buffer[i] && temp[k, 1] == (int)buffer[i + 1] && temp[k, 2] == (int)buffer[i + 2])
{
temp[k, 3]++;
break;
}
if (temp[k, 0] == 0 && temp[k, 1] == 0 && temp[k, 2] == 0)
{
temp[k, 0] = buffer[i];
temp[k, 1] = buffer[i + 1];
temp[k, 2] = buffer[i + 2];
temp[k, 3] = 1;
break;
}
}
}
i += 3;
}
int max = temp[0, 3];
int pos = 0;//验证码颜色在temp数组中的位置
for (int i = 0; i < 200; i++)
{
if (temp[i, 3] > max)
{
max = temp[i, 3];
pos = i;
}
if (temp[i, 3] == 0)
{
break;
}
}
char[,] real = new char[60, 200];
int temppos = 0;
for (int i = 0; i < 60; i++)
{
for (int j = 0; j < 200; j++)
{
if (buffer[temppos] == temp[pos, 0] && buffer[temppos + 1] == temp[pos, 1] && buffer[temppos + 2] == temp[pos, 2])
{
real[i, j] = '*';
}
else
{
real[i, j] = '_';
}
temppos += 3;
}
}
for (int i = 0; i < 4; i++)//匹配
{
int countx=0;
for (int j = 0; j < 60; j++)
{
for (int k = i * 50; k < i * 50 + 50; k++)
{
if (real[j, k] == '*') countx++;
}
}
char c = '1';
char lbl = '0';
while (true)
{
int right = 0;
if (c == '#')
{
lbl_answer.Text += '?';
break;
}
StreamReader sr = new StreamReader(path+c+".txt", Encoding.GetEncoding("gb2312"));
string tm = sr.ReadLine();
sr.Close();
int strpos = 0;
for (int m = 0; m < tm.Length / 3000; m++)
{
for (int j = 59; j >=0; j--)
{
for (int k = i*50; k < i*50 + 50; k++)
{
if (real[j, k] =='*' && tm[strpos]=='*')
{
right++;
}
strpos++;
}
}
double rate=Convert.ToDouble(right)/Convert.ToDouble(countx);
double setrate = Convert.ToDouble(txt_rate.Text);
if (rate>setrate)
{
lbl = c;
lbl_answer.Text += lbl;
break;
}
else
right = 0;
}
if (lbl != '0') break;
c = next(c);
}
}
/* int pp = 0;
for (int m = 0; m < 4; m++)
{
s = s.ToUpper();
StreamWriter sw = File.AppendText(path + s[m] + ".txt");
for (int i = 59; i > 0; i--)
{
for (int j = pp; j < 50 + pp; j++)
{
sw.Write(real[i, j]);
}
sw.WriteLine();
}
sw.Flush();
sw.Close();
pp += 50;
}
* */
}
private void btn_submit_Click(object sender, EventArgs e)
{
if (txt_check.Text.Length != 4)
{
MessageBox.Show("验证码不对吧!", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
string s = txt_check.Text.ToString().Trim();
pb_check.Image.Save("a.bmp");
FileStream fileStream = new FileStream("a.bmp", FileMode.Open, FileAccess.Read);
BinaryReader binaryReader = new BinaryReader(fileStream);
fileStream.Position = 54;
byte[] buffer = binaryReader.ReadBytes((int)fileStream.Length - 54);
binaryReader.Close();
fileStream.Close();
File.Delete("a.bmp");
int[,] temp = new int[200, 4];
for (int i = 54; i < buffer.Length; )
{
if (buffer[i] != 255 || buffer[i + 1] != 255 || buffer[i + 2] != 255)//不是白色,可能是杂色或者验证码的颜色
{
for (int k = 0; k < 200; k++)
{
if (temp[k, 0] == (int)buffer[i] && temp[k, 1] == (int)buffer[i + 1] && temp[k, 2] == (int)buffer[i + 2])
{
temp[k, 3]++;
break;
}
if (temp[k, 0] == 0 && temp[k, 1] == 0 && temp[k, 2] == 0)
{
temp[k, 0] = buffer[i];
temp[k, 1] = buffer[i + 1];
temp[k, 2] = buffer[i + 2];
temp[k, 3] = 1;
break;
}
}
}
i += 3;
}
int max = temp[0, 3];
int pos = 0;//验证码颜色在temp数组中的位置
for (int i = 0; i < 200; i++)
{
if (temp[i, 3] > max)
{
max = temp[i, 3];
pos = i;
}
if (temp[i, 3] == 0)
{
break;
}
}
char[,] real = new char[60, 200];
int temppos = 0;
for (int i = 0; i < 60; i++)
{
for (int j = 0; j < 200; j++)
{
if (buffer[temppos] == temp[pos, 0] && buffer[temppos + 1] == temp[pos, 1] && buffer[temppos + 2] == temp[pos, 2])
{
real[i, j] = '*';
}
else
{
real[i, j] = '_';
}
temppos += 3;
}
}
int pp = 0;
for (int m = 0; m < 4; m++)
{
s = s.ToUpper();
StreamWriter sw = File.AppendText(path + s[m] + ".txt");
for (int i = 59; i >= 0; i--)
{
for (int j = pp; j < 50 + pp; j++)
{
sw.Write(real[i, j]);
}
}
sw.Flush();
sw.Close();
pp += 50;
}
// pb_check.ImageLocation = @"http://vote.cdyee.com/code.asp";
int ctemp = Convert.ToInt32(lbl_count.Text.Trim());
lbl_count.Text = (++ctemp).ToString();
if (cb_check.Checked == true) ;
else
btn_change.PerformClick();
}
private void btn_change_Click(object sender, EventArgs e)
{
pb_check.ImageLocation = @"http://vote.cdyee.com/code.asp";
pb_check.Refresh();
txt_check.Text = "";
}
private void found_FormClosed(object sender, FormClosedEventArgs e)
{
File.WriteAllText(path+"count.txt", lbl_count.Text, Encoding.Default);
}
private void cb_lock_CheckedChanged(object sender, EventArgs e)
{
if (cb_lock.Checked == true)
{
txt_check.ReadOnly = true;
}
else
{
txt_check.ReadOnly = false;
}
}
private void pb_check_Click(object sender, EventArgs e)
{
btn_change.PerformClick();
}
}
}
这个还在继续完善中,希望给一些思路,也希望各位有经验的大大提供一些经验。