使用python、nodeJS、浏览器模拟执行JS

准备材料

getToken.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
const players = [
{
name: '凯文-杜兰特',
image: 'durant.png',
birthday: '1988-09-29',
height: '208cm',
weight: '108.9KG'
}, {
name: '勒布朗-詹姆斯',
image: 'james.png',
birthday: '1984-12-30',
height: '206cm',
weight: '113.4KG'
}
]
new Vue({
el: '#app',
data: function () {
return {
players,
key: 'fipFfVsZsTda94hJNKJfLoaqyqMZFFimwLt'
}
},
methods: {
getToken(player) {
let key = CryptoJS.enc.Utf8.parse(this.key)
const {name, birthday, height, weight} = player
// 引用CryptoJS
let base64Name = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(name))
// 引用CryptoJS
let encrypted = CryptoJS.DES.encrypt(`${base64Name}${birthday}${height}${weight}`, key, {
// 引用CryptoJS
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
return encrypted.toString()
}
}
})

CryptoJS.js

1
2
3
4
5
!function(t, e) {
"object" == typeof exports ? module.exports = exports = e() : "function" == typeof define && define.amd ? define([], e) : t.CryptoJS = e()
}(this, function() {
// 一些token的逻辑运算规则
});

从上面能够看出getToken.js引用了CryptoJS.js中的方法进行加密获得最后的token

Python模拟

  • 需要对原生的CryptoJS.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
var CryptoJS;
// 这属于JS的自执行方法
!function(t, e) {
// 在浏览器中没有exports和define对象,所以执行的代码为 t.CryptoJS = e(),将cryptojs挂载在this全局对象中
// 在pyexecjs包中,"object" == typeof exports 为 True,等于执行 module.exports = exports = e(),没有定义cryptojs
// 所以在此处,python环境下需要定义一个变量为e()
CryptoJS = e()
"object" == typeof exports ? module.exports = exports = e() : "function" == typeof define && define.amd ? define([], e) : t.CryptoJS = e()
}(this, function() {
// 一些token的逻辑运算规则
});

//将getToken方法也一并放入js中
function getToken(player) {
let key = CryptoJS.enc.Utf8.parse("fipFfVsZsTda94hJNKJfLoaqyqMZFFimwLt");
const { name, birthday, height, weight } = player;
// CryptoJS为上面定义的CryptoJS变量为e()
let base64Name = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(name));
let encrypted = CryptoJS.DES.encrypt(
`${base64Name}${birthday}${height}${weight}`,
key,
{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
}
);
return encrypted.toString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import execjs
import json

node = execjs.get()
file = 'crypto-change.js'
ctx = node.compile(open(file).read())

item = {
'name': '凯文-杜兰特',
'image': 'durant.png',
'birthday': '1988-09-29',
'height': '208cm',
'weight': '108.9KG'
}

js = f"getToken({json.dumps(item, ensure_ascii=False)})"
# DG1uMMq1M7OeHhds71HlSMHOoI2tFpWCB4ApP00cVFqptmlFKjFu9RluHo2w3mUw
result = ctx.eval(js)

nodeJS模拟

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
// 最原始的cryptojs文件,不需要像python一样经过处理
const CryptoJS = require('./crypto.js')

function getToken(player) {
let key = CryptoJS.enc.Utf8.parse("fipFfVsZsTda94hJNKJfLoaqyqMZFFimwLt");
const {name, birthday, height, weight} = player;
let base64Name = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(name));
let encrypted = CryptoJS.DES.encrypt(
`${base64Name}${birthday}${height}${weight}`,
key,
{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
}
);
return encrypted.toString();
}

const item = {
'name': '凯文-杜兰特',
'image': 'durant.png',
'birthday': '1988-09-29',
'height': '208cm',
'weight': '108.9KG'
}
// node node-main.js 使用node命令运行js文件
console.log(getToken(item))

浏览器模拟

  • 首先需要找到加密的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
onFetchData: function() {
var t = this;
this.loading = !0;
var a = (this.page - 1) * this.limit
, e = Object(i["a"])(this.$store.state.url.index, a);
window.x = Object(i["a"]);
this.$axios.get(this.$store.state.url.index, {
params: {
limit: this.limit,
offset: a,
token: e
}
}).then((function(a) {
var e = a.data
, s = e.results
, n = e.count;
t.loading = !1,
t.movies = s,
t.total = n
}
))
}
  1. debug发现this.$store.state.url.index固定值为/api/movie,a为(this.page - 1) * this.limit即offset,所以比较难处理的就是Object(i["a"])这个方法
  2. 该方法的入餐就两个this.$store.state.url.indexa,那么我们可以不管Object(i["a"])内部的具体实现,定义一个window的全局变量x,将整个Object(i["a"])挂载给window对象的x属性即可
  3. 最后取值的时候只需要调用window.x(this.$store.state.url.index,a)即可
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
import requests
from playwright.sync_api import sync_playwright

BASE_URL = 'https://spa2.scrape.center'
INDEX_URL = BASE_URL + '/api/movie/?limit=10&offset={offset}&token={token}'
content = sync_playwright().start()
browser = content.chromium.launch()
page = browser.new_page()
# 将线上路径的js文件替换为本地js文件
page.route(
'/js/chunk-10192a00.243cb8b7.js',
lambda route: route.fulfill(path='./playeright-change.js')
)
page.goto(BASE_URL)
# 调用window.x(this.$store.state.url.index,a)获取token
def get_token(offset):
result = page.evaluate(
'''
()=>{
return window.x("%s","%s")
}
''' % ('/api/movie', offset)
)
return result

for i in range(5):
offset = i * 10
token = get_token(offset)
get = requests.get(INDEX_URL.format(offset=offset, token=token))
print(get.json())
赏个🍗吧
0%