验证码识别与处理细节总结

news/2025/1/10 21:07:31/文章来源:https://www.cnblogs.com/ocr12/p/18664716

验证码的破解虽然整体难度不大,但某些细节处理不当可能会导致各种错误。在本文中,我们总结了一些常见问题及其解决方法,包括验证码识别方案和轨迹处理等。

关于 w 值
在某些验证码系统(如第三代系统)中,有几个请求接口会包含 w 值。需要特别注意的是,除了最后一个校验接口 ajax.php 外,其他接口的 w 值可以为空,但在某些模式下,如无感验证模式,在请求 get.php 接口获取 c 和 s 值时,也会校验 w 值。

常见错误:

json

{'status': 'error', 'error': 'param decrypt error', 'user_error': '网络不给力', 'error_code': 'error_03'}
关于时间间隔
验证码系统通常要求请求之间有一定的时间间隔。如果请求太快,验证会失败。例如,在第三代系统中,如果生成 w 值后没有随机停留 2 秒左右,验证可能会失败。

常见错误:

json

{'status': 'success', 'data': {'result': 'fail', 'msg': ['duration short']}}
关于 challenge
在第三代系统中,challenge 值会参与多次请求。第一次获取到一个 challenge,后续的 get.php 请求返回的数据中会包含一个新的 challenge,新 challenge 比第一次多了两位数。所有后续请求都必须使用新的 challenge。

常见错误:

json

{'success': 0, 'message': 'fail'}
关于 c 和 s
在第三代系统中,c 和 s 值会参与 w 的计算。点选和滑块验证码类型中,第一次 get.php 请求返回的 c 和 s 值,第二次 get.php 请求返回的 s 值会变化。生成 w 时需要使用第二次返回的 s 值。

常见错误:

json

{'success': 0, 'message': 'forbidden'}
关于两次 get.php 和 ajax.php 请求
在第三代系统中,点选和滑块验证码会有两次以 get.php 和 ajax.php 结尾的请求。第一次 get.php 返回主题、域名、提示文字等信息,第一次 ajax.php 返回验证码类型。这两次请求返回的数据虽然对我们没太大用处,但仍需发起请求,否则后续请求将失败。

关于智能组合验证
智能组合验证可以处理多种验证码类型。四代系统更为简洁,通过 load 接口的 captcha_type 字段直接告诉你验证码类型,如滑块、点选(及其子类型)、五子棋或九宫格等。

关于 w 值的算法
w 值生成算法中涉及一些细节,包括 passtime、pow_sign 和 pow_msg。

passtime
passtime 值在生成 w 时参与计算。对于滑块验证码,passtime 是滑动花费的时间,直接取轨迹的最后一个时间值。其他情况下,该值可设为一个随机值。

pow_sign 和 pow_msg
这两个参数是四代系统独有的。pow_msg 的格式如下:

text

1|0|md5|datetime|captcha_id|lot_number||随机字符串
pow_sign 是 pow_msg 经 MD5 加密后的值。需要注意,pow_sign 的生成必须符合特定条件,否则会导致验证失败。下面是一个处理示例:

javascript

var CryptoJS = require("crypto-js");

function getRandomString(){
    function e(){
        return (65536 * (1 + Math.random()) | 0).toString(16).substring(1);
    }
    return e() + e() + e() + e();
}

