滑动验证码破解(一)
第一步:把滑动验证码的Canvas输出图片并保存到本地
利用canvas2image.js脚本
前端用js封装,调用canvas2image.js脚本(github可以下到的)中的方法,缺点是只能保存到电脑c盘中的downloads文件夹中,如果要布置到服务器上,依然是下在自己的电脑上。如果不布置到服务器上,这个方法就很简单了。
<script type="text/javascript"> // 点击转成图片 function savePic() { var oCanvas = document.getElementById("canvas"); Canvas2Image.saveAsImage(oCanvas,420,420,"png","image1"); } </script>
例:获取极验验证码的canvas标签生成的图片,爬虫实现滑动验证码
在浏览器控制台操作
首先加载canvas2image.js到控制台
/** * covert canvas to image * and save the image file */ var Canvas2Image = function () { // check if support sth. var $support = function () { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); return { canvas: !!ctx, imageData: !!ctx.getImageData, dataURL: !!canvas.toDataURL, btoa: !!window.btoa }; }(); var downloadMime = 'image/octet-stream'; function scaleCanvas (canvas, width, height) { var w = canvas.width, h = canvas.height; if (width == undefined) { width = w; } if (height == undefined) { height = h; } var retCanvas = document.createElement('canvas'); var retCtx = retCanvas.getContext('2d'); retCanvas.width = width; retCanvas.height = height; retCtx.drawImage(canvas, 0, 0, w, h, 0, 0, width, height); return retCanvas; } function getDataURL (canvas, type, width, height) { canvas = scaleCanvas(canvas, width, height); return canvas.toDataURL(type); } function saveFile (strData) { document.location.href = strData; } function genImage(strData) { var img = document.createElement('img'); img.src = strData; return img; } function fixType (type) { type = type.toLowerCase().replace(/jpg/i, 'jpeg'); var r = type.match(/png|jpeg|bmp|gif/)[0]; return 'image/' + r; } function encodeData (data) { if (!window.btoa) { throw 'btoa undefined' } var str = ''; if (typeof data == 'string') { str = data; } else { for (var i = 0; i < data.length; i ++) { str += String.fromCharCode(data[i]); } } return btoa(str); } function getImageData (canvas) { var w = canvas.width, h = canvas.height; return canvas.getContext('2d').getImageData(0, 0, w, h); } function makeURI (strData, type) { return 'data:' + type + ';base64,' + strData; } /** * create bitmap image * 按照规则生成图片响应头和响应体 */ var genBitmapImage = function (oData) { // // BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx // BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx // var biWidth = oData.width; var biHeight = oData.height; var biSizeImage = biWidth * biHeight * 3; var bfSize = biSizeImage + 54; // total header size = 54 bytes // // typedef struct tagBITMAPFILEHEADER { // WORD bfType; // DWORD bfSize; // WORD bfReserved1; // WORD bfReserved2; // DWORD bfOffBits; // } BITMAPFILEHEADER; // var BITMAPFILEHEADER = [ // WORD bfType -- The file type signature; must be "BM" 0x42, 0x4D, // DWORD bfSize -- The size, in bytes, of the bitmap file bfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff, // WORD bfReserved1 -- Reserved; must be zero 0, 0, // WORD bfReserved2 -- Reserved; must be zero 0, 0, // DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits. 54, 0, 0, 0 ]; // // typedef struct tagBITMAPINFOHEADER { // DWORD biSize; // LONG biWidth; // LONG biHeight; // WORD biPlanes; // WORD biBitCount; // DWORD biCompression; // DWORD biSizeImage; // LONG biXPelsPerMeter; // LONG biYPelsPerMeter; // DWORD biClrUsed; // DWORD biClrImportant; // } BITMAPINFOHEADER, *PBITMAPINFOHEADER; // var BITMAPINFOHEADER = [ // DWORD biSize -- The number of bytes required by the structure 40, 0, 0, 0, // LONG biWidth -- The width of the bitmap, in pixels biWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff, // LONG biHeight -- The height of the bitmap, in pixels biHeight & 0xff, biHeight >> 8 & 0xff, biHeight >> 16 & 0xff, biHeight >> 24 & 0xff, // WORD biPlanes -- The number of planes for the target device. This value must be set to 1 1, 0, // WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap // has a maximum of 2^24 colors (16777216, Truecolor) 24, 0, // DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed 0, 0, 0, 0, // DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps biSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff, // LONG biXPelsPerMeter, unused 0,0,0,0, // LONG biYPelsPerMeter, unused 0,0,0,0, // DWORD biClrUsed, the number of color indexes of palette, unused 0,0,0,0, // DWORD biClrImportant, unused 0,0,0,0 ]; var iPadding = (4 - ((biWidth * 3) % 4)) % 4; var aImgData = oData.data; var strPixelData = ''; var biWidth4 = biWidth<<2; var y = biHeight; var fromCharCode = String.fromCharCode; do { var iOffsetY = biWidth4*(y-1); var strPixelRow = ''; for (var x = 0; x < biWidth; x++) { var iOffsetX = x<<2; strPixelRow += fromCharCode(aImgData[iOffsetY+iOffsetX+2]) + fromCharCode(aImgData[iOffsetY+iOffsetX+1]) + fromCharCode(aImgData[iOffsetY+iOffsetX]); } for (var c = 0; c < iPadding; c++) { strPixelRow += String.fromCharCode(0); } strPixelData += strPixelRow; } while (--y); var strEncoded = encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData); return strEncoded; }; /** * saveAsImage * @param canvasElement * @param {String} image type * @param {Number} [optional] png width * @param {Number} [optional] png height */ var saveAsImage = function (canvas, width, height, type) { if ($support.canvas && $support.dataURL) { if (typeof canvas == "string") { canvas = document.getElementById(canvas); } if (type == undefined) { type = 'png'; } type = fixType(type); if (/bmp/.test(type)) { var data = getImageData(scaleCanvas(canvas, width, height)); var strData = genBitmapImage(data); saveFile(makeURI(strData, downloadMime)); } else { var strData = getDataURL(canvas, type, width, height); saveFile(strData.replace(type, downloadMime)); } } }; var convertToImage = function (canvas, width, height, type) { if ($support.canvas && $support.dataURL) { if (typeof canvas == "string") { canvas = document.getElementById(canvas); } if (type == undefined) { type = 'png'; } type = fixType(type); if (/bmp/.test(type)) { var data = getImageData(scaleCanvas(canvas, width, height)); var strData = genBitmapImage(data); return genImage(makeURI(strData, 'image/bmp')); } else { var strData = getDataURL(canvas, type, width, height); return genImage(strData); } } }; return { saveAsImage: saveAsImage, saveAsPNG: function (canvas, width, height) { return saveAsImage(canvas, width, height, 'png'); }, saveAsJPEG: function (canvas, width, height) { return saveAsImage(canvas, width, height, 'jpeg'); }, saveAsGIF: function (canvas, width, height) { return saveAsImage(canvas, width, height, 'gif'); }, saveAsBMP: function (canvas, width, height) { return saveAsImage(canvas, width, height, 'bmp'); }, convertToImage: convertToImage, convertToPNG: function (canvas, width, height) { return convertToImage(canvas, width, height, 'png'); }, convertToJPEG: function (canvas, width, height) { return convertToImage(canvas, width, height, 'jpeg'); }, convertToGIF: function (canvas, width, height) { return convertToImage(canvas, width, height, 'gif'); }, convertToBMP: function (canvas, width, height) { return convertToImage(canvas, width, height, 'bmp'); } }; }();
然后获取极验的canvas标签对象(因为极验的canvas中没有id属性只有类属性,所以只能通过类属性获取对象,通过类获取的是集合所以需要取[0])
var oCanvas1 = document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0]; var oCanvas2 = document.getElementsByClassName("geetest_canvas_slice geetest_absolute")[0]; var oCanvas3 = document.getElementsByClassName("geetest_canvas_fullbg geetest_fade geetest_absolute")[0];
通过canvas2image.js中的saveAsImage方法下载
Canvas2Image.saveAsImage(oCanvas1,260,160,"png"); Canvas2Image.saveAsImage(oCanvas2,260,160,"png"); Canvas2Image.saveAsImage(oCanvas3,260,160,"png");
下载好的图片(如果下载好的图片没有后缀名,手动更改为.png)
通过此原理,就可以使用爬虫对获取图片进行像素点比对,确定坐标,模拟用户操作,从而破解滑动验证