补环境系列-2

学习目标:

  1. 熟悉 jsvmp技术
  2. 熟悉 补环境的调试方法
  3. 掌握 插桩调试
  4. 掌握 自动吐环境操作
  5. 掌握 Selenium加载环境
  6. 熟悉 jsdom加载环境

一.环境检测

1. 什么是环境检测

  • 由于浏览器和node的差别,会导致浏览器的js代码在node没有办法执行,js代码会根据浏览器的这些属性来判断你是不是在真正的浏览器执行的代码,要不是正确的浏览器环境则不会返回正确的数据信息.
  • 拿到代码在node里面执行、经常看到这一类型的错误,提示xxx未定义,其实这一块就是浏览器对象的一些特征
1
2
3
4
 if (navigator['userAgent']){
^

ReferenceError: navigator is not defined

2.案例讲解

  • 检测执行代码是否存在navigator, 可以通过补空的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14

navigator = {}
navigator.userAgent = '11111'

function ps(){
if (navigator['userAgent']){
return 'hello world'
} else {
return '失败'
}
}

console.log(ps());

  • 检测属性长度,会根据长度来判断你的数据是否正确,是不是一个空数据
1
2
3
4
5
6
7
8
9
10
11
12

location = {}
location.href = '123123'
function ps(){
if (location['href'].length > 3){
return 'hello world'
} else {
return '失败'
}
}

console.log(ps());
  • js异常代码捕获,很多情况下可能js代码会把异常给捕获掉导致我们结果不对
  • 可以输出异常捕获的内容, 或者可以直接把异常捕获的代码直接删除,把错误暴露出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
location = {}
location.host = '12334'
navigator = {}
navigator.userAgent = '1231234'
function pn() {
// try {
verify_local()
if (navigator['userAgent']) {
return 'hello world'
}
// } catch (e) {
// console.log(e)
// return '错误的数据'
// }
}

function verify_local() {
if (location.host.length > 2) {
return 'xxx'
}
}

console.log(pn());
  • 浏览器和node环境差异
  • 在 Node.js 中,exports 是一个用于导出模块中的函数、对象、变量等的对象。
  • 浏览器是undefined
  • 可以删除, 或者可以修改的判断成功
1
2
3
// 浏览器和 node差异
sss = "undefined" != typeof exports ? exports : void 0
console.log(typeof sss);
  • global检测
1
glb= "undefined" == typeof window ? global:window

二 .吐环境脚本

1. 简介

Proxy可以理解为,在目标对象之前设一层”拦截”,外界对该对象的访问,都必须通过这层拦截,可以对外界的访问进行过滤和改写(表示可以用它”代理”某些操作,可以翻为“代理器”)。

api地址: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

Proxy对象由两个部分组成:target、handler

target:目标对象
handler:是一个对象,声明了代理target的指定行为,支持的拦截操作,一共13种:

  • get(target,propKey,receiver):拦截对象属性的读取。
    • target: 目标对象
    • propKey: 被获取的属性名。
    • receiver: Proxy 或者继承 Proxy 的对象
  • set(target,propKey,value,receiver):拦截对象属性的设置,返回一个布尔值(修改成功)。
    • target: 目标对象
    • propKey : 被获取的属性名。
    • value: 新属性值。
    • receiver: Proxy 或者继承 Proxy 的对象

一般的补环境的是通过运行程序后的undefined报错去一点一点分析,一点一点的去补一些环境,是非常掉头发的。

所以我们使用 Proxy 对全局遍历window、document、navigator等常见环境检测点进行代理,拦截代理对象的读取、函数调用等操作,并通过控制台输出,这样的话我们就能够实现检测环境自吐的功能,后续我们再针对吐出来的环境统一的进行补环境,这样就会方便的多。

2. 基础使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var target = {
name: 'JACK',
age: 18,
};

var p = new Proxy(target, {

get: function (target, propertyKey, receiver) {
// 1 原对象
// 2 访问属性
// 3 代理器处理对象
console.log(target, propertyKey, receiver)
},
set: function(target,propertyKey,value,receiver){
// 1. 原对象
// 2. 设置的属性
// 3. 设置的值
// 4. 代理器代理的对象
console.log(target, propertyKey, value, receiver)
}
})
p.age
p.user = 'aa'

注意:

我们现在写的代码,已经能够去拦截到取值和设置的操作,但是这个代码会打乱代码的后续运行,还并没有把对应的操作作用在原对象上,怎么解决呢?

3.数据返回

1
2
Reflect.get(target, propertyKey, receiver); //查找并返回target对象的name属性,receiver绑定this
Reflect.set(target, propertyKey, value, receiver); //设置target对象的name属性等于value

Reflect.set(target, name, value, receiver) 是 JavaScript 中的 Reflect 对象的一个方法。它用于设置指定对象的属性值,并返回一个布尔值,表示设置是否成功。

参数的含义如下:

  • target:要设置属性值的目标对象。
  • propertyKey:要设置的属性名。
  • value:要设置的属性值。
  • receiver(可选):设置属性时绑定的 this 值。

4. 完整代理使用

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
var target = {
name: 'JACK',
age: 18,
};

var p = new Proxy(target, {

get: function (target, propertyKey, receiver) {
temp = Reflect.get(target, propertyKey, receiver); //查找并返回target对象的name属性,receiver绑定this
// 1 原对象
// 2 访问属性
// 3 代理器处理对象
// console.log(target, propertyKey, receiver)
console.log(`对象${target}--> get了属性--> ${propertyKey} 值是--> ${temp}`);
return temp
},
set: function(target,p,value,receiver){
temp = Reflect.set(target, p, value, receiver);
// 1. 原对象
// 2. 设置的属性
// 3. 设置的值
// 4. 代理器代理的对象
// console.log(target, propertyKey, value, receiver)
console.log("set: ", target, p, target[p]);
return temp
}
})
// console.log(p.age);
p.user = 'aa'
console.log(p.user)

5. 代理封装

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
// 目标对象(被代理对象)
var target = {
name: 'JACK',
age: 18,
lili:{
zs:'nana'

}
};

function XlProxy(obj,name){
return new Proxy(obj,{
get(target, p, receiver) {
temp = Reflect.get(target,p,receiver)
console.log(`对象${name}--> get了属性--> ${p} 值是--> ${temp}`);
if (typeof temp == 'object'){
// 对于对象套对象进行挂代理
temp = XlProxy(temp,name + '-->' + p)
}
return temp
}
})
}
sss = XlProxy(target,'target')
sss.name
sss.lili.zs

6. 封装所有使用方法

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
// 代理器封装
function get_enviroment(proxy_array) {
for(var i=0; i<proxy_array.length; i++){
handler = '{\n' +
' get: function(target, property, receiver) {\n' +
' console.log("方法:", "get ", "对象:", ' +
'"' + proxy_array[i] + '" ,' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
// '" 属性值:", ' + 'target[property], ' +
'" 属性值类型:", typeof target[property]);\n' +
' return target[property];\n' +
' },\n' +
' set: function(target, property, value, receiver) {\n' +
' console.log("方法:", "set ", "对象:", ' +
'"' + proxy_array[i] + '" ,' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
// '" 属性值:", ' + 'target[property], ' +
'" 属性值类型:", typeof target[property]);\n' +
' return Reflect.set(...arguments);\n' +
' }\n' +
'}'
eval('try{\n' + proxy_array[i] + ';\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}catch (e) {\n' + proxy_array[i] + '={};\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}')
}
}
proxy_array = ['window', 'document', 'location', 'navigator', 'history','screen']
get_enviroment(proxy_array)

7.吐环境实战案例

1. 逆向目标
2. 逆向结果
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
// 代理器封装
function get_enviroment(proxy_array) {
for(var i=0; i<proxy_array.length; i++){
handler = '{\n' +
' get: function(target, property, receiver) {\n' +
' console.log("方法:", "get ", "对象:", ' +
'"' + proxy_array[i] + '" ,' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
// '" 属性值:", ' + 'target[property], ' +
'" 属性值类型:", typeof target[property]);\n' +
' return target[property];\n' +
' },\n' +
' set: function(target, property, value, receiver) {\n' +
' console.log("方法:", "set ", "对象:", ' +
'"' + proxy_array[i] + '" ,' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
// '" 属性值:", ' + 'target[property], ' +
'" 属性值类型:", typeof target[property]);\n' +
' return Reflect.set(...arguments);\n' +
' }\n' +
'}'
eval('try{\n' + proxy_array[i] + ';\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}catch (e) {\n' + proxy_array[i] + '={};\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}')
}
}
proxy_array = ['window', 'document', 'location', 'navigator', 'history','screen']

window = global;
window.addEventListener = function(args){
console.log('window的addEventListener:', args)
}

document = {
head: {},
createElement: function (args) {
console.log('document的createElement:', args)
if (args == 'div'){
return {}
}

},
addEventListener: function (args) {
console.log('document的addEventListener:', args)
},
// documentElement: function (args) {
// console.log('document的documentElement:', args)
// }
}

// navigator = {
// userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
// }

get_enviroment(proxy_array)


n[e[57]] = P;
function D() {
return O()
}
window.aaaa = D;
n[v(an, a[198], r[199])] = D


function aaa() {
console.log(window.aaaa());
// console.log(document.cookie);
}
aaa()

三.Selenium补环境

1. 简介

Selenium就是一个真实的环境地址,对于我们拿下来的js代码,在node是需要补环境的,但是在浏览器去执行的话,他就是一个真实的浏览器环境,所以可以节省我们扣代码的时间,我们可以把扣下来的代码直接用Selenium来进行访问

2.实战案例

1. 逆向目标
2. 实现代码
  • 我们把同花顺的js代码直接放到html文件

  • python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

import os
from selenium import webdriver

PRO_DIR = os.path.dirname(os.path.abspath(__file__))
def driver_sig(html_file):
option = webdriver.ChromeOptions()
option.add_argument('--disable-blink-features=AutomationControlled')
option.add_argument('headless')
driver = webdriver.Chrome(options=option)
driver.get(PRO_DIR +'\\'+ html_file)
# time.sleep(2)
# sig = driver.execute_script('return window.aaa()')
# print(sig)
return driver

html_file = 'index.html'
driv = driver_sig(html_file)

print(driv.execute_script('return window.aaa()'))

3.实现接口

1
pip install flask
1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask

# 创建 Flask 应用实例
app = Flask(__name__)

# 定义路由和视图函数
@app.route('/')
def hello():
return 'Hello, Flask!'

# 启动应用
if __name__ == '__main__':
app.run()
  • Flask 是一个基于 Python 的轻量级、简单易用的 Web 应用框架。它提供了一个灵活且容易扩展的方式来构建 Web 应用程序。以下是一个简单的示例展示了如何使用 Flask 框架创建一个简易的 Web 应用:

  • 在上述示例中,我们首先导入了 Flask 模块,并创建了一个 Flask 应用实例 app。然后,使用 @app.route() 装饰器定义了一个路由以及对应的视图函数。在本例中,根路由 '/' 对应的视图函数是 hello(),它返回了一个简单的字符串 'Hello, Flask!'。最后,通过调用 app.run() 来启动应用。

  • 要运行这个应用,你需要确保已经安装了 Flask 模块。运行应用后,在浏览器中访问 http://localhost:5000/,你将看到输出的 'Hello, Flask!'

1.实际使用
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
# -*- coding: utf-8 -*-

from flask import Flask, request

from selenium import webdriver
import os
from selenium.webdriver.common.by import By
# pip install flask
from flask import Flask, jsonify

PRO_DIR = os.path.dirname(os.path.abspath(__file__))


def driver_sig(html_file):
option = webdriver.ChromeOptions()
option.add_argument('--disable-blink-features=AutomationControlled')
option.add_argument('headless')
driver = webdriver.Chrome(options=option)
driver.get(PRO_DIR + '\\' + html_file)
# time.sleep(2)
# sig = driver.execute_script('return window.aaa()')
# print(sig)
return driver


html_file = 'index.html'
driv = driver_sig(html_file)

# 创建 Flask 应用实例
app = Flask(__name__)


# 定义路由和视图函数
@app.route('/s', methods=['get', 'post'])
def hello():
context = {
# 加载本地地址 生成cookie值
'v': driv.execute_script('return window.aaa()')
}
# 返回cookie值
return jsonify(context=context)



# 启动应用
if __name__ == '__main__':
app.run()

四.jsdom补环境

参考地址:https://github.com/jsdom/jsdom/wiki/jsdom-中文文档

jsdom是一个纯粹由 javascript 实现的一系列 web标准,特别是 WHATWG 组织制定的DOMHTML 标准,用于在nodejs中使用。大体上来说,该项目的目标是模拟足够的Web浏览器子集,以便用于测试和挖掘真实世界的Web应用程序

1. 环境安装

1
npm install jsdom --save

2. 基本使用

1
2
3
4
5
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`);
title = dom.window.document.querySelector("p").textContent
console.log(title)

3. 添加参数形式

1
2
3
4
5
6
7
const dom = new JSDOM(``, {
url: "http://q.10jqka.com.cn/",
referrer: "http://q.10jqka.com.cn/",
contentType: "text/html",
includeNodeLocations: true,
storageQuota: 10000000
});

五.了解jsvmp技术

1. js虚拟机保护方案

参考文章:https://mp.weixin.qq.com/s/YDx5Dr-HDfAm-sAqeWW0qg

  • JSVMP 的概念最早应该是由西北大学2015级硕士研究生匡开圆,在其2018年的学位论文中提出的,论文标题为:《基于 WebAssembly 的 JavaScript 代码虚拟化保护方法研究与实现》,同年还申请了国家专利,专利名称:《一种基于前端字节码技术的 JavaScript 虚拟化保护方法》,网上可以直接搜到
  • 常见的jsvmp的实现方法,你可以理解为:就是自己写了一段代码解释器,用来解释自己的代码 而这个自己的代码:可以是密文,也可以是所谓的明文

2.jsvmp实现原理

  • JSVMP 的核心是在 JavaScript 代码保护过程中引入代码虚拟化思想,实现源代码的虚拟化过程,将目标代码转换成自定义的字节码,这些字节码只有特殊的解释器才能识别,隐藏目标代码的关键逻辑。在匡开圆的论文中,利用 WebAssembly 技术实现了特殊的虚拟解释器,通过编译隐藏解释器的执行逻辑。JSVMP 的保护流程如下图所示:

  • 大致的架构应该是这样子的:服务器端读取 JavaScript 代码 —> 词法分析 —> 语法分析 —> 生成AST语法树 —> 生成私有指令 —> 生成对应私有解释器,将私有指令加密与私有解释器发送给浏览器,然后一边解释,一边执行。

3. 模拟jsvmp执行过程

  • 准备数据
1
2
3
4
5
var a = '丽丽'
var b = '菲菲'
var c = '莹莹'
var d = a+b
var e = c + d
  • 将数据进行第一次转换
1
2
3
4
5
6
7
8
9
10
11
12
var a ;
a = '丽丽'
var b;
b = '菲菲'
var c;
c = '莹莹'
var d;
a+b;
d = ?;
var e;
c+d;
e = ?;
  • 再次转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 我们假设赋值指令为 1, 加和指令为 2,声明指令为 3
// 如果按照从上到下的顺序,我们就可以将他们的操作变成指令性的[用|分割左侧和右侧]
// 1 赋值
// 2 加
// 3 声明

3 --- var | a
1 --- a | '丽丽'
3 --- var | b
1 --- b | '菲菲'
3 --- var | c
1 --- c | '莹莹'
3 --- var | d
2 --- a | b -----> ?
1 --- d | ?
3 --- var | e
2 --- d | c -----> ?
1 --- e | ?(此处的d 与 c的和)
  • 在将数据压缩到数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
_stack = [
[3, 'var', 'a'],
[1, 'a', '丽丽'],
[3, 'var', 'b'],
[1, 'b', '菲菲'],
[3, 'var', 'c'],
[1, 'c', '莹莹'],
[3, 'var', 'd'],
[2, 'a', 'b'],
[1, 'd', '?'],
[3, 'var', 'e'],
[2, 'd', 'c'],
[1, 'e', '?'],
]
  • 通过自己写的自执行函数,对数据进行处理
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
!function(_stack) {
var register; // 这个就当做是问号的存储位置
var variable = {}; // 这个就当做是var变量的存储位置。由于没有其他声明方式的存在,所就不写其他的了
for (let i = 0; i < _stack.length; i++) {
instruct = _stack[i][0];
left = _stack[i][1];
right = _stack[i][2];
if (instruct === 3) {
variable[right] = ''
}
if (instruct === 1) {
if (right === '?') {
variable[left] = register
} else {
variable[left] = right
}
}
if (instruct === 2) {
register = variable[left] + variable[right]
}
};
console.log(variable)
} ([
[3, 'var', 'a'],
[1, 'a', '丽丽'],
[3, 'var', 'b'],
[1, 'b', '菲菲'],
[3, 'var', 'c'],
[1, 'c', '莹莹'],
[3, 'var', 'd'],
[2, 'a', 'b'],
[1, 'd', '?'],
[3, 'var', 'e'],
[2, 'd', 'c'],
[1, 'e', '?'],
]
)

  • 实际jsvmp会更加的复杂,这个是基本的逻辑,就行自己写一个解释器来解释自己的代码

关于jsvmp的解法一般有3种,补环境,和插桩扣逻辑,jsrpc,当然还有自动化等方式可自行研究试试

六. 项目实战

1. 案例

1. 逆向目标
2.逆向分析
  • 通过xhr定位加密位置
  • xhr定位数据在xhr之前,我们需要跟栈找数据位置
  • 定位的位置在上一个栈
  • 可以通过日志调试查看数据信息
  • 通过条件断点,准确定位到那一次信息
  • 进去之后就是单独的自执行方法为jsvmp方法
3.逆向结果
  • JavaScript代码
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
// 代理器封装
function get_enviroment(proxy_array) {
for (var i = 0; i < proxy_array.length; i++) {
handler = '{\n' +
' get: function(target, property, receiver) {\n' +
' console.log("方法:", "get ", "对象:", ' +
'"' + proxy_array[i] + '" ,' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
// '" 属性值:", ' + 'target[property], ' +
'" 属性值类型:", typeof target[property]);\n' +
' return target[property];\n' +
' },\n' +
' set: function(target, property, value, receiver) {\n' +
' console.log("方法:", "set ", "对象:", ' +
'"' + proxy_array[i] + '" ,' +
'" 属性:", property, ' +
'" 属性类型:", ' + 'typeof property, ' +
// '" 属性值:", ' + 'target[property], ' +
'" 属性值类型:", typeof target[property]);\n' +
' return Reflect.set(...arguments);\n' +
' }\n' +
'}'
eval('try{\n' + proxy_array[i] + ';\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}catch (e) {\n' + proxy_array[i] + '={};\n'
+ proxy_array[i] + '=new Proxy(' + proxy_array[i] + ', ' + handler + ')}')
}
}

proxy_array = ['window', 'document', 'location', 'navigator', 'history', 'screen']

window = global
window.requestAnimationFrame = function (args) {
console.log('window的requestAnimationFrame参数', args)
}
window._sdkGlueVersionMap = {
"sdkGlueVersion": "1.0.0.51",
"bdmsVersion": "1.0.1.5",
"captchaVersion": "4.0.2"
}

XMLHttpRequest = function (args) {
console.log('XMLHttpRequest', args)
}

document = {
createElement: function (arg) {

console.log('document的createElement参数', arg)
if (arg == 'span') {
return {}
}
},
documentElement: function (arg) {
console.log('document的documentElement参数', arg)
},
createEvent: function (arg) {
console.log('document的createEvent参数', arg)
},
all: []
}


navigator = {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'

}

get_enviroment(proxy_array)


!function () {


function e(r, t, n, a, f) {
var c, s, u, b, l, d = -1, p = [], h = null, v = [t];
for (s = Math.min(t.length, n),
u = 0; u < s; ++u)
v.push(t[u]);
v.p = a;
for (var g = []; ;)
try {
var m = i[r++];
if (m < 37)
if (m < 18)
if (m < 7)
m < 3 ? p[++d] = m < 1 || 1 !== m && null : m < 4 ? (c = i[r++],
p[++d] = c << 24 >> 24) : 4 === m ? (c = (i[r] << 8) + i[r + 1],
r += 2,
p[++d] = c << 16 >> 16) : (c = ((c = ((c = i[r++]) << 8) + i[r++]) << 8) + i[r++],
p[++d] = (c << 8) + i[r++]);
else if (m < 13)
m < 8 ? (c = (i[r] << 8) + i[r + 1],
r += 2,
p[++d] = o[c]) : 8 === m ? p[++d] = void 0 : (c = (i[r] << 8) + i[r + 1],
r += 2,
d = d - c + 1,
s = p.slice(d, d + c),
p[d] = s);
else if (m < 14)
p[++d] = {};
else if (14 === m)
c = (i[r] << 8) + i[r + 1],
r += 2,
s = o[c],
u = p[d--],
p[d][s] = u;
else {
for (s = i[r++],
u = i[r++],
b = v; s > 0; --s)
b = b.p;
p[++d] = b[u]
}
else if (m < 26)
if (m < 22)
if (m < 19)
c = (i[r] << 8) + i[r + 1],
r += 2,
s = o[c],
p[d] = p[d][s];
else if (19 === m)
s = p[d--],
p[d] = p[d][s];
else {
for (s = i[r++],
u = i[r++],
b = v; s > 0; --s)
b = b.p;
b[u] = p[d--]
}
else if (m < 23)
s = p[d--],
u = p[d--],
b = p[d--],
u[s] = b;
else if (23 === m) {
for (s = i[r++],
u = i[r++],
b = v,
b = v; s > 0; --s)
b = b.p;
p[++d] = b,
p[++d] = u
} else
s = p[d--],
p[d] += s;
else
m < 29 ? m < 27 ? (s = p[d--],
p[d] *= s) : 27 === m ? (s = p[d--],
p[d] /= s) : (s = p[d--],
p[d] %= s) : m < 35 ? 29 === m ? p[d] = -p[d] : (s = p[d--],
b = (u = p[d--])[s]++,
p[++d] = b) : 35 === m ? (s = p[d--],
p[d] = p[d] == s) : (s = p[d--],
p[d] = p[d] != s);
else if (m < 53)
m < 47 ? m < 41 ? m < 38 ? (s = p[d--],
p[d] = p[d] === s) : 38 === m ? (s = p[d--],
p[d] = p[d] !== s) : (s = p[d--],
p[d] = p[d] < s) : m < 44 ? (s = p[d--],
p[d] = p[d] > s) : 44 === m ? (s = p[d--],
p[d] = p[d] >> s) : (s = p[d--],
p[d] = p[d] & s) : m < 50 ? m < 48 ? (s = p[d--],
p[d] = p[d] | s) : 48 === m ? p[d] = ~p[d] : (s = p[d--],
p[d] = p[d] ^ s) : m < 51 ? p[d] = !p[d] : 51 === m ? (c = (c = (i[r] << 8) + i[r + 1]) << 16 >> 16,
r += 2,
p[d] ? --d : r += c) : (c = (c = (i[r] << 8) + i[r + 1]) << 16 >> 16,
r += 2,
p[d] ? r += c : --d);
else if (m < 66)
if (m < 58)
m < 54 ? (s = p[d--],
(u = p[d--])[s] = p[d]) : 54 === m ? (s = p[d--],
p[d] = p[d] in s) : p[d] = void 0;
else if (m < 59)
p[d] = typeof p[d];
else {
if (59 !== m)
throw s = p[d--];
c = i[r++],
s = p[d--],
(u = function e() {
var r = e._v;
return (0,
e._u)(r[0], arguments, r[1], r[2], this)
}
)._v = [s, c, v],
u._u = e,
p[++d] = u,
window._U = u
}
else if (m < 69)
if (m < 67) {
for (s = p[d--],
u = null; b = g.pop();)
if (2 === b[0] || 3 === b[0]) {
u = b;
break
}
if (u)
r = u[2],
u[0] = 0,
g.push(u);
else {
if (!h)
return s;
r = h[1],
f = h[2],
v = h[3],
g = h[4],
p[++d] = s,
h = h[0]
}
} else if (67 === m)
d -= c = i[r++],
u = p.slice(d + 1, d + c + 1),
s = p[d--],
b = p[d--],
s._u === e ? (s = s._v,
h = [h, r, f, v, g],
r = s[0],
null == b && (b = function () {
return this
}()),
f = b,
(v = [u].concat(u)).length = Math.min(s[1], c) + 1,
v.p = s[2],
g = []) : (l = s.apply(b, u),
p[++d] = l);
else {
for (c = i[r++],
b = [void 0],
l = c; l > 0; --l)
b[l] = p[d--];
u = p[d--],
l = new (s = Function.bind.apply(u, b)),
p[++d] = l
}
else
m < 73 ? 69 === m ? r += 2 + (c = (c = (i[r] << 8) + i[r + 1]) << 16 >> 16) : (c = (c = (i[r] << 8) + i[r + 1]) << 16 >> 16,
r += 2,
(s = p[d--]) || (r += c)) : 73 === m ? --d : (s = p[d],
p[++d] = s)
} catch (e) {
for (; (c = g.pop()) && !c[0];)
;
if (!c) {
e: for (; h;) {
for (s = h[4]; c = s.pop();)
if (c[0])
break e;
h = h[0]
}
if (!h)
throw e;
r = h[1],
f = h[2],
v = h[3],
g = h[4],
h = h[0]
}
1 === (s = c[0]) ? (r = c[2],
c[0] = 0,
g.push(c),
p[++d] = e) : 2 === s ? (r = c[2],
c[0] = 0,
g.push(c)) : (r = c[3],
c[0] = 2,
g.push(c),
p[++d] = e)
}
}

(u, [], 0, r, t)


}();


function get_a_bogus(arg) {
var r = window._U._v;
console.log(r)
return window._U._u(r[0], arg, r[1], r[2], null)
}


function get_ab(arguments) {

arg = [
0,
1,
0,
arguments,
"",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
]
var data = get_a_bogus(arg)
return data
}


// arg = 'device_platform=webapp&aid=6383&channel=channel_pc_web&sec_user_id=MS4wLjABAAAAyGi6PcV0GZZRrjzymBW6N942bvw1A-rT5RWuc5rHTwg&max_cursor=1709713733000&locate_item_id=7347993775496629542&locate_query=false&show_live_replay_strategy=1&need_time_list=0&time_list_query=0&whale_cut_token=&cut_version=1&count=18&publish_video_strategy_type=2&update_version_code=170400&pc_client_type=1&version_code=290100&version_name=29.1.0&cookie_enabled=true&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=124.0.0.0&browser_online=true&engine_name=Blink&engine_version=124.0.0.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=100&webid=7367233937146922550'
//
// data = get_ab(arg)
// console.log(data, data.length)

  • 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
import requests
import execjs
from loguru import logger
from concurrent.futures import ThreadPoolExecutor
import os

requests = requests.session()


class Dy_vido:
def __init__(self):
self.url_search = "https://www.douyin.com/aweme/v1/web/general/search/single/"
self.url_api = "https://www.douyin.com/aweme/v1/web/aweme/post/"
self.headers = {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9",
"cache-control": "no-cache",
"pragma": "no-cache",
"priority": "u=1, i",
"referer": "https://www.douyin.com/user/MS4wLjABAAAAldrNlnQQf3JhlLukKhaL9iqca0V6Gkj15x_n6SL8I1UOjnfumyxnX4f5qCRAX_KP",
"sec-ch-ua": "\"Chromium\";v=\"124\", \"Google Chrome\";v=\"124\", \"Not-A.Brand\";v=\"99\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
}
self.cookies = {
"douyin.com": "",
"device_web_cpu_core": "16",
"device_web_memory_size": "8",
"architecture": "amd64",
"dy_swidth": "1707",
"dy_sheight": "960",
"csrf_session_id": "aca2b514d419a67ec1d3712f9a4dc796",
"xgplayer_user_id": "507803728518",
"ttwid": "1%7Cl5T98nlCqMCO7T0d5uY96_2rbhkTHkXngPnjzpmHUqI%7C1713185942%7Cc8ac8722f14814307857a9ae165ae4a3790c018c2c31c237d21420ed31711c12",
"passport_csrf_token": "283a2650bc26bb72a24567ae1b2ec8eb",
"passport_csrf_token_default": "283a2650bc26bb72a24567ae1b2ec8eb",
"bd_ticket_guard_client_web_domain": "2",
"s_v_web_id": "verify_lv0yr1ds_du5mRJsn_QRSm_43px_87jz_BrwhS6w2XF7G",
"d_ticket": "07b0468c1986f139b9b978cd117e14ffc8183",
"n_mh": "07vLiXVhkr6Gw426Bs6p1qWtmm_l_Xu5Jlf1_2HXC3I",
"LOGIN_STATUS": "1",
"_bd_ticket_crypt_doamin": "2",
"__security_server_data_status": "1",
"store-region": "cn-hb",
"store-region-src": "uid",
"volume_info": "%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Afalse%2C%22volume%22%3A1%7D",
"xgplayer_device_id": "44066781593",
"my_rd": "2",
"pwa2": "%220%7C0%7C3%7C0%22",
"SEARCH_RESULT_LIST_TYPE": "%22single%22",
"download_guide": "%223%2F20240423%2F1%22",
"FORCE_LOGIN": "%7B%22videoConsumedRemainSeconds%22%3A180%2C%22isForcePopClose%22%3A1%7D",
"passport_assist_user": "CkEbt8_PRee0QqNldlg_vuDKflZ08ZUy940T20VnRV8DeQ7XhefuiXMhGzPFO_PPn1WDeM8pKsj-wPJHM_KXmDg0HRpKCjzxFVLw-ycHEX01ficJvCsU57VaC_SS3-UA3ql9dIWu64PHQHueH2OO0QMqOj-aN3oStH6KSnwO_HEgipgQ6tfPDRiJr9ZUIAEiAQPHhKJY",
"sso_auth_status": "efadc6b39c77be11a67f7f6ce3fa432d",
"sso_auth_status_ss": "efadc6b39c77be11a67f7f6ce3fa432d",
"sso_uid_tt": "a651e1bb5d18167a9b46f2beb2247d62",
"sso_uid_tt_ss": "a651e1bb5d18167a9b46f2beb2247d62",
"toutiao_sso_user": "cf9df014482a7540219eb9242192d586",
"toutiao_sso_user_ss": "cf9df014482a7540219eb9242192d586",
"sid_ucp_sso_v1": "1.0.0-KDVjNTZlMWVhMjU4N2VhZjU3NzYzMzRlYmU4NzBiYWI5OWYxYzI0OGEKHwjHkOCI2YyDBBDW262xBhjvMSAMMLei5JoGOAJA8QcaAmxmIiBjZjlkZjAxNDQ4MmE3NTQwMjE5ZWI5MjQyMTkyZDU4Ng",
"ssid_ucp_sso_v1": "1.0.0-KDVjNTZlMWVhMjU4N2VhZjU3NzYzMzRlYmU4NzBiYWI5OWYxYzI0OGEKHwjHkOCI2YyDBBDW262xBhjvMSAMMLei5JoGOAJA8QcaAmxmIiBjZjlkZjAxNDQ4MmE3NTQwMjE5ZWI5MjQyMTkyZDU4Ng",
"passport_auth_status": "d15c2e08dad1e23895d9c664654c1084%2C13da54fe5f1fbee864c0b8e80e641be7",
"passport_auth_status_ss": "d15c2e08dad1e23895d9c664654c1084%2C13da54fe5f1fbee864c0b8e80e641be7",
"uid_tt": "00bda122099d4f86db207d4877cc5f9f",
"uid_tt_ss": "00bda122099d4f86db207d4877cc5f9f",
"sid_tt": "35f03fdeb189545ddbbe75bbf136685e",
"sessionid": "35f03fdeb189545ddbbe75bbf136685e",
"sessionid_ss": "35f03fdeb189545ddbbe75bbf136685e",
"_bd_ticket_crypt_cookie": "0e831c2cdfd22a60e55918e219aed7c1",
"sid_guard": "35f03fdeb189545ddbbe75bbf136685e%7C1714122209%7C5183992%7CTue%2C+25-Jun-2024+09%3A03%3A21+GMT",
"sid_ucp_v1": "1.0.0-KGM0MzczYTIzNjlkZmVlMGI5Y2YyODg5NjUwMzc0ODUxNzIzNDM4Y2MKGwjHkOCI2YyDBBDh262xBhjvMSAMOAJA8QdIBBoCaGwiIDM1ZjAzZmRlYjE4OTU0NWRkYmJlNzViYmYxMzY2ODVl",
"ssid_ucp_v1": "1.0.0-KGM0MzczYTIzNjlkZmVlMGI5Y2YyODg5NjUwMzc0ODUxNzIzNDM4Y2MKGwjHkOCI2YyDBBDh262xBhjvMSAMOAJA8QdIBBoCaGwiIDM1ZjAzZmRlYjE4OTU0NWRkYmJlNzViYmYxMzY2ODVl",
"publish_badge_show_info": "%220%2C0%2C0%2C1714122210977%22",
"__live_version__": "%221.1.1.9879%22",
"live_can_add_dy_2_desktop": "%220%22",
"live_use_vvc": "%22false%22",
"stream_recommend_feed_params": "%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A1707%2C%5C%22screen_height%5C%22%3A960%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A16%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A50%7D%22",
"strategyABtestKey": "%221714278043.162%22",
"passport_fe_beating_status": "true",
"__ac_signature": "_02B4Z6wo00f01AzwHuwAAIDDMVkEcZTWSOwM0BpAAGUY31U7alOJ0FycLF.18.sDKHM1IWDRfseUWqYYSYLKJtfRfP6-1XJgqlFRSLCNxbwJrT70nt3ZA7vC4RBK-4U3f9r7Rvl1sh.tARoe39",
"xg_device_score": "7.78551698226827",
"WallpaperGuide": "%7B%22showTime%22%3A1714133771488%2C%22closeTime%22%3A0%2C%22showCount%22%3A2%2C%22cursor1%22%3A50%2C%22cursor2%22%3A0%2C%22hoverTime%22%3A1713863026166%7D",
"stream_player_status_params": "%22%7B%5C%22is_auto_play%5C%22%3A1%2C%5C%22is_full_screen%5C%22%3A0%2C%5C%22is_full_webscreen%5C%22%3A0%2C%5C%22is_mute%5C%22%3A0%2C%5C%22is_speed%5C%22%3A1%2C%5C%22is_visible%5C%22%3A0%7D%22",
"IsDouyinActive": "true",
"home_can_add_dy_2_desktop": "%221%22",
"bd_ticket_guard_client_data": "eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtcmVlLXB1YmxpYy1rZXkiOiJCQmRzNmUvQStNczVFcFIveHBQb3BESEp6Wm9QbGZ3UWtCYjdlQVlRR2xTVlQ2YkxrQkdZV0l6V2JNaVhVSjJsUjJwUDIraFRKMjJLSllNZmIrNTAybWM9IiwiYmQtdGlja2V0LWd1YXJkLXdlYi12ZXJzaW9uIjoxfQ%3D%3D",
"odin_tt": "608458b8484214e7a897698aad4f36d9c853fd4f9cd18a8360c55cfa12df9e0032b8967c75ddaeba940a82ca5067e252",
"msToken": "mpCRhFSSOGW5qYEomUka1BtJ1k_FlErWXm38gr110DNbF22aa3bfu0CeUafkUeq8B5ThBBvRRhnqFK8G0cfxwvtSAHndDKEk_6RXlYBuhH-eHMEAhgcBTdAIwiMtRbfbng=="
}
self.js = execjs.compile(open("1111.js", encoding="utf-8").read())
def get_video(self):
params = "device_platform=webapp&aid=6383&channel=channel_pc_web&sec_user_id=MS4wLjABAAAAyGi6PcV0GZZRrjzymBW6N942bvw1A-rT5RWuc5rHTwg&max_cursor=1710063607000&locate_item_id=7347993775496629542&locate_query=false&show_live_replay_strategy=1&need_time_list=0&time_list_query=0&whale_cut_token=&cut_version=1&count=18&publish_video_strategy_type=2&update_version_code=170400&pc_client_type=1&version_code=290100&version_name=29.1.0&cookie_enabled=true&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Edge&browser_version=124.0.0.0&browser_online=true&engine_name=Blink&engine_version=124.0.0.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=200&webid=7368067800928552457&verifyFp=verify_lw3fjlx9_2kadDKDS_WPRK_4AMh_8atO_ZDTzGcHPryci&fp=verify_lw3fjlx9_2kadDKDS_WPRK_4AMh_8atO_ZDTzGcHPryci&msToken=9xVkCpm3P_Hqi33kNjaT0sDA-n1wDVsfrfot3FCF3pcgBuU99FceWq_D5NVVTfBNPp4oazC3PAfFhW4sHVh0CLBrvXziaWe97ikZN8ePAHOKIyjhTg0KPX5vP3FmsXk%3D&msToken=9xVkCpm3P_Hqi33kNjaT0sDA-n1wDVsfrfot3FCF3pcgBuU99FceWq_D5NVVTfBNPp4oazC3PAfFhW4sHVh0CLBrvXziaWe97ikZN8ePAHOKIyjhTg0KPX5vP3FmsXk%3D"
ab = self.js.call("get_ab", params)
# print(ab)
url = self.url_api + "?" + params + '&a_bogus=' + ab
# print(url)
response = requests.get(url, headers=self.headers, cookies=self.cookies).json()
for item in response['aweme_list']:
items = {}
items['nickname'] = item['author']['nickname']
items['video_url'] = item['video']['play_addr']['url_list'][0]
items['video_title'] = item['preview_title'].split("#")[0]
print(items)
def main(self):
self.get_video()



if __name__ == '__main__':
dy = Dy_vido()
dy.main()