如何识别高级的验证码

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_at_hotmail.com>  ]=--------------------=|
|=---------------------------------------------------------------------------=|一、验证码的基本知识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.字母宽度一定,大小一定2)Clubic
    使用的反破解技巧: 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.识别阶段,都是印刷体,简单地很四、网上的一些高级验证码1)ICQ
    2)IMDb
    3)MS MVPS
 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))
#endif#ifndef ABS
#define ABS(x) ((x<0) ? (-x) : (x))
#endifbool 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>::iterator it;std::vector<XBlock> xList;//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).first;Point xy=(*it).second;int x=xy.x;int y=xy.y;if(y > startY || startY == -1){startY = y;startX = x;startI = i;}}//for(it=layout.begin();it!=layout.end();it++){int i = (*it).first;Point xy=(*it).second;int x=xy.x;int y=xy.y;}//xList.clear();for(it=layout.begin();it!=layout.end();it++){			int i = (*it).first;Point xy=(*it).second;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[0]=output;temp[1]=0;final.append(temp);//printf("final=%s\n",final.c_str());}else{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");if(!fp){printf("Can not load chararters project file.");return;}Charater* oneChar;while(true){char flag;int result = fscanf(fp,"%c",&flag);if(result <=0){break;}else{std::map<char,Charater>::iterator li = chars.find(flag);if(li != chars.end()){oneChar=&((*li).second);}else{oneChar=new Charater();}int size = 0;fscanf(fp,"(%d)",&size);int data;double diff = 0.0;std::string line;char buff[256];for(int i=0;i<size;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));}if(fp){fclose(fp);fp=NULL;}	
};RegionGrow::RegionGrow(int maxWidth,int maxHeight)
{nMaxWidth = maxWidth;nMaxHeight = maxHeight;//pucRegion = new unsigned char[maxWidth * maxHeight];//pbMirror = new bool*[maxHeight];for(int cy=0;cy<maxHeight;cy++){pbMirror[cy] = new bool[maxWidth];for(int cx=0;cx<maxWidth;cx++){			pbMirror[cy][cx] = true;}}//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<nMaxHeight;dy++) {delete[] pbMirror[dy];}delete[] pbMirror;//delete []pucRegion;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 ){return true;}else{return false;}
};
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);}else{sprintf(line,"%s%d:%lf ",line,index++,SVM_MIN);}}}lines.insert(std::map<int,std::string>::value_type(saveName,line));}//if(config.trainData){CxImage image;int nWidthROI = nRightX-nLeftX+1;int nHeightROI = nRightY-nLeftY+1;image.Create(nWidthROI,nHeightROI,24,CXIMAGE_SUPPORT_BMP);RGBQUAD rgbSet;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;}else{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++);}else{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  1
#define ROI_X_RIGHT  1
#define ROI_Y_LEFT  1
#define ROI_Y_RIGHT  1//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));exit(1);}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 = gray;gryCS.rgbGreen = gray;gryCS.rgbBlue = gray;cxImage->SetPixelColor(cx,cy,gryCS);			nLocAvg = nLocAvg + gray;}}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> lines;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;nLeftX = mx;nLeftY = my;nRightX = mx;nRightY = my;int nStart = 0 ;int nEnd   = 0 ;pnGrowQueueX[nEnd] = mx;pnGrowQueueY[nEnd] = my;int nCurrX ;int nCurrY ;int xx;int yy;int k ;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)){nEnd++;pnGrowQueueX[nEnd] = xx;pnGrowQueueY[nEnd] = yy;pucRegion[yy*nWidth+xx] = 1;nPixel++;if(xx < nLeftX) {nLeftX=xx;}else if(xx > nRightX){nRightX=xx;}if(yy < nLeftY) {nLeftY=yy;}else if(yy > nRightY){nRightY=yy;}pbMirror[yy][xx] = false;pbMirror[nCurrY][nCurrX] = false;   //FAST}}}nStart++;}		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);continue;}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);continue;}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);continue;}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;   // *1.1while(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);delete 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;}}//5-scaleprojectXScaled[index] = (int)( (double)projectX[index] / (double)aH * 5.0 );}//轮廓Charater oneChar;std::map<char,Charater>::iterator li = project.chars.find(output);if(li != project.chars.end()){oneChar=(*li).second;			}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/matchedDiffif(diff < matchedDiff){matchedDiff = diff;matchedSize = size;}}delete projectXScaled;delete projectX;//printf("matchedSize=%d  matchedDiff=%lf\n",matchedSize,matchedDiff);//if(matchedSize == 0){matchedSize = nHeightROI;}aLeftX=aLeftX+matchedSize;aRightX=aLeftX+nHeightROI-1;	//*1.1if(aLeftX >= nRightX-1){break;}if(aRightX > nRightX){aRightX=nRightX;}}				}else{//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;delete line;}}}}layout.compute(config,lines,final);
};

