js逆向案例四-滑块验证码

案例一二三

四、验证码的逆向(⭐⭐⭐)

1、案例11_普通的滑块验证码
  • (1)案例网址:点击网址
    在这里插入图片描述
  • (2)案例反爬点:验证码缺口的识别,以及识别偏移量的js加密,cookie反爬
    在这里插入图片描述
  • (3)案例分析:cookie只需要首次请求时把set-cookie拿到即可,slidevalue是滑块缺口加密,由于校验图片时是xhr请求,所以我们这里直接通过监听xhr断点,然后追溯堆栈找加密位置 在这里插入图片描述
    • 通过堆栈很快找到加密位置,和encode64()这个js函数相关,加密参数就是滑块距离,至此js逆向已完成,接下来我们只要识别滑块缺口就行
      在这里插入图片描述
2、案例插曲_滑块验证码识别3种方法
  • 滑块识别分析的3种方法:
    • 滑块缺口是单一颜色用连续rgb像素点判断
    • 通过模板匹配cv2.matchTemplate()来确定缺口位置
    • 带有缺口的背景图和原始图进行对比
(1)滑块缺口颜色为单一颜色时用rgb判断
  • 滑块缺口是单一的颜色,此处的白色rgb为固定值,所以我们只需要判断当前像素点连续39个点和对角线连续39个点的rgb为固定值,选择39个像素点是因为滑块长宽差不多是40
  • 此处rgb固定值白色[245, 250, 255],即可定位到缺口x的位置 (凸出部分朝向会影响实际x的识别距离);当滑块凸出部分朝向左的时候,实际的缺口距离应当减去凸出来的部分,此处缺口应该是x-10
    在这里插入图片描述 在这里插入图片描述

  • 当然有的其它滑块,初始距离可能有个6~10的间隙,应当具体案例具体分析,此处初始距离为0,如果有间隙,我们识别出来的距离还应当减去初始间隙,如某验滑块
    在这里插入图片描述

  • 滑块识别代码截图
    • 使用PIL.Image.open读取出来是RGB格式
    • 通过np.array(slider_image)以3通道按行展开读取rgb数组
    • 通过np.all()判断给定轴向上的所有元素是否都为True,如是否都为固定的rgb值
    • bulge_offset为凸出的宽度, 默认凸出只有上,左,下 3个方向,如果有朝向为左,实际距离应当减去凸出部分
      在这里插入图片描述
  • 滑块识别代码截图,通过灰度后的判断,rgb不完全统一,但是灰度后的单一通道大于一个值
    在这里插入图片描述
    def get_slider_offset_method1(pic_content, fixed_rgb_l=250):
        """
        返回缺口距离
        :param pic_content: 滑块
        :param fixed_rgb_l: 单一固定的rgb,转灰度后只有一个通道,需要观察,此种缺口非单一固定rgb,但是rgb转灰度后都大于250
        :return:
        """
        slider_image = Image.open(BytesIO(pic_content))
        slider_image = slider_image.convert('L')
        array_img = np.array(slider_image)
        height, width = array_img.shape
        for y in range(height - 40):  # 从上往下
            for x in range(40, width - 40):  # 从左往右
                if np.all(array_img[y:y + 1, x:x + 39] >= fixed_rgb_l):  # 横向的连续39个元素点的rgb值大于250
                    list_side = [array_img[y, x]]
                    for side in range(1, 39):
                        list_side.append(array_img[y + side][x + side])
                    return x
        return 0
    
