首页 > 其他分享 >记录--前端验证码破解

记录--前端验证码破解

时间:2023-12-23 18:46:31浏览次数:26  
标签:字符 code const -- 验证码 length data 破解

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

最近受够了公司内部站点每次登陆都需要填写用户名和密码,还有输入验证码。

要是能够直接跳过登陆页面就好啦。

说干就干,决定使用油猴插件实现自动登陆功能。

其中最难解决的就是验证码破解,花了一天的时间完美解决,现在整理出来。

1.分析验证码

分析验证码,是破解验证码一切工作的开始。

  • 验证码有哪些特征?
  • 是否容易破解?
  • 采用什么策略破解?

特征总结

这里仅是总结一下公司网站验证码(上面验证码图片)的特征。

  1. 仅有字母(大小写)和数字,并且剔除了难以区分的字符:1iIlL0oO
  2. 同一字符每次出现的大小、粗细、倾斜都一致(容易做成标准的字符样本库)
  3. 首字符开始的位置一致(方便裁剪左侧背景)
  4. 有干扰线和背景色,颜色相较于字符都比较亮(方便通过阈值来区分像素是否属于字符)

制定破解策略

根据上一步分析的验证码特征来制定破解该验证码的策略。

  1. 制作标准样本库
  2. 使用标准样本对验证码图片进行卷积比对(下面会有介绍)

2.制作样本库

  1. 请求获取验证码
  2. 提取图片像素
  3. 二值化(将像素处理成0和1)
  4. 用canvas绘制二值化后的验证码(白底黑字,也可等比放大以便查看和截图)
  5. 从绘制的二值化后的验证码上截取合适的字符
  6. 处理字符截图(去白边,去噪点)
  7. 还原图片的放大比例(若之前有放大处理)
  8. 保存为模板字符串

获取验证码

// 返回图片base64数据
function getVerifyCode() {
  return fetch(VERIFY_CODE_API)
    .then(rsp => rsp.json())
    .then(data => `data:image/png;base64,${data.data}`)
}

将base64数据转成像素

使用canvas。

// 支持base64数据或本地图片路径
async function getImageData(imageSrc) {
  const image = new Image();
  image.src = imageSrc;
  // 等待图片加载完成
  await new Promise(resolve => {
    image.onload = resolve;
  });
  // 创建canvas
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  context.drawImage(image, 0, 0);
  return context.getImageData(0, 0, image.width, image.height);
}

返回ImageData类型的对象。

data是一个Uint8ClampedArray,一个类型数组,每4位表示一个像素的rgba值(0-255)。

二值化处理

首先需设置好一个阈值,亮度高于阈值认定为背景,低于阈值暂认定为字符(有可能是噪点或干扰线)。

阈值需要根据实际效果进行调优(不断修改)。

推荐初始阈值可以设置为[130, 130, 130](rgb通道值,alpha固定是255就不设置了),约是0-255的中间数。

const threshold = [130, 130, 130];

// 返回每一项都是0或1的二维数组
function binarization(imageData) {
  const pixel2binary = pixel => 
    pixel.every((chValue, index) => chValue > threshold[index]) ? '0' : '1';
  
  // data中每4位表示一个像素
  const { data, width, height } = imageData;
  const binaryData = [];
  let x, y, row, rowLoc, pixel, pixelLoc;
  for (y = 0; y < height; y++) {
    row = [];
    // 当前行起始位置
    rowLoc = y * width * 4;
    for (x = 0; x < width; x++) {
      pixelLoc = rowLoc + x * 4;
      // 取该点的rgb色值
      pixel = imageData.slice(pixelLoc, 3);
      row.push(pixel2binary(pixel));
    }
    binaryData.push(row);
  }
  return binaryData;
}

绘制二值化的数据(黑字白底)