六、对验证码设计的一些建议 1.在噪音等类型的使用上,尽力让字符和用来混淆的前景和背景不容易区分。尽力让坏人(噪音)长得和好人(字母)一样。 2.特别好的验证码的设计,要尽力发挥人类擅长而AI算法不擅长的。 比如粘连字符的分割和手写体(通过印刷体做特别的变形也可以)。 而不要一味的去加一些看起来比较复杂的噪音或者其他的花哨的东西。即使你做的足够复杂,但如果人也难识别,显然别人认为你是没事找抽型的。 3. 从专业的机器视觉的角度说,验证码的设计,一定要让破解者在识别阶段,反复在低阶视觉和高阶视觉之间多反复几次才能识别出来。 这样可以大大降低破解难度和破解的准确率。七、个人郑重申明 1.这个问题,本身是人工智能,计算机视觉,模式识别领域的一个难题。我是虾米,菜得不能再菜的那种。作为破解者来说,是出于劣势地位。要做的很好,是很难得。总体来说,我走的是比较学院派的线路,能真正的破解难度比较高的验证码,不同于网上很多不太入流的破解方法。我能做的只有利用有限的知识,抛砖引玉而已。 很多OCR的技术,特别是离线手写体中文等文字识别的技术,个人了解有限的很,都不敢在这里乱写。 2.希望不要把这种技术用于非法用途。-EOF-



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/53069.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hcaptcha 我是人类验证码怎么跳过怎么验证自动识别

相信这个验证码很多人都见过&#xff0c;这个叫hcaptcha验证码 在网页上偶尔出现&#xff0c;提示需要你证明“我是人类” 这种验证码与谷歌的reCaptcha有异曲同工之处&#xff0c;但是其实hcaptcha与recaptcha是完全不同的产品&#xff0c;不是同一个公司出品的。 这种hcapt…

手把手教你识别FunCaptcha验证码

今天&#xff0c;我们将专注于FunCaptcha&#xff0c;这是一种独特而具有挑战性的CAPTCHA类型&#xff0c;在整个网络上越来越流行。我们将深入探讨FunCaptcha是什么&#xff0c;不同类型的FunCaptcha挑战&#xff0c;如何使用CapSolver解决它们等等。 什么是FunCaptcha&#…

基于openai chatgpt和embeddings制作私有知识库聊天机器人

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、原理、流程二、制作预料库三、制作问答功能总结 如果有问题可以联系我**&#xff1a;https://gitee.com/xiaoyuren/gpt3 前言 在当今信息爆炸的时代&#…

基于 Quivr 搭建个人知识库

目录 Quivr介绍 Quivr特性 Quivr演示 Demo with GPT3.5: Demo of the new version&#xff1a; Quivr实战 Quiv 使用的主要技术 Quiv 实践依赖 创建Supabase项目 部署Quiv项目 第一步&#xff1a;现在源码 第二步&#xff1a;设置环境变量 第三步&#xff1a;执行sql 第…

标书打印分册小技巧

标书打印出来后&#xff0c;一般都有很多本&#xff0c;去打印店胶装标书时&#xff0c;需要把每一本标书分出来&#xff0c;黑帽大师用便签纸就能方便的分出标书。 把便签纸贴在每本标书的最后一页上&#xff0c;这样就能方便的分出每一本了。

学校计算机维护投标书,信息化系统硬件及应用系统安全运维服务投标书范本

这是一份信息化系统硬件及应用系统安全运维服务投标书范本&#xff0c;含运维服务方案&#xff0c;word格式&#xff0c;可编辑&#xff0c;有需要的朋友可以参考学习。 信息化系统硬件及应用系统安全运维服务 本次服务范围为XX局信息化系统硬件及应用系统&#xff0c;各类软硬…

招投标小程序开发功能及源码

一般获取招投标信息的渠道主要有三种&#xff0c;一&#xff0c;来源于官方、正规的政府网站、公共资源交易中心等&#xff1b;二&#xff0c;能提供针对性的招投标信息平台&#xff1b;三是通过个人的人脉资源来获取项目信息。今天我们重点讲下招投标平台怎么运营的&#xff0…

python制作标书_爬取比比网中标标书,并保存为PDF格式文件

前言 本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。 以下文章来源于CSDN&#xff0c;作者嗨学编程 python开发环境 python 3.6 pycharm import requests import parsel import pdfkit import time 相关模块pip安装即可 …

