在本文中,我们将介绍如何使用Elixir来逆向工程gcaptcha4.js文件,找到并破解w参数的加密算法。整个过程包括观察verify请求,定位加密位置,分析加密算法,并最终还原w参数的明文。
一. 观察verify请求
首先,我们需要观察verify请求的发起者。通过网络请求分析工具,我们可以看到所有verify请求都来自于gcaptcha4.js文件。因此,这个文件成为我们分析的重点。
我们使用Elixir的HTTPoison库来捕获和分析这些请求。
elixir
defmodule HttpClient do
use HTTPoison.Base
def fetch(url) do
case get(url) do
{:ok, response} -> IO.puts(response.body)
{:error, reason} -> IO.puts("Error: #{reason}")
end
end
end
HttpClient.fetch("https://example.com/verify")
通过分析响应内容,可以发现所有的verify请求都指向了gcaptcha4.js文件。
二. 定位w参数加密位置
为了找到w参数的加密位置,我们需要解析gcaptcha4.js文件。我们可以下载并格式化这个文件,然后使用正则表达式搜索关键词:w、.w、'w'或"w"。
elixir
defmodule FileSearch do
def search(file_path) do
{:ok, content} = File.read(file_path)
regex = ~r/[.\'"]w[.\'"]/
for match <- Regex.scan(regex, content), do: IO.puts(Enum.at(match, 0))
end
end
FileSearch.search("gcaptcha4.js")
通过搜索"w",我们找到了相关代码。在第2527行,我们发现了w的值r在第2525行被定义。
三. 分析w参数加密算法
接下来,我们需要简化代码中w的定义。假设我们已经提取了相关的JavaScript代码,我们可以使用Elixir来模拟这个过程。
elixir
defmodule JsonProcessor do
def to_json(map) do
Jason.encode!(map)
end
def md5(string) do
:crypto.hash(:md5, string) |> Base.encode16(case: :lower)
end
end
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",
passtime: 166,
pow_msg: "1|0|md5|2022-03-25T14:23:36.364152+08:00|24f56dc13c40dc4a02fd0318567caef5|7e22264d4f3e4dd8a6ffbf6e82e1122d||29f07cebf938aa4e",
pow_sign: "2b47a3a9425dd19dd5abf902c8bb0763",
setLeft: 88,
track: [[38, 18, 0], [1, 0, 33]],
userresponse: 87.47978686742837
}
a = "your_key"
r = JsonProcessor.md5(JsonProcessor.to_json(e) <> a)
IO.puts(r)
4.1 分析pow_msg和pow_sign
通过搜索pow_msg,我们找到相关代码并解析如下:
elixir
defmodule PowMsgProcessor do
def generate_guid do
for _ <- 1..4, into: "", do: Integer.to_string(:rand.uniform(65536), 16)
end
def md5(string) do
:crypto.hash(:md5, string) |> Base.encode16(case: :lower)
end
end
n = "example_n"
a = "example_a"
s = "example_s"
o = "example_o"
t = "example_t"
e = "example_e"
r = "example_r"
u = "#{n}|#{a}|#{s}|#{o}|#{t}|#{e}|#{r}|"
p = PowMsgProcessor.generate_guid()
g = u <> p
pow_msg = u <> p
pow_sign = PowMsgProcessor.md5(g)
IO.puts("pow_msg: #{pow_msg}")
IO.puts("pow_sign: #{pow_sign}")
4.2 分析set_left、track、passtime、userresponse
set_left:滑块移动距离的整数值
track:移动轨迹,从第二步开始,是相对上一步的相对移动距离(x, y, t)
passtime:总移动时间
userresponse:计算公式为set_left / (0.8876 * 340 / 300)
elixir
set_left = 88
track = [[38, 18, 0], [1, 0, 33]]
passtime = 166
user_response = set_left / (0.8876 * 340 / 300)
IO.puts("set_left: #{set_left}")
IO.puts("track: #{inspect(track)}")
IO.puts("passtime: #{passtime}")
IO.puts("userresponse: #{user_response}")
至此,我们已经完成了w参数的明文解析。