pwp 发表于 2024-2-3 18:01

2024寒假某研修模拟学习思路分析

本帖最后由 pwp 于 2024-2-4 00:02 编辑

1、点击立即报名:




发现两个post,咱也不知道哪个post有啥用,都模拟一下:

import requests

url = "https://elearning-train-gateway.ykt.eduyun.cn/v3/spi/trains/action_rules?current_train_id=ee0d68b5-6a9d-441b-97dc-d52bfdfece83&action=access&need_verify_audit=true"
headers = {
    "accept": "application/json, text/plain, */*",
    "accept-language": "zh-CN,zh;q=0.9",
    "authorization": "MAC id=\"7F938B205F876FC39BD5FD64A3C6E3CBBC682E5F9A58550F6BD9431CE859CF308D8C762D1AFFBAFECC7CF21AF76E4BDB127\",nonce=\"1706953412553:K218AKFG\",mac=\"RdDeqkQbGgPgWGleCn2JiYbFsQo6zknDjZx2sE=\"",
    "cache-control": "no-cache",
    "pragma": "no-cache",
    "sdp-app-id": "e5649925-441d-4a53-b525-51a2f1c4e0a8",
    "sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "cross-site"
}
response = requests.get(url, headers=headers)

print(response.text)


import requests

url = "https://elearning-train-gateway.ykt.eduyun.cn/v2/spi/trains/ee0d68b5-6a9d-441b-97dc-d52bfdfece83/actions/async_joins"
headers = {
    "accept": "application/json, text/plain, */*",
    "accept-language": "zh-CN,zh;q=0.9",
    "authorization": "MAC id=\"7F938B205F876FC39BD5FD64A3C8216721E3CBB2C0F6BD9431CE859CF308D8C762D1AFFBAFECC7CF21AF41C4BDB127\",nonce=\"1706953412840:CAPWS9EJ\",mac=\"HhPbxhQ0Psggvvpg1YYioggaJ3Pc/tUfaNGe0Zfepqs=\"",
    "cache-control": "no-cache",
    "content-type": "application/x-www-form-urlencoded",
    "pragma": "no-cache",
    "sdp-app-id": "e5649925-441d-4a53-b525-51a2f1c4e0a8",
    "sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "cross-site"
}
data = None
response = requests.post(url, headers=headers, data=data)

print(response.text)

能够模拟立即报名的前提是信息完整度100%。

前后端分离项目逆向成也Authorization,败也Authorization,首当其冲的是解决Authorization。
如上面代码片段所示,headers 中的Authorization是直接复制浏览器出来的,具体他是怎么产生的呢?

因为学历太低,根本不知道怎么形成的,只有暴力搜索:






这个函数好复杂,鼠标摆上去一个一个查看是什么意思:





e是一个链接 "https://uc-gateway.ykt.eduyun.cn/v1.1/users/452596678222?with_ext=true&session_id=637ec2d0-17bf-4108-b602-2a55xxxxxxcd"
t是get
n感觉是登录后的一系列参数:xxxxxxxxxxxx

进入这个函数,还有个Ge函数,是骡子是马溜出来看看:


根本看不懂!

真的费力,这不,He(i)和Ze(e, s, t, o)又是啥,javascript没学过,还带那么多参数,想想都可怕,要不,问问AI?

先解决He(i)
function He(e) {
            return (new Date).getTime() + parseInt(e, 10) + ":" + Ne(8)
      }


function Ze(e, t, n, r) {
            var o = f.parse(e)
            , i = t + "\n" + n.toUpperCase() + "\n" + o.relative + "\n" + o.authority + "\n";
            return ee.HmacSHA256(i, r).toString(ee.enc.Base64)
      }



差Ne函数,

function Ne(e) {
            for (var t = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""), n = "", r = 0; r < e; r++)
                n += t;
            return n
      }

再来




哦哦,原来是这个意思。

用python模拟出来:

import random
import time

def Ne(e):
    t = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    n = ""
    for i in range(e):
      n += t
    return n

def He(i):
    timestamp = int(time.time())
    return str(timestamp) + str(i) + ":" + Ne(8)



好了,解决完He(i),准备来解决Ze(e, s, t, o),

先看看这个函数是神马意思:



哦哦,原来是这个意思。

那么,就来下断点,运行,看看到底是啥。运行到此处的时候,鼠标摆上去,咋变量不显灵了?

一看log发现还有限制,这些蛛丝马迹可能就是后面能否模拟自动学习的关键,给截个图摆着:





重新运行到此处,感觉思路足见清晰:




