数美滑块验证码分析

简介: 本文以官网的滑块验证码为例,分析验证过程,完成模拟验证。

小红书、蘑菇街、脉脉、抖鱼等很多都用了数美的验证码。

本文以官网的滑块验证码为例,分析验证过程,完成模拟验证。


数美验证码官网:https://www.ishumei.com/trial/captcha.html
在这里插入图片描述


1.验证码申请

打开控制台多看几遍请求过程,就大抵明白请求步骤了,这里就不再细说。

api: 'https://captcha.fengkongcloud.com/ca/v1/conf?'
在这里插入图片描述

params= {
        'organization': 'RlokQwRlVjUrTUlkIqOg',
        'model': 'slide',
        'sdkver': '1.1.3',
        'rversion': '1.0.3',
        'appId': 'default',
        'lang': 'zh-cn',
        'channel': 'YingYongBao',
        'callback': 'sm_{}'.format(int(time.time() * 1000))
    }

该接口返回的js参数,是下一步需要请求的目标。
在这里插入图片描述

2.提取js参数

js地址:https://castatic.fengkongcloud.com/pr/auto-build/v1.0.3-70/captcha-sdk.min.js
需要提取该js中的参数名,会在最后验证的时候使用。
不过一般情况下参数名不会变,也可以省略此处,在后面写死。
在这里插入图片描述
在该js文件中的参数是倒序的
在这里插入图片描述


2.验证码注册

api: https://captcha.fengkongcloud.com/ca/v1/register?
在这里插入图片描述
bg和fg是验证码图片地址。
在这里插入图片描述

3.计算滑块位置

根据上一步可以得到验证图片的地址。
验证码图片:https://castatic.fengkongcloud.com/crb/set-000006/v2/a13e3325e9f864fa42a94a6e07cd95fc_bg.jpg
滑块图片:https://castatic.fengkongcloud.com/crb/set-000006/v2/a13e3325e9f864fa42a94a6e07cd95fc_fg.png
在这里插入图片描述

使用opencv查找并匹配图像模板中的滑块。
需要注意的是,这里是以原图计算的,而页面上的图片大小只有(300,150),(应用不同的产品可能大小也不同)
所以需要按比例进行缩小。

4.验证

api:https://captcha.fengkongcloud.com/ca/v2/fverify?

params = {
    'protocol': '70',
    'organization': 'RlokQwRlVjUrTUlkIqOg',
    'rversion': '1.0.3',
    'oe': 'V/QxFC7ISm1=',
    'nj': '1ISY9IKNM+OGjwl0F7LP...省略...7VOki5p4sm+h/qMX2QAhN/4w',
    'zl': '8LwMmaImogs=',
    'sq': '14RHMSbfhJU=',
    'kh': 'qCcI31wL/Fs=',
    'mn': '4AAyKWNy6K0=',
    'rid': '20210113105643e313f68a420c9d240d',
    'ch': 'uiJ+hjbCOka=',
    'yv': 'b9NFDsBKGcg=',
    'ga': 'WssDiJ1wOQI=',
    'ko': '14bDqW72JnI=',
    'callback': 'sm_1610506618948',
    'act.os': 'web_pc',
    'vj': 'IFXKu8Pjb3k=',
    'ostype': 'web',
    'sdkver': '1.1.3'
}

params参数里的 oe,mn,kh等等,都经过了DES加密。

验证后会返回,
在这里插入图片描述
message = success,riskLevel=PASS 说明验证通过


5.完整代码

"""
数美滑块验证码破解验证
"""

import base64
import json
import random
import re
import time
from io import BytesIO
import cv2
import numpy as np
import requests
from pyDes import des, ECB

CAPTCHA_DISPLAY_WIDTH = 310
CAPTCHA_DISPLAY_HEIGHT = 155

p = {}


def pad(b):
    """
    块填充
    """
    block_size = 8
    while len(b) % block_size:
        b += b'\0'
    return b


def split_args(s):
    """
    分割js参数
    """
    r = []
    a = ''
    i = 0
    while i < len(s):
        c = s[i]
        if c == ',' and (a[0] != '\'' or len(a) >= 2 and a[-1] == '\''):
            r.append(a)
            a = ''
        elif c:
            a += c
        i += 1
    r.append(a)
    return r


