吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 12491|回复: 97
上一主题 下一主题
收起左侧

[Web逆向] [验证码识别]点选验证码识别的通用解决方案-相似度识别

    [复制链接]
跳转到指定楼层
楼主
s1lencee 发表于 2024-2-5 21:37 回帖奖励
本帖最后由 s1lencee 于 2024-2-5 21:40 编辑

[验证码识别]点选验证码识别的通用解决方案-相似度识别

本文章所有内容仅供学习和研究使用,本人不提供具体模型和源码。若有侵权,请联系我立即删除!维护网络安全,人人有责。

前言

最近发了个关于机器学习的教程,没想到大家都挺感兴趣的。

点选验证码是比较常见的验证码之一,主要有3种:

  • 文字点选验证
  • 图标点选验证
  • 语序点选验证

其中语序点选是难度最大的。

以下是一些主流验证码

我看了网上关于这类验证码的教程有些过时了或讲解比较模糊,便打算自己出文章

本文章我将以某美文字点选验证码为例子介绍如何识别点选验证码

目录

  • 准备工作

    • 验证码图片下载
    • 神经网络选择
    • 注意事项
  • 数据集构建

    • 图片标注
    • 注意事项
  • yolov5目标检测

    • 训练模型
  • 相似度模型构建

    • 数据集
    • 代码准备
    • 模型训练
    • 导出onnx
    • 模型评估
  • 验证图片识别

    • 两种模型结合
    • 示例代码
  • 其他验证类型

    • 语序识别
    • 语音验证码
    • 手势验证码
    • 九宫格验证码
  • 总结和注意事项

准备工作

验证码图片下载

老样子,我们将下载好的图片按照内容计算MD5值,保存到本地。(当然也可以不用怎么干)

因为该验证码图片干扰较少,所以目标检测不需要太多的数据集,我认为大概200张到400张就够了。

神经网络选择

目前主流的文字点选识别大部分使用2种方式:

  • yolo目标检测 + siamese网络计算相似度
  • yolo目标检测 + crnn识别具体文字

文字和图标我推荐第一种,而语序只能使用第二种,

因为crnn需要的数据集巨大,并且训练周期长,所有本文我将使用第一种方式来识别图片

注意事项

  • 某验4代的参考文字和图标图片是带有透明背景的图片,在训练相似模型时会有较大影响,所以我们可以将该图片的透明背景转换为白色,这样在预测相似度时就比较准确了。

数据集构建

图片标注

我们只需要标注一种类型,即图片中的文字即可,不需要其他标签。

图片标注我在之前的文章已经讲过了,在这里我不做过多赘述。

注意事项

  • 某验3代参考文字和图标是和目标文字是在一张图片上的,这时我们需要把两种都标记上,即参考文字一种类型,目标文字一种类型。

  • 某盾的图标也在图片上,但是坐标是固定的,裁剪下来即可。(极小概率会出现4种图标)

yolov5目标检测

训练模型

yolo训练我已经在之前的文章讲过了,有兴趣可以去看看

https://www.52pojie.cn/thread-1886001-1-1.html

相似度模型构建

介绍

这里我们使用以VGG16为主干的Siamese神经网络来训练相似度模型

https://blog.csdn.net/weixin_44791964/article/details/107343394

我们要实现的功能是: 将两张图片处理后输入网络,网络会输出0-1之间的值。该值就是这两张图片的相似度。

数据集

按照你喜欢的方式将所有经过目标检测并切割下来后的图片进行分类

因为相似度模型不能输入文字,我们需要将文字转换为图片

from PIL import Image, ImageDraw, ImageFont
from io import BytesIO

def text_to_image(text, text_color='black', bg_color='white'):
    """文字转图片"""
    img = Image.new('RGB', (100, 100), color=bg_color)
    img_draw = ImageDraw.Draw(img)
    width, height = img.size
    font = ImageFont.truetype('msyh.ttc', size=75)
    w = font.getlength(text)
    img_draw.text(((width - w) / 2, 0), text, fill=text_color, font=font)
    buf = BytesIO()
    img.save(buf, format="JPEG")
    return buf.getvalue()

这里我用pyqt5写了一个界面来进行人工分类

最后整理一下分类好的图片

目录结构如下

train
   丁
     丁_672.png
     丁_673.png
     .....
   七
     七_7408.png
     七_7409.png
     .....

val
   丁
     丁_672.png
     丁_673.png
     .....
   七
     七_7408.png
     七_7409.png
     .....

验证集和数据集比较按照你的爱好给即可, 用的是1:9

代码准备

相关存储库:

  1. https://github.com/bubbliiiing/Siamese-pytorch
  2. https://github.com/2833844911/dianxuan (这位作者代码写的不是很好,但是能用)

