skuukzky
文章10
标签0
分类2

文章分类

2025新春挑战-猿人学

2025新春挑战-猿人学

2025 新春挑战-猿人学

headers 中的 a

d[d.length - 1] ? console.log("func::",d[d.length - 2],"this::",d[d.length - 3],"arg::",a) : console.log("func::",d[d.length - 2],"this::",window,"arg::",a);false;

我们插装看到一个很熟悉的值
image-20250120152449333

拿去百度一搜是 sha256

但是我们仔细对比 k 表发现 他的 k 值对不上 可以确信的是 a 应该为魔改的 sha256 我们知道 sha256 是 64 为 但实际上 a 的长度为 128 位

那么可能猜一下应该是分两段的

我们继续往下查看传参

第一段加密的是 b 的值

第二段加密的是 参赛 params 的内容

合起来得到的就是 128 位的 a 值 那么也就清楚了

魔改了 sha256 就先去 b 站看有具体的分析教程 我这里就不一个个分析了

headers 中的 b

b 加密是 aes 的 cbc 这里我是算法推理出来的 然后拿着 key 搜索 发现就是 websocket 中返回的东西 具体找 key 和 iv 的教程 也可以去 b 站看分析

key 是 ws 中返回的 key

iv 也是 ws 中返回的 key

插桩的地方可以看到
image-20250120153602719

1
2
3
4
5
sha256_hash = encrypt.sha256(encrypt_array[-2])
print(sha256_hash)
big_int = int(sha256_hash, 16)
iv_at = big_int % (len(encrypt_array) - 1)
key_at = (int(encrypt_array[-1]) - iv_at + len(encrypt_array) - 1) % (len(encrypt_array) - 1)

响应中的 b

也可以算法推理出来

返回的 b 加密 是 aes ecb 模式。key 是 websocket 中返回 array 中的倒第二位 key
具体可以去 b 站看有分析教程 能学到一些技巧。

最终结果

image-20250120150415786

代码部分

js 部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// SHA-256 Implementation in JavaScript (pure JS)
function sha256(ascii) {
  const K = [
    0, 0, 1116352408, 1899447441, -1245643825, -373957723, 961987163,
    1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278,
    1426881987, 1925078388, -2132889090, -1680079193, -1046744716, -459576895,
    -272742522,
  ];

  function rightRotate(value, amount) {
    return (value >>> amount) | (value << (32 - amount));
  }

  function utf8Encode(str) {
    return unescape(encodeURIComponent(str));
  }

  let words = [];
  const preprocessed = utf8Encode(ascii);
  const asciiBitLength = preprocessed.length * 8;

  for (let i = 0; i < preprocessed.length; i++) {
    words[i >> 2] |= preprocessed.charCodeAt(i) << (24 - (i % 4) * 8);
  }
  words[preprocessed.length >> 2] |=
    0x80 << (24 - (preprocessed.length % 4) * 8);
  words[(((asciiBitLength + 64) >> 9) << 4) + 15] = asciiBitLength;

  let hash = [
    0, 0, 1779033703, -1150833019, 1013904242, -1521486534, 1359893119,
    -1694144372,
  ];

  for (let j = 0; j < words.length; j += 16) {
    const w = new Array(16).fill(0);
    for (let i = 0; i < 16; i++) {
      w[i] = words[j + i] | 0;
    }
    // for (let i = 16; i < 64; i++) {
    //     const s0 = rightRotate(w[i - 15], 7) ^ rightRotate(w[i - 15], 18) ^ (w[i - 15] >>> 3);
    //     const s1 = rightRotate(w[i - 2], 17) ^ rightRotate(w[i - 2], 19) ^ (w[i - 2] >>> 10);
    //     w[i] = (w[i - 16] + s0 + w[i - 7] + s1) | 0;
    // }

    let [a, b, c, d, e, f, g, h] = hash;

    for (let i = 0; i < 64; i++) {
      let mdeida;
      let s00, s11;
      if (i - 2 >= 0) {
        s00 =
          rightRotate(w[i - 2], 17) ^
          rightRotate(w[i - 2], 19) ^
          (w[i - 2] >>> 10);
      } else {
        s00 = 0;
      }
      if (i - 15 >= 0) {
        s11 =
          rightRotate(w[i - 15], 7) ^
          rightRotate(w[i - 15], 18) ^
          (w[i - 15] >>> 3);
      } else {
        s11 = 0;
      }
      mdeida = ((w[i - 16] | 0) + s00 + (w[i - 7] | 0) + s11) | 0;
      w[i] = (w[i] + mdeida) | 0;
      const s1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25);
      const ch = (e & f) ^ (~e & g);
      const temp1 = (h + s1 + ch + K[i] + w[i]) | 0;
      const s0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22);
      const maj = (a & b) ^ (a & c) ^ (b & c);
      const temp2 = (s0 + maj) | 0;

      h = g;
      g = f;
      f = e;
      e = (d + temp1) | 0;
      d = c;
      c = b;
      b = a;
      a = (temp1 + temp2) | 0;
    }

    for (let i = 0; i < 8; i++) {
      hash[i] = (hash[i] + [a, b, c, d, e, f, g, h][i]) | 0;
    }
  }

  return hash
    .map((h) => ("00000000" + (h >>> 0).toString(16)).slice(-8))
    .join("");
}

