极验验证码是一种广泛应用的滑动验证码系统,通过分析其加密和验证机制,我们可以模拟滑动操作并破解验证码。本文将详细介绍如何在Go语言环境下实现这一过程。
一、观察verify请求
首先,我们需要观察verify请求的来源。在浏览器开发者工具中查看verify请求的发起者,可以发现它来自于gcaptcha4.js文件。由于该文件经过混淆处理,我们需要对其进行还原和分析。
二、AST还原混淆代码
为了便于调试和分析,我们使用AST(抽象语法树)还原混淆的JS文件。通过Chrome的reres插件替换混淆后的JS文件,我们可以在浏览器中加载还原后的代码进行调试。
go
package main
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"time"
)
// 获取UUID
func generateChallenge() string {
return fmt.Sprintf("%x", md5.Sum([]byte(time.Now().String())))
}
// 生成滑动轨迹
func generateTrack(distance int) [][]int {
var track [][]int
current := 0
for current < distance {
step := rand.Intn(5) + 1
current += step
track = append(track, []int{step, rand.Intn(2), rand.Intn(15)})
}
return track
}
// 计算滑动距离
func getSetLeft(track [][]int) int {
setLeft := 0
for _, step := range track {
setLeft += step[0]
}
return setLeft
}
// 示例主函数
func main() {
captchaId := "24f56dc13c40dc4a02fd0318567caef5"
challenge := generateChallenge()
clientType := "web"
riskType := "slide"
lang := "zh"
callback := fmt.Sprintf("geetest_%d", time.Now().UnixNano()/1e6)
// 加载请求
loadResponse := loadRequest(captchaId, challenge, clientType, riskType, lang, callback)
lotNumber := loadResponse["lot_number"].(string)
bgImage := loadResponse["bg"].(string)
fullImage := loadResponse["fullbg"].(string)
// 分析背景图与完整图以确定滑块位置
setLeft := analyzeImage(bgImage, fullImage)
// 生成滑动轨迹
track := generateTrack(setLeft)
passtime := 0
for _, step := range track {
passtime += step[2]
}
// 计算userresponse
userResponse := float64(setLeft) / (0.8876 * 340 / 300)
// 构造w参数
wParam := map[string]interface{}{
"device_id": "A8A0",
"em": map[string]interface{}{
"cp": 0,
"ek": "11",
"nt": 0,
"ph": 0,
"sc": 0,
"si": 0,
"wd": 1,
},
"ep": "123",
"geetest": "captcha",
"fq6a": "1925502591",
"lang": "zh",
"lot_number": lotNumber,
"passtime": passtime,
"pow_msg": generatePowMsg(lotNumber, passtime),
"pow_sign": generatePowSign(generatePowMsg(lotNumber, passtime)),
"setLeft": setLeft,
"track": track,
"userresponse": userResponse,
}
// 发送验证请求
verifyResponse := verifyRequest(wParam)
fmt.Println("Verify Response:", verifyResponse)
}
// 示例函数:加载请求
func loadRequest(captchaId, challenge, clientType, riskType, lang, callback string) map[string]interface{} {
// 模拟发送请求并获取响应
return map[string]interface{}{
"lot_number": "7e22264d4f3e4dd8a6ffbf6e82e1122d",
"bg": "bg_image_data",
"fullbg": "full_image_data",
}
}
// 示例函数:分析图像
func analyzeImage(bgImage, fullImage string) int {
// 简化的图像分析函数,返回假定的滑块位置
return 88
}
// 示例函数:生成pow_msg
func generatePowMsg(lotNumber string, passtime int) string {
return fmt.Sprintf("1|0|md5|2022-03-25T14:23:36.364152+08:00|24f56dc13c40dc4a02fd0318567caef5|%s||29f07cebf938aa4e", lotNumber)
}
// 示例函数:生成pow_sign
func generatePowSign(powMsg string) string {
hash := md5.Sum([]byte(powMsg))
return hex.EncodeToString(hash[:])
}
// 示例函数:验证请求
func verifyRequest(wParam map[string]interface{}) string {
// 模拟发送验证请求并获取响应
wParamJSON, _ := json.Marshal(wParam)
fmt.Println("Sending Verify Request with wParam:", string(wParamJSON))
// 模拟响应
return "Success"
}
三、定位w参数加密位置
通过对代码的分析和调试,可以确定w参数的加密位置。通过搜索关键字"w",可以找到相关代码并在适当位置打上断点,观察w参数的生成过程。
四、分析w参数加密算法
通过分析w参数的生成代码,可以简化为以下内容:
go
r := d.default(l.default.stringify(e), a)
其中,e是一个包含多个固定值和从请求响应中获取的数据的对象。通过进一步分析,可以确定e的组成如下:
go
e := map[string]interface{}{
"device_id": "A8A0",
"em": map[string]interface{}{
"cp": 0,
"ek": "11",
"nt": 0,
"ph": 0,
"sc": 0,
"si": 0,
"wd": 1,
},
"ep": "123",
"geetest": "captcha",
"fq6a": "1925502591",
"lang": "zh",
"lot_number": lotNumber,
"passtime": passtime,
"pow_msg": generatePowMsg(lotNumber, passtime),
"pow_sign": generatePowSign(generatePowMsg(lotNumber, passtime)),
"setLeft": setLeft,
"track": track,
"userresponse": userResponse,
}