function get_pow(pow_detail, captcha_id, lot_number) {
    var n = pow_detail.hashfunc;
    var i = pow_detail.version;
    var r = pow_detail.bits;
    var s = pow_detail.datetime;
    var o = "";

var a = r % 4;
    var u = parseInt(r / 4, 10);
    var c = function g(e, t) {
        return new Array(t + 1).join(e);
    }("0", u);
    var _ = i + "|" + r + "|" + n + "|" + s + "|" + captcha_id + "|" + lot_number + "|" + o + "|";

while (1) {
        var h = getRandomString()
          , l = _ + h
          , p = void 0;
        switch (n) {
            case "md5":
            p = CryptoJS.MD5(l).toString();
            break;
        case "sha1":
            p = CryptoJS.SHA1(l).toString();
            break;
        case "sha256":
            p = CryptoJS.SHA256(l).toString();
        }
        if (0 == a) {
            if (0 === p.indexOf(c))
                return {
                    "pow_msg": _ + h,
                    "pow_sign": p
                };
        } else if (0 === p.indexOf(c)) {
            var f = void 0
              , d = p[u];
            switch (a) {
            case 1:
                f = 7;
                break;
            case 2:
                f = 3;
                break;
            case 3:
                f = 1;
            }
            if (d <= f)
                return {
                    "pow_msg": _ + h,
                    "pow_sign": p
                };
        }
    }
}
随机变化的字符串
在验证码生成过程中,通常会有一个 16 位随机字符串参与 w 的加密计算。这个字符串一般会用到两次,且两次必须相同。

常见错误:

json

{'status': 'error', 'error': 'param decrypt error', 'user_error': '网络不给力', 'error_code': 'error_03'}
随机变化的键值对
在三代和四代系统中,w 值的生成过程中会有一个随机键值对。以三代滑块为例,以下是一个动态获取该键值对的示例:

python

import re
import execjs
import requests

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
}

gct_path = "https://static.geetest.com/static/js/gct.b71a9027509bc6bcfef9fc6a196424f5.js"
gct_js = requests.get(gct_path, headers=headers).text
function_name = re.findall(r")){return (.*?)(", gct_js)[0]
break_position = gct_js.find("return function(t){")
gct_js_new = gct_js[:break_position] + "window.gct=" + function_name + ";" + gct_js[break_position:]
gct_js_new = "window = global;" + gct_js_new + """
function getGct(){
    var e = {"lang": "zh", "ep": "test data"};
    window.gct(e);
    delete e["lang"];
    delete e["ep"];
    return e;
}"""
gct = execjs.compile(gct_js_new).call("getGct")
print(gct)

补环境中可能用到的方法
window.crypto.getRandomValues
在某些情况下,如三代滑块验证中,可能会用到 window.crypto.getRandomValues() 方法。

javascript

window = global;
window.crypto = {
    getRandomValues: getRandomValues_
}

function randoms(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min)
}

function getRandomValues_(buf) {
    var min = 0,
    max = 255;
    if (buf.length > 65536) {
        var e = new Error();
        e.code = 22;
        e.message = 'Failed to execute 'getRandomValues' : The ' + 'ArrayBufferView's byte length (' + buf.length + ') exceeds the ' + 'number of bytes of entropy available via this API (65536).';
        e.name = 'QuotaExceededError';
        throw e;
    }
    if (buf instanceof Uint16Array) {
        max = 65535;
    } else if (buf instanceof Uint32Array) {
        max = 4294967295;
    }
    for (var element in buf) {
        buf[element] = randoms(min, max);
    }
    return buf;
}

// 测试
// var a = new Uint32Array(256);
// console.log(window.crypto.getRandomValues(a))
window.performance.timing
某些性能指标需要 window.performance.timing 方法来获取。例如:

javascript

window = global;
window.performance = {
    timing: {
        navigationStart: Date.parse(new Date()),
        unloadEventStart: 0,
        unloadEventEnd: 0,
        redirectStart: 0,
        redirectEnd: 0,
        fetchStart: 0,
        domainLookupStart: 0,
        domainLookupEnd: 0,
        connectStart: 0,
        connectEnd: 0,
        secureConnectionStart: 0,
        requestStart: 0,
        responseStart: 0,
        responseEnd: 0,
        domLoading: 0,
        domInteractive: 0,
        domContentLoadedEventStart: 0,
        domContentLoadedEventEnd: 0,
        domComplete: 0,
        loadEventStart: 0,
        loadEventEnd: 0,
    }
}
完整的环境搭建代码示例
javascript

const { v4: uuidv4 } = require('uuid');
const axios = require('axios');
const CryptoJS = require('crypto-js');