(2)滑块缺口颜色非单一值用模板匹配
  • 当缺口部分颜色非单一值时,我们可以用cv2.matchTemplate()模板匹配方法来进行缺口识别,模板匹配的意思就是拿小图到大图里面去寻找和它差不多的地方,然后确定缺口
    在这里插入图片描述 在这里插入图片描述

  • 通过cv2.matchTemplate()最后的效果如下,cv2.matchTemplata(img_big,img_temp,cv2.method): img_big:在该图上查找图像, img_temp:待查找的图像,模板图像, method: 模板匹配的方法
    在这里插入图片描述 在这里插入图片描述

  • 滑块识别代码截图

    • 彩色图像是三通道(RGB),也就是Red,Green,Blue 三个通道

    • 灰度图是单通道,单通道中的每个像素的值介于0~255之间,代表黑色(0)到白色(255)之间的灰度程度

    • 黑白图像指的是二值图,每个像素非黑即白,黑是0,白是1,通常会用灰度图来表示二值图,黑是0,白是255

    • 使用cv2.imread(path, flag):读进来直接是BGR格式,flag=0(8位深度,1通道,灰度图),flag不填默认3通道

    • 使用cv2.cvtColor(slider_img, cv2.COLOR_BGR2GRAY):是颜色空间转换函数,slider_img是需要转换的图片,cv2.COLOR_BGR2GRAY转成灰度格式

    • cv2.Canny(img, minVal, maxVal, apertureSize=3):第一个参数是原图像,第二个和第三个参数分别是两个阈值,minVal 和 maxVal,来确定真实和潜在的边缘,apertureSize是sobel算子(卷积核)大小;调整minVal,检测出的边缘会增多,扩大apertureSize算子,会获得更多的细节参考文档理解

    • 模板匹配cv2.matchTemplate(background_pic, slider_pic, cv2.TM_CCOEFF_NORMED):此处通过归一化相关系数法进行匹配
      在这里插入图片描述

(3)滑块带有缺口的图与原始图进行比较
  • 如果你同时有带缺口的图片,和不带缺口的背景图,此时可以通过判断两张图片的像素点rgb之差,设定一个阈值差,如果在阈值差内认为图片像素点一致,如果超过这个阈值差则认为是缺口位置
    在这里插入图片描述 在这里插入图片描述
  • 具体代码实现:这里设置了阈值60为限制来判断缺口点,具体案例具体操作
    from PIL import Image
    
    
    def get_slider_offset_method3(pic_path, cut_pic_path, threshold=60):
        """比较两张图片的像素点RGB的绝对值是否小于阈值60,如果在阈值内则相同,反之不同"""
        pic_img = Image.open(pic_path)
        cut_img = Image.open(cut_pic_path)
        width, height = pic_img.size
        for x in range(40, width - 40):  # 从左往右
            for y in range(5, height - 10):  # 从上往下
                pixel1 = pic_img.load()[x, y]
                pixel2 = cut_img.load()[x, y]
                if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(pixel1[2] - pixel2[2]) < threshold:
                    continue
                else:
                    return x
    
3、案例12_jy的滑块验证码
  • 案例网址 :aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vc2hvdw==
(1)大概的流程了解
  • 一般是两次get+ajax,第一次get+ajax获得challenge使其有效,第二次get+ajax即获取验证码与校验验证码
  • 每个应用网站都会对应一个唯一的id值即gt,在获取验证码之前会初始化并校验获得第一个有效challenge,携带有效的challenge后可以获得验证码
  • 验证码校验的过程会涉及到w参数,w参数逆向是关键,校验通过则返回validate值
  • 整个流程主要涉及如下4个请求包,具体网站具体分析
    在这里插入图片描述
  • 第一个链接初始化获得challenge,gt值
    在这里插入图片描述
  • 第二个链接ajax.php校验challenge,此时出现一次w值(可置空,可固定,可能要js生成),具体网站具体分析,本次案例此处w值不研究,直接置空
    在这里插入图片描述
  • 第三个链接get.php携带有效challenge请求获得图片链接,该图片是个乱序图片,还需要还原;如果有返回challenge则取代前面的第一个challenge,同时请求图片的响应会返回c和s参数,在后面研究w参数加密会用到这两个
    在这里插入图片描述
    在这里插入图片描述
  • 第四个链接ajax.php校验图片,携带challenge,w值,w值的加密会涉及到轨迹信息,轨迹是一难点,随意生成的轨迹并不可用,需尽量趋向人为的轨迹曲线,以及前面的c和s参数校验通过则返回validate
    • 距离识别错误返回{“success”: 0, “message”: “fail”}
    • 距离正确但轨迹异常则返回{“success”: 0, “message”: “forbidden”}
    • 校验通过则返回{success: 1, message: “success”, validate: “f98af504ec3208dc19911b0de0b083c7”, score: “3”}
      在这里插入图片描述
      在这里插入图片描述