其中链接1是大家用得比较多的,但是该项目并不适应于我们。

查看代码会发现

它会从每种类型的数据集中取出若干图片。

而我们数据集有些这类最少只要2张,如果使用该存储库,代码会找不到更多图片,就会进入死循环。

当然,你也可以使用数据增强来增加数据集,为了方便我就不演示了。

我们将第二个存储库克隆下来,并按照里面的教程训练即可。

这位作者把test写成了text

刚开始, 模型会下载预训练模型vgg16-397923af.pth

但是这个下载速度实在太慢了, 我们可以直接打开浏览器输入以下链接下载

https://download.pytorch.org/models/vgg16-397923af.pth

然后在资源管理器打开%USERPROFILE%\.cache\torch\hub\checkpoints, 将下载好的模型复制进去即可, 如果提示找不到文件夹就按照提示创建

模型训练

输入以下命令开始训练

python train.py

等待训练结束

其中loss代表模型的损失, acc代表正确率

导出onnx

在该存储库同一文件夹创建export.py

import torch
from text import Siamese

out_onnx = 'model.onnx'
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
dummy = (torch.randn(1, 3, 105, 105).to(device), torch.randn(1, 3, 105, 105).to(device))
model = torch.load('你的模型路径')
model.eval()

model = model.to(device)
torch_out = torch.onnx.export(model, dummy, out_onnx,input_names=["x1", "x2"])
print("finish!")

模型评估

创建predict.py

import cv2
import onnx
import onnxruntime
import numpy as np

# 导出的ONNX模型的路径
onnx_model_path = 'model.onnx'

# 加载ONNX模型
onnx_model = onnx.load(onnx_model_path)

# 创建ONNX Runtime推理会话
ort_session = onnxruntime.InferenceSession(onnx_model_path)

image1 = cv2.imread(r'1.jpg')
image2 = cv2.imread(r'2.jpg')

image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)
image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2RGB)

# 将图片转换为numpy数组
input1 = cv2.resize(image1, (105, 105)).astype(np.float32) / 255
input2 = cv2.resize(image2, (105, 105)).astype(np.float32) / 255

# 确保输入的形状是 (C, H, W)
input1 = np.transpose(input1, (2, 0, 1))
input2 = np.transpose(input2, (2, 0, 1))

# 添加 batch 维度
input1 = np.expand_dims(input1, axis=0)
input2 = np.expand_dims(input2, axis=0)

outputs = ort_session.run(None, {'x1': input1, 'x2': input2})

# 输出
print(outputs[0][0][0])

运行后会输出

0.29165006

该值就是相似度了

验证图片识别

两种模型结合

同样的, 先使用目标检测并切割出目标图片, 然后将文字转换为图片, 再使用相似度模型预测每2张图片之间的相似度

这样会得到一个矩阵

[
  [0.1, 0.2, 0.8, 0.5],
  [0.9, 0.3, 0.5, 0.1],
  [0.5, 0.1, 0.1, 0.8],
  [0.2, 0.7, 0.1, 0.3],
]

然后我们取最大值的索引, 将该索引的行和列设为0, 依次类推

[
  [0.1, 0.2, 0.8, 0.5],
  [0.9, 0.3, 0.5, 0.1],
  [0.5, 0.1, 0.1, 0.8],
  [0.2, 0.7, 0.1, 0.3],
]
# 最大值0.9, 索引0, 1
# 将该索引的行和列设为0, 处理后
[
  [0. , 0.2, 0.8, 0.5],
  [0. , 0. , 0. , 0. ],
  [0. , 0.1, 0.1, 0.8],
  [0. , 0.7, 0.1, 0.3],
]

直到该二维矩阵全部为0即可

识别结果

示例代码

from PIL import Image, ImageDraw, ImageFont
from matplotlib import pyplot as plt
from io import BytesIO
import numpy as np

def get_position(bg_img_data, words):
    """获取点选坐标"""
    word_img_list = [text_to_image(word) for word in words]
    # 目标位置识别, 返回左上角和右下角坐标
    result = detection(bg_img_data)
    draw(bg_img_data, result)
    img = Image.open(BytesIO(bg_img_data))
    # 相似度矩阵
    siamese_matrix = []
    plt.figure()
    i = 1
    for j, word_img in enumerate(word_img_list):
        icon_img = Image.open(BytesIO(word_img))
        plt.subplot(len(word_img_list), len(result) + 1, i)
        plt.imshow(icon_img.resize((50, 50))), plt.title(f'word-{j}'), plt.axis('off')
        row = []
        i += 1
        for box in result:
            crop_img = img.crop(box).convert("L")

            # siamese_pre为相似度识别, 返回一个浮点数
            s = siamese_pre(icon_img, crop_img)

            plt.subplot(len(word_img_list), len(result) + 1, i)
            plt.imshow(crop_img.resize((50, 50))), plt.title(f'{s:.4f}'), plt.axis('off')
            row.append(s)
            i += 1
        siamese_matrix.append(row)
    plt.show()
    siamese_matrix = np.array(siamese_matrix)
    p = []
    for i in range(siamese_matrix.shape[0]):
        max_index = np.argmax(siamese_matrix[i, :])
        update_matrix(siamese_matrix, (i, max_index))
        p.append(result[max_index])
    points = [[int((p[i][0] + p[i][2]) / 2), int((p[i][1] + p[i][3]) / 2)] for i in range(len(p))]
    return points