function drawBinaryData(context, data, scale = 1) {
  const binary2pixel = binary => 
    binary === '0' ? [255, 255, 255, 255] : [0, 0, 0, 255];
  const repeatAction = (action) => {
    for (let i = 0; i < scale; i++) action();
  };
  const h = data.length;
  const w = data[0].length;
  let x, y, row;
  cosnt pixelData = [];
  for (y = 0; y < h; y++) repeatAction(() => {
    for (x = 0; x < w; x++) repeatAction(() => {
      pixelData.push(...binary2pixel(data[y][x]));
    });
  });
  // 创建ImageData实例
  const imageData = new ImageData(
    Uint8ClampedArray.from(pixelData),
    w * scale,
    h * scale
  );
  return context.putImageData(imageData, 0, 0);
}

输出宽高都放大4倍的验证码:

截图保存样本

挑选合适的验证码将字符截图出来。

 上面验证码中的字符5就不适合作为样本,因为截取后右下方会有其它字符的点。当然也可以使用工具或写代码去除.

 将所有字符样本都保存下来。这需要不断请求获取验证码图片。

去掉字符截图白边

function cutWhiteEdge(data) {
  let edge;
  const isWhiteEdge = () => 
    edge.every(binary => binary === '0');
  // 连续切边
  const cutEdgeContinuous = (resetEdge, cutEdge) => {
    const _resetEdge = () => (edge = resetEdge());
    for (_resetEdge(); isWhiteEdge(); cutEdge(), _resetEdge());
  };
  // 切边顺序:上下左右
  // 上
  cutEdgeContinuous(
    () => data[0],
    () => data.shift()
  );
  // 下
  cutEdgeContinuous(
    () => data[data.length - 1],
    () => data.pop()
  );
  // 左
  cutEdgeContinuous(
    () => data.map(r => r[0]),
    () => data.forEach(r => r.shift())
  );
  // 右
  cutEdgeContinuous(
    () => data.map(r => r[r.length - 1]),
    () => data.forEach(r => r.pop())
  );
}

还原二值化数据的缩放

function restoreDataScale(data, scale) {
  const scaleData = [];
  let x, y, row;
  const h = data.length;
  const w = data[0].length;
  for (y = 0; y < h; y += scale) {
    row = [];
    for (x = 0; x < w; x += scale) {
      row.push(data[y][x]);
    }
    scaleData.push(row);
  }
  return scaleData;
}

保存模板字符串

就是将处理后的二值化数组,转为字符串形式,方便保存(数据库等)。

function binaryData2Template(data) {
  return data.map(r => r.join('')).join(' ');
}

右侧控制台打印出的就是模板字符串,不过是使用换行符进行每行的分隔。

读取字符截图

上面刚刚介绍了字符截图和处理截图,当中少了读取字符截图这一步。

可以写代码直接读取字符截图的文件夹,一次性处理所有字符截图。

我在做这一步时,是使用input[type=file]手动每次选择一张字符截图进行处理的(时间紧张),这里贴一下代码。

fileInput.addEventListener('change', e => {
  // 获取文件
  if (fileInput.files.length === 0) return;
  const file = fileInput.files[0];
  const reader = new FileReader();
  reader.addEventListener('load', async e => {
    // e.target.result是图片的base64资源
    const imageData = getImageData(e.target.result);
    const binaryData = binarization(imageData);
    cutWhiteEdge(binaryData);
    // 还原之前对图片的放大
    const restoreData = restoreDataScale(binaryData, 4);
    const template = binaryData2Template(restoreData);
    // 使用clipboard将模板写入剪切板
    navigator.clipboard.writeText(template);
    // 也可以发接口写入数据库...
  });
  reader.readAsDataURL(file);
});

FileReader的load事件

二值化阈值调整

经过多次获取验证码、二值化、然后输出查看发现,有些验证码的图片二值化后有的字符被去除了或去除了部分,原因是这些字符的颜色也比较亮。

 比如这一张验证码,打印出来是这样的(字符S亮度较高):

 此时需要调整阈值(调高一点):

const threshold = [140, 140, 140];

3.卷积比对

