13168362680的验证码错误明明是对的码

13679人阅读
Java(14)
http://sebug.net/paper/pst_WebZine/pst_WebZine_0x02/html/PSTZine_0x02_0x09.html
==Ph4nt0m Security Team==
Issue 0x02, Phile #0x09 of 0x0A
|=---------------------------------------------------------------------------=|
|=-----------------------=[
如何识别高级的验证码
]=------------------------=|
|=---------------------------------------------------------------------------=|
|=---------------------------------------------------------------------------=|
|=----------------------=[
By moonblue333
]=------------------------=|
|=-------------------=[
&moonblue333_&
]=--------------------=|
|=---------------------------------------------------------------------------=|
一、验证码的基本知识
1. 验证码的主要目的是强制人机交互来抵御机器自动化攻击的。
2. 大部分的验证码设计者并不得要领,不了解图像处理,机器视觉,模式识别,人工智能
的基本概念。
3. 利用验证码,可以发财,当然要犯罪:比如招商银行密码只有6位,验证码形同虚设,计
算机很快就能破解一个有钱的账户,很多帐户是可以网上交易的。
4. 也有设计的比较好的,比如Yahoo,Google,Microsoft等。而国内Tencent的中文验证
码虽然难,但算不上好。
二、人工智能,模式识别,机器视觉,图像处理的基本知识
1)主要流程:
比如我们要从一副图片中,识别出验证码;比如我们要从一副图片中,检测并识别出一张
人脸。 大概有哪些步骤呢?
1.图像采集:验证码呢,就直接通过HTTP抓HTML,然后分析出图片的url,然后下载保存就
可以了。 如果是人脸检测识别,一般要通过视屏采集设备,采集回来,通过A/D转操作,存为
数字图片或者视频频。
2.预处理:检测是正确的图像格式,转换到合适的格式,压缩,剪切出ROI,去除噪音,灰度
化,转换色彩空间这些。
3.检测:车牌检测识别系统要先找到车牌的大概位置,人脸检测系统要找出图片中所有
的人脸(包括疑似人脸);验证码识别呢,主要是找出文字所在的主要区域。
4.前处理:人脸检测和识别,会对人脸在识别前作一些校正,比如面内面外的旋转,扭曲
等。我这里的验证码识别,“一般”要做文字的切割
5.训练:通过各种模式识别,机器学习算法,来挑选和训练合适数量的训练集。不是训练
的样本越多越好。过学习,泛化能力差的问题可能在这里出现。这一步不是必须的,有些识
别算法是不需要训练的。
6.识别:输入待识别的处理后的图片,转换成分类器需要的输入格式,然后通过输出的类
和置信度,来判断大概可能是哪个字母。识别本质上就是分类。
2)关键概念:
图像处理:一般指针对数字图像的某种数学处理。比如投影,钝化,锐化,细化,边缘检测,
二值化,压缩,各种数据变换等等。
1.二值化:一般图片都是彩色的,按照逼真程度,可能很多级别。为了降低计算复杂度,
方便后续的处理,如果在不损失关键信息的情况下,能将图片处理成黑白两种颜色,那就最好
2.细化:找出图像的骨架,图像线条可能是很宽的,通过细化将宽度将为1,某些地方可能
大于1。不同的细化算法,可能有不同的差异,比如是否更靠近线条中间,比如是否保持联通
3.边缘检测:主要是理解边缘的概念。边缘实际上是图像中图像像素属性变化剧烈的地
方。可能通过一个固定的门限值来判断,也可能是自适应的。门限可能是图像全局的,也可
能是局部的。不能说那个就一定好,不过大部分时候,自适应的局部的门限可能要好点。被
分析的,可能是颜色,也可能是灰度图像的灰度。
机器视觉:利用计算机来模式实现人的视觉。 比如物体检测,定位,识别。按照对图像
理解的层次的差别,分高阶和低阶的理解。
模式识别:对事物或者现象的某种表示方式(数值,文字,我们这里主要想说的是数值),
通过一些处理和分析,来描述,归类,理解,解释这些事物,现象及其某种抽象。
人工智能:这种概念比较宽,上面这些都属于人工智能这个大的方向。简单点不要过分
学院派的理解就是,把人类的很“智能”的东西给模拟出来协助生物的人来处理问题,特别是
在计算机里面。
三、常见的验证码的破解分析
以http://libcaca.zoy.org/wiki/PWNtcha这里PWNtcha项目中的资料为例分析,各种验
证码的破解。(方法很多,仅仅从我个人乍看之下觉得可行的方法来分析)
1)Authimage
使用的反破解技巧:
1.不连续的点组成字符
2.有一定程度的倾斜
设计不好的地方:
1.通过纵横的直方图投影,可以找到字幕区域
2.通过Hough变换,适当的参数,可以找到近似的横线,可以做倾斜矫正
3.字符串的倾斜式面内的,没有太多的破解难度
4.字母宽度一定,大小一定
使用的反破解技巧:
1.字符是手写体
设计不好的地方:
1.检测切割阶段没有任何技术含量,属于设计的比较丑的
2.只有数字,而且手写体变化不大
3.表面看起来对识别阶段有难度,仔细分析,发现几乎不用任何高级的训练识别算法,就
固定的招某些像素点是否有色彩就够了
3)linuxfr.org
使用的反破解技巧:
1.背景颜色块
2.前景的横线或矩形
设计不好的地方:
1.背景色是单一色块,有形状,通过Region-Growth区域增长来很容易把背景给去掉
2.前景色是标准的线条,色彩单一
3.字母无粘连
4.都是印刷体
4)Ourcolony
使用的反破解技巧:
1.设计的太低级,不屑于去评价
设计不好的地方:
1.这种验证码,设计的最丑,但还是能把菜鸟搞定,毕竟学计算机的少,搞这个破解的更
少,正所谓隔行如隔山
5)LiveJournal
使用的反破解技巧:
1.这个设计略微好点,使用个随机噪音,而且作为前景
2.字母位置粗细都有变化
设计不好的地方:
1.字母没有粘连
2.噪音类型单一
3.通过在X轴的直方图投影,能准确分割字幕
4.然后在Y周作直方图投影,能准确定位高度
5.识别阶段,都是印刷体,简单地很
四、网上的一些高级验证码
4)MVN Forum
这些类型是被很多人认为比较难得类型,分析一下可以发现,字符检测,定位和分割都不
是难。 唯一影响识别率的是IMDBb和MVPS这两类,字体变形略大。
总体来说,这些类型的破解也不难,很容易做到50%以上的识别率。
五、高级验证码的破解分析
时间关系,我简单介绍如何利用图像处理和模式识别技术,自动识别比较高级的验证码。
(以风头正劲的Google为例)
1)至少从目前的AI的发展程度看,没有简单的做法能自动处理各种不同的验证码,即使
能力很强,那么系统自然也十分复杂强大。所以,要想在很简单的算法实现比较高级的验证
码破解,必须分析不同验证码算法的特点:
作为一般的图像处理和计算机视觉,会考虑色彩,纹理,形状等直接的特征,同时也考虑
直方图,灰度等统计特征,还考虑FFT,Wavelet等各种变换后的特征。但最终目标都是
Dimension Reduction(降维)然后利于识别,不仅仅是速度的考虑。从图像的角度看,很多系
统都考虑转换为灰度级甚者黑白图片。
Google的图片可以看出,颜色变化是虚晃一枪,不存在任何处理难度。难度是字体变形
和字符粘连。
如果能成功的分割字符,那么后期识别无论是用SVM等分类算法,还是分析笔顺比划走向
来硬识别,都相对好做。
2)图像处理和粘连分割
代码中的part1目录主要完成图像预处理和粘连字符分割
001:将图像从jpg等格式转换为位图便于处理
002:采用Fix/Adaptive的Threshold门限算法,将图片Bin-Value二值化。
(可用003算法)
003:采用OSTU分水岭算法,将图片Bin-Value二值化。
(更通用,大部分时候效果更好)
005:获取ROI感兴趣的区域。
006:Edge Trace边缘跟踪。
007:Edge Detection边界检测。
008:Thin细化去骨架。
009:做了一些Tidy整理。
  (这个一般要根据特定的Captcha算法调整)