成也Authorization,败也Authorization,20楼有大老说这个他也搞过,并不能模拟出来,我想这里就被套路了。
(因截图泄漏个人账号数据,择日重新上传

pwp 发表于 2024-2-3 18:06

本帖最后由 pwp 于 2024-2-4 18:26 编辑

这个diff我是搜了好久才搜到他生成的地方,大家搜索的时候可以不再慢慢去找diff了,直接搜索t.saveToken函数就行了,这个函数原型如下,
t.saveToken = function(e, t) {
                return e && !e.diff && (e.diff = e.server_time ? wt()(e.server_time).toNumber() - wt()().toNumber() : 0),
                this._tokenStorage.set("token", JSON.stringify(e), void 0, t),
                e
            }

这个函数咋看也看不懂,让ai帮我们换一种写法并解释:
t.saveToken = function(e, t) {
    if (e && !e.diff) {
      e.diff = e.server_time ? wt()(e.server_time).toNumber() - wt()().toNumber() : 0;
    }
    this._tokenStorage.set("token", JSON.stringify(e), void 0, t);
    return e;
}


这段代码是一个名为saveToken的函数,它接受两个参数:e和t。函数的作用是将一个包含令牌信息的对象e保存到本地存储中,并返回该对象。首先,函数会检查对象e是否存在且不包含diff属性。如果满足条件,它会计算diff的值并将其添加到对象e中。diff的值是通过将服务器时间(e.server_time)转换为数字并与当前时间相减得到的。接下来,函数使用this._tokenStorage.set()方法将对象e以JSON字符串的形式保存到本地存储中,键为"token",过期时间为t。最后,函数返回保存后的对象e。
这wt()又是啥,点进去后长这样:function V(e) {
            var t = e.url
            , n = e.headers
            , r = e.data
            , o = void 0 === r ? "{}" : r
            , i = e.method
            , s = void 0 === i ? "GET" : i
            , a = e.isProxy
            , c = void 0 === a || a;
            n = K(K({}, F), n),
            n.Host = z(t).host,
            null === o["org_name"] && delete o["org_name"];
            var u = {
                $headers: n,
                $body: o,
                $method: s
            }
            , d = "$proxy=proxyhttp&bodys=".concat(encodeURIComponent(JSON.stringify(u)));
            return c && (t += (t.indexOf("?") >= 0 ? "&" : "?") + d),
            t = t.replace("?&", "?"),
            new Promise((function(e, n) {
                Z(t, {
                  prefix: "nd_uc_sdk_".concat(+(new Date).getTime())
                }, (function(t) {
                  var r = arguments.length > 1 && void 0 !== arguments ? arguments : {};
                  if (t)
                        return "string" === typeof t ? void n(new L(t)) : void n(t);
                  var o = r.$status >= 200 && r.$status < 300 || 304 === r.$status || W(r);
                  if (o)
                        if (void 0 === r.$body && (r.$body = {}),
                        N()(r.$body))
                            e(r.$body);
                        else
                            try {
                              e(JSON.parse(r.$body))
                            } catch (_o) {
                              n(new L("UCSDK/SERVER_ERROR"))
                            }
                  else if (!1 === o && N()(r.$body))
                        n(r.$body);
                  else
                        try {
                            n(JSON.parse(r.$body))
                        } catch (_o) {
                            n(new L("UCSDK/SERVER_ERROR"))
                        }
                }
                ))
            }
            ))
      }
问ai他说:这段代码是一个名为V的函数,它接受一个包含请求信息的对象作为参数。该函数的作用是发送一个HTTP请求,并返回一个Promise对象。    请注意,上述代码中的wt()函数未提供定义,因此无法确定其具体实现。


越搞越糊涂,没办法,直接执行这个函数看看是啥:



看起来是一个时间戳啊为了验证我的猜想我给他转换一下:而这个时间,视乎在哪里看见过:




我为了猜测他们是什么意思,我尽然无底线地运行了无限次:




最终和程序猿想到一块去了:服务器时间-当前时间。

服务器时间又哪里来的呢,其实就是登录成功后来的,为了验证我的猜想,我给他刷新了看,果不其然:






真的是啊,那所有参数都搞定了,就来模拟吧。

模拟前,需要注意的是,这里有个耿,大于5分钟就会弹出错误提示,小伙伴们自己测试吧!!!

经过多次测量求平均值,发现这地方在1120左右,估计就是登录成功到给这个参数赋值所需要的时间,

两时间做和差积商是我的短板,我就将这个时间差设为(1000,1200)。

用python模拟这些过程得:

      def Ne(e):
            t = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            n = ""
            for i in range(e):
                n += t
            return n

      def He(i):
            timestamp = int(time.time())
            return str(timestamp) + str(i) + ":" + Ne(8)



      def Ze(e, t, n, r):
            parsed_url = urlparse(e)
            o_relative = parsed_url.path + '?' + parsed_url.query# 路径+参数
            o_authority = parsed_url.netloc# 域名
            # i = t + "\n" + n.toUpperCase() + "\n" + o.relative + "\n" + o.authority + "\n";
            i = (t + '\n' + str.upper(n) + '\n' + o_relative + '\n' + o_authority + '\n').encode('utf-8')
            # # Calculate HMAC SHA256
            signature = hmac.new(r.encode(), i, digestmod=hashlib.sha256).digest()
            # # Encode the signature in Base64 format
            return base64.b64encode(signature).decode('utf-8')

      def Ge(e):

            t = 'GET' #t = arguments.length > 1 && void 0 !== arguments ? arguments : "GET"
            r = access_token #r = n.accessToken
            o = mac_key_decrpyt #o = n.macKey
            i =random.randint(1000,1200) #i = n.diff
            s = He(i)
            a =Ze(e,s,t,o)
            c = f'MAC id="{r}",nonce="{s}",mac="{a}"'
            #c = 'MAC id="'.concat(r, '",nonce="').concat(s, '",mac="').concat(a, '"');
            return c

      e = 'https://uc-gateway.ykt.eduyun.cn/v1.1/users/45259xxxxxxx?with_ext=true&session_id=8da6f745-071f-4317-8d87-axxxxxxxxxxx'
      t = 'GET'
      n = logininfo
      def getMACAuthorizationHeaders(e):
            r = Ge(e)
            return r
      Authorization = getMACAuthorizationHeaders(e)
      print('Authorization:', Authorization)

这东西解决了,我认为就没啥难度了。

接下来应该就是获取专题id,获取专题列表,看视频,提交参数了。

如果您耐心地看到这里,相信后面您也会了。

但为了有始有终,话不多说,继续开干。

打开这个页面,好多请求,发现可疑链接train_courses.json。看他返回数据,果不其然,和网页显示的一模一样啊:





激动之余,赶紧码代码:

import requests

url = "https://s-file-2.ykt.cbern.com.cn/teach/api_static/trains/2024hjpx/train_courses.json"
headers = {
    "accept": "application/json, text/plain, */*",
    "accept-language": "zh-CN,zh;q=0.9",
    "cache-control": "no-cache",
    "pragma": "no-cache",
    "sec-ch-ua": '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "cross-site",
}
referrer = "https://basic.smartedu.cn/training/2024hjpx"
referrer_policy = "no-referrer-when-downgrade"

response = requests.get(url, headers=headers, timeout=10)
print(response.json())
return response.json()





绝对奈斯!

fulls.json好像和页面有很多关联的数据:



而链接里面的这个数据视乎又来自这个json,那就好办了吧:












Pwaerm 发表于 2024-2-4 10:45

本帖最后由 Pwaerm 于 2024-2-4 13:33 编辑

document.querySelector("video").dispatchEvent(new Event("ended"))

sai609 发表于 2024-2-3 18:47

Pwaerm 发表于 2024-2-3 18:24
不晓得为啥子,说我违规了。 希望楼主这一贴坚持住。

里面敏感的网址我都替换了

认真阅读论坛规则再说

Scoundreel 发表于 2024-2-5 00:11

function Encrypt(t,authority,relative,key) {
        var n = "GET";
        var i = t + '\n' + n + '\n' + relative + '\n' + authority + '\n';
      return CryptoJS.HmacSHA256(i,key).toString(CryptoJS.enc.Base64);
}
function Fe(e) {
      return (new Date).getTime() + parseInt(e, 10) + ':' + Ze(8)
      };
function Ze(e) {
      for (
          var t = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
          n = '',
          r = 0;
          r < e;
          r++
      ) n += t;
      return n
      };
关键代码就这些了 前面扣个crypto-js直接调用就可以了

wushengli 发表于 2024-2-4 19:48

东西不错要是有成品就好了!

cnng789 发表于 2024-2-3 18:09

你这是雪中送炭啊,坐板凳围观中。。。。

123-木头人 发表于 2024-2-3 18:11

插眼,等待完成

Agann 发表于 2024-2-3 18:18

插眼,等待完成

Pwaerm 发表于 2024-2-3 18:24




{:301_999:}

不晓得为啥子,说我违规了。 希望楼主这一贴坚持住。

里面敏感的网址我都替换了

Fwind 发表于 2024-2-3 18:45

正好需要!坐等!

Pwaerm 发表于 2024-2-3 18:48

sai609 发表于 2024-2-3 18:47
认真阅读论坛规则再说

嗯嗯。

可能是某个地方的域名没替换完,现在帖子被删除了,自己也检查不到了。

向楼主学习,玩python

vqzhanshi 发表于 2024-2-3 18:55

学习一下
页: [1] 2 3 4 5 6 7 8
查看完整版本: 2024寒假某研修模拟学习思路分析