// Example usage
console.log(sha256("123"));

python 部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# -*- coding: utf-8 -*-
"""
@author: 眠棠
@software: PyCharm
@file: requests_new.py
@time: 2025/1/20 上午9:54
@Software: PyCharm
"""

import base64
import json
import random
from functools import reduce
import websocket
import encrypt
import os
import pyhttpx


def get_header(post_data):
    def get_encrypt_array():
        def get_random_str():
            base36_str = ''.join(
                random.choice('abcdefghijklmnopqrstuvwxyz0123456789') for _ in range(random.randint(9, 11)))
            return base36_str

        ws_obj = websocket.create_connection("wss://match.yuanrenxue.cn/api/match/22",
                                             cookie="sessionid=wc9lak3tci1cukuxav3id54y38hwy3ek", header={
                "Origin": "https://match.yuanrenxue.cn",
                "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
                "Sec-WebSocket-Key": base64.b64encode(os.urandom(16)).decode(),
                "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0",
                "Sec-WebSocket-Version": "13",
                "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits"
            })
        res_array = []
        if ws_obj.status == 101:
            while True:
                ws_obj.send(get_random_str())
                response = ws_obj.recv()
                if response.isdigit():
                    res_array.append(response)
                    ws_obj.close()
                    break
                else:
                    res_array.append(response)

        print(res_array)
        return res_array

    # websocks返回的加密数组
    encrypt_array = get_encrypt_array()

    sha256_hash = encrypt.sha256(encrypt_array[-2])
    print(sha256_hash)
    big_int = int(sha256_hash, 16)
    iv_at = big_int % (len(encrypt_array) - 1)
    key_at = (int(encrypt_array[-1]) - iv_at + len(encrypt_array) - 1) % (len(encrypt_array) - 1)
    print(iv_at, key_at, post_data)
    print(encrypt_array[key_at], encrypt_array[iv_at])
    A = encrypt.aes_encrypt_cbc(key_str=encrypt_array[key_at], iv_str=encrypt_array[iv_at], data=post_data)
    B = encrypt.sha256(A) + encrypt.sha256(post_data)
    return {
        "a": A,
        "b": B
    }, encrypt_array