010:做切割,注意图片中红色的交叉点。
011:将边缘检测和骨干交叉点监测的图像合并。
  (合并过程可以做分析: 比如X坐标偏移门限分析,交叉点区域纹理分析,线条走势分析,
等等各种方法,找出更可能的切分点和分离后部件的组合管理。)
代码:(代码质量不高,从其他项目拷贝过来,简单修改的。)
(./pstzine_09_01.txt)
注: 在这里,我们可以看到,基本的部件(字母是分割开了,但可以造成统一字母的被切
割成多个Component。 一种做法是:利用先验知识,做分割; 另外一种做法是,和第二部分的
识别结合起来。 比如按照从左至右,尝试增加component来识别,如果不能识别而且
component的总宽度,总面积还比较小,继续增加。 当然不排除拒识的可能性。 )
3)字符部件组合和识别。
part2的代码展示了切割后的字母组合,和基于svm的字符识别的训练和识别过程。
Detection.cpp中展示了ImageSpam检测过程中的一些字符分割和组合,layout的分析和利用
的简单技术。 而Google的验证码的识别,完全可以不用到,仅做参考。
SVM及使用:
本质上,SVM是一个分类器,原始的SVM是一个两类分类的分类器。可以通过1:1或者1:n
的方式来组合成一个多类分类的分类器。 天生通过核函数的使用支持高维数据的分类。从
几何意义上讲,就是找到最能表示类别特征的那些向量(支持向量SV),然后找到一条线,能最
大化分类的Margin。
libSVM是一个不错的实现。
训练间断和识别阶段的数据整理和归一化是一样的。 这里的简单做法是:
#define SVM_MAX
+0.999
#define SVM_MIN
+0.001
扫描黑白待识别字幕图片的每个像素,如果为0(黑色,是字母上的像素),那么svm中该位
置就SVM_MAX,反之则反。
训练阶段,在svm的input的前面,为该类打上标记,即是那一个字母。
识别阶段,当然这个类别标记是SVM分类出来。
如果是SVM菜鸟,最好找一个在SVM外边做了包装的工具,比如样本选择,交叉验证,核函
数选择这些,让程序自动选择和分析。
代码:通过ReginGrowth来提取单个单个的字符,然后开始识别。
(./pstzine_09_02.txt)
#include &SpamImage.h&
#include &svm-predict.h&
#include &algorithm&
#include &string&
#include &stdio.h&
#ifndef MAX
#define MAX(x,y) (((x) & (y)) ? (x) : (y))
#ifndef ABS
#define ABS(x) ((x&0) ? (-x) : (x))
bool x_more_than(const XBlock & m1, const XBlock & m2)
return m1.x & m2.x;
void Layout::insert(int i,int x,int y)
layout.insert(std::map&int,Point&::value_type(i,Point(x,y)));
void Layout::compute(Config& config,std::map&int,std::string&& lines,std::string& final)
std::map&int,Point&::
std::vector&XBlock& xL
int newFile = 1;
while(layout.size() & 0)
int startY = -1;
int startX = -1;
int startI = -1;
for(it=layout.begin();it!=layout.end();it++)
int i = (*it).
Point xy=(*it).
int x=xy.x;
int y=xy.y;
if(y & startY || startY == -1)
for(it=layout.begin();it!=layout.end();it++)
int i = (*it).
Point xy=(*it).
int x=xy.x;
int y=xy.y;
xList.clear();
for(it=layout.begin();it!=layout.end();it++)
int i = (*it).
Point xy=(*it).
int x=xy.x;
int y=xy.y;
if(y & startY - 12)
XBlock xBlock(i,x,y);
xList.push_back(xBlock);
std::sort(xList.begin(), xList.end(), x_more_than);
for(int i=0;i&xList.size();i++)
XBlock xBlock=xList[i];
layout.erase(xBlock.i);
char output='?';
std::map&int,std::string&::iterator li = lines.find(xBlock.i);
if(li!=lines.end())
const char* line = (*li).second.c_str();
//printf(&%s\n&,line);
output = predict_take((char*)line);
//printf(&output1=%c\n&,output);
char temp[2];
temp[1]=0;
final.append(temp);
//printf(&final=%s\n&,final.c_str());
printf(&Error case 1\n&);
if(config.trainData)
char zFile[MAX_PATH];
sprintf(zFile,&%s\\Z%08d.bmp&,config.midstPath,xBlock.i);
char aFile[MAX_PATH];
sprintf(aFile,&%s\\A%08d(%c).bmp&,config.midstPath,newFile,output);
rename(zFile,aFile);
//printf(&%s --& %s\n\n&,zFile,aFile);
newFile = newFile + 1;
Project::Project(char* fileName)
FILE* fp=fopen(fileName,&r&);
printf(&Can not load chararters project file.&);
Charater* oneC
while(true)
int result = fscanf(fp,&%c&,&flag);
if(result &=0)
std::map&char,Charater&::iterator li = chars.find(flag);
if(li != chars.end())
oneChar=&((*li).second);
oneChar=new Charater();
int size = 0;
fscanf(fp,&(%d)&,&size);
double diff = 0.0;
char buff[256];
for(int i=0;i&i++)
fscanf(fp,&%d:&,&data);
sprintf(buff,&%d&,data);
line.append(buff);
//printf(&flag=%c
line=%s\n&,flag,line.c_str());
oneChar-&lines.push_back(line);
fscanf(fp,&\n&,buff);
chars.insert(std::map&char,Charater&::value_type(flag,*oneChar));
fclose(fp);
RegionGrow::RegionGrow(int maxWidth,int maxHeight)
nMaxWidth = maxW
nMaxHeight = maxH
pucRegion = new unsigned char[maxWidth * maxHeight];
pbMirror = new bool*[maxHeight];
for(int cy=0;cy&maxHcy++)
pbMirror[cy] = new bool[maxWidth];
for(int cx=0;cx&maxWcx++)
pbMirror[cy][cx] =
pnGrowQueueX = new int[maxWidth*maxHeight];
pnGrowQueueY = new int[maxWidth*maxHeight];
RegionGrow::~RegionGrow()
delete []pnGrowQueueX;
delete []pnGrowQueueY;
pnGrowQueueX = NULL ;
pnGrowQueueY = NULL ;
for (int dy=0;dy&nMaxHdy++)
delete[] pbMirror[dy];
delete[] pbM
delete []pucR
pucRegion = NULL
bool RegionGrow::isNeighbor(RGBQUAD sourceCS,RGBQUAD targetCS,int average)
int sourceGray=(sourceCS.rgbRed+sourceCS.rgbGreen+sourceCS.rgbBlue)/3.0;
int targetGray=(targetCS.rgbRed+targetCS.rgbGreen+targetCS.rgbBlue)/3.0;
if( abs(sourceGray - targetGray) & 256/4 )
void RegionGrow::recognizeSave(std::map&int,std::string& &lines,unsigned char* pUnRegion,int nWidth,int nHeight,int nLeftX,int nLeftY,int nRightX,int nRightY,Config& config,int saveName,char* line)
if(line != NULL)
sprintf(line,&%d &,saveName);
int index = 1;
for(int y=nLeftY;y&=nRightY;y++)
for(int x=nLeftX;x&=nRightX;x++)
if(pUnRegion[y*nWidth+x] == 1)
sprintf(line,&%s%d:%lf &,line,index++,SVM_MAX);
sprintf(line,&%s%d:%lf &,line,index++,SVM_MIN);
lines.insert(std::map&int,std::string&::value_type(saveName,line));
if(config.trainData)
int nWidthROI = nRightX-nLeftX+1;
int nHeightROI = nRightY-nLeftY+1;
image.Create(nWidthROI,nHeightROI,24,CXIMAGE_SUPPORT_BMP);
RGBQUAD rgbS
for(int sy=nLeftY;sy&=nRightY;sy++)
for(int sx=nLeftX;sx&=nRightX;sx++)
if(pUnRegion[sy*nWidth+sx] == 1)
rgbSet.rgbRed=255;
rgbSet.rgbGreen=0;
rgbSet.rgbBlue=0;
rgbSet.rgbRed=0;
rgbSet.rgbGreen=0;
rgbSet.rgbBlue=0;
image.SetPixelColor(sx-nLeftX,sy-nLeftY,rgbSet);
char file[MAX_PATH];
if(line == NULL)
static int notText = 1;
sprintf(file,&%s\\N%08d.bmp&,config.midstPath,notText++);
sprintf(file,&%s\\Z%08d.bmp&,config.midstPath,saveName);
image.Save(file,CXIMAGE_SUPPORT_BMP);
void RegionGrow::runRegionGrow(CxImage* cxImage,int nWidth,int nHeight,Config& config,Project &project,std::string& final)
#define ROI_X_LEFT
#define ROI_X_RIGHT
#define ROI_Y_LEFT
#define ROI_Y_RIGHT
//static int nDn = 4;
//static int nDx[]={-1,+0,+1,+0};
//static int nDy[]={+0,+1,+0,-1};
static int nDn = 8;
static int nDx[]={-1,+0,+1,+0, -1,-1,+1,+1};
static int nDy[]={+0,+1,+0,-1, +1,-1,+1,-1};
//static int nDn = 20;
//static int nDx[]={-1,+0,+1,+0, -1,-1,+1,+1, -2,+2,-2,+2,-2,+2,+0,+0,-1,-1,+1,+2};
//static int nDy[]={+0,+1,+0,-1, +1,-1,+1,-1, +0,+0,+1,+1,-1,-1,+2,-2,+2,-2,+1,-2};
if(nWidth &= ROI_X_LEFT+ROI_X_RIGHT || nHeight &= ROI_Y_LEFT+ROI_Y_RIGHT)
printf(&The image must be bigger than %d x %d (width * height)!\n&,(ROI_X_LEFT+ROI_X_RIGHT),(ROI_Y_LEFT+ROI_Y_RIGHT));
int nLocAvg = 0;
for(int cy=nHeight-ROI_Y_RIGHT;cy&ROI_Y_LEFT;cy--)
for(int cx=ROI_X_LEFT;cx&nWidth-ROI_X_RIGHT;cx++)
RGBQUAD rgbCS = cxImage-&GetPixelColor(cx,cy);
RGBQUAD yuvCS = CxImage::RGBtoXYZ(rgbCS);
int gray = (yuvCS.rgbRed + yuvCS.rgbGreen + yuvCS.rgbBlue) / 3.0;
RGBQUAD gryCS;
gryCS.rgbRed =
gryCS.rgbGreen =
gryCS.rgbBlue =
cxImage-&SetPixelColor(cx,cy,gryCS);
nLocAvg = nLocAvg +
nLocAvg /= ( (nHeight-ROI_Y_RIGHT-ROI_Y_LEFT) * (nWidth-ROI_X_RIGHT-ROI_X_LEFT) ) ;
int nPixel = 0;
int nLeftX = 0;
int nLeftY = 0;
int nRightX = 0;
int nRightY = 0;
int debugFile=1;
std::map&int,std::string&
for(int my=nHeight-ROI_Y_RIGHT;my&ROI_Y_LEFT;my--)
for(int mx=ROI_X_LEFT;mx&nWidth-ROI_X_RIGHT;mx++)
if(pbMirror[my][mx])
memset(pucRegion,0,sizeof(unsigned char)* nWidth * nHeight);
nPixel = 1;
int nStart = 0 ;
pnGrowQueueX[nEnd] =
pnGrowQueueY[nEnd] =
int nCurrX ;
int nCurrY ;
while (nStart&=nEnd)
nCurrX = pnGrowQueueX[nStart];
nCurrY = pnGrowQueueY[nStart];
for (k=0;k&nDn;k++)
xx = nCurrX+nDx[k];
yy = nCurrY+nDy[k];
if ((xx & nWidth) && (xx&=0) && (yy&nHeight) && (yy&=0) && (pucRegion[yy*nWidth+xx]==0) )
if(isNeighbor(cxImage-&GetPixelColor(xx,yy),cxImage-&GetPixelColor(nCurrX,nCurrY),nLocAvg))
pnGrowQueueX[nEnd] =
pnGrowQueueY[nEnd] =
pucRegion[yy*nWidth+xx] = 1;
if(xx & nLeftX)
else if(xx & nRightX)
if(yy & nLeftY)
else if(yy & nRightY)
pbMirror[yy][xx] =
pbMirror[nCurrY][nCurrX] =
const static int TOO_SMALL = 11;
const static int TOO_HIGH = 19;
const static int TOO_SHORT = 6;
if(nPixel & TOO_SMALL)
//面积太小
//printf(&xxx: found no-text region case: too small (pixels: %d&%d)\n&,nPixel,TOO_SMALL);
//recognizeSave(lines,pucRegion,nWidth,nHeight,nLeftX,nLeftY,nRightX,nRightY,config,debugFile,NULL);
else if(nRightY-nLeftY & TOO_HIGH)
//printf(&xxx: found no-text region case: too high (height: %d&%d)\n&,nRightY-nLeftY,TOO_HIGH);
//recognizeSave(lines,pucRegion,nWidth,nHeight,nLeftX,nLeftY,nRightX,nRightY,config,debugFile,NULL);
else if(nRightY-nLeftY & TOO_SHORT)
//printf(&xxx: found no-text region case: too short (height: %d&%d)\n&,nRightY-nLeftY,TOO_SHORT);
//recognizeSave(lines,pucRegion,nWidth,nHeight,nLeftX,nLeftY,nRightX,nRightY,config,debugFile,NULL);
else if( (nRightX-nLeftX) &= (nRightY-nLeftY) * 1.6 )
//宽大于高
//printf(&???: found merged block: (%d,%d) --& (%d,%d)\n&,nLeftX,nLeftY,nRightX,nRightY);
int nWidthROI = nRightX-nLeftX+1;
int nHeightROI = nRightY-nLeftY+1;
int aLeftY=nLeftY;
int aRightY=nRightY;
int aLeftX=nLeftX;
int aRightX=nLeftX+nHeightROI-1;
while(true)
int aW=aRightX-aLeftX+1;
int aH=aRightY-aLeftY+1;
char* line = new char[aW*aH*32];
memset(line, 0, aW*aH*32);
recognizeSave(lines,pucRegion,nWidth,nHeight,aLeftX,aLeftY,aRightX,aRightY,config,debugFile,line);
layout.insert(debugFile,nLeftX,nLeftY);
debugFile = debugFile + 1;
char output = predict_take(line);
//X投影 (上轮廓 + 下轮廓)
int* projectX =
new int[aW];
int* projectXScaled =
new int[aW];
for(int px1=aLeftX,index=0;px1&=aRightX;px1++,index++)
projectX[index] = 0;
for(int py1=aLeftY;py1&=aRightY;py1++)
if(pucRegion[py1*nWidth+px1] == 1)
projectX[index] = projectX[index]+1;
projectXScaled[index] = (int)( (double)projectX[index] / (double)aH * 5.0 );
Charater oneC
std::map&char,Charater&::iterator li = project.chars.find(output);
if(li != project.chars.end())
oneChar=(*li).
int matchedSize = 0;
double matchedDiff = aW * 5.0;
for(int c=0;c&oneChar.lines.size();c++)
const char* line=oneChar.lines[c].c_str();
int size=strlen(line);
double diff = 0.0;
for(int i=0;i&size && i&aW;i++)
char temp[2];
temp[0]=line[i];
temp[1]=0;
int data=atoi(temp);
diff = diff + abs(projectXScaled[i]-data);
//printf(&project=%d
current=%d
diff=%lf\n&,projectXScaled[i],data,diff);
//需要设计这里的评价函数 size/aW, size/matchedSize, diff/matchedDiff
if(diff & matchedDiff)
matchedDiff =
matchedSize =
delete projectXS
delete projectX;
//printf(&matchedSize=%d
matchedDiff=%lf\n&,matchedSize,matchedDiff);
if(matchedSize == 0)
matchedSize = nHeightROI;
aLeftX=aLeftX+matchedS
aRightX=aLeftX+nHeightROI-1; //*1.1
if(aLeftX &= nRightX-1)
if(aRightX & nRightX)
aRightX=nRightX;
//printf(&vvv: found ok-text region case: other condition\n&);
int aW=nRightX-nLeftX+1;
int aH=nRightY-nLeftY+1;
char* line = new char[aW*aH*32];
memset(line, 0, aW*aH*32);
RegionGrow::recognizeSave(lines,pucRegion,nWidth,nHeight,nLeftX,nLeftY,nRightX,nRightY,config,debugFile,line);
layout.insert(debugFile,nLeftX,nLeftY);
debugFile =
debugFile + 1;
pute(config,lines,final);
};六、对验证码设计的一些建议
1.在噪音等类型的使用上,尽力让字符和用来混淆的前景和背景不容易区分。尽力让坏人(噪音)长得和好人(字母)一样。
2.特别好的验证码的设计,要尽力发挥人类擅长而AI算法不擅长的。 比如粘连字符的分割和手写体(通过印刷体做特别的变形也可以)。 而不要一味的去加一些看起来比较复杂的噪音或者其他的花哨的东西。即使你做的足够复杂,但如果人也难识别,显然别人认为你是没事找抽型的。
3. 从专业的机器视觉的角度说,验证码的设计,一定要让破解者在识别阶段,反复在低阶视觉和高阶视觉之间多反复几次才能识别出来。 这样可以大大降低破解难度和破解的准确率。七、个人郑重申明
1.这个问题,本身是人工智能,计算机视觉,模式识别领域的一个难题。我是虾米,菜得不能再菜的那种。作为破解者来说,是出于劣势地位。要做的很好,是很难得。总体来说,我走的是比较学院派的线路,能真正的破解难度比较高的验证码,不同于网上很多不太入流的破解方法。我能做的只有利用有限的知识,抛砖引玉而已。 很多OCR的技术,特别是离线手写体中文等文字识别的技术,个人了解有限的很,都不敢在这里乱写。
2.希望不要把这种技术用于非法用途。-EOF-
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:420578次
积分:4975
积分:4975
排名:第3636名
原创:104篇
转载:49篇
评论:250条
(5)(1)(2)(2)(3)(3)(3)(3)(4)(4)(2)(2)(1)(1)(1)(1)(2)(1)(3)(1)(4)(2)(8)(1)(9)(2)(2)(6)(5)(10)(4)(1)(2)(3)(4)(3)(3)(5)(4)(1)(3)(4)(1)(1)(1)(1)(3)(2)(3)(2)(1)(1)(3)(1)(3)啥都不懂也能识别验证码
[问题点数:20分,结帖人cownew]
啥都不懂也能识别验证码
[问题点数:20分,结帖人cownew]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2010年9月 挨踢职涯大版内专家分月排行榜第一
2010年12月 .NET技术大版内专家分月排行榜第二2010年10月 挨踢职涯大版内专家分月排行榜第二2010年8月 挨踢职涯大版内专家分月排行榜第二
2013年 总版技术专家分年内排行榜第一
2014年 总版技术专家分年内排行榜第三
本帖子已过去太久远了,不再提供回复功能。侦查员潜伏进去发现惊人一幕:全是吸管,K粉。
井底有一名3岁男孩死亡,双手双脚还都被铁丝捆绑。
  昨日,有登录12306(铁路客户服务中心)网站的网友会发现,验证码换了新形式。在密码下方的验证码一栏中,点击“点此开始验证”6个蓝色字体后,页面就弹出8张小图,根据提醒,用户需要点击8张图片中所有相似的图片方可登录成功,反之则需要重新验证。羽毛球、猴子、茶杯、蛋糕……看似简单的“图形验证码”却“逼疯”抢票软件。
  到本文末尾挑战-&&史上最强IQ测试题,你敢挑战吗?
  昨日,许多抢票软件出现了短时故障。而看热闹的网友更是笑称:“这是要变成游戏网站的节奏吗?好像在玩找你妹游戏呢。”
  重技术的验证码是一场拉锯战
  当我们谈论验证码时,不免地提到两个人。第一个是计算机科学之父、人工智能之父艾伦.图灵,图灵对整个计算机科学的贡献和意义在此就不用展开,之前所以提到他,是因为他提出的“图灵测试”,这一理论第一次提到将电脑和人区分开。第二个必须提到的是卡内基梅隆大学的路易斯&冯&安,他在2002年第一次将扭曲的文字用于区别人和计算机,就是我们现在普遍见到的英文字符验证码,后来他将验证码公司Re-CAPTCHA卖给了google。
  现在12306同样也用了扭曲的英文字符,但是却抵挡不住黄牛党和刷屏软件机器识别,是因为在这近十年计算机科学技术的发展,OCR(Optical Character Recognition,光学字符识别)等技术发展的已经十分成熟,识别扭曲英文字符并非难事,根据现有实验报告统计及真实调查,普通的验证码的破解率基本在75%以上。说到这里我们看看百度和腾讯是怎么解决的。
  腾讯将验证码图片背景直接贴上真实图片做干扰,而且颜色采取的近似值。
  被称为百度神兽的九宫格汉字验证码,利用中文的博大精深,在防刷上有较大的提升,但是对人的用户体验上就略差了。
  网友提供了破解思路
  所以,12306的图片验证码被迅速破解也不是什么难事,反而将门槛降低。
  下面的具体破解举例引用知乎用户王猫猫在问题“如何评价 12306 的最新版验证码?”下的回答。
  (此图来自知乎用户王猫猫的回答)
  直接将图片处理后丢入google、百度的识图接口,返回的数值让人惊讶(第二张图居然能精准识别到是沙县小吃?)。后来根据王同学提供的代码,我进行了下一步的处理工作,再次利用第三方软件识别中文字符,然后将字符与图片字符进行匹配,之后选择图片。整个测试图片大概200张(只是模拟了登录,没有去刷掉一整车票),通过率在85%左右。所以,仅仅是技术爱好者动用一些公用接口就轻松能识别图片类容,而且一旦识别后,还可以将这张出现过的图片存库,再次出现就更加快速准确的定位了。暂且不谈图像识别和机器学习这样高大上的破解方法了。
  图片验证码之所以不安全,是因为目前的图片识别技术也是相当的成熟。12306这些图片如果是人工标记,无疑是将自己摆到一个愚公移山的悲壮位置;如果是机器识别,也一样是可以被识别内容,即用图片内容的识别作为验证核心将毫无意义。
  这下,难道12306又开始研发新的防刷票技术咯?
  网友声音:倒霉的还是用户
  对于12306此次推出的图片验证码,不少网友还玩上瘾了。“挺好玩的,有点像找你妹,不过,对于色盲或色弱来说,估计比较晕吧。”然而,对于其防范黄牛刷票的初衷,大多数网友仍然表示不看好,“虽然暂时可以阻止抢票软件,但是上有政策下有对策,说不定新的抢票软件直接就跳过了呢,最后倒霉的还是广大群众。”网友“@晔叶业夜耶”感叹道。正如该网友所言,昨晚,当记者再次使用抢票软件进行登录时,系统已经恢复正常。
  ___________
  趣味IQ测试题:答案有些恐怖,慎入!
  电梯最多能乘坐10人,你正好是第10个,走进电梯后却超重了,你只好走出电梯,电梯门关上后,你想到了一件恐怖的事情,立即报警。 请问,怎么回事?
  提示1:当时是夏天,电梯里9人有男有女,没有孕妇,没有胖子,没有宠物。
  提示2:电梯顶部没有尸体。
  提示3:没有人携带拉杆箱之类可抛尸的。
  提示4:电梯没有坏
  提示5:不要去相信网上那些答案!现在网上很多答案都是错的!
  提示6:各位大侠,千万不要急着看答案哦,要先自己琢磨,再进行查看答案哦!
  微信关注【九个头条】公众号,回复“超重”二字即可查看答案。关注后,每天还能收到有趣、有料的精彩资讯,免费订阅哦!
  关注方法:打开微信&点右上角+号&点&添加朋友&搜索:topnews-9(&&长按可复制),点击关注即可!
欢迎举报抄袭、转载、暴力色情及含有欺诈和虚假信息的不良文章。
搜狐公众平台官方账号
生活时尚&搭配博主 /生活时尚自媒体 /时尚类书籍作者
搜狐网教育频道官方账号
全球最大华文占星网站-专业研究星座命理及测算服务机构
九个头条网():有品味的财...
主演:黄晓明/陈乔恩/乔任梁/谢君豪/吕佳容/戚迹
主演:陈晓/陈妍希/张馨予/杨明娜/毛晓彤/孙耀琦
主演:陈键锋/李依晓/张迪/郑亦桐/张明明/何彦霓
主演:尚格?云顿/乔?弗拉尼甘/Bianca Bree
主演:艾斯?库珀/ 查宁?塔图姆/ 乔纳?希尔
baby14岁写真曝光
李冰冰向成龙撒娇争宠
李湘遭闺蜜曝光旧爱
美女模特教老板走秀
曝搬砖男神奇葩择偶观
柳岩被迫成赚钱工具
大屁小P虐心恋
匆匆那年大结局
乔杉遭粉丝骚扰
男闺蜜的尴尬初夜
客服热线:86-10-
客服邮箱:}

我要回帖

更多关于 手机不停的收到验证码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信