def update_matrix(matrix, index):
    """将最大值所在的行和列置为零"""
    matrix[index[0], :] = 0  # 将行置为零
    matrix[:, index[1]] = 0  # 将列置为零
    return matrix

def draw(img_path, data):
    """绘制识别结果"""
    image_ = Image.open(BytesIO(img_path))
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签SimHei
    plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
    plt.imshow(image_, interpolation='none')
    current_axis = plt.gca()
    for i, box in enumerate(data):
        if len(box) == 2:
            x, y = box
            box_w = 40
            box_h = 40
            box_ = (x - 20, y - 20)
        else:
            x, y, x2, y2 = box
            box_w = x2 - x
            box_h = y2 - y
            box_ = (x, y)
        current_axis.add_patch(
            plt.Rectangle(box_, box_w, box_h, color='blue', fill=False, linewidth=2))
        plt.text(
            box_[0],
            box_[1],
            s=f"{i}",
            color="white",
            verticalalignment="top",
            bbox={"color": "black", "pad": 0},
        )
    plt.show()

def text_to_image(text, text_color='black', bg_color='white'):
    img = Image.new('RGB', (100, 100), color=bg_color)
    img_draw = ImageDraw.Draw(img)
    width, height = img.size
    font = ImageFont.truetype('msyh.ttc', size=75)
    w = font.getlength(text)
    img_draw.text(((width - w) / 2, 0), text, fill=text_color, font=font)
    buf = BytesIO()
    img.save(buf, format="JPEG")
    return buf.getvalue()

最后, 携带坐标去验证, 最后正确率85%左右

其他验证类型

语序识别

语序验证码识别流程一般如下:

  1. 目标检测
  2. 文字识别
  3. 语序还原

前面两步较为简单, 第三步较为困难, 网上已经有教程使用jieba分词来还原语序, 但是效果不是很理想

实际上, 语序还原已经有了解决方案

https://github.com/shibing624/pycorrector

这是一款开源的中文纠错工具, 并提供了多种模型

Kenlm统计语言模型不仅能纠正拼写错误, 也能用于语序还原

开发者提供了小中大三种模型, 其中大模型达到了3GB, 但是语序还原效果非常好

当然也可以自己使用数据集训练

import os
import kenlm
from itertools import permutations

class WordOrder(object):
    def __init__(self, model_path):
        self.model = kenlm.LanguageModel(model_path)

    def n_gram(self, word):
        word_list = list(word)
        # n-gram
        candidate_list = list(permutations(word_list, r=len(word_list)))
        a_c_s = -100
        a_c = ""
        b_c_s = 1000
        for candidate in candidate_list:
            candidate = ' '.join(candidate)
            a = self.model.score(candidate)
            b = self.model.perplexity(candidate)
            if a > a_c_s:
                a_c = candidate
                a_c_s = a
            if b_c_s > b:
                b_c_s = b
        return a_c.replace(" ", '')

    def predict(self, text):
        return self.n_gram(text)

if __name__ == "__main__":
    # 模型路径
    model_dir = os.path.join(os.path.dirname(__file__), 'models')
    language_model_path = os.path.join(model_dir, '语言模型路径')
    char_order = WordOrder(language_model_path)

    word = "等烟天雨色青"
    order = char_order.predict(word)
    print(word + " => " + order)

使用zh_giga.no_cna_cmn.prune01244.klm大模型的识别结果

语音验证码

OpenAI已经开放了免费的语言识别模型whisper, 虽然速度有些慢, 但是在没有干扰的情况下识别正确率还可以

https://github.com/openai/whisper

安装whisper

pip install whisper

识别代码

import whisper

# 可以选base, small等多种模型, 具体查看官方文档
model = whisper.load_model("small", download_root="models")
voice_path = "xxx.mp3"
result = model.transcribe(voice_path)
text = result["text"]
print(f"{voice_path} => {text}")

可以看到识别结果还是可以的

手势验证码

目标检测 + 回归模型(待更新)

