使用Haskell破解gcaptcha4.js中的w参数加密算法

在本文中,我们将介绍如何使用Haskell来逆向工程gcaptcha4.js文件,找到并破解w参数的加密算法。整个过程包括观察verify请求,定位加密位置,分析加密算法,并最终还原w参数的明文。

一. 观察verify请求
首先,我们需要观察verify请求的发起者。通过网络请求分析工具,我们可以看到所有verify请求都来自于gcaptcha4.js文件。因此,这个文件成为我们分析的重点。

我们使用Haskell的http-conduit库来捕获和分析这些请求。

haskell

{-# LANGUAGE OverloadedStrings #-}

import Network.HTTP.Simple

main :: IO ()
main = do
    response <- httpLBS "https://example.com/verify"
    let responseBody = getResponseBody response
    putStrLn $ "The response body is: " ++ show responseBody
通过分析响应内容,可以发现所有的verify请求都指向了gcaptcha4.js文件。

二. 定位w参数加密位置
为了找到w参数的加密位置,我们需要解析gcaptcha4.js文件。我们可以下载并格式化这个文件,然后使用正则表达式搜索关键词:w、.w、'w'或"w"。

haskell

import Text.Regex.Posix
import System.IO

searchFile :: FilePath -> IO ()
searchFile filePath = do
    content <- readFile filePath
    let regex = "[.\\'\"]w[.\\'\"]" :: String
    let matches = getAllTextMatches (content =~ regex :: AllTextMatches [] String)
    mapM_ putStrLn matches

main :: IO ()
main = searchFile "gcaptcha4.js"
通过搜索"w",我们找到了相关代码。在第2527行,我们发现了w的值r在第2525行被定义。

三. 分析w参数加密算法
接下来,我们需要简化代码中w的定义。假设我们已经提取了相关的JavaScript代码,我们可以使用Haskell来模拟这个过程。

haskell

{-# LANGUAGE OverloadedStrings #-}

import Data.Aeson (encode, object, (.=))
import Data.ByteString.Lazy.Char8 as B
import Crypto.Hash.MD5 (hash)
import Data.ByteString.Base16 (encodeBase16)

toJSON :: [(String, String)] -> ByteString
toJSON = encode . object . map (\(k, v) -> k .= v)

md5 :: ByteString -> ByteString
md5 = encodeBase16 . hash

main :: IO ()
main = do
    let 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")]
    let a = "your_key"
    let r = md5 (toJSON e <> a)
    B.putStrLn r
4.1 分析pow_msg和pow_sign
通过搜索pow_msg,我们找到相关代码并解析如下:

haskell

import System.Random (randomIO)
import Data.Time.Clock (getCurrentTime)

generateGUID :: IO String
generateGUID = do
    g1 <- randomIO :: IO Int
    g2 <- randomIO :: IO Int
    g3 <- randomIO :: IO Int
    g4 <- randomIO :: IO Int
    return $ concatMap (flip showHex "") [g1, g2, g3, g4]

md5String :: String -> String
md5String = B.unpack . encodeBase16 . hash . B.pack

main :: IO ()
main = do
    let n = "example_n"
    let a = "example_a"
    let s = "example_s"
    let o = "example_o"
    let t = "example_t"
    let e = "example_e"
    let r = "example_r"

    let u = n ++ "|" ++ a ++ "|" ++ s ++ "|" ++ o ++ "|" ++ t ++ "|" ++ e ++ "|" ++ r ++ "|"
    p <- generateGUID
    let g = u ++ p
    let pow_msg = u ++ p
    let pow_sign = md5String g

    putStrLn $ "pow_msg: " ++ pow_msg
    putStrLn $ "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)
haskell

setLeft :: Int
setLeft = 88

track :: [[Int]]
track = [[38, 18, 0], [1, 0, 33]]

passtime :: Int
passtime = 166

userResponse :: Double
userResponse = fromIntegral setLeft / (0.8876 * 340 / 300)

main :: IO ()
main = do
    putStrLn $ "set_left: " ++ show setLeft
    putStrLn $ "track: " ++ show track
    putStrLn $ "passtime: " ++ show passtime
    putStrLn $ "userresponse: " ++ show userResponse
至此,我们已经完成了w参数的明文解析。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值