吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8318|回复: 130
收起左侧

[Web逆向] 顶象滑块逆向分析——背景图还原分析

    [复制链接]
timeslover 发表于 2022-7-24 17:29

本章节内容,是对顶象滑块背景图的还原算法分析。
官网demo地址

https://www.dingxiang-inc.com/business/captcha

你可以用官网提供的demo去分析,也可以直接注册一个顶象账号,然后放到本地调用调试。
我是自己注册了一个账号,调用官方SDK做的测试,具体细节可自行查看文档相关SDK,分析代码是通用的,好处是可以防止一些其他东西的干扰,方便调试分析

环境准备

在这里插入图片描述
后台配置是这样噻的
在这里插入图片描述

在这里插入图片描述

在本地起一个服务,成功之后就是这个样子

在这里插入图片描述

开始分析

可以看到,背景图是做了乱序处理的

在这里插入图片描述
通过请求堆栈,这里是要去查看第二张请求完的背景图图片的请求堆栈,不要去看第一张的,直接定位到最后一个调用堆栈
在这里插入图片描述

格式化之后,在此处下断点,可以看到,这是一个赋值操作,断点上面有几个需要注意的可疑点,这里提一嘴,像这种图片还原操作,目前正向开发99%绝壁是会用到canvas去操作。

在这里插入图片描述

开启Fiddler文件替换调试
替换之后可能会出现跨域问题,这里的建议是自己制作一个跨域浏览器,简单方便,无视跨域问题,一劳永逸,这种问题就不要花太久时间折腾。

在basic-Captcha-js.js文件中:

在这里插入图片描述

在p.toDataURL()这行代码,这是在获取canvas的base64的内容,我们输出看一下

在这里插入图片描述

再将base64内容显示一下,可以看到,这是他还原之后的内容,那继续往回跟堆栈

在这里插入图片描述
我们先看一下他的上下文代码,这一块全是逗号表达式调用,而且关键字很多,这里可以手动还原一下

在这里插入图片描述

简化之后的代码,这样看他的逻辑就清晰很多了

n.exports = function (n, e, r, b, x, m, A) {
      var j = "ous",
        _ = "8";
      return new l(function (l, m) {
        var C = "}";
        var  y = "l";
        var w = new Image();
        var S = d("_r_") + Math.floor(1e10 * Math.random());
        (window[S] = w),
          w.setAttribute("crossOrigin", "Anonymous"),
          g("begin to load img"),
          g(b),
          (w.onload = function () {
            var d = w.width;
            var u = w.height;
            try {
              if (A)
                (n.innerHTML = ""),
                  n.appendChild(w),
                  w.setAttribute("name", "piece-complete");
              else {
                n.innerHTML = (function (n, e) {
                  return (
                    '<canvas width="' + n + '" height="' + e + '"></canvas>'
                  );
                })(d, u);
                var p = n.getElementsByTagName("canvas")[0];
                !(function (n, e, r, i, o) {
                  var a = n.getContext("2d");
                  a.drawImage(e, 0, 0, r, i);
                  var c = Math["floor"](r / o.length);
                  h(o, function (n, r) {
                    for (var t = [2, 0, 1], o = 0; ; ) {
                      switch (t[o++]) {
                        case 0:
                          var s = c;
                          continue;
                        case 1:
                          a.drawImage(e, d, 0, s, i, r * c, 0, s, i);
                          continue;
                        case 2:
                          var d = n * c;
                          continue;
                      }
                      break;
                    }
                  });
                })(p, w, d, u, x),
                  (p.style.width = e + "px"),
                  (p.style.height = r + "px"),
                  g("canvas element"),
                  g(p),
                  g("canvas data"),
                  _dx.inSDK && g(p.toDataURL());
                  console.log(p.toDataURL())
                  (window[S] = null);
                  delete window[S];
              }
              l({ w: d, h: u });
            } catch (v) {}
          }),
          (w.onerror = function (n) {
            m("img_load_error");
          }),
          p(b) || (b = b + "&_r=" + Math.random()),
          (w.src = b),
          A &&
            ((w.style.width = e + v(["70,7", _].join(""))),
            (w.style.height = r + v("70,78")));
      });
    };

这有一个地方需要说明一下,官网demo和通过SDK调用的代码,会有一些区别,在上面代码中
!(function (n, e, r, i, o) 这个匿名函数,在官网demo中会是一个 h 函数,或者其他名字,他的内容是下面这样的

在这里插入图片描述
在这里插入图片描述
回到正题,我们稍微瞟一眼这个代码,了解下他大概在干什么:

  1. var w = new Image(); // 实例化一个图片对象;
    1. w.onload,图片加载完毕之后,创建canvas标签;
    2. 然后执行一个匿名函数!(function (n, e, r, i, o);
    3. 在匿名函数里面又有画图方法a.drawImage,对canvas进行画图;
    4. 最后执行其他操作,然后结束

了解了这些之后,我们看一下匿名函数里面的参数,输出一下这几个参数,可以看到,这个参数o是一个数组,可以基本判定他就是图片还原的路径列表
在这里插入图片描述

我们知道了这个,还要找出一个东西,路径是怎么生成的??

继续往回找,他是由这个x传进来的
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

到此,我们基本上把背景还原算法需要的东西都整理出来了。

背景还原路径算法

下面是图片URL还原路径的算法:

function En (n, e, r) {
  if (!r) return []
  var t
  var i
  var o = { o: undefined }
  return o.o
    ? _n(o.o)
    : _n(
        e
          ? ((t = 'len'), (i = (i = r.split('/'))[i.length - 1]).split('.')[0])
          : Cn(r)
      )
}

function r (n, e) {
  if (n.includes) return n.includes(e)
  for (var r = 0, t = n.length; r < t; r++) if (n[r] === e) return !0
  return !1
}

function _n (n) {
  for (var e, t = [], i = 0; i < n.length; i++) {
    var o = n.charCodeAt(i)
    if (32 === i) break
    for (; r(t, o % 32); ) o++
    t[
      ((e = 'hsup'),
      e
        .split('')
        .reverse()
        .join(''))
    ](o % 32)
  }
  return t
}

function Cn (n) {
  var e
  if (!n) return ''
  e = n.split('?')[1][['spl', 'it'].join('')]('&')
  var r = [0, 0]
  return (
    t(e, function (n) {
      var e = n.split('=')
      n && 'c' === e[0] && e[1] && 'null' !== e[1]
        ? (r = [e[1]])
        : ('aid' === e[0] && (r[1] = e[1]), 'sid' === e[0] && (r[0] = e[1]))
    }),
    r.join('')
  )
}

// 这里的图片链接会失效,请拿最新的链接去测试
let urlPATH = 'http://static.dingxiang-inc.com/picture/dx/79bBXvCGvU/zib3/4ac41ecfd57b44c1b62fa07c9c843ed4.webp'
let pathList = En({}, true, urlPATH)
console.log(pathList)

验证一下,路径列表一致

在这里插入图片描述

背景图片还原

拿到路径列表算法之后,我们再去还原图片,部分算法如下,在上面n.exports = function (n, e, r, b, x, m, A) 的基础上做了一些删改,将不需要的代码简化了,另外做了函数封装处理,完整部分在这个链接

https://gitcode.net/weixin_45307278/dxslider_test.git

function getImageBase64 (urlPATH) {
  function En (n, e, r) {
    if (!r) return []
    var t
    var i
    var o = { o: undefined }
    return o.o
      ? _n(o.o)
      : _n(
          e
            ? ((t = 'len'),
              (i = (i = r.split('/'))[i.length - 1]).split('.')[0])
            : Cn(r)
        )
  }

  function r (n, e) {
    if (n.includes) return n.includes(e)
    for (var r = 0, t = n.length; r < t; r++) if (n[r] === e) return !0
    return !1
  }

  function _n (n) {
    for (var e, t = [], i = 0; i < n.length; i++) {
      var o = n.charCodeAt(i)
      if (32 === i) break
      for (; r(t, o % 32); ) o++
      t[
        ((e = 'hsup'),
        e
          .split('')
          .reverse()
          .join(''))
      ](o % 32)
    }
    return t
  }

  function Cn (n) {
    var e
    if (!n) return ''
    e = n.split('?')[1][['spl', 'it'].join('')]('&')
    var r = [0, 0]
    return (
      t(e, function (n) {
        var e = n.split('=')
        n && 'c' === e[0] && e[1] && 'null' !== e[1]
          ? (r = [e[1]])
          : ('aid' === e[0] && (r[1] = e[1]), 'sid' === e[0] && (r[0] = e[1]))
      }),
      r.join('')
    )
  }
  var pathList = En({}, true, urlPATH)
  console.log(pathList)

  let w = new Image()
  w.setAttribute('crossOrigin', 'Anonymous')
  w.src = urlPATH
  w.onload = function () {
    var d = w.width
    var u = w.height
    let el = document.getElementsByTagName('body')[0]
    el.innerHTML = '<canvas id="canvas"></canvas>'
    let p = document.getElementById('canvas')
    !(function (n, e, r, i, o) {
      var a = n.getContext('2d')
      a.drawImage(e, 0, 0, r, i)
      var c = Math.floor(r / o.length)
      win_h(o, function (n, r) {
        for (var t = [2, 0, 1], o = 0; ; ) {
          switch (t[o++]) {
            case 0:
              var s = c
              continue
            case 1:
              a.drawImage(e, d, 0, s, i, r * c, 0, s, i)
              continue
            case 2:
              var d = n * c
              continue
          }
          break
        }
      })
    })(p, w, d, u, pathList)
    console.log(p.toDataURL())
  }
}

// 这里的图片链接会失效,请拿最新的链接去测试
var urlPATH = 'http://static.dingxiang-inc.com/picture/dx/79bBXvCGvU/zib3/4a64f9302585450da279c527c161a35b.webp'
getImageBase64(urlPATH)

输出验证结果如下

在这里插入图片描述

下面是我自己写的识别算法,你也可以使用其他库去识别,这里用的是opencv的模型匹配,识别成功率在92%左右,代码我放到了上面的仓库链接中,使用时记得 pip install opencv-python

import cv2
import time

# 顶象滑块识别
def dXImgSlider(origin, sliderImg):
    slider = cv2.imread(sliderImg)
    originImg = cv2.imread(origin)
    bgImg = cv2.cvtColor(originImg, cv2.COLOR_BGR2GRAY)
    sliderImg = cv2.cvtColor(slider, cv2.COLOR_BGR2GRAY)
    # 反相
    frame_gray_c = sliderImg.copy()
    height, width = frame_gray_c.shape
    for i in range(height):
        for j in range(width):
            pv = frame_gray_c[i, j]
            frame_gray_c[i][j] = 255 - pv

    # 高斯滤波
    imgGaussianBlur1 = cv2.GaussianBlur(frame_gray_c, (3, 3), 0)
    imgGaussianBlur2 = cv2.GaussianBlur(bgImg, (7, 7), 0)

    # 获取模板图像的高和宽
    th, tw = sliderImg.shape[:2]
    # 使用标准相关系数匹配,1表示完美匹配,-1表示糟糕的匹配,0表示没有任何相关性
    result = cv2.matchTemplate(imgGaussianBlur2, imgGaussianBlur1, cv2.TM_CCOEFF_NORMED)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    tl = max_loc
    br = (tl[0] + tw, tl[1] + th)

    # 绘制识别框
    im = cv2.rectangle(originImg, tl, br, (0, 0, 255), 2)
    t = time.time() - 60 * 60 * 24 * 30
    time_string = time.strftime("%Y%m%d%H%M%S", time.localtime(t))
    cv2.imwrite("result" + time_string + ".jpg", im)
    resObj = [tl[0], tl[1], br[0], br[1]]
    print(resObj)
    return resObj

if __name__ == '__main__':
# 第一个参数背景图,第二个参数滑块图
dXImgSlider('./dingxiang/bg1.png', './dingxiang/icon1.png')

以上就是图片还原的算法分析,学习使用

免费评分

参与人数 38吾爱币 +41 热心值 +34 收起 理由
wangkaixuan0122 + 1 + 1 我很赞同!
fbl119 + 1 + 1 我很赞同!
橙木登 + 1 用心讨论,共获提升!
QiuChenly + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
xzhtx + 1 + 1 谢谢@Thanks!
emptynullnill + 1 + 1 用心讨论,共获提升!
liu1392010 + 1 + 1 已经处理,感谢您对吾爱破解论坛的支持!
hiplease + 1 + 1 热心回复!
shuaiqi + 1 + 1 谢谢@Thanks!
0x10 + 1 + 1 谢谢@Thanks!
zuimenr1918 + 1 + 1 向大佬致敬
杨辣子 + 1 + 1 用心讨论,共获提升!
腰围两尺99 + 1 + 1 我很赞同!
肉蛋葱鸡 + 1 + 1 好像底图每次都是变动的
sdsgyr + 1 我很赞同!
Nicklobin + 1 用心讨论,共获提升!
warehouse + 1 + 1 用心讨论,共获提升!
笨蛋蛋 + 1 + 1 用心讨论,共获提升!
DBsan + 1 + 1 大佬厉害!学习思路
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
不爱everyone + 1 用心讨论,共获提升!
liqing0305 + 1 + 1 谢谢@Thanks!
努力加载中 + 1 + 1 热心回复!
503671998 + 1 + 1 谢谢@Thanks!
lebon + 1 + 1 请收下我的膝盖!
笙若 + 1 + 1 谢谢@Thanks!
time2s + 1 + 1 我很赞同!
xu167 + 1 谢谢@Thanks!
红烧排骨 + 1 热心回复!
笔墨纸砚 + 3 + 1 用心讨论,共获提升!
1MajorTom1 + 1 热心回复!
gaosld + 1 + 1 谢谢@Thanks!
萌哒哒小乐天 + 1 用心讨论,共获提升!
神枪泡泡丶 + 2 + 1 用心讨论,共获提升!
舒默哦 + 2 + 1 谢谢@Thanks!
WuJ1n9 + 2 + 1 我很赞同!
coolcat + 1 + 1 用心讨论,共获提升!
唐小样儿 + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| timeslover 发表于 2022-10-10 17:04
overlords 发表于 2022-10-10 15:29
大佬,能出一篇腾讯验证码的吗

不是很想发
萌新与小白 发表于 2022-7-26 22:56
timeslover 发表于 2022-7-25 22:47
也是可以,不过走打码或者自己实现识别算法做成服务是最好的

1.请问纯js逆向的流程是怎样的?比如针对点选验证码和滑块验证码(有缺口),是不是先找其他加密参数的生成方式,再手动识别缺口坐标,再模拟生成滑动轨迹,再用人家的加密方式对轨迹加密,最后将参数传出去?是的话,那点选的验证码的流程呢?
2.我没用过打码平台,它们返回什么数据啊?坐标?是坐标的话我接下来该怎么办?
hjw01 发表于 2022-7-25 12:04
阿政0506 发表于 2022-7-25 12:23
鸭子头上站青蛙,属实顶呱呱
bfczdxt 发表于 2022-7-25 13:38
学习了 谢谢
imGz 发表于 2022-7-25 13:54
感谢分享
LuHaHa627 发表于 2022-7-25 14:34
向大佬膜拜
cheese0723 发表于 2022-7-25 15:10
感谢楼主分享!
coolcat 发表于 2022-7-25 15:22
学习到了,膜拜
actionnoo 发表于 2022-7-25 15:45
谢谢分享
约定的童话 发表于 2022-7-25 16:07
这个属实厉害,膜拜
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-25 12:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表