九宫格验证码

九宫格可以使用相似度模型或分类模型(待更新)

总结和其他

其他

  1. 如果大家显卡比较好的话建议安装torch的GPU版本, 一般情况下训练模型GPU速度比GPU快很多
  2. 关于为什么使用onnx, onnx可以跨平台和跨设备使用, 并且在CPU上推理速度上比较快, 便于在服务器使用, 并且不像安装torch那样繁琐
  3. 其他语言可以参考思路分析

总结

验证码与打码的攻防对抗是一场持久战,随着技术的发展,双方不断演进和创新。

在黑灰产盛行的年代,人工智能将扮演着重要的角色,对于攻方和防守方都具有深远的影响。

从最开始的单纯字母验证码到现在的智能无感验证, 验证码技术经历了多个阶段的演进。

我们需要了解验证码的破解才能更好地防御, 知己知彼百战百胜, 维护网络安全人人有责。

最后, 我就是一个小白, 欢迎大佬们指教。

免费评分

参与人数 43吾爱币 +45 热心值 +39 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
aAChengYay + 1 + 1 我很赞同!
geekfan + 1 + 1 谢谢@Thanks!
ggbool + 1 + 1 谢谢@Thanks!
mink + 1 + 1 鼓励转贴优秀软件安全工具和文档!
debug_cat + 2 + 1 谢谢@Thanks!
AK471 + 1 未能按照本版块发帖要求发帖,请仔细阅读本版块版规再发帖,谢谢理解!
iTMZhang + 1 + 1 用心讨论,共获提升!
RainFalls1 + 1 热心回复!
jialesmile + 1 + 1 我很赞同!
polli52pj + 1 热心回复!
cdners + 1 用心讨论,共获提升!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
dantelians + 1 我很赞同!
Yangzaipython + 1 用心讨论,共获提升!
malio9950 + 1 + 1 我很赞同!
我嗳破解 + 1 + 1 热心回复!
kolt1911 + 1 + 1 热心回复!
简单メ传说 + 1 + 1 用心讨论,共获提升!
Issacclark1 + 1 谢谢@Thanks!
guoruihotel + 1 + 1 谢谢@Thanks!
TaoTaoZhu + 1 + 1 我很赞同!
lz132 + 1 + 1 谢谢@Thanks!
zyastc521 + 1 + 1 谢谢@Thanks!
yixi + 1 + 1 谢谢@Thanks!
ailang + 1 + 1 谢谢@Thanks!
pod2023 + 1 谢谢@Thanks!
wzvideni + 1 + 1 我很赞同!
prince_cool + 2 + 1 鼓励转贴优秀软件安全工具和文档!
Voccoo + 1 + 1 谢谢@Thanks!
风动鸣 + 1 + 1 用心讨论,共获提升!
魔道书生 + 2 + 1 我很赞同!
Heasitation + 1 + 1 用心讨论,共获提升!
weizhao911 + 1 + 1 用心讨论,共获提升!
trxh2023 + 1 + 1 用心讨论,共获提升!
allspark + 1 + 1 用心讨论,共获提升!
gunxsword + 1 + 1 谢谢@Thanks!
billsmiless + 3 + 1 鼓励转贴优秀软件安全工具和文档!
hktwa + 1 + 1 我很赞同!
lccccccc + 1 + 1 用心讨论,共获提升!
uuwatch + 1 + 1 我很赞同!
featmellwo + 2 + 1 我很赞同!
zzlya + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
涛之雨 发表于 2024-2-6 21:13
AI太难了



一边GPT一边学知识,到最后准确率为0。。。

结果你那边200张久解决了。。。

能给个方向不。。。我用的是(GPT说)CNN做预测。。。

有点迷茫

免费评分

参与人数 1吾爱币 +3 热心值 +1 收起 理由
fanssong + 3 + 1 抓到苦逼版主一枚,哈哈哈

查看全部评分

推荐
wangguang 发表于 2024-2-6 11:11
3#
落克 发表于 2024-2-6 11:04
4#
Learn3days 发表于 2024-2-6 11:42
学到了感谢大佬
5#
dircou 发表于 2024-2-6 12:01
学到了感谢大佬
6#
zhang120300 发表于 2024-2-6 12:04
请问 google的验证码 怎么破?
7#
ameiz 发表于 2024-2-6 12:31
学到了感谢大佬,能做个浏览器插件么。
8#
Shadowshaw 发表于 2024-2-6 13:03
学习到了,这样比二次验证还多一个,可以避免被暴力
9#
cp8086 发表于 2024-2-6 14:33
这个比较好,学习了。
10#
xiawan 发表于 2024-2-6 15:21
啥也不说了,帖子就是带劲!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 17:38

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表