def get_page_requests(page):
    cookies = {
        "sessionid": "wc9lak3tci1cukuxav3id54y38hwy3ek",
    }
    headers = {
        "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0",
        "referer": "https://match.yuanrenxue.cn/match/22",
    }

    data = json.dumps({
        "page": page,
        "url": "https://match.yuanrenxue.cn/match/22",
        "c": json.dumps([
            f"sessionid={cookies['sessionid']}"
        ], separators=(',', ':')),
    }, separators=(',', ':'))
    session = pyhttpx.HttpSession(http2=True, browser_type="chrome")
    session.get("https://match.yuanrenxue.cn/api/loginInfo", cookies=cookies, headers=headers)
    print("加密data", data)
    new_headers, encrypt_array = get_header(data)
    print(new_headers, encrypt_array)
    # 直接更新 headers 字典
    headers.update(new_headers)
    # 验证更新后的 headers
    print(headers)
    response = session.post("https://match.yuanrenxue.cn/api/match/22", headers=headers, data=data, cookies=cookies)
    json_res = response.json()
    print(json_res)
    ecb_key = encrypt_array[int(encrypt_array[-1])]
    print('ecb->key', ecb_key)
    print(json_res['B'])
    decrypted_str = encrypt.aes_decrypt_ecb(key_str=ecb_key, data=json_res['B'])
    # 去除不可见填充字符(常见的填充字符可能包括 \x05, \x00, \n, \r, 空格等)
    cleaned_str = decrypted_str.rstrip("\x05\x00\n\r ")
    # 转换为 JSON
    try:
        json_data = json.loads(cleaned_str)
        extracted_data = json_data.get("data", [])  # 提取 data 字段
        print("提取的 data:", extracted_data)
        return extracted_data
    except json.JSONDecodeError as e:
        print("JSON解析错误:", e)


def run():
    sum_all = 0
    for page in range(1, 6):
        page_res = get_page_requests(page)
        print(page_res)
        if page_res:
            sum_all += dict(reduce(lambda x, y: {"value": x['value'] + y['value']}, page_res))['value']
        else:
            print(f"当前page{page}出错")
    print(sum_all)


if __name__ == '__main__':
    run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import json
import  execjs
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

def sha256(text):
    f = open("sha256.js", mode="r", encoding="utf-8")
    js_code = f.read()
    f.close()
    js = execjs.compile(js_code)  # 加载JS代码
    mi = js.call("sha256", text)  # 运行JS代码
    return mi


# AES加密函数(CBC模式)
def aes_encrypt_cbc(key_str, iv_str, data):
    # 将key和iv字符串转换为字节
    key = key_str.encode('utf-8')
    iv = iv_str.encode('utf-8')

    # 确保key和iv的长度正确
    key = key.ljust(16, b'\0')[:16]  # 填充或截断为16字节
    iv = iv.ljust(16, b'\0')[:16]  # 填充或截断为16字节

    cipher = AES.new(key, AES.MODE_CBC, iv)
    encrypted_data = cipher.encrypt(pad(data.encode(), AES.block_size))
    return base64.b64encode(encrypted_data).decode()

def aes_decrypt_ecb(key_str, data):
    message = data
    # 将key和iv字符串转换为字节
    key = key_str.encode('utf-8')
    message = base64.b64decode(message.encode())
    aes  = AES.new(key, AES.MODE_ECB)
    aes_result = aes.decrypt(message).decode()
    # print(aes_result)
    return aes_result

if __name__ == '__main__':
    print(sha256('123')) aes_decrypt_ecb('LjW9ax99r6DWYOMP','71NkvqbIDTN5ih80w+GbMnIczZVdsyMu/cbP74GchId1V9ATxawUG/TuMn2bvomjxAUxmjiaezYTAkEH7b2AAyYAV9AZs7HF5dAFIutgKjhHgFGx1tMeAkJU7dnJ+82+8S2utMGSwjQGQEJTir2kDT/KSZIxfDU2yr3fM7n8lmNqY+NFMls801ua/qCn9JK3CjUuXlc28eGZlAZ4KxVE26WPu7P2p5XD/RPs3079sl0CJwze2JqGGrowIsggYimMzLdhG5FhYSWwxQyi88YPOAfn5mLTwNXcXH9ClZMQQsI=')

参考链接

【猿人学爬虫题目详解】22 - 2025 新年挑战!

本文作者:skuukzky
本文链接:https://lpy30m.github.io/skuukzky.github.io/2025/01/20/%E9%80%86%E5%90%91/2025%E6%96%B0%E6%98%A5%E6%8C%91%E6%88%98-%E7%8C%BF%E4%BA%BA%E5%AD%A6/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可