function getRandomString(){
    function e(){
        return (65536 * (1 + Math.random()) | 0).toString(16).substring(1);
    }
    return e() + e() + e() + e();
}

function get_pow(pow_detail, captcha_id, lot_number) {
    var n = pow_detail.hashfunc;
    var i = pow_detail.version;
    var r = pow_detail.bits;
    var s = pow_detail.datetime;
    var o = "";

var a = r % 4;
    var u = parseInt(r / 4, 10);
    var c = function g(e, t) {
        return new Array(t + 1).join(e);
    }("0", u);
    var _ = i + "|" + r + "|" + n + "|" + s + "|" + captcha_id + "|" + lot_number + "|" + o + "|";

while (1) {
        var h = getRandomString()
          , l = _ + h
          , p = void 0;
        switch (n) {
            case "md5":
            p = CryptoJS.MD5(l).toString();
            break;
        case "sha1":
            p = CryptoJS.SHA1(l).toString();
            break;
        case "sha256":
            p = CryptoJS.SHA256(l).toString();
        }
        if (0 == a) {
            if (0 === p.indexOf(c))
                return {
                    "pow_msg": _ + h,
                    "pow_sign": p
                };
        } else if (0 === p.indexOf(c)) {
            var f = void 0
              , d = p[u];
            switch (a) {
            case 1:
                f = 7;
                break;
            case 2:
                f = 3;
                break;
            case 3:
                f = 1;
            }
            if (d <= f)
                return {
                    "pow_msg": _ + h,
                    "pow_sign": p
                };
        }
    }
}

window = global;
window.crypto = {
    getRandomValues: getRandomValues_
}

function randoms(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min)
}

function getRandomValues_(buf) {
    var min = 0,
    max = 255;
    if (buf.length > 65536) {
        var e = new Error();
        e.code = 22;
        e.message = 'Failed to execute 'getRandomValues' : The ' + 'ArrayBufferView's byte length (' + buf.length + ') exceeds the ' + 'number of bytes of entropy available via this API (65536).';
        e.name = 'QuotaExceededError';
        throw e;
    }
    if (buf instanceof Uint16Array) {
        max = 65535;
    } else if (buf instanceof Uint32Array) {
        max = 4294967295;
    }
    for (var element in buf) {
        buf[element] = randoms(min, max);
    }
    return buf;
}

window.performance = {
    timing: {
        navigationStart: Date.parse(new Date()),
        unloadEventStart: 0,
        unloadEventEnd: 0,
        redirectStart: 0,
        redirectEnd: 0,
        fetchStart: 0,
        domainLookupStart: 0,
        domainLookupEnd: 0,
        connectStart: 0,
        connectEnd: 0,
        secureConnectionStart: 0,
        requestStart: 0,
        responseStart: 0,
        responseEnd: 0,
        domLoading: 0,更多内容联系1436423940
        domInteractive: 0,更多内容访问ttocr.com或联系1436423940
        domContentLoadedEventStart: 0,
        domContentLoadedEventEnd: 0,
        domComplete: 0,
        loadEventStart: 0,
        loadEventEnd: 0,
    }
}

async function getCaptchaData() {
    const response = await axios.get('https://api.example.com/captcha');
    const captchaData = response.data;
    return captchaData;
}

(async () => {
    try {
        const captchaData = await getCaptchaData();
        const powResult = get_pow(captchaData.pow_detail, captchaData.captcha_id, captchaData.lot_number);
        console.log('POW Result:', powResult);
    } catch (error) {
        console.error('Error fetching captcha data:', error);
    }
})();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/867325.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C api简单查询需要的几步

C api查询简单需要的几步 1.准备sql语句 2.绑定变量 通过sqlstatement类bindout方法将查询出来的值与存储他们的变量绑定。 这里绑定是为了以后从结果集中提取一行时只需要一行就可以完成将提取出 来的变量赋值。 3.执行execute 在这一步将sql语句执行。在这里有几个重点 mysql…

iterm2

