Repository: chxj1992/slide_captcha_cracker
Branch: master
Commit: 0215fb92552c
Files: 5
Total size: 6.2 KB
Directory structure:
gitextract_6a0k9blj/
├── .gitignore
├── README.md
├── app.py
├── run.sh
└── templates/
└── index.html
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.idea
__pycache__
================================================
FILE: README.md
================================================
# slide_captcha_cracker

[在线Demo](http://slide-captcha.chxj.name/)
### 简介
本项目是一个通过简单图片边缘检测算法来定位滑动验证码拼图在背景图中位置的一个例子。
代码主要采用`opencv`提供的一些函数对图片进行处理并实现定位,在这里只是提供一个思路,抛砖引玉。
### 关于滑动验证码
演示项目所用到的滑动验证码的实现相对简单,整个交互过程主要包含以下步骤:
* 服务端将背景图片和拼图图片合并为一张图片,并记录下拼图在背景图中的`x坐标`,然后将拼好图片和单独的拼图图片返回给客户端
* 客户端实现单片拼图在背景图上拖动的动画效果,并在用户完成拖动动作后,将当前拼图所处位置的坐标数据加密后返回给服务端
* 服务端解密数据并比较客户端返回的`x坐标`数据并与之前保存的`x坐标`数据进行比较,允许小范围内的误差
### 实现原理
基于以上的验证码实现,本例子通过以下方式实现对验证码拼图在背景图中的定位(其他步骤较为简单,不做考虑):
* 利用`opencv`库中提供的边界查找函数(cv2.findContours)提取单片拼图边缘轨迹并构造成一个二维矩阵(算子)
* 利用 `高斯模糊算子`(cv2.GaussianBlur)和 `Canny边缘检测算子`(cv2.Canny)对背景图进行处理,凸显出拼图在图片中的边缘
* 用拼图轨迹算子在处理后的背景图上进行 `互相关操作`,所得最大(小)值的位置就是拼图在背景图中的坐标
### 其他
拼图的定位只是破解滑动验证码的一个中间环节,想要破解一个好的验证码产品除了定位拼图在整个图片中的位置外,可能还有以下几个问题需要考虑:
* 从服务端获取的背景图可能是经过加密的(需要阅读js源码获得恢复图片的算法)
* 返回给服务端的参数一般是经过加密处理的(需要阅读js源码理解算法)
* 服务端可能会对用户滑动行为的移动轨迹对用户进行校验(可以尝试通过selenium等工具模拟拖动行为,或者积累真实的拖动数据后学习规律并通过js源码获得构造数据的算法)
================================================
FILE: app.py
================================================
from flask import Flask
from flask import jsonify, render_template
from matplotlib.pyplot import imsave
app = Flask(__name__)
import base64
from io import BytesIO
import cv2
import numpy as np
import requests
from PIL import Image
# 偏移量
bias = -1
slide_captcha_url = 'http://api.shuibei.chxj.name/slide-captcha'
def predict():
response = requests.get(slide_captcha_url)
base64_image = response.json()['data']['dataUrl']
base64_image_without_head = base64_image.replace('data:image/png;base64,', '')
bytes_io = BytesIO(base64.b64decode(base64_image_without_head))
img = np.array(Image.open(bytes_io).convert('RGB'))
img_blur = cv2.GaussianBlur(img, (3, 3), 0)
img_gray = cv2.cvtColor(img_blur, cv2.COLOR_BGR2GRAY)
img_canny = cv2.Canny(img_gray, 100, 200)
operator = get_operator('shape.png')
(x, y), _ = best_match(img_canny, operator)
x = x + bias
print('the position of x is', x)
buffer = mark(img, x, y)
return {'value': x, 'image': base64.b64encode(buffer.getbuffer()).decode()}
def get_operator(path, url=False, expand=False):
if url:
req = requests.get(path)
arr = np.asarray(bytearray(req.content), dtype=np.uint8)
shape = cv2.resize(cv2.imdecode(arr, -1), (69, 69))
else:
shape = cv2.resize(cv2.imread('shape.png'), (69, 69))
shape_gray = cv2.cvtColor(shape, cv2.COLOR_BGR2GRAY)
_, shape_binary = cv2.threshold(shape_gray, 127, 255, cv2.THRESH_BINARY)
_, contours, hierarchy = cv2.findContours(shape_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
contour = contours[0]
operator = np.zeros((69, 69))
for point in contour:
operator[point[0][0]][point[0][1]] = 1
if expand:
if point[0][0] > 0:
operator[point[0][0] - 1][point[0][1]] = 1
if point[0][0] < 68:
operator[point[0][0] + 1][point[0][1]] = 1
if point[0][1] > 0:
operator[point[0][0]][point[0][1] - 1] = 1
if point[0][1] < 68:
operator[point[0][0]][point[0][1] + 1] = 1
return operator
def best_match(image, operator):
y_range, x_range = image.shape
max_value, position = 0, (1, 1)
for y in range(1, y_range - 1):
for x in range(1, x_range - 1):
if y + 69 > 185 or x + 69 > 315:
continue
block = image[(y - 1):(y + 68), (x - 1):(x + 68)]
value = (block * operator).sum()
if value > max_value:
max_value = value
position = (x, y)
return position, max_value
def mark(img, x, y):
cv2.putText(img, 'O', (x - 15, y + 70), cv2.FONT_HERSHEY_SIMPLEX, 4, (255, 0, 0), 2, cv2.LINE_AA)
buffer = BytesIO()
imsave(buffer, img)
return buffer
@app.route('/')
def index():
return render_template('index.html')
@app.route('/fetch')
def fetch():
return jsonify(predict())
================================================
FILE: run.sh
================================================
export FLASK_APP=app.py
flask run --host=0.0.0.0
================================================
FILE: templates/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Slide Captcha</title>
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
</head>
<body style="padding-top: 130px;">
<a href="https://github.com/chxj1992/slide_captcha_cracker">
<img style="position: absolute; top: 0; right: 0; border: 0; z-index: 9999;"
src="https://camo.githubusercontent.com/38ef81f8aca64bb9a64448d0d70f1308ef5341ab/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f6461726b626c75655f3132313632312e706e67"
alt="Fork me on GitHub"
data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png">
</a>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand">
<span class="glyphicon glyphicon-th"> </span> 滑动验证码识别 (Canny算子边缘检测)
</a>
</div>
</div>
</nav>
<div class="container">
<br>
<div class="row">
<div class="text-center">
<img id="img" src="" alt="">
</div>
</div>
<br>
<div class="row">
<div class="text-center">
滑动点X坐标 : <strong id="value"></strong>
</div>
</div>
<br>
<div class="row">
<div class="text-center">
<button id="btn" class="btn btn-success"> 刷新</button>
</div>
</div>
</div>
<script>
$(document).ready(function () {
var fetch = function () {
$.getJSON('/fetch', function (res) {
$('#img').attr('src', 'data:image/png;base64,' + res['image']);
$('#value').text(res['value']);
$('#btn').attr('disabled', false);
$('body').css('cursor', 'auto');
});
};
$('#btn').click(function () {
$('#btn').attr('disabled', true);
$('body').css('cursor', 'wait');
fetch();
});
fetch();
});
</script>
</body>
</html>
gitextract_6a0k9blj/
├── .gitignore
├── README.md
├── app.py
├── run.sh
└── templates/
└── index.html
SYMBOL INDEX (6 symbols across 1 files) FILE: app.py function predict (line 20) | def predict(): function get_operator (line 43) | def get_operator(path, url=False, expand=False): function best_match (line 75) | def best_match(image, operator): function mark (line 92) | def mark(img, x, y): function index (line 101) | def index(): function fetch (line 106) | def fetch():
Condensed preview — 5 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8K chars).
[
{
"path": ".gitignore",
"chars": 18,
"preview": ".idea\n__pycache__\n"
},
{
"path": "README.md",
"chars": 1029,
"preview": "# slide_captcha_cracker\r\n\r\n\r\n\r\n[在线Demo]("
},
{
"path": "app.py",
"chars": 2943,
"preview": "from flask import Flask\nfrom flask import jsonify, render_template\nfrom matplotlib.pyplot import imsave\n\napp = Flask(__n"
},
{
"path": "run.sh",
"chars": 49,
"preview": "export FLASK_APP=app.py\nflask run --host=0.0.0.0\n"
},
{
"path": "templates/index.html",
"chars": 2290,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width"
}
]
About this extraction
This page contains the full source code of the chxj1992/slide_captcha_cracker GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 5 files (6.2 KB), approximately 2.3k tokens, and a symbol index with 6 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.