(2)乱序图片的还原逆向
  • 从网页拿到的图片是乱序的,还原后如下
    在这里插入图片描述 >>>>>>>>在这里插入图片描述

  • 逆向分析:由于验证码图片是通过Canvas画布画出来的,所以我们这里直接通过监听Canvas下断点,然后就可以调试即可发现还原乱序代码的关键部分

    • getImageData(x,y,width,height):开始复制的左上角位置的 x 坐标、左上角位置的 y 坐标,width要复制的矩形区域的宽度、height要复制的矩形区域的高度
    • putImageData(imgData,x,y,dirtyX):将图像数据(从指定的 ImageData 对象)放回画布上,左上角位置的 x 坐标、左上角位置的 y 坐标,
    • 原图是260×160的图片,由乱序的2×26份的10×80小图片拼接而成,根据数组依次判断去乱序的图片里面去取这52份小图片然后依次拼接则形成原始图片
      在这里插入图片描述
  • 还原代码:按照如上还原逻辑,代码如下

    • Image.new(mode, (width, height)):创建新图片
    • image.crop(x1, y1, x2, y2):左上角点到右下角点进行切割图片
    • image.paste(im, box):box为要粘贴到的区域,box类型为(x1, y1):将im左上角对齐(x1,y1)点,其余部分粘贴,超出部分抛弃
      from PIL import Image
      
      image = Image.open('./luanxu.png')
      standard_img = Image.new("RGBA", (260, 160))
      position = [39, 38, 48, 49, 41, 40, 46, 47, 35, 34, 50, 51, 33, 32, 28, 29, 27, 26, 36, 37, 31, 30, 44, 45, 43, 42, 12, 13, 23, 22, 14, 15, 21, 20, 8, 9, 25, 24, 6, 7, 3, 2, 0, 1, 11, 10, 4, 5, 19, 18, 16, 17]
      s, u = 80, 10
      for c in range(52):
          a = position[c] % 26 * 12 + 1
          b = s if position[c] > 25 else 0
          im = image.crop(box=(a, b, a + 10, b + 80))
          standard_img.paste(im, box=(c % 26 * 10, 80 if c > 25 else 0))
      standard_img.save("./restore_bg.png")
      
