43 12
发新话题
打印

[原创]浅谈验证码识别

[原创]浅谈验证码识别

信息来源:邪恶八进制信息安全团队(www.eviloctal.com
文章作者:icexiaoye

  题目忽悠了点,其实,是这么回事情,老婆申请了校内网(xiaonei.com),然后看看自己,觉得人气不够高,想到刷人气,然后把,一个人分析了半天,得出了,要刷人气,只要自己多看看别人的页面,那么,你就会出现在别人页面里“最近谁看过XX的页面”,然后别人肯定会回过来看你的页面,于是就为自己增加了一点人气。

  于是乎,我家可爱的团子,操起delphi开始写程序,前面代码部分倒不难,实现个登录页面,自动浏览页面就OK了,然后发现个问题,就是访问100个页面后,会跳出来要求输入验证码,这下团子傻眼了,来问我,我本着偷懒的精神,在网上找了下,发现关于验证码部分的代码真的很少,而且比较不通用,要么太复杂,对于校内这样简单的验证码,完全是因为太过于复杂二无法读出简单的东东

罗嗦了一大堆,开始吧
验证码地址 http://xiaonei.com/RandImageServ ... p;rnd=1191388205304

大家刷新下可以看到不同的验证码

很明显滴说,验证码还是相对比较简单的,没什么倾斜角,没什么噪点,清清楚楚的白字黑底
随便下载几个验证码,在本地用画图板打开

看到我圈出来的数字米,5是纵坐标,3是横坐标

OK,我们用PS打开看看,顺便做几条辅助线更清楚鸟
看到米

(记得放大1600%后再看哈)
我们要读取验证码,采用的原理是选择几个特殊定位点,才确定那个字符是数字几
(- -!总觉得网上用的什么二制化后然后用字典太麻烦了,还要考虑容错)
解释下哈我
我们把第一位是0-9的验证码放一竖排,然后拉条辅助线,很明显
在纵坐标5这一列上, 0-9所有的白色点位置是不同,通过找出不同点,很容易就确定出验证码了

说了半天,还是看代码吧

其实很简单的
复制内容到剪贴板
代码:
procedure TForm1.Button1Click(Sender: TObject);
var
c:TGIFImage;
a:tbitmap ;
b1,b2,b3,b4,b5,b6,b7,b8,b9:tcolor;
mstrm : TMemoryStream;
i,z:integer;
y:array [1..4] of string;
yz:string;

begin
mstrm := TMemoryStream.Create();
c:= TGIFImage.Create ;
a := TBitmap.Create ;
IdHttp1.Get(imgurl, mstrm);
mstrm.Position := 0;
c.LoadFromStream(mstrm);
image1.Picture.Assign(c);
a.Assign(c);
for i:=1 to 4 do
begin
z:=8*i-3 ;
b1:=a.Canvas.Pixels[z,3];
b2:=a.Canvas.Pixels[z,4];
b3:=a.Canvas.Pixels[z,5];
b4:=a.Canvas.Pixels[z,6];
b5:=a.Canvas.Pixels[z,7];
b6:=a.Canvas.Pixels[z,8];
b7:=a.Canvas.Pixels[z,9];
b8:=a.Canvas.Pixels[z,10];
b9:=a.Canvas.Pixels[z,11];
if (colorToString(b1)='clBlack') and (colorToString(b3)='clBlack') and (colorToString(b7)='clBlack') then
y[i]:='1' ;
if (colorToString(b1)='clWhite') and (colorToString(b8)='clWhite') then
y[i]:='2'  ;

if (colorToString(b5)='clWhite') and (colorToString(b7)='clBlack') then
y[i]:='4' ;
if (colorToString(b1)='clWhite') and (colorToString(b2)='clWhite') then
y[i]:='5' ;
if (colorToString(b1)='clWhite') and (colorToString(b5)='clBlack') and (colorToString(b9)='clBlack') then
y[i]:='7';
if (colorToString(b2)='clWhite') and (colorToString(b4)='clBlack') then
y[i]:='8'  ;
if (colorToString(b4)='clWhite') and (colorToString(b5)='clBlack') and (colorToString(b2)='clBlack') then
y[i]:='9'  ;
if (colorToString(b7)='clWhite') and (colorToString(b8)='clBlack') and (colorToString(a.Canvas.Pixels[z+1,7])='clBlack') then
y[i]:='0' ;
if (colorToString(b7)='clWhite') and (colorToString(b8)='clBlack') and (colorToString(a.Canvas.Pixels[z+1,7])='clWhite') then
y[i]:='6';
if (colorToString(b1)='clWhite') and (colorToString(b2)='clBlack') and (colorToString(b9)='clBlack') and (colorToString(a.Canvas.Pixels[z,12])='clWhite') then
y[i]:='3';
end;

yz:=y[1]+y[2]+y[3]+y[4];
showmessage(yz);
Freeandnil(a);
FreeAndNil(c);
end;
越来越发现自己的语言表达能力不行了
至于那些点是怎么确定的,自己拿纸笔,把白色是第几格子,黑色是那些格子一个个列下,然后找出差额就好了

- -!
变量只所以定义的这么乱
纯粹是团子说这样打起来方便点的,大家凑活着看吧

至于说我根本没谈验证码识别
其实,我只是觉得没什么通用的办法,也不太会讲一大堆理论,所以正好自己刚写个,就拿出来说说

欢迎大家拍砖吧
玩世不恭彼此 ⌒ ˇ互相鼓励信任 認眞體驗每⒈兲.!﹏演藝⒉.個亾啲莞鎂傳奇( [淇]儭滗.

TOP

LZ的方法可以说很新颖了.
但是现在越来越多的地方开始使用杂乱背景验证码.你这个可能就不会那么好用了.
还是用二制化后然后用字典这个方法我觉得比较实在.
你这个对于简单的验证码是有效果的. 但是对于一些复杂的验证码那不是写到累死?
还有. 变量我觉得写的不是很工整

b1:=a.Canvas.Pixels[z,3];
b2:=a.Canvas.Pixels[z,4];
b3:=a.Canvas.Pixels[z,5];
b4:=a.Canvas.Pixels[z,6];
b5:=a.Canvas.Pixels[z,7];
b6:=a.Canvas.Pixels[z,8];
b7:=a.Canvas.Pixels[z,9];
b8:=a.Canvas.Pixels[z,10];
b9:=a.Canvas.Pixels[z,11];

if (colorToString(b1)='clBlack') and (colorToString(b3)='clBlack') and (colorToString(b7)='clBlack') then
y:='1' ;
if (colorToString(b1)='clWhite') and (colorToString(b8)='clWhite') then
y:='2' ;

if (colorToString(b5)='clWhite') and (colorToString(b7)='clBlack') then
y:='4' ;
if (colorToString(b1)='clWhite') and (colorToString(b2)='clWhite') then
y:='5' ;
if (colorToString(b1)='clWhite') and (colorToString(b5)='clBlack') and (colorToString(b9)='clBlack') then
y:='7';
if (colorToString(b2)='clWhite') and (colorToString(b4)='clBlack') then
y:='8' ;
if (colorToString(b4)='clWhite') and (colorToString(b5)='clBlack') and (colorToString(b2)='clBlack') then
y:='9' ;
if (colorToString(b7)='clWhite') and (colorToString(b8)='clBlack') and (colorToString(a.Canvas.Pixels[z+1,7])='clBlack') then
y:='0' ;
if (colorToString(b7)='clWhite') and (colorToString(b8)='clBlack') and (colorToString(a.Canvas.Pixels[z+1,7])='clWhite') then
y:='6';
if (colorToString(b1)='clWhite') and (colorToString(b2)='clBlack') and (colorToString(b9)='clBlack') and (colorToString(a.Canvas.Pixels[z,12])='clWhite') then
y:='3';
end;
这些. 完全可以找出差额这样会剩很多力气的啊.
LZ说这样是看着简单明了了... 不过确实这个有些麻烦啊.....
愚人小小的建议. 如果有不同意见希望大家批评.

TOP

楼主
我今天在写一个论坛自动登陆器
使用post向服务器发送数据包
但论坛开始登陆时就需要输入认证码
登陆时图片认证码的原理就是把
认证码值和用户的 session绑定在一起
使用post方式向服务器发送数据包的方式"隐藏"登陆,
那么该如何获取认证码呢????????
liuka

TOP

佩服佩服,虽然算法没有优化,但是思路很好。但如果有杂点就不能这样了~

TOP

引用:
引用第1楼1种痛于2007-10-06 01:57发表的 :
LZ的方法可以说很新颖了.
但是现在越来越多的地方开始使用杂乱背景验证码.你这个可能就不会那么好用了.
还是用二制化后然后用字典这个方法我觉得比较实在.
你这个对于简单的验证码是有效果的. 但是对于一些复杂的验证码那不是写到累死?
还有. 变量我觉得写的不是很工整

b1:=a.Canvas.Pixels[z,3];
b2:=a.Canvas.Pixels[z,4];
b3:=a.Canvas.Pixels[z,5];
b4:=a.Canvas.Pixels[z,6];
b5:=a.Canvas.Pixels[z,7];
b6:=a.Canvas.Pixels[z,8];
b7:=a.Canvas.Pixels[z,9];
b8:=a.Canvas.Pixels[z,10];
b9:=a.Canvas.Pixels[z,11];

if (colorToString(b1)='clBlack') and (colorToString(b3)='clBlack') and (colorToString(b7)='clBlack') then
y:='1' ;
if (colorToString(b1)='clWhite') and (colorToString(b8)='clWhite') then
y:='2' ;

if (colorToString(b5)='clWhite') and (colorToString(b7)='clBlack') then
y:='4' ;
if (colorToString(b1)='clWhite') and (colorToString(b2)='clWhite') then
y:='5' ;
if (colorToString(b1)='clWhite') and (colorToString(b5)='clBlack') and (colorToString(b9)='clBlack') then
y:='7';
if (colorToString(b2)='clWhite') and (colorToString(b4)='clBlack') then
y:='8' ;
if (colorToString(b4)='clWhite') and (colorToString(b5)='clBlack') and (colorToString(b2)='clBlack') then
y:='9' ;
if (colorToString(b7)='clWhite') and (colorToString(b8)='clBlack') and (colorToString(a.Canvas.Pixels[z+1,7])='clBlack') then
y:='0' ;
if (colorToString(b7)='clWhite') and (colorToString(b8)='clBlack') and (colorToString(a.Canvas.Pixels[z+1,7])='clWhite') then
y:='6';
if (colorToString(b1)='clWhite') and (colorToString(b2)='clBlack') and (colorToString(b9)='clBlack') and (colorToString(a.Canvas.Pixels[z,12])='clWhite') then
y:='3';
end;
这些. 完全可以找出差额这样会剩很多力气的啊.
LZ说这样是看着简单明了了... 不过确实这个有些麻烦啊.....
愚人小小的建议. 如果有不同意见希望大家批评
引用:
引用第3楼nnyy0130于2007-10-06 14:42发表的 :
佩服佩服,虽然算法没有优化,但是思路很好。但如果有杂点就不能这样了~
写的匆忙,没有进一步找差额
不过也是找过了,要不就不会这么简单了
其实找也不累
拿纸笔,列表,一看就看出来了
有杂点噪音到没什么
可以用调整阈值的办法
不过用来对付那些随机倾斜的就有点麻烦了
引用:
引用第2楼liuka于2007-10-06 10:47发表的 :
楼主
我今天在写一个论坛自动登陆器
使用post向服务器发送数据包
但论坛开始登陆时就需要输入认证码
登陆时图片认证码的原理就是把
认证码值和用户的 session绑定在一起
使用post方式向服务器发送数据包的方式"隐藏"登陆,
那么该如何获取认证码呢????????
用webbrower操作网页表单就可以了
不需要直接post
玩世不恭彼此 ⌒ ˇ互相鼓励信任 認眞體驗每⒈兲.!﹏演藝⒉.個亾啲莞鎂傳奇( [淇]儭滗.

TOP

不过DZ那种BT的论坛验证码,文字和背景都是彩色的、随机的,非常难处理啊,呵呵

TOP

嘿嘿,移动的验证码也适用
加点东西
工行的也OK
玩世不恭彼此 ⌒ ˇ互相鼓励信任 認眞體驗每⒈兲.!﹏演藝⒉.個亾啲莞鎂傳奇( [淇]儭滗.

TOP

啊哈哈,昨天也研究了一下校内网的验证码,搜了一些资料,里面的方法都很繁琐,后来意外的看到了lz大哥的这篇文章,感觉思路和其它的完全不一样,刚好用自己的能力能够实现,我的是PHP版本的,算法方面还没有优化过
复制内容到剪贴板
代码:
<?
set_time_limit(0);
$content=file_get_contents(&#39;http://xiaonei.com/RandImageServlet?post=doodle_224467352&#39;);
$f=fopen(&#39;a.gif&#39;,&#39;w&#39;);
fwrite($f,$content);
fclose($f);
$gif=&#39;a.gif&#39;;
$yzm=&#39;&#39;;
for($x=5;$x<=29;$x+=8){
  $n=&#39;&#39;;
  for($y=0;$y<=15;$y++){
    color(read($x,$y))==true?$n.=$y:&#39;&#39;;
  }
  switch($n){
    case &#39;56789&#39;:
      $num=chk($x+4);
      break;
    case &#39;&#39;:
      $num=1;
      break;
    case &#39;31011&#39;:
      $num=2;
      break;
    case &#39;312&#39;:
      $num=3;
      break;
    case &#39;78&#39;:
      $num=4;
      break;
    case &#39;345611&#39;:
      $num=5;
      break;
    case &#39;3&#39;:
      $num=7;
      break;
    case &#39;458910&#39;:
      $num=8;
      break;
    case &#39;5611&#39;:
      $num=9;
      break;
  }
  $yzm.=$num;
}
echo $yzm;

function read($x,$y){
  global $gif;
  $im=ImageCreateFromgif($gif);
  $rgb=ImageColorAt($im,$x,$y);
  $color=imagecolorsforindex($im,$rgb);
  return $color;
}

function color($c){
  if($c[&#39;red&#39;]>200&&$c[&#39;green&#39;]>200&&$c[&#39;blue&#39;]>200){
    return true;
  }else{
    return false;
  }
}

function chk($x){
  if(color(read($x,4))){
    return 0;
  }else{
    return 6;
  }
}
?>
本来ImageCreateFromgif函数也可以读取远程图片的,但是发现效率很低,所以就先将图片保存到本地了
Inking's Blog http://www.inkings.cn

TOP

没有倾斜和变形的验证码还是非常好验证的,即使加了躁点和色彩也增加不了多少难度。
拿网上一个通用的asp图片验证码程序来说,其生成的彩色带噪点图片在被转换成256色灰度图片并进一步选区阀值变为2色图后里面的躁点和色彩全部消失,下一步就可以按照lz的思路来。

lz的程序是一个针对性很强的专用版,如果验证码没有倾斜和变形并且躁点不能完全被去除的情况下,可以为每一个字母建立一个2色图,然后在处理过的验证码对应位置上依次对所有字母进行逐点对比,符合度最大的判为改图片所对应的字母即可,效果也非常好。

再有倾斜变形以及不规则图形干扰的情况下……还没找到特别好的方法。
http://hi.baidu.com/anuiz anuiz#163.com

TOP

先预先对每个数字hash出一个码,做成一个字典。然后,在自动识别的时候,把hash出来的码查字典就可以得到了。

TOP

引用:
引用第8楼虫虫于2007-11-14 14:28发表的 :
没有倾斜和变形的验证码还是非常好验证的,即使加了躁点和色彩也增加不了多少难度。
拿网上一个通用的asp图片验证码程序来说,其生成的彩色带噪点图片在被转换成256色灰度图片并进一步选区阀值变为2色图后里面的躁点和色彩全部消失,下一步就可以按照lz的思路来。

lz的程序是一个针对性很强的专用版,如果验证码没有倾斜和变形并且躁点不能完全被去除的情况下,可以为每一个字母建立一个2色图,然后在处理过的验证码对应位置上依次对所有字母进行逐点对比,符合度最大的判为改图片所对应的字母即可,效果也非常好。

.......
写这东西的时候没想过那么多
谢谢虫虫给的思路哈~

至于倾斜+变形
的确没什么比较简单点办法

最近借了本书
主要是写车牌号码识别的
代码复杂的有点想让我崩溃
玩世不恭彼此 ⌒ ˇ互相鼓励信任 認眞體驗每⒈兲.!﹏演藝⒉.個亾啲莞鎂傳奇( [淇]儭滗.

TOP

一句话,lz的程序是一个针对性很强的专用版

TOP

这个验证码的背景很纯净,数字与背景对比也很强烈,可以使用拉普拉斯算子很轻易的提取出来的。
乡亲们!想不到吧!!我XXX又回来啦!!!

TOP

相对来说楼主测试用的验证码属于很常规的那种,楼主所用的方法我曾经也用过,但后来遇到了颜色,图案,全部随机生成的验证码,束手无策。不知可有高见?
很好.

TOP

怎么没有人谈论应用程序上的图片识别呢...
游戏吧  http://www.game8.cc/MyBlog    http://www.asm32.cn

TOP

引用:
引用第14楼asm于2007-11-23 14:26发表的 :
怎么没有人谈论应用程序上的图片识别呢...
到三亚了现在只能手机上网。好悲惨。。。
那找你呀。我们一起开展我们的“研究探讨工作???时间不多抓紧时间呀”

遇到随机背景干扰色的可以把图片取单色
然后再进行分析判断。
比如清除掉长距离的扩展色。。。
遇到TX那种变换字体的就需要一个不错的识别引擎了。。。

应用程序的识别也很简单呀
会网页的就可以实现应用程序的了
只不过你需要先根据屏幕大小来进行截图然后
判断--喜欢Hash吗?不喜欢?那算了直接比较吧

TOP

引用:
引用第13楼bink于2007-11-23 12:41发表的 :
相对来说楼主测试用的验证码属于很常规的那种,楼主所用的方法我曾经也用过,但后来遇到了颜色,图案,全部随机生成的验证码,束手无策。不知可有高见?
颜色可以全部灰色

不过那种随即倾斜的就很麻烦

实在没什么通用点方法
大家讨论下色

asm:
不知道你的意思,能不能说详细点啊
玩世不恭彼此 ⌒ ˇ互相鼓励信任 認眞體驗每⒈兲.!﹏演藝⒉.個亾啲莞鎂傳奇( [淇]儭滗.

TOP

彩色的先转成灰度图再处理。

TOP

引用:
引用第16楼icexiaoye于2007-11-26 21:33发表的 :


颜色可以全部灰色

不过那种随即倾斜的就很麻烦
.......
我的意思就是颜色和图形都随机出来的,颜色到好搞定,我们装色盲嘛,但图形倾斜?
突然想到个办法,但或许不太实用吧。
很好.

TOP

引用:
引用第18楼bink于2007-11-27 16:27发表的 :


我的意思就是颜色和图形都随机出来的,颜色到好搞定,我们装色盲嘛,但图形倾斜?
突然想到个办法,但或许不太实用吧。
说说色
玩世不恭彼此 ⌒ ˇ互相鼓励信任 認眞體驗每⒈兲.!﹏演藝⒉.個亾啲莞鎂傳奇( [淇]儭滗.

TOP

可能我不太能表达清楚。
能否根据区域图形,也就是说假设某随机颜色和随机倾斜的数字加字母验证码中有4个等待验证数字,我们根据图形处理组件等方法将他们4个等待验证字符分离,然后将每一个图形做360度旋转,本地比较库中有一个正面垂直的标本字体,当等待验证字体在倾斜到某一角度时与标本字体中某个类似,我们就将他确认为某个字。

能明白么?
很好.

TOP

引用:
引用第20楼bink于2007-11-28 10:55发表的 :
可能我不太能表达清楚。
能否根据区域图形,也就是说假设某随机颜色和随机倾斜的数字加字母验证码中有4个等待验证数字,我们根据图形处理组件等方法将他们4个等待验证字符分离,然后将每一个图形做360度旋转,本地比较库中有一个正面垂直的标本字体,当等待验证字体在倾斜到某一角度时与标本字体中某个类似,我们就将他确认为某个字。

能明白么?
现在的变形验证码不仅仅是作了旋转,有的是根据波形算法进扭曲的。

对这种验证码识别比较麻烦,各位可有更好的见解。

TOP

需要记住一点:再这么变形扭曲,始终需要人可以看明白,那么就是说必须按照字符或者文字的某特征来变形,这样就让我们可以找到规律,我记得我老大曾经说过:没有规律也是一种规律,那就是无规律。
很好.

TOP

引用:
引用第20楼bink于2007-11-28 10:55发表的 :
可能我不太能表达清楚。
能否根据区域图形,也就是说假设某随机颜色和随机倾斜的数字加字母验证码中有4个等待验证数字,我们根据图形处理组件等方法将他们4个等待验证字符分离,然后将每一个图形做360度旋转,本地比较库中有一个正面垂直的标本字体,当等待验证字体在倾斜到某一角度时与标本字体中某个类似,我们就将他确认为某个字。

能明白么?
感觉麻烦了

那么如果和肥鸟网站里的中文验证码呢
玩世不恭彼此 ⌒ ˇ互相鼓励信任 認眞體驗每⒈兲.!﹏演藝⒉.個亾啲莞鎂傳奇( [淇]儭滗.

TOP

那个中文的话。恩。你把方正字库存一份。然后。。。。
很好.

TOP

 43 12
发新话题