def find_arg_names(script):
    """
    通过js解析出参数名
    """
    names = {}
    a = []
    for r in re.findall(r'function\((.*?)\)', script):
        if len(r.split(',')) > 100:
            a = split_args(r)
            break

    r = re.search(r';\)(.*?)\(}', script[::-1]).group(1)
    v = split_args(r[::-1])

    d = r'{%s}' % ''.join([((',' if i else '') + '\'k{}\':([_x0-9a-z]*)'.format(i + 1)) for i in range(15)])

    k = []
    r = re.search(d, script)
    for i in range(15):
        k.append(r.group(i + 1))

    n = int(v[a.index(re.search(r'arguments;.*?,(.*?)\);', script).group(1))], base=16)

    for i in range(n // 2):
        v[i], v[n - 1 - i] = v[n - 1 - i], v[i]

    for i, b in enumerate(k):
        t = v[a.index(b)].strip('\'')
        names['k{}'.format(i + 1)] = t if len(t) > 2 else t[::-1]

    return names


def get_encrypt_content(message, key, flag):
    """
    接口参数的加密、解密
    """
    des_obj = des(key.encode(), mode=ECB)
    if flag:
        content = pad(str(message).replace(' ', '').encode())
        return base64.b64encode(des_obj.encrypt(content)).decode('utf-8')
    else:
        return des_obj.decrypt(base64.b64decode(message)).decode('utf-8')


def get_random_ge(distance):
    """
    生成随机的轨迹
    """
    ge = []

    y = 0
    v = 0
    t = 1
    current = 0
    mid = distance * 3 / 4
    exceed = 20
    z = t

    ge.append([0, 0, 1])

    while current < (distance + exceed):
        if current < mid / 2:
            a = 15
        elif current < mid:
            a = 20
        else:
            a = -30
        a /= 2
        v0 = v
        s = v0 * t + 0.5 * a * (t * t)
        current += int(s)
        v = v0 + a * t

        y += random.randint(-5, 5)
        z += 100 + random.randint(0, 10)

        ge.append([min(current, (distance + exceed)), y, z])

    while exceed > 0:
        exceed -= random.randint(0, 5)
        y += random.randint(-5, 5)
        z += 100 + random.randint(0, 10)
        ge.append([min(current, (distance + exceed)), y, z])

    return ge


def make_mouse_action_args(distance):
    """
    生成鼠标行为相关的参数
    """
    ge = get_random_ge(distance)
    args = {
        p['k']['k5']: round(distance / CAPTCHA_DISPLAY_WIDTH, 2),
        p['k']['k6']: get_random_ge(distance),
        p['k']['k7']: ge[-1][-1] + random.randint(0, 100),
        p['k']['k8']: CAPTCHA_DISPLAY_WIDTH,
        p['k']['k9']: CAPTCHA_DISPLAY_HEIGHT,
        p['k']['k11']: 1,
        p['k']['k12']: 0,
        p['k']['k13']: -1,
        'act.os': 'android'
    }
    return args


def get_distance(fg, bg):
    """
    计算滑动距离
    """
    target = cv2.imdecode(np.asarray(bytearray(fg.read()), dtype=np.uint8), 0)
    template = cv2.imdecode(np.asarray(bytearray(bg.read()), dtype=np.uint8), 0)
    result = cv2.matchTemplate(target, template, cv2.TM_CCORR_NORMED)
    _, distance = np.unravel_index(result.argmax(), result.shape)
    return distance


def update_protocol(protocol_num, js_uri):
    """
    更新协议
    """
    global p
    r = requests.get(js_uri, verify=False)
    names = find_arg_names(r.text)
    p = {
        'i': protocol_num,
        'k': names
    }


def conf_captcha(organization):
    """
    获取验证码设置
    """
    url = 'https://captcha.fengkongcloud.com/ca/v1/conf'

    args = {
        'organization': organization,
        'model': 'slide',
        'sdkver': '1.1.3',
        'rversion': '1.0.3',
        'appId': 'default',
        'lang': 'zh-cn',
        'channel': 'YingYongBao',
        'callback': 'sm_{}'.format(int(time.time() * 1000))
    }

    r = requests.get(url, params=args, verify=False)
    resp = json.loads(re.search(r'{}\((.*)\)'.format(args['callback']), r.text).group(1))
    return resp


def register_captcha(organization):
    """
    注册验证码
    """
    url = 'https://captcha.fengkongcloud.com/ca/v1/register'

    args = {
        'organization': organization,
        'channel': 'YingYongBao',
        'lang': 'zh-cn',
        'model': 'slide',
        'appId': 'default',
        'sdkver': '1.1.3',
        'data': '{}',
        'rversion': '1.0.3',
        'callback': 'sm_{}'.format(int(time.time() * 1000))
    }

    r = requests.get(url, params=args, verify=False)
    resp = json.loads(re.search(r'{}\((.*)\)'.format(args['callback']), r.text).group(1))

    return resp


def verify_captcha(organization, rid, key, distance):
    """
    提交验证
    """
    url = 'https://captcha.fengkongcloud.com/ca/v2/fverify'
    args = {
        'organization': organization,
        p['k']['k1']: 'default',
        p['k']['k2']: 'YingYongBao',
        p['k']['k3']: 'zh-cn',
        'rid': rid,
        'rversion': '1.0.3',
        'sdkver': '1.1.3',
        'protocol': p['i'],
        'ostype': 'web',
        'callback': 'sm_{}'.format(int(time.time() * 1000))
    }

    args.update(make_mouse_action_args(distance))

    key = get_encrypt_content(key, 'sshummei', 0)

    for k, v in args.items():
        if len(k) == 2:
            args[k] = get_encrypt_content(v, key, 1)
    print(args)
    r = requests.get(url, params=args, verify=False)
    resp = json.loads(re.search(r'{}\((.*)\)'.format(args['callback']), r.text).group(1))

    return resp


def get_verify(organization):
    """
    进行验证
    """
    resp = conf_captcha(organization)
    protocol_num = re.search(r'build/v1.0.3-(.*?)/captcha-sdk.min.js', resp['detail']['js']).group(1)

    if not p.get('id') or protocol_num != p['i']:
        update_protocol(protocol_num, ''.join(['https://', resp['detail']['domains'][0], resp['detail']['js']]))

    resp = register_captcha(organization)

    rid = resp['detail']['rid']
    key = resp['detail']['k']

    domain = resp['detail']['domains'][0]
    fg_uri = resp['detail']['fg']
    bg_uri = resp['detail']['bg']

    fg_url = ''.join(['http://', domain, fg_uri])
    bg_url = ''.join(['http://', domain, bg_uri])

    r = requests.get(fg_url, verify=False)
    fg = BytesIO(r.content)

    r = requests.get(bg_url, verify=False)
    bg = BytesIO(r.content)

    distance = get_distance(fg, bg)
    print(distance)
    r = verify_captcha(organization, rid, key, int(distance / 600 * 310))

    return rid, r


def test():
    # 表示小红书
    organization = 'eR46sBuqF0fdw7KWFLYa'

    # rid是验证过程中响应的标示,r是最后提交验证返回的响应
    rid, r = get_verify(organization)
    print(rid, r)

    # riskLevel为PASS说明验证通过
    if r['riskLevel'] == 'PASS':
        # 这里需要向小红书提交rid
        # 具体可抓包查看,接口:/api/sns/v1/system_service/slide_captcha_check
        pass


if __name__ == '__main__':
    test()
目录
相关文章
|
安全 API 数据安全/隐私保护
​验证码邮件API有哪些?分析最好的3个接口平台
验证码邮件API如AOKSend、SendGrid和Mailgun是用户身份验证的关键工具。这些API提供高效、可靠的邮件发送服务,确保验证码的安全传输。AOKSend以其快速发送和易用性著称,SendGrid则以全面功能和扩展性见长,而Mailgun则以灵活性和高送达率闻名。开发者可以根据需求选择合适的API,通过示例代码轻松集成到应用中,增强安全性和用户体验。
|
存储 前端开发 NoSQL
TienChin 验证码响应结果分析&验证码生成接口分析
首先从前端开始进行分析,进入到登录页面,打开开发者工具(f12),找到 network,f5 刷新一下页面,然后,筛选一下,筛选内容为 Fetch/XHR:
181 0
TienChin 验证码响应结果分析&验证码生成接口分析
|
缓存 JavaScript 数据安全/隐私保护
Crack Captcha | 某招投标信息查询网站滑块验证码分析
Crack Captcha | 某招投标信息查询网站滑块验证码分析
229 0
|
自然语言处理 算法 安全
语音验证码短信原理和应用场景分析(附Java 接入代码)
语音验证码短信原理和应用场景分析(附Java 接入代码)
707 0
|
文字识别 安全 测试技术
APP漏洞检测 验证码被重复利用漏洞分析与汇总
在对客户网站以及APP进行安全检测的同时,我们SINE安全对验证码功能方面存在的安全问题,以及验证码漏洞检测有着十多年的经验,在整个APP,网站的安全方面,验证码又分2种,第一个是登陆的身份验证码,再一个对重要的操作进行的操作验证码,虽然从名字上都是验证码,但这两种所包含的内容是不一样的。
395 0
APP漏洞检测 验证码被重复利用漏洞分析与汇总
|
存储 NoSQL 数据库
认证服务---整合短信验证码,用户注册和登录 ,密码采用MD5加密存储 【二】
这篇文章讲述了在分布式微服务系统中添加用户注册和登录功能的过程,重点介绍了用户注册时通过远程服务调用第三方服务获取短信验证码、使用Redis进行验证码校验、对密码进行MD5加密后存储到数据库,以及用户登录时的远程服务调用和密码匹配校验的实现细节。
认证服务---整合短信验证码,用户注册和登录 ,密码采用MD5加密存储 【二】
|
9月前
|
缓存 Java 应用服务中间件
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
1775 5
|
资源调度 JavaScript API
nest.js + sms 实现短信验证码登录
本文介绍了在Nest.js框架中集成短信验证码登录的实现方案,详细阐述了使用阿里云短信服务的配置流程、资质申请、短信模板设置,并提供了API调用示例和工程代码的运行步骤。
nest.js + sms 实现短信验证码登录
|
C#
C# 图形验证码实现登录校验代码
C# 图形验证码实现登录校验代码
318 2
|
Java
Java 登录输入的验证码
Java 登录输入的验证码
153 1
下一篇
oss云网关配置