(3)案例分析思路
  • 目标研究参数,第4个链接的w参数,即校验图片的那个w参数,主要会涉及rsa+aes加密,aes的key参数是随机生成的,会通过rsa加密传给服务器
    在这里插入图片描述

  • 确定了目标研究参数w参数,可以采用调用栈回溯的方法,这里由于js文件做了一个unicode混淆字符串加密,所以我们直接通过搜索w的unicode码"\u0077":来寻找加密位置并打断点, w=_+s,所以只要研究s和_即可
    在这里插入图片描述

  • 我们先研究s参数,打入断点进入psgR这个函数
    在这里插入图片描述

  • s参数:发现最后返回的是r参数,而这个r参数的加密方式本质是个rsa加密,rsa加密需要两个参数:公钥+待加密文本,那只需要调试进去找到公钥即可(此处省略,自行调试)
    在这里插入图片描述

  • s参数:那待加密文本即aes_key是怎么生成的,我们可以进入t[MIDa(753)]这个函数逐步看一下,一下就发现了加密逻辑,就是4个S4()函数组成
    在这里插入图片描述

  • s参数:用python还原rsa加密,传入公钥和待加密文本aes_key即可

    import rsa
    from binascii import b2a_hex
    
    
    def rsa_encrypt_text(key, _text: str):
        """
        RSA加密
        :param key: 公钥的参数
        :param _text: 待加密的明文
        :return: 加密后的数据
        """
        e = int('010001', 16)
        n = int(key, 16)
        pub_key = rsa.PublicKey(e=e, n=n)
        return b2a_hex(rsa.encrypt(_text.encode(), pub_key)).decode()
    
  • _参数:_参数和u参数有关,先看u参数,这个u参数分解下,传入了带加密文本o参数(传入前进行了字典转字符串操作即JSON.stringify())和前面随机生成的aes_key,其中这个o参数我们后面再分析
    在这里插入图片描述

  • _参数的u参数:深入u参数加密函数ee[rndd(365)]再具体分析,对aes的加密结果进行了一个转数组的操作
    在这里插入图片描述
    在这里插入图片描述

  • _参数的u参数:继续深入看aes加密是CBC加密模式,所以只要3个参数:key,iv,待加密文本,key就是前面所说的aes_key,待加密文本是传进来的(后面具体分析),iv参数调试找到为"0000000000000000",js代码实现,那u参数已研究完
    在这里插入图片描述

  • _参数:是对前面的u参数又做了一层加密,这个加密也比较好扣,省略
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • w参数:w=_+s,前面_和s参数合成即可,至此w参数加密已完成研究,细节部分继续往后看
    在这里插入图片描述

  • w参数>_参数>u参数>o参数分析:可变参数分析,我们着重看下aa参数和userresponse参数

    • aa:对轨迹和响应的c和s参数进行了加密
    • ep:涉及v和tm,v是js文件的版本,tm和window[“performance”][“timing”]相关,可以根据Performance.timing 时间产生的先后顺序以及时间间隔,用当前的时间戳减去相应的值来模拟
    • imgload:图片加载时间,随机生成
    • passtime:轨迹滑动总时长
    • rp:对gt、challenge、passtime进行了md5加密
    • userresponse:对滑块距离和challenge进行了加密
    • its5:随机参数影响不大
      在这里插入图片描述
      在这里插入图片描述
  • aa参数:aa参数unicode码"\u0061\u0061",我们发现是上一个函数传过来的,也就是现在的t值
    在这里插入图片描述
    • aa参数:往前回溯看上一个函数,发现对应的是f值,也就是aa现在就是f值,只要看f值是怎么生成的,此处打断点,重新调试,可见f是一个函数的返回结果,传入的三个参数分别对应着:轨迹加密、响应的c、s参数
      在这里插入图片描述
      在这里插入图片描述
    • aa参数r[LsRU(1063)][LsRU(1047)]()就是对轨迹加密的,其中r[LsRU(1063)]就是传入的数组轨迹,进入SOQd就可以看具体的轨迹加密逻辑,扣完SOQd()这个函数,轨迹加密就有了
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
    • aa参数的轨迹: 轨迹生成传入,此处需注意你的轨迹是叠加传入的值,还是已经做了减法的值,这涉及到你要不要再加个e函数进行差减;轨迹生成可网上自行查找
    • 如我传的xyt_list是这种格式(x和t已经做了差):[[28,34,0],[1,0,45],[3,0,63],[4,-1,8],[4,0,9],[3,0,7],[4,0,9]…
    • 而非这种格式(x和t都是累加的):[[-31,-19,0],[0,0,0],[1,0,36],[2,0,44],[2,0,52],[4,0,61],[6,0,69],[8,0,77],[14,0,85],[17,0,92],[21,1,99],[25,2,106],…
      在这里插入图片描述
  • userresponse参数:U函数加密结果返回而得,传入滑块距离和challenge,去掉冗余代码,U函数可以直接扣出来
    在这里插入图片描述
  • rp参数:对gt、challenge、passtime进行了md5加密
    在这里插入图片描述
  • 最终效果
    在这里插入图片描述
  • 19
    点赞
  • 93
    收藏
    觉得还不错? 一键收藏
  • 23
    评论
嗨!对于爬虫中的 JavaScript 逆向案例,我可以给你提供一个简单的示例。请注意,这个示例只是为了帮助你理解逆向过程,真实的应用中可能涉及法律和伦理问题,请合法使用爬虫技术。 假设我们要爬取一个网站上的某个页面数据,但是该页面通过 JavaScript 动态生成。我们可以通过分析网页的 JavaScript 代码来逆向工程,获取所需数据。 首先,打开 Chrome 浏览器并进入开发者工具(按 F12 键或右键点击页面并选择“检查”)。然后切换到“网络”选项卡。 接下来,在浏览器地址栏中输入目标网页的 URL 并按下回车,浏览器将开始加载页面。在网络选项卡中,你将看到所有请求和响应的列表。 查找其中一个请求,该请求可能包含我们所需的数据。点击该请求并查看其请求头、响应头和响应体。 在响应体中,你可能会看到一些 JavaScript 代码,这些代码负责生成页面上的内容。你可以仔细阅读该代码,并找到生成目标数据的部分。 如果你发现目标数据是通过 Ajax 请求获取的,你可以查看该 Ajax 请求的 URL 和参数,然后使用 Python 的 requests 库或其他适当的方法模拟该请求,并解析响应获取数据。 如果你发现目标数据是在 JavaScript 代码中直接生成的,你可以尝试分析代码逻辑并编写相应的 Python 代码来模拟该过程。这可能涉及到使用 JavaScript 解释器或库来执行 JavaScript 代码。 需要注意的是,JavaScript 逆向工程是一项复杂的任务,需要对 JavaScript 和网络协议有一定的理解。同时,网站所有者可能会采取一些反爬虫措施来阻止你的行为,所以请务必遵守法律法规和网站的使用规则。 希望这个示例能够帮助你入门 JavaScript 逆向工程。如果你有任何其他问题,欢迎继续提问!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值