分析过程网上有很多,这里只说个大概,主要是提供golang源码
像这种网站,我们可以使用hook cookie的方式来调试这段代码,然后找到最终加密的方式然后导出来。
// ==UserScript==
// @name Hook Cookie
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @include *
// @grant none
// @run-at document-start
// ==/UserScript==
// ==UserScript==
// @name 获取 cookie 设置的地方 hook
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://www.python-spider.com/challenge/2
// @include https://www.python-spider.com/
// @icon https://www.google.com/s2/favicons?sz=64&domain=python-spider.com
// @grant none
// @run-at document-start
// ==/UserScript==
alert('hook success')
var cookieTemp = "";
Object.defineProperty(document, 'cookie', {
set: function(val) {
console.log('Hook捕获到cookie设置->', val);
debugger;
cookieTemp = val;
return val;
},
get: function() {
return cookieTemp;
}
})
三、源码
package main
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"regexp"
"github.com/robertkrimen/otto"
"github.com/pkg/errors"
)
func main() {
// 创建http客户端
baseUrl := "这里换成你自己网站的url"
method := "GET"
cookie, jsLuidCookie, _ := getFirstCookie(baseUrl, method)
realCookie, _ := getRealCookie(method, baseUrl, cookie)
result := fmt.Sprintf("%s;%s", jsLuidCookie, realCookie)
fmt.Println(result)
}
// getFirstCookie 第一次请求网站,获取第一次cookie;返回第二次请求需要的cookie以及jsluid-Cookie
func getFirstCookie(baseUrl, method string) (cookie string, cookie2 string, err error) {
// 创建请求
req, err := http.NewRequest(method, baseUrl, nil)
if err != nil {
return "", "", errors.WithStack(err)
}
// 这里会验证user-agent,必须与获取cookie保持一致
req.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36")
req.Header.Add("Accept-Encoding", "deflate, gzip")
// 发送请求
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", "", errors.WithStack(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", "", errors.WithStack(err)
}
// 解析出返回内容中的cookie
cookie, err = parseCookie(string(body))
if err != nil {
return "", "", errors.WithStack(err)
}
// 获取响应头中的cookie
if len(resp.Cookies()) < 1 {
return "", "", errors.New("response cookie error")
}
cookie2 = fmt.Sprintf("%s=%s", resp.Cookies()[0].Name, resp.Cookies()[0].Value)
return cookie2 + ";" + cookie, cookie2, nil
}
/**
返回格式为:
*/
// parseCookie 解析出cookie部分并执行js进行拼接
func parseCookie(script string) (string, error) {
re := regexp.MustCompile(`document\.cookie=([^"]*);location`)
matches := re.FindStringSubmatch(script)
if len(matches) < 2 {
return "", errors.New("first cookie parse error")
}
vm := otto.New()
// 返回格式需要执行js拼接
cookieStr, err := vm.Run(matches[1])
if err != nil {
return "", errors.WithStack(err)
}
return cookieStr.String(), nil
}
// getRealCookie 解密出真正的cookie
func getRealCookie(method, baseUrl, cookie string) (string, error) {
// 创建请求
// 这里会验证user-agent,必须与获取cookie保持一致
req, err := http.NewRequest(method, baseUrl, nil)
req.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
req.Header.Add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36")
req.Header.Add("Cookie", cookie)
// 发送请求
res, err := http.DefaultClient.Do(req)
if err != nil {
return "", errors.WithStack(err)
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", errors.WithStack(err)
}
// 从混淆代码中取出相关信息
cookieData, err := parseCookie2(string(body))
if err != nil {
return "", errors.WithStack(err)
}
var data scriptData
if err := json.Unmarshal([]byte(cookieData), &data); err != nil {
return "", errors.WithStack(err)
}
// 从返回数据获取到对应的cookie
cookieValue := decryptCookie(&data)
if cookieValue == "" {
return "", errors.New("cookie empty")
}
// 拼接出真正的cookie
realCookie := fmt.Sprintf("%s=%s", data.Tn, cookieValue)
return realCookie, nil
}
// decryptCookie 解密cookie,这部分代码是从js中一点一点分析出来的
func decryptCookie(data *scriptData) string {
chars := data.Chars
for i := 0; i < len(chars); i++ {
for j := 0; j < len(chars); j++ {
realCookie := data.Bts[0] + chars[i:i+1] + chars[j:j+1] + data.Bts[1]
value := hash(data.Ha, realCookie)
if value == data.Ct {
return realCookie
}
}
}
return ""
}
type scriptData struct {
Bts []string `json:"bts"`
Chars string `json:"chars"`
Ct string `json:"ct"`
Ha string `json:"ha"`
Is bool `json:"is"`
Tn string `json:"tn"`
Vt string `json:"vt"`
Wt string `json:"wt"`
}
// parseCookie2 返回的数据是ob混淆,经过分析后提出有用的部分为:
/**
; go({
"bts": ["1692867818.638|0|j2C", "SjkeBkkPhhJzCsfPy8YxCg%3D"],
"chars": "nIqNnycLKKKdDxvfkwMHdO",
"ct": "1756d5ed879c6b656cf7afb10b75a54690ab7bd17b21c84b6d2bded8f9524fe1",
"ha": "sha256",
"is": false,
"tn": "__jsl_clearance_s",
"vt": "3600",
"wt": "1500"
})
*/
func parseCookie2(script string) (string, error) {
re := regexp.MustCompile(`;go\((.*?)\)`)
matches := re.FindStringSubmatch(script)
if len(matches) < 2 {
return "", errors.New("cookie2 error")
}
return matches[1], nil
}
// hash 解密用到的方法
func hash(typ string, value string) string {
switch typ {
case "md5":
h := md5.Sum([]byte(value))
return hex.EncodeToString(h[:])
case "sha1":
h := sha1.Sum([]byte(value))
return hex.EncodeToString(h[:])
case "sha256":
h := sha256.Sum256([]byte(value))
return hex.EncodeToString(h[:])
}
return ""
}