外观 这个github上的项目提供了很多主题 https://iterm2colorschemes.com/ 亮色主题感觉还可以的几个: OneHalfLight、BuiltinLight、BlulocoLight、Materil、ProLight、Tango Half Adapted 下面截图是基于 Tango Half Adapted用蓝色代替青色,黄色加深之后的效果(vim括号插件…

22. MDI窗口设计

一、什么是MDI窗口MDI 窗口(Multiple-Document Interface),又称多文档界面,它主要用于同时显示多个文档,每个文档显示在各自的窗口中。MDI 窗口中通常包含子菜单和窗口菜单,用于在窗口或文档之间进行切换。用 QMainWindow 建立的主界面,通常会同时建立或打开多个相互独立…

详解:订单履约系统规划

大家好,我是汤师爷~ 什么是订单履约系统? 订单履约是从消费者下单支付到收到商品的全流程管理过程,包括订单接收、订单派单、库存分配、仓储管理和物流配送等环节,核心目标是确保商品准时、准确地送达消费者手中。 通过订单履约系统,消费者可以实时了解商品的物流状态和预…

文件单独编译生成

编译设备树也最好在 source过的那个窗口中来编译错误提示如下: 同时在makefile文件修改时也要注意

In‐band Network Telemetry

#卫星 #遥测技术 #INT 一、INT是什么? INT,In‐band Network Telemetry ,带内网络遥监测。telemetry,英文原意是遥测技术。从其英文名称可以了解如下: a.In-band,说明监测指令及数据均在带内传输 b.telemetry ,说明是长距离,远程获取网络数据的方法。 想象一下卫星在…

潮汐指纹识别工具 : 在线网站识别利器

最近发现了一个免费在线收集网站信息的工具,感觉挺好用的,给各位推荐下。潮汐在线指纹识别是山东新潮信息技术有限公司安全团队提供的一个免费开源在线网站信息收集工具。 功能亮点: 全面扫描 只需输入目标网站的URL或IP地址,即可获取该网站的标题、中间件、操作系统、域名…

JAVA-Day 11:数组的静态初始化和遍历

数组的静态初始化和遍历 数组静态初始化格式 数组的静态初始化与遍历 完整格式:数据类型 [] 数组名=new 数据类型[]{元素1,元素2,元素3,....} 简化格式:数据类型 [] 数组名={元素1,元素2,元素3,....} []在数组名前后都可以 代码如下: int number[]={1,2,3,4,5};for (int …

JAVA-Day 10:Do...While循环语句

Do...While循环语句 Do...While循环格式 初始化语句; do{ 循环体语句; 条件控制语句; }while(条件判断语句); 例: 使用do...while循环输出1-10的和(包括10) int i=1;int count=0;do{count+=i;i++;}while(i<=10);System.out.println(count);代码运行结果如下图所示:

配置tigerVNC,登陆远程服务器

1.在远程服务器安装、配置 (1) sudo apt update sudo apt install xfce4 xfce4-goodies (2) 安装TigerVNC sudo apt install tigervnc-standalone-server (3) 配置vnc vncpasswd (4) 配置.vnc: vim ~/.vnc/xstartup 添加:cat .vnc/xstartup#!/bin/sh # 启动 D-Bus 会话 (如果未…

CF1993F2 Dyn-scripted Robot (Hard Version)

Dyn-scripted Robot (Hard Version) 题目链接。 Problem Easy Version:\(K \le n\)。 Hard Version:\(K \le 10^{12}\)。 一个 \(Oxy\) 平面上有一个 \(w \times h\) 矩形,矩形的左下方有点 \((0, 0)\) ,右上方有点 \((w, h)\) 。 您还有一个最初位于点 \((0, 0)\) 的机器人…

OmniNxt 论文阅读

来源: https://arxiv.org/html/2403.20085?_immersive_translate_auto_translate=1 标题: OmniNxt: A Fully Open-source and Compact Aerial Robot with Omnidirectional Visual Perception OmniNxt 感觉是, 取自 Omnidirectional 中 全的意思, Nxt 像是 Next 的意思,表…