上面介绍了如何获取字符模板。在进行卷积比对前,需要处理和保存好所有字符的模板(这是一个辛苦活

标签:字符,code,const,--,验证码,length,data,破解
From: https://www.cnblogs.com/smileZAZ/p/17923451.html

相关文章

  • cloudflare,vercel and netlify的作用和区别
    Cloudflare,Vercel和Netlify都是为开发者提供的云计算服务,但它们的功能和特性有所不同。Cloudflare的主要使命是帮助构建更好的互联网。它是世界上最大的网络之一,为企业、非营利组织、博客作者和任何有互联网存在的人提供更快、更安全的网站和应用。Cloudflare的网络上有数百万......
  • 网络与数据安全领域的框架模型
    1.PDR模型PDR模型是由美国国际互联网安全系统公司(ISS)提出,它是最早体现主动防御思想的一种网络安全模型。保护(Protection)就是采用一切可能的措施来保护网络、系统以及信息的安全。保护通常采用的技术及方法主要包括加密、认证、访问控制、防火墙以及防病毒等。检测(Detection)可以......
  • TDSQL集群扩容DB节点
    具体步骤见百度云盘:https://pan.baidu.com/s/1zMrjhckI2I4O1G1RzC09uQ提取码:h5mm。本文档适用于TDSQL(MYSQL版)和TDSQL(PG版)。内容如下:一、以下步骤在新增机器上面执行:1、检查audit服务(Mysql版本特有):systemctlstatusauditd,#如果存活则关闭:systemctlstopauditd&&systemc......
  • GalaxyOJ 8699 午夜后的棒棒糖
    挺高妙的题,思维套结论。题意:给定\(n\)个数,求在其中选三个不交的子集,使得其异或和相等的方案数。三个不交的集合异或和相等\(\Leftrightarrow\)两两异或和为\(0\)。观察两个异或和为\(0\)的集合\(S,T(\not=\varnothing)\)和答案有什么关系。有交但不包含设\(R=S\c......
  • Linux操作系统之面试必刷50题
    1、static作用static是一个关键字,在不同的上下文中具有不同的作用。以下是static关键字的几种常见用法和作用:静态变量(StaticVariables):在函数内部使用static关键字声明的变量称为静态变量。静态变量在程序的整个生命周期内都存在,不会随着函数的调用而被销毁。静态变量的作用域......
  • Maven 知识点
    目录Maven[1]1.基础知识1.1.Maven相关目录、文件1.1.1.setting.xml文件设置1.2.POM示例1.3.基础设置2.坐标和依赖2.1.坐标2.2.依赖2.2.1.依赖范围2.2.2.传递性依赖2.2.3.优化依赖3.生命周期和插件3.1.生命周期3.2.插件3.2.1.绑定生命周期阶段3.2.2.插件设置4.仓库4.1.本地仓库4......
  • openGauss学习笔记-170 openGauss 数据库运维-备份与恢复-导入数据-更新表中数据-使用
    openGauss学习笔记-170openGauss数据库运维-备份与恢复-导入数据-更新表中数据-使用合并方式更新和插入数据在用户需要将一个表中所有的数据或大量的数据添加至现有表的场景下,openGauss提供了MERGEINTO语句通过两个表合并的方式高效地将新数据添加到现有表。MERGEINTO语句将......
  • 零门槛Serverless课堂 应用全托管 so easy!
    前言一切要从一个风和日丽的早上说起:那天,阳光正好,微风不燥。还来不及从容吃口早饭,我就接到了线上报警,赶忙打开了电脑,处理突发的流量高峰导致的页面报错。重启好服务,饭都冷了。我心里想着,如果能够简化操作就好了,如果是界面化的操作该多好,如果几分钟就能轻松搞定就好了,......上述情况......
  • 【江鸟中原】——实现简单的2048
    一、引言经过一学期的学习,运用自己学习的知识,自己实践完成了一个简单的小游戏。二、游戏介绍完成一个2048小游戏,在一个4×4的空间中,可以实现上下左右移动,相同块消除形成倍数块,如果没有可移动块,则游戏结束。否则游戏可一直进行。三、程序设计(1)页面实现在4×4的表格中随机生成2或者4......
  • 12/23每日总结
    因为学习pythonweb没有学数据分析,但是比较感兴趣,所以来了要用到的库为numpy跟pandas,介绍如下:NumPy系统是Python的一种开源的数值计算扩展,这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表结构要高效的多(该结构也可以用来表示矩阵(matrix))。pandas是基于NumPy的一种工具,该工......