网站的登陆页、注册页等等等到处都是验证码,然而你的验证码真的安全么?也许只需要一段简单的小程序,你的验证码就会如同虚设。本文只是简单实现,不会太过深入。
有攻就有防
写这篇文章完全是因为同事的公众号发了一篇文章叫"实践-写个验证码",你简单写了一下,我就简单破解一下试试,生活处处有乐趣啊~
生成验证码
Copy代码,执行,生成如下验证码:
Y
识别
识别的过程就是重复上面的:二值化->切割->提取特征码,再加上和之前提取的特征码比对相似度,就OK了。
PHP代码实现
/** * 简单验证码识别 * @author zhjx922 */ class vCode{ //字符特征码 private $_wordKeys = array ( &#39;A&#39; => &#39;000**00000****000**00**0**0000****0000****0000************0000****0000****0000**&#39;, &#39;B&#39; => &#39;******00**000**0**0000****000**0******00**000**0**0000****0000****000**0******00&#39;, &#39;C&#39; => &#39;00*****00**000****00000***000000**000000**000000**000000**00000*0**000**00*****0&#39;, &#39;D&#39; => &#39;******00**000**0**0000****0000****0000****0000****0000****0000****000**0******00&#39;, &#39;E&#39; => &#39;*********00000**00000**00000******0**00000**00000**00000**00000*******&#39;, &#39;F&#39; => &#39;**********000000**000000**000000******00**000000**000000**000000**000000**000000&#39;, &#39;G&#39; => &#39;00*****00**000****000000**000000**000000**000*****0000****0000**0**000**00*****0&#39;, &#39;H&#39; => &#39;**0000****0000****0000****0000************0000****0000****0000****0000****0000**&#39;, &#39;I&#39; => &#39;******00**0000**0000**0000**0000**0000**0000**0000**00******&#39;, &#39;J&#39; => &#39;00****0000**0000**0000**0000**0000**0000***000****0**00***00&#39;, &#39;K&#39; => &#39;**0000****000**0**00**00**0**000****0000****0000**0**000**00**00**000**0**0000**&#39;, &#39;L&#39; => &#39;**00000**00000**00000**00000**00000**00000**00000**00000**00000*******&#39;, &#39;M&#39; => &#39;**0000*****00*************0**0****0**0****0**0****0000****0000****0000****0000**&#39;, &#39;N&#39; => &#39;**0000*****000******00******00****0**0****0**0****00******000*****000*****0000**&#39;, &#39;P&#39; => &#39;*******0**0000****0000****0000*********0**000000**000000**000000**000000**000000&#39;, &#39;Q&#39; => &#39;00****000**00**0**0000****0000****0000****0000****0**0****00****0**00**000****0*&#39;, &#39;R&#39; => &#39;*******0**0000****0000****0000*********0*****000**00**00**000**0**0000****0000**&#39;, &#39;S&#39; => &#39;0******0**0000****000000**0000000******0000000**000000**000000****0000**0******0&#39;, &#39;T&#39; => &#39;********000**000000**000000**000000**000000**000000**000000**000000**000000**000&#39;, &#39;U&#39; => &#39;**0000****0000****0000****0000****0000****0000****0000****0000**0**00**000****00&#39;, &#39;V&#39; => &#39;**0000****0000****0000**0**00**00**00**00**00**000****0000****00000**000000**000&#39;, &#39;W&#39; => &#39;**0000****0000****0000****0000****0**0****0**0****0**0*************00*****0000**&#39;, &#39;X&#39; => &#39;**0000****0000**0**00**000****00000**000000**00000****000**00**0**0000****0000**&#39;, &#39;Y&#39; => &#39;**0000****0000**0**00**000****00000**000000**000000**000000**000000**000000**000&#39;, &#39;Z&#39; => &#39;*******00000**00000**0000**0000**0000**0000**0000**00000**00000*******&#39;, &#39;a&#39; => &#39;00*****00**000**000000**0*********0000****000***0****0**&#39;, &#39;b&#39; => &#39;**000000**000000**000000**0***00***00**0**0000****0000****0000*****00**0**0***00&#39;, &#39;c&#39; => &#39;00*****00**000****000000**000000**0000000**000**00*****0&#39;, &#39;d&#39; => &#39;000000**000000**000000**00***0**0**00*****0000****0000****0000**0**00***00***0**&#39;, &#39;e&#39; => &#39;00****000**00**0**0000************0000000**000**00*****0&#39;, &#39;f&#39; => &#39;000****000**00**00**00**00**000000**0000******0000**000000**000000**000000**0000&#39;, &#39;g&#39; => &#39;0*****0***000*****000**0**000**00*****00**0000000******0**0000**0******0&#39;, &#39;h&#39; => &#39;**000000**000000**000000**0***00***00**0**0000****0000****0000****0000****0000**&#39;, &#39;i&#39; => &#39;00**0000**000000000***0000**0000**0000**0000**0000**00******&#39;, &#39;k&#39; => &#39;**00000**00000**00000**00**0**0**00****000****000**0**00**00**0**000**&#39;, &#39;l&#39; => &#39;***00**00**00**00**00**00**00**00**0****&#39;, &#39;m&#39; => &#39;*0**0**0**0**0****0**0****0**0****0**0****0**0****0**0**&#39;, &#39;n&#39; => &#39;**0***00***00**0**0000****0000****0000****0000****0000**&#39;, &#39;o&#39; => &#39;00****000**00**0**0000****0000****0000**0**00**000****00&#39;, &#39;p&#39; => &#39;**0***00***00**0**0000****0000****0000*****00**0**0***00**000000**000000&#39;, &#39;q&#39; => &#39;00***0**0**00*****0000****0000****0000**0**00***00***0**000000**000000**&#39;, &#39;r&#39; => &#39;**0****00***00**0**000000**000000**000000**000000**00000&#39;, &#39;s&#39; => &#39;0******0**0000****0000000******0000000****0000**0******0&#39;, &#39;t&#39; => &#39;00**000000**0000******0000**000000**000000**000000**000000**00**000****0&#39;, &#39;u&#39; => &#39;**0000****0000****0000****0000****0000**0**00***00***0**&#39;, &#39;v&#39; => &#39;**0000****0000**0**00**00**00**000****0000****00000**000&#39;, &#39;w&#39; => &#39;**0000****0000****0**0****0**0****0**0**********0**00**0&#39;, &#39;x&#39; => &#39;**0000**0**00**000****00000**00000****000**00**0**0000**&#39;, &#39;y&#39; => &#39;**0000****0000****0000****0000****0000**0**00***00***0***00000**0******0&#39;, &#39;z&#39; => &#39;******0000**000**000**000**000**0000******&#39;, &#39;0&#39; => &#39;000**00000****000**00**0**0000****0000****0000****0000**0**00**000****00000**000&#39;, &#39;1&#39; => &#39;00**000***00****0000**0000**0000**0000**0000**0000**00******&#39;, &#39;2&#39; => &#39;00****000**00**0**0000**000000**00000**00000**00000**00000**00000**00000********&#39;, &#39;3&#39; => &#39;0*****00**000**0000000**00000**0000***0000000**0000000**000000****000**00*****00&#39;, &#39;4&#39; => &#39;00000**00000***0000****000**0**00**00**0**000**0********00000**000000**000000**0&#39;, &#39;5&#39; => &#39;*******0**000000**000000**0***00***00**0000000**000000****0000**0**00**000****00&#39;, &#39;6&#39; => &#39;00****000**00**0**0000*0**000000**0***00***00**0**0000****0000**0**00**000****00&#39;, &#39;7&#39; => &#39;********000000**000000**00000**00000**00000**00000**00000**00000**000000**000000&#39;, &#39;8&#39; => &#39;00****000**00**0**0000**0**00**000****000**00**0**0000****0000**0**00**000****00&#39;, &#39;9&#39; => &#39;00****000**00**0**0000****0000**0**00***00***0**000000**0*0000**0**00**000****00&#39;, ); /** * 生成验证码 * @author 武老师 */ public function make($verCode = &#39;&#39;) { if(empty($verCode)) { $baseChars = &#39;ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789&#39;; $verCode = &#39;&#39;; $codeCharLenth = 4; for ($i = 1; $i <= $codeCharLenth; $i++) { // 通过字符串下标形式随机获取 $verCode .= $baseChars{mt_rand(0, strlen($baseChars) - 1)}; } } // 以下代码是将生成的验证码生成图片 $font_size = 20; $width = 60; $height = 30; $img = imagecreate($width, $height); // 新建一个基于调色板的图像 $bgR = mt_rand(50, 200); //r(ed) $bgG = mt_rand(50, 200); //g(reen) $bgB = mt_rand(50, 200); //b(lue) $background = imagecolorallocate($img, $bgR, $bgG, $bgB); // 背景色 $black = imagecolorallocate($img, 0, 0, 0); imagestring($img, 5, 9, 8, $verCode, $black); // 水平地画一行字符串 ob_start(); imagepng($img); $image = ob_get_contents(); ob_end_clean(); return array( &#39;image&#39; => $image, &#39;code&#39; => $verCode ); } /** * 获取原始图像数组 * @param string $imageString * @return array */ public function getImage($imageString) { $im = imagecreatefromstring($imageString); list($width, $height) = getimagesizefromstring($imageString); $image = array(); for($x = 0;$x <$width;$x++) { for($y =0;$y <$height;$y++) { $rgb = imagecolorat($im, $x, $y); $rgb = imagecolorsforindex($im, $rgb); if($rgb[&#39;red&#39;] == 0 && $rgb[&#39;green&#39;] == 0 && $rgb[&#39;blue&#39;] == 0) { $image[$y][$x] = &#39;*&#39;; } else { $image[$y][$x] = 0; } } } return $image; } /** * 移除无用数据 * @param array $image * @return array */ public function remove($image) { //计算x和y轴的 $xCount = count($image[0]); //60 $yCount = count($image); //30 $xFilter = array(); for($x = 0;$x <$xCount;$x++) { $filter = true; for($y = 0;$y <$yCount;$y++) { $filter = $filter && ($image[$y][$x] == &#39;0&#39;); } if($filter) { $xFilter[] = $x; } } //有字符的列 $xImage = array_values(array_diff(range(0, 59), $xFilter)); //存放关键字 $wordImage = array(); $preX = $xImage[0] - 1; $wordCount = 0; foreach($xImage as $xKey => $x) { if($x != ($preX + 1)) { $wordCount++; } $preX = $x; for($y = 0;$y <$yCount;$y++) { $wordImage[$wordCount][$y][$x] = $image[$y][$x]; } } foreach($wordImage as $key=>$image) { $wordImage[$key] = $this->removeByLine($image); } return $wordImage; } /** * 按行移除无用数据 * @param array $image * @return array */ public function removeByLine($image) { $isFilter = false; foreach($image as $y => $yImage) { if($isFilter == true || array_filter($yImage)) { $isFilter = true; } else { unset($image[$y]); } } krsort($image); $isFilter = false; foreach($image as $y => $yImage) { if($isFilter == true || array_filter($yImage)) { $isFilter = true; } else { unset($image[$y]); } } ksort($image); return $image; } /** * 获取关键字字符串 * @param array $wordImage * @return string */ public function getWordString($wordImage) { $wordString = &#39;&#39;; foreach($wordImage as $image) { foreach($image as $string) { $wordString .= $string; } } return $wordString; } /** * 匹配关键字 * @param array $image * @return array */ public function match($image) { $match = array( &#39;min&#39; => &#39;&#39;, &#39;key&#39; => &#39;&#39; ); foreach($this->_wordKeys as $k => $v) { $percent = 0.0; similar_text($this->getWordString($image), $v, $percent); if($match[&#39;min&#39;] == &#39;&#39;) { $match[&#39;min&#39;] = $percent; $match[&#39;key&#39;] = $k; } else { if($percent > $match[&#39;min&#39;]) { $match[&#39;min&#39;] = $percent; $match[&#39;key&#39;] = $k; } } } return $match; } /** * 终端显示验证码 * @param $image */ public function show($image) { foreach($image as $xImage) { foreach($xImage as $yImage) { echo $yImage; } echo PHP_EOL; } echo PHP_EOL; } } $vCode = new vCode(); $codeImage = $vCode->make(); $imageString = $codeImage[&#39;image&#39;]; $image = $vCode->getImage($imageString); //原图 $vCode->show($image); //去除干扰边框、拆字 $newImage = $vCode->remove($image); $word = array(); $code = &#39;&#39;; foreach($newImage as $image) { $vCode->show($image); $code .= $vCode->match($image)[&#39;key&#39;]; } echo "生成的验证码为:{$codeImage[&#39;code&#39;]}" . PHP_EOL; echo "识别的验证码为:{$code}" . PHP_EOL; /* //用来批量生成验证码的特征码。识别他人网站验证码,需要自己采集多张,人肉标记特征码 $vCode = new vCode(); $string = &#39;ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789&#39;; $max = ceil(strlen($string) / 4); $wordKeys = array(); for($i=0;$i<$max;$i++) { $code = substr($string, $i * 4, 4); $imageString = $vCode->make($code)[&#39;image&#39;]; $image = $vCode->getImage($imageString); $newImage = $vCode->remove($image); foreach($newImage as $key => $image) { $word = $vCode->getWordString($image); isset($code[$key]) && $wordKeys[$code[$key]] = $word; } } echo var_export($wordKeys); */
运行结果: