百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

「验证码逆向专栏」安某客滑块逆向

lipiwang 2024-11-04 14:33 10 浏览 0 评论



声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K哥爬虫】联系作者立即删除!

逆向目标

  • 目标:安某客滑动验证码逆向分析
  • 主页:aHR0cHM6Ly93d3cuYW5qdWtlLmNvbS9jYXB0Y2hhLXZlcmlmeS8/Y2FsbGJhY2s9c2hpZWxkJmZyb209YW50aXNwYW0=

抓包分析

首页请求,有个初始化函数,其中有个 sessionId 后续会用到。

然后有个 getInfoTp 的请求,Form Data 里有个 dInfo 是加密参数,返回值里 info 也是加密的,包含了图片信息,返回值 responseId 在后续的请求也会用到。

滑动之后,有个 checkInfoTp 请求,Form Data 里有个 data 是加密参数,包含了轨迹信息,返回值 message 可以看到是否校验成功。

整体流程就是:请求首页获取 sessionId,请求 getInfoTp 获取图片信息和 responseId,请求 checkInfoTp 校验是否成功,中间涉及到 dInfodata 两个加密参数,以及 getInfoTp 返回得到的 info 的解密。

dInfo 生成

先来看 getInfoTp 请求的 dInfo 参数,直接搜索可定位,刷新断下,大致就可以看出是 AES 加密,传入了 sessionId 和一个 _taN() 函数的返回值:

_taN() 函数是一些 URL,UA 之类的信息,可以写死:

往里跟就可以看到 AES 算法了:

这里简简单单扣一下,JavaScript 代码如下:

/* ==================================
# @Time    : 2021-12-14
# @Author  : 微信公众号:K哥爬虫
# @FileName: ajk.js
# @Software: PyCharm
# ================================== */


var CryptoJS = require('crypto-js')

function AESEncrypt(_cRV, _2undefinedp) {
    _2undefinedp = _2undefinedp.split("").reduce(function(_PUi, _JrX, _JP9) {
        return _JP9 % 2 == 0 ? _PUi + "" : _PUi + _JrX;
    }, "");
    _2undefinedp = CryptoJS.enc.Utf8.parse(_2undefinedp);
    _cRV = "string" == typeof _cRV ? _cRV : JSON.stringify(_cRV);
    _cRV = CryptoJS.AES.encrypt(_cRV, _2undefinedp, {
        iv: _2undefinedp,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return encodeURIComponent(_cRV.toString())
}

function u() {
    return {
        "sdkv": "3.0.1",
        "busurl": "https://www.脱敏处理.com/captcha-verify/?callback=shield&from=antispam",
        "useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
        "clienttype": "1"
    }
}

function getDInfo(sessionId){
    return AESEncrypt(u(), sessionId)
}

// 测试样例
var sessionId = "a8b339ec0c26459598786fee1cce8dc2"
console.log(getDInfo(sessionId))

这段逻辑也可以用 Python 来实现,关键代码如下(脱敏处理,不能直接运行):

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-12-14
# @Author  : 微信公众号:K哥爬虫
# @FileName: ajk.py
# @Software: PyCharm
# ==================================


import json
import base64
import requests
from lxml import etree
from loguru import logger
from urllib.parse import quote_plus
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad


class AESAlgorithm:
    @staticmethod
    def encrypt(aes_key_iv, text):
        """ 对明文进行加密 """
        cipher = AES.new(key=bytes(aes_key_iv, encoding='utf-8'), mode=AES.MODE_CBC, iv=bytes(aes_key_iv, encoding='utf-8'))
        result = base64.b64encode(cipher.encrypt(pad(text.encode('utf-8'), 16))).decode('utf-8')
        result = quote_plus(result)
        return result

    @staticmethod
    def decrypt(aes_key_iv, text):
        """ 对密文进行解密 """
        cipher = AES.new(key=bytes(aes_key_iv, encoding='utf-8'), mode=AES.MODE_CBC, iv=bytes(aes_key_iv, encoding='utf-8'))
        result = unpad(cipher.decrypt(base64.b64decode(text)), 16).decode('utf-8')
        return result


class AJKSlide:
    def __init__(self, index_url, user_agent):
        self.aes = AESAlgorithm()
        self.index_url = index_url
        self.user_agent = user_agent
        self.headers = {"user-agent": self.user_agent}

    def get_session_id(self):
        """ 获取 sessionId """
        response = requests.get(url=self.index_url, headers=self.headers).text
        session_id = etree.HTML(response).xpath("//input[@name='sessionId']/@value")[0]
        logger.info(f"sessionId ==> {session_id}")
        return session_id

    @staticmethod
    def get_aes_key_iv(session_id):
        """设置 AES key 和 iv"""
        aes_key_iv = ''
        for index, value in enumerate(session_id):
            if index % 2 != 0:
                aes_key_iv += value
        logger.info(f"处理 sessionId 获取 aes key iv ==> {aes_key_iv}")
        return aes_key_iv

    def get_d_info(self, aes_key_iv):
        """获取 dInfo"""
        sdk_info = {
            "sdkv": "3.0.1",
            "busurl": self.index_url,
            "useragent": self.user_agent,
            "clienttype": 1
        }
        d_info = self.aes.encrypt(aes_key_iv, json.dumps(sdk_info))
        logger.info(f'dInfo ==> {d_info}')
        return d_info

    def run(self, session_id=None):
        if not session_id:
            session_id = self.get_session_id()
        aes_key_iv = self.get_aes_key_iv(session_id)
        self.get_d_info(aes_key_iv)


if __name__ == '__main__':
    UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"
    index_url_ = "https://www.脱敏处理.com/captcha-verify/?callback=shield&from=antispam"
    ajk_slide = AJKSlide(index_url_, UA)
    ajk_slide.run()

getInfoTp 解密

getInfoTp 这个接口返回的 info 的值是加密的,前面我们已经知道用到了 AES 加密算法,这里可以直接猜测也是用的的 AES 来解密的,找到 AESDecrypt 这个方法,下个断点,刷新发现断下之后传入了两个参数,第一个正是 info 的内容,第二个则是 sessionId

解密结果可以看到滑块的图片地址等信息:

data 生成

接下来就是 checkInfoTp 提交验证了,要搞清楚提交的 data 是什么东西,同样搜索打断点,如下图所示 _5DD 就是 data 值,传过来的。

往上跟栈,可以看到 _Ug0 里面有个 track 参数,这明显就是轨迹了,同样最后的结果经过了 AES 加密。

再往上跟,可以看到 _Ug0 由三个参数组成,x 是水平滑动的距离,track 是轨迹,p 是定值。

轨迹处理

轨迹生成前,得先识别缺口得到要滑动的距离,方式有很多,比如 OpenCV、开源的 ddddocr,或者直接打码平台都行,这里唯一要注意的一点就是图片是有缩放的,原始尺寸 480 × 270 px 渲染后的尺寸 280 × 158 px,比例大概是 1:0.5833333333333333,可以先将图片进行缩放后再识别,也可以先识别距离后再将距离进行缩放。

轨迹的处理,该站点校验并不太严格,所以可以自己写一下,关于滑块的轨迹处理,主要有缩放法、本地轨迹库、根据一些函数来生成轨迹,如缓动函数、贝塞尔曲线等,K哥以后再单独写一篇文章来介绍,本例中可以使用缩放法,先采集一条正常的,手动滑出来的轨迹,然后根据识别出的实际距离和样本轨迹中的距离相比,得到一个比值,然后将样本中的 x 值和时间值都做一个对应的缩放,生成新的轨迹,主要代码如下:

def generate_track(distance):
    """生成轨迹,样本距离为 126"""
    ratio = distance / 126
    new_track = ""
    base_track = "29,11,0|29,11,11|29,11,26|33,11,56|34,11,66|36,11,67|39,11,76|41,11,83|43,11,86|46,11,92|49,11,98|50,11,102|52,11,106|53,11,111|55,11,116|57,11,118|59,11,123|60,11,126|62,11,132|64,12,134|65,12,138|66,12,142|68,12,148|69,12,151|70,13,155|71,13,158|72,13,164|74,13,166|75,13,170|76,14,174|77,14,180|79,14,182|81,14,186|82,14,196|84,14,198|86,14,207|87,15,212|89,15,219|90,15,223|92,15,230|93,15,234|94,15,239|95,15,243|98,15,246|100,15,250|102,15,260|105,15,262|106,15,266|108,15,270|109,16,276|111,16,278|113,16,283|115,16,286|117,16,291|118,16,294|119,16,298|121,16,302|123,16,309|124,16,311|125,16,315|126,16,319|129,16,324|130,16,327|131,16,331|132,16,334|132,16,388|132,16,522|133,16,566|134,16,574|135,16,575|136,16,594|137,16,620|138,16,625|139,16,652|140,16,657|141,17,676|141,18,680|142,18,684|143,18,688|144,18,716|145,18,724|146,18,796|147,19,828|148,19,860|149,19,888|149,19,890|150,19,916|151,20,932|152,20,936|152,20,1021|153,20,1150|154,20,1152|155,20,1236|155,20,1388|155,20,1522|155,20,1717|"
    base_track = base_track.split("|")[:-1]
    for track in base_track:
        t = track.split(",")
        new_track += str(int(int(t[0]) * ratio)) + "," + str(t[1]) + "," + str(int(int(t[2]) * ratio)) + "|"
        logger.info(f"new_track ==> {new_track}")
        return new_track

结果验证

整个过程比较简单,验证成功。

相关推荐

前端入门——css 网格轨道详细介绍

上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...

HTML中script标签中的那些属性

HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...

CSS 中各种居中你真的玩明白了么

页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...

CSS样式更改——列表、表格和轮廓

上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...

一文吃透 CSS Flex 布局

原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...

css实现多行文本的展开收起

背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...

css 垂直居中的几种实现方式

前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...

WordPress固定链接设置

WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...

面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿

前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...

3种CSS清除浮动的方法

今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...

2025 年 CSS 终于要支持强大的自定义函数了?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...

css3属性(transform)的一个css3动画小应用

闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...

CSS基础知识(七)CSS背景

一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...

CSS 水平居中方式二

<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...

取消回复欢迎 发表评论: