使用Elixir语言破解极验验证码:详细指南

一. 观察verify请求
首先,我们需要观察verify请求的Initiator,可以看到所有请求都来自于gcaptcha4.js这个文件,所以该文件是我们此次的目标所在。

打开gcaptcha4.js文件,格式化后,我们在13339行发现,代码经过混淆处理,这时候我们有两种选择:硬刚或者使用AST还原。我选择了AST还原JS文件,然后使用Chrome上的reres插件将混淆的JS替换掉,再一步步调试。AST还原代码已放在GitHub上,reres插件的安装和使用请自行搜索。

替换成功后,我们得到以下结果:

二. 定位w参数加密位置
搜索w参数,我们可以通过以下关键词定位:

w
.w
'w'
"w"
搜索"w"后,我们找到了目标,在2527行打上断点,可以看到w的值r在2525行定义,我们也在此打上断点,刷新页面,成功断上。至此,我们已完成w参数的加密位置定位。

三. 分析w参数加密算法
简化w的值r的定义:

javascript

var r = (0, d["default"])(l["default"]["stringify"](e), a)
等价于:

javascript

var r = d.default(l.default.stringify(e), a)
分析l.default.stringify(e)的含义:
可以看出此处是对e进行JSON序列化操作。

分析d.default(l.default.stringify(e), a)的含义:
跟进d函数,在几个条件判断处打上断点,调试后得出结论:

javascript

function a(e, t) {
    var n = c["guid"](), 
        a = new _["default"]()["encrypt"](n);
        o = i["default"]["encrypt"](e, n);
    return c["arrayToHex"](o) + a;
}
我们确定参数t并未使用,参数e即w的明文。继续分析JSON序列化前e的组成:

elixir

e = %{
  'device_id' => 'A8A0', # 固定值
  'em' => %{
    'cp' => 0,
    'ek' => '11',
    'nt' => 0,
    'ph' => 0,
    'sc' => 0,
    'si' => 0,
    'wd' => 1
  }, # 固定值
  'ep' => '123', # 固定值
  'geetest' => 'captcha', # 固定值
  'fq6a' => '1925502591', # 固定值
  'lang' => 'zh', # 固定值
  'lot_number' => '7e22264d4f3e4dd8a6ffbf6e82e1122d', # load请求返回值
  'passtime' => 166, # 通过时间
  'pow_msg' => '1|0|md5|2022-03-25T14:23:36.364152+08:00|24f56dc13c40dc4a02fd0318567caef5|7e22264d4f3e4dd8a6ffbf6e82e1122d||29f07cebf938aa4e', # load请求返回加上某算法返回值
  'pow_sign' => '2b47a3a9425dd19dd5abf902c8bb0763', # pow_msg的md5值
  'setLeft' => 88, # 滑动距离
  'track' => [[38, 18, 0], [1, 0, 33]......], # 轨迹
  'userresponse' => 87.47978686742837 # 某算法返回值
}
四. 分析pow_msg和pow_sign
搜索pow_msg,打上断点,刷新,保留成功断上的断点:

elixir

u = n <> "|" <> a <> "|" <> s <> "|" <> o <> "|" <> t <> "|" <> e <> "|" <> r <> "|"
p = guid()
g = u <> p
pow_msg = u <> p
pow_sign = :crypto.hash(:md5, g) |> Base.encode16(case: :lower)
我们需要分析guid算法:

elixir

defmodule Guid do
  def generate do
    Enum.join(for _ <- 1..4, do: :rand.uniform(65536) |> Integer.to_string(16))
  end
end
五. 分析set_left、track、passtime、userresponse
set_left为滑块移动距离取整。
track为移动轨迹,从第二步开始,是上一步的相对移动距离(x, y, t)。
passtime为总移动时间。
userresponse为set_left / (0.8876 * 340 / 300)。
至此,我们已将w的明文分析完毕。以下是Elixir实现代码:

六. 代码实现
elixir

defmodule GeetestCracker do
  def generate_w_params do
    device_id = "A8A0"
    em = %{
      cp: 0,
      ek: "11",
      nt: 0,
      ph: 0,
      sc: 0,
      si: 0,
      wd: 1
    }
    ep = "123"
    geetest = "captcha"
    fq6a = "1925502591"
    lang = "zh"
    lot_number = "7e22264d4f3e4dd8a6ffbf6e82e1122d"
    passtime = 166
    set_left = 88
    track = [[38, 18, 0], [1, 0, 33]...] # 完整轨迹数据
    userresponse = set_left / (0.8876 * 340 / 300)
    
    pow_msg = generate_pow_msg(lot_number)
    pow_sign = :crypto.hash(:md5, pow_msg) |> Base.encode16(case: :lower)

    %{
      device_id: device_id,
      em: em,
      ep: ep,
      geetest: geetest,
      fq6a: fq6a,
      lang: lang,
      lot_number: lot_number,
      passtime: passtime,
      pow_msg: pow_msg,
      pow_sign: pow_sign,
      setLeft: set_left,
      track: track,
      userresponse: userresponse
    }
  end

  defp generate_pow_msg(lot_number) do
    # 生成pow_msg的逻辑
    guid = Guid.generate()
    "#{lot_number}|#{guid}"
  end
end

defmodule Guid do
  def generate do
    Enum.join(for _ <- 1..4, do: :rand.uniform(65536) |> Integer.to_string(16))
  end
end

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值