python制作标书_Python爬取比比网中标标书并保存成PDF格式

前言 本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。 python开发环境 python 3.6 pycharm requests parsel pdfkit time 相关模块pip安装即可 目标网页分析 1、先从列表页中获取详情页的URL地址 是静态网站,可以直接请求…

第一次写标书

由于工作需要开始写起标书。前后大概花了五天时间。 经过自我学习和老师指导&#xff0c;知道了一件事情&#xff0c;不管做什么&#xff0c;其实都是能够有所学习的。 而学习&#xff0c;为了有所收获&#xff0c;需要用心再去体会每一个过程&#xff0c;并记录下来&#xf…

小程序投标书_快来学习招投标小技巧!中标率提高50%(建议收藏)

99%的投标人使用【建企同盟APP】都中标了&#xff01; 建企同盟APP 招标信息不遮挡 订阅推送零费用 从保证中标的因素来看&#xff0c;三个因素最为重要&#xff0c;首先是关系&#xff0c;其次是能力&#xff0c;最后才是价格。关系指与用户的关系&#xff0c;既有最终用户又包…

小程序投标书_程序员接私活常用哪些平台?

给大家推荐国内外几个接外包比较靠谱的平台&#xff0c;相对来说规模和专业性都还不错。 想要接外包或者积累行业人脉的小伙伴都可以收藏一波&#xff1a; 国外篇 如果打算接国外的软件外包&#xff0c;首先以下几点能力需要提前掌握&#xff1a; 基本的英语沟通能力(能够基本沟…

重磅:AI 的 “iPhone 时刻” 已经到来

大家好&#xff0c;我是校长。 上周英伟达 CEO 黄仁勋在 GTC 大会主题演讲火爆了全网。 一起来看看黄仁勋说了什么。 英伟达 CEO 黄仁勋在 GTC 大会主题演讲上开场时这么说&#xff1a; “近四十年来&#xff0c;摩尔定律一直是引领计算机行业动态发展的重要规律&#xff0c;而…

AI内容生成检查器

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 AI进行内容生成已经是是当下的人们话题&#xff0c;那么怎么判断文本的内容是不是AI生成的呢&#xff1f;AI 生成的句子将被突出显示&#xff0c;目前工具可以检测…

ChatGPT生成文本检测器-task5

#### 任务五&#xff1a;使用TFIDF特征和XGBoost完成训练和预测 说明&#xff1a;在这个任务中&#xff0c;你需要使用TFIDF特征和XGBoost算法完成训练和预测&#xff0c;进一步提升文本分类的性能。实践步骤&#xff1a; 准备TFIDF特征矩阵和相应的标签。划分训练集和测试集。…

ChatGPT生成文本检测器-task2

#### 任务二&#xff1a;对数据集字符进行可视化&#xff0c;统计标签和字符分布 说明&#xff1a;在这个任务中&#xff0c;你需要使用Pandas库对数据集的字符进行可视化&#xff0c;并统计数据集中的标签和字符的分布情况&#xff0c;以便更好地理解数据集。实践步骤&#x…

ChatGPT生成文本检测器-task8

#### 任务八&#xff1a;使用Word2Vec词向量&#xff0c;搭建BILSTM模型进行训练和预测 说明&#xff1a;在这个任务中&#xff0c;你将使用Word2Vec词向量&#xff0c;搭建BILSTM模型进行文本分类的训练和预测&#xff0c;通过双向长短期记忆网络来进行文本分类。实践步骤&am…

ChatGPT生成文本检测器-task1

### 背景介绍 近年来人工智能在自然语言处理领域取得了巨大的进展。其中一项引人注目的技术是生成模型&#xff0c;如OpenAI的GPT-3.5。这类模型通过学习大量的文本数据&#xff0c;具备了生成高质量文本的能力&#xff0c;从而引发了一系列关于文本生成真实性的讨论。 正因为…

Mac GoLand打不开(闪退)也不报错

Mac用过GoLand&#xff0c;电脑应用初始化后就打不开了&#xff0c;下其他版本也不行 原因就是之前的配置文件还在需要清理&#xff1a; /Users/你的文件/Library/Preferences/ 配置文件在这个文件下

MAC 有些网址可以 ping 通,但是浏览器打不开

MAC 访问外网网址打不开&#xff0c;因为平时开着 VPN&#xff0c;故开始逐步排查问题&#xff1a; 重启电脑ping ip切换网络改 DNS 方法都试过了在公司还是不行&#xff0c;但是在家里能连上 最后解决办法是点击网络&#xff0c;新建位置就一切正常了。