js速成基础

笔记转载来自:HD236300的个人空间-HD236300个人主页-哔哩哔哩视频

JS基础

认识 JavaScript

1
2
3
网页开发三大核心之一
HTML(骨架) + CSS(衣服) + JavaScript(动作)
用途广泛:前后端开发、手机app开发、pc端应用程序开发

最基本的概念

1
2
3
4
5
6
7
8
(复习:
解释型语言:将源代码翻译为机器码进行执行,执行速度慢
编译型语言:将源代码编译为可执行文件,直接运行可执行文件,运行速度快)
js 是解释型语言,但做了非常多的优化,相比其他解释型语言性能较高
就像 Python 解释器,js 也依赖于 JavaScript 引擎进行运行

为什么不叫 JavaScript 解释器?
因为采用了一些其他的技术进行优化,不能简单的归类为解释型语言

V8 引擎

1
2
3
4
5
6
7
8
9
10
11
12
13
在学习过程中,我们会接触两个东西:
1. 谷歌浏览器内核
打开浏览器访问一个网页,就会运行 js 代码
这个时候的代码是利用谷歌浏览器内核进行运行的
2. node.js
不需要依赖浏览器,使用 node 解释器进行运行

两者都是在 V8 引擎的基础上进行实现的
也就是说:
1. 实际上我们只需要有 V8 引擎就能执行 js 代码
2. 谷歌内核和 node 只是在 V8 的基础上提供了一些内置对象给你进行使用
3. 两者的用途不同,提供的内置对象也不一样
4. 现在不懂没关系,后面会再回过头来复习

在谷歌浏览器内核环境运行 js 代码

1
2
3
4
5
edge 和 国内一些浏览器使用的都是谷歌的内核,但都会做一些修改,建议使用谷歌浏览器
1. 通过 HTML 运行
内嵌式:在 script 标签中编写 js 代码
外链式:通过 script 标签的 src 属性引入 js 文件
2. 使用控制台或临时脚本

在 node.js 环境运行 js 代码

1
2
3
4
5
6
1. 安装 node.js
下载链接:https://nodejs.org/zh-cn
安装教学:https://blog.csdn.net/Nicolecocol/article/details/136788200

2. 安装代码编辑器,需要专业版 pycharm
下载链接:https://www.jetbrains.com/zh-cn/pycharm/

ES 语法标准

1
2
3
4
5
6
7
8
规定了 JavaScript 的语法标准
JavaScript 引擎需要实现这些标准(比如需要有整数类型和字符串类型数据,类似这样的规定)
所以世界上其实有很多由不同的人实现的 JavaScript 引擎,V8 只是最常用的一个

ES 语法标准经历过数次更新
目前最新的标准为 ES6,2015 年更新
后续也有很多更新的版本,但更新内容不大,也不是非常必要
了解下即可,现在 ES6 标准已经普及了

打印

1
console.log,等价于 print

定义变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var:
可重复声明
默认值为 undefined(代表未定义,可理解为 py 中的 None)
变量提升(可以在声明前访问变量,值为 undefined)
函数作用域(在函数内使用 var 声明的变量,只在当前函数内可用)

let(ES6):
不能重复声明
不会变量提升
块级作用域(仅在当前代码块(大括号)中可以访问)

const(ES6):
不能重复声明
值不能修改
块级作用域

定义未声明变量:
全局作用域

严格模式

1
2
3
4
5
在 js 文件开头写上:
"use strict";
会启用严格模式,严格模式下:
1. 不允许定义没有经过声明的变量
2. this 默认指向 undefined

算术运算符

1
2
3
4
5
6
7
加减乘除、取余
+=

a++,等价于 a += 1
a++:先得到a,再加1
++a:先加1,再得到a
a--,等价于 a -= 1

比较运算符

1
2
3
==,比较值是否相等
===,比较值和类型是否相等
!=,!==

逻辑运算符

1
2
3
&&,等价于 and
||,等价于 or
!,等价于 not

数据类型

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
整数、浮点数(Number)、布尔(Boolean,true 和 false)

null:代表空值(通常手动赋值),转为布尔类型表现为 false
undefined:代表未定义(默认值),转为布尔类型表现为 false
NaN:数字类型,代表无效的数字,转为布尔类型表现为 false

类型转换:
比如:Number('1')
转浮点数:parseFloat
转整数:parseInt

字符串(String):
length 属性,获得长度
(可以取索引,但不能使用负数和切片,索引越界也不会报错)
slice 方法,切片,只有两个参数
includes 方法,判断是否包含某个字符,在较新的 ES 标准中添加
indexOf 方法,判断是否包含某个字符,并返回索引
split 方法,同 py
replace 方法,只会替换第一个匹配的
replaceAll 方法,同 py replace,在较新的 ES 标准中添加
格式化字符串(ES6):`Hello ${name}`

数组(Array):
可以包含不同类型类型数据(同 py)
length 属性,获得长度
push 方法,添加元素
(可以取索引,但不能使用负数和切片,索引越界也不会报错)
slice 方法,切片,只有两个参数
includes 方法,判断元素是否存在于数组中,在较新的 ES 标准中添加
indexOf 方法,得到相等的第一个元素在数组中的索引,没有则返回 -1
concat 方法,合并两个数组,不能像 py 使用加法运算
join 方法,同 py 字符串的 join 方法

对象(Object):
js 没有字典,使用对象进行代替
我们定义的对象都是 Object 类的实例对象(几乎所有对象都是 Object 类的实例对象)
Object.keys 方法,返回对象的所有属性名称
Object.values 方法,返回对象的所有属性值
取不存在的属性值时,不会报错
判断两个对象是否相等时,判断的是内存地址是否相等,而不是值
取属性有两种方式:
obj.attr
obj["attr"]

typeof:
用于得到数据类型,写法:typeof a
typeof null; // "object"(历史遗留 bug)
null === a,判断一个变量是否是 null
typeof [1, 2]; // "object"(无法区分数组和普通对象)
Array.isArray 方法:判断参数是否是数组
typeof NaN; // "number"(无法区分无效数字)
Number.isNaN 方法:判断参数是否是 NaN

加法运算:
数字 + 数字
数字 + 布尔、null,右侧被转为数字
数字 + undefined,undefined 被转为 NaN
数字 + 对象,会使用对象的 valueOf 方法的返回值进行计算,类似 py 类中的魔法方法(如 __len__)
字符串 + 字符串
字符串 + 任意类型,右侧被转为字符串
字符串 + 对象,会使用对象的 toString 方法的返回值进行计算

条件判断

1
2
3
4
5
6
7
8
9
10
11
if (条件) {
条件为真时执行
} else if (条件2) {
条件2为真时执行
} else {
以上条件都为假时执行
}
和 py 最大的区别是用大括号代表 py 中的缩进
没有 elif 关键字
条件需要放在括号中
简写:如果 条件为真时执行 只有一行代码的话,可以省略大括号

三元表达式

1
2
条件 ? 条件为真时执行并返回 : 条件为假时执行并返回
三元表达式可以嵌套很多层,可读性非常差,真实的开发中不会有人这样做

while 循环

1
2
3
4
5
6
7
8
9
while (条件) {
条件为真时循环执行
}
先判断,再循环

do {
至少执行一次的循环体
} while (条件)
先循环,再判断

for 循环形式一

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
for (定义变量; 条件; 改变变量) {
条件为真时循环执行
}
for (循环开始时执行一次; 条件(每次循环开始前执行一次); 每次循环结束后执行一次)

所以以下的写法基本上都是等价的:
for (let i = 0; i < 5; i++) {
console.log(i);
}

let i = 0;
for (; i < 5; i++) {
console.log(i);
}

let i = 0;
for (; i < 5; ) {
console.log(i);
i++;
}

let i = 0;
while (i < 5) {
console.log(i);
i++;
}
可以看出这种形式的 for 循环和 while 循环基本没有区别

for 循环形式二

1
2
3
4
5
for (let i of 可迭代对象) {}
等价于 py:for i in 可迭代对象:

可迭代对象只是一种叫法,比如数组
但 js 中没有字典,对象也不能直接遍历

for 循环形式三

1
2
3
4
5
6
for (let i in 对象) {}

这种形式是专为对象准备的,用于遍历对象的所有属性名称
其实也可以用下面的方式实现,区别在于前者性能稍微好一些:

for (let i of Object.keys(对象)){}

函数

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
定义函数,必须指定名称:
function get_name() { 函数体 }

定义匿名函数,赋值给一个变量:
get_name = function (){}

参数传递:
传递少了不会报错,而是参数值为 undefined
传递多了也不会报错

函数也是一个对象,拥有一些内置属性和方法

函数的属性方法:
length 属性,得到参数个数
name 属性,得到函数名称
toString 方法,转为字符串形式

arguments 对象:
在函数中取 arguments,可以得到调用函数时传递的所有参数

自执行函数:
(function (){})()

箭头函数(ES6):
写法一,类似 py 中的 lambda 函数:
(参数) => 返回值
写法二,类似一个普通的匿名函数:
(参数) => { 函数体 }

异常处理

1
2
3
4
5
6
7
try {} catch (e){}
等价于 py 中的:
try: except Exception as e:

throw new Error("");
等价于 py 中的:
raise Exception("")

逗号表达式

1
2
用逗号连接多个语句,这些语句能够被看作是一个逗号表达式,拥有一个返回结果
在一个语句中执行多个操作,返回最后一个表达式的结果

switch case

1
根据值跳转到指定代码

V8 常用对象和函数

1
2
3
4
5
6
7
8
9
10
11
12
13
Date,时间相关对象
需要实例化
getTime 方法,获得 13 位时间戳,单位为毫秒,py 的时间戳是 10 位的,单位为秒
Math,数学相关对象
静态方法类,不需要实例化
random 方法,获得随机数
JSON
静态方法类,不需要实例化
stringify 方法,对象转 json 字符串
parse 方法,json 字符串转对象
(不属于 V8,但 node 和浏览器中都具备的常用函数)
setTimeout(func, time) 函数,指定毫秒过后执行指定函数
setInterval(func, time) 函数,每经过一次指定毫秒,执行一次指定函数

谷歌内核常用对象和函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
window:
全局对象(他的属性等同于全局变量,全局变量等同于他的属性)
navigator:
浏览器信息对象
userAgent 属性,浏览器版本等信息
platform 属性,平台
location、screen 等
document:
HTML 文档对象
getElementsBy... 方法,获得页面中的标签对象
querySelector 方法,根据 CSS 选择器获得标签
createElement 方法,创建一个标签对象
对于单个标签对象:
getAttribute 方法,获得属性值
setAttribute 方法,设置属性值
appendChild 方法,添加子标签
innerText 属性,标签内的文本
innerHTML 属性,标签内的 HTML 文本
addEventListener(事件类型, func) 方法:
指定类型事件触发时,调用指定函数并通过参数传递相关信息

node.js 常用对象和函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
global:
全局对象(他的属性等同于全局变量,全局变量等同于他的属性)
process:
argv 属性,得到命令行调用时传递的参数
exit 方法,强制结束当前进程
require 函数:
导入模块
没有自动补全提示:
打开 Pycharm 终端,执行命令:
npm install --save-dev @types/node
如果报错可能存在权限问题,将 node 安装路径下的 node_cache、node_global、node_modules 文件添加完全控制权限
右键文件夹 -> 属性 -> 安全 -> 编辑 -> 勾选完全控制
下载慢:
更换淘宝源: npm config set registry https://registry.npmmirror.com

使用第三方模块

1
2
3
4
5
6
7
8
9
10
11
12
浏览器环境:
通过外链式 script 标签导入
node 环境:
通过 npm install xxx 命令安装,require 函数导入
安装模块后会在当前路径下生成一个 node_modules 文件夹,存放你安装的模块
导入模块时会从当前目录开始,不断向上寻找这个文件夹,并尝试找到你需要的模块
通过 npm install -g xxx 命令全局安装
模块会被安装到 node 安装路径下的 node_modules 文件夹
下载慢:
更换淘宝源: npm config set registry https://registry.npmmirror.com
如果报错可能存在权限问题,将 node 安装路径下的 node_cache、node_global、node_modules 文件添加完全控制权限
右键文件夹 -> 属性 -> 安全 -> 编辑 -> 勾选完全控制

常用第三方模块

1
2
3
4
5
6
7
8
9
10
11
浏览器,jQuery:
使用 $ 符号按 CSS选择器语法 选取页面元素,如:$("#id")
$.ajax({ // 发送请求
url: "链接",
type: "get",
data: { 请求参数 },
success: function (res){ 当成功得到响应后,立刻执行该函数 }
});

node,express:
npm install express

闭包

1
2
3
4
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
JavaScript 的面向对象非常随意(灵活)
不要用其他语言面向对象的思维去理解
不学 class 关键字,只是做了点封装,本质上还是下面的方式

使用 function 关键字定义类的构造函数
this 关键字
使用 new 关键字实例化时,this 指向新的实例对象,等价于 py 中的 self
this 默认指向全局对象
作为对象的方法被调用时,this 指向该对象
箭头函数不会有自己的 this

prototype 属性得到函数的原型
实例对象会拥有类的原型上的属性方法(并不是实例对象直接拥有,而是当实例对象自身没有时,会去原型中拿)
可以理解为:定义类属性(静态属性,每个实例共享)需要在原型上定义
每个实例对象都会有 __proto__ 属性,指向函数的 prototype

原型链:
函数的原型就是一个 Object 类的实例对象,所以也拥有 __proto__ 属性
对象.__proto__.__proto__.__proto__...,这就是原型链
当取实例对象的属性时:
看实例对象自身有没有
没有就去原型链中找
这一层原型没有就去上一层原型找
这一层原型找到了那就直接用,不会再看上一层原型了
可用于实现继承

JavaScript 中,new 一个对象可以理解为就两个步骤:
1. 将新实例对象的 __proto__ 属性赋值为类的 prototype 属性
2. 执行构造函数
这些步骤可以通过 new 关键字实现,也可以手动

每个函数都会有 prototype 属性
每个实例对象都会有 __proto__ 属性
函数也是一个实例对象,所有同时拥有这两个属性
每个函数都是 Function 类的实例对象
Function 类的 prototype 是 Object 类的实例对象
可以通过直接赋值的方式创建实例对象(只拥有类属性)

instanceof 关键字:
判断左侧对象的原型链中是否存在右侧对象的原型

自定义 this 指向

1
2
3
4
5
6
第一个参数:要指向的自定义 this 对象

方法名 | 立刻执行 | 传递参数
apply | 是 | 列表
call | 是 | 按顺序
bind | 否 | 按顺序

Promise 异步

1

JS逆向

基本概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
加密解密:
加密:通过算法和密钥将数据(明文)转换为无法阅读的形式(密文)
解密:通过同样的算法和密钥将密文转为明文
1. 发送请求时,数据从客户端发送至服务端的过程中,可能被他人获取 -> 如果加密了,别人就看不懂
2. 对抗爬虫,使爬虫模拟请求变复杂
所以这种情况下,服务端只认加密了的数据,我们模拟请求时也需要进行加密 -> 调试网站js代码,找到加密的算法和密钥
第二种情况是服务端的响应进行了加密,我们需要进行解密

编码解码:
通过算法将数据转为一种特定格式的过程
和加解密很像,但不需要密钥
用途也不同,学习实际的编码算法时再讲

摘要:
通过算法(称之为哈希函数)对数据(“明文”)进行处理,生成一段固定长度的字符串(称之为摘要值、哈希值)
特征:
1. 无法从摘要值还原出“明文”,所以摘要并不是加密
2. 无论“明文”有多大,生成的摘要长度都固定
3. “明文”的微小变化会导致摘要显著不同
用途:
1. 校验数据完整性,确保没有被他人篡改
2. 对抗爬虫,使爬虫模拟请求变复杂
3. 存储密码(作为了解)

常见摘要算法

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
共同特征:
需要的输入为二进制数据,计算机中任何数据都以二进制存储,所以任何数据都能进行摘要
输出也为二进制数据,通常将其转为十六进制字符串形式(只包含 0-9、a-f)
能够被暴力破解,也存在哈希碰撞

md5:16 字节、32 长度字符
sha1:20 字节、40 长度字符
sha256:32 字节、64 长度字符
sha512:64 字节、128 长度字符
计算量递增
安全性递增
抗碰撞性递增

from hashlib import md5
md5_obj = md5()
md5_obj.update('123456'.encode('utf-8')) # encode 将字符串转为 byte 类型数据,也就是二进制数据
md5_obj.hexdigest() # 得到十六进制字符串结果
md5_obj.digest() # 得到二进制结果

加盐:
原本的“明文”拼接上一串自定义字符后再进行摘要
不加盐的流程:
1. 数据+md5(数据) --> 发送至服务端 --> 校验成功
2. 数据+md5(数据) --> 数据被篡改 --> 发送至服务端 --> 校验失败
3. 数据+md5(数据) --> 同时篡改数据和md5值 --> 发送至服务端 --> 校验成功
加盐的流程:
数据+md5(数据+盐) --> 不知道盐是什么,无法篡改数据 --> 发送至服务端 --> 校验成功(校验时也需要加盐)

常见编码解码算法

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
url 编码:
url 中如果包含中文空格等字符可能导致一些错误,所以需要进行编码
Python 的请求库都会自动帮你编码

from urllib.parse import quote, unquote
quote("https://url") # 编码
unquote("https://url") # 解码

quote:
空格编码为 %20
默认不编码斜杠
类似 js 中的 encodeURI,但 js 默认不会编码 :?=&! 等特殊字符
可以通过参数 safe="/:?=&!" 指定哪些字符不进行编码
quote_plus:
空格编码为 +
默认编码斜杠
等价 js 中的 encodeURIComponent

base64 编码:
输入为二进制数据,输出为只包含(A-Z, a-z, 0-9, +, /)的字符

特征:
1. 输出一定是 4 的整数倍
2. 一次处理 3 个字节,通过一个映射关系将其转为 4 个字节,所以编码后的数据大小会膨胀约 33%
3. 如果数据不足 3 字节,会用 “=” 进行填充,编码结果就会以等于号结尾
用途:
1. 在只能存在字符的情况下嵌入二进制数据
2. 避免特殊字符导致问题
3. 对数据进行伪加密

from base64 import b64encode, b64decode
b64encode("HD") # 编码,js 中为 btoa(浏览器内置,高版本 node 内置)
b64decode("SEQ=") # 解码,js 中为 atob

变种 base64 编码:将 + 替换为 -,/ 替换为 _,避免在 url 中产生歧义

常见加解密算法

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
安装模块:pip install pycryptodome
导入时的模块名称:Crypto

共同特征:
输入明文为二进制数据,输出密文为二进制数据,一般对密文进行 base64 编码或转为 16 进制字符串
密钥、偏移量也都是二进制数据

DES:
对称加密:加解密使用相同密钥
分组加密:每次处理 8 字节的明文,输出 8 字节密文
密钥长度:8 字节
偏移量长度:8 字节
已经不安全,易受暴力破解
模式(不同模式存在本质区别):
ECB:需要密钥
CBC:需要密钥(key)、偏移量(iv)
填充(padding):
明文必须是 8 字节的整数倍,否则需要进行填充:向原始数据末尾添加额外字节
存在不同的填充方案,只是填充不同的字符这样一个区别
pkcs7:填充了 n 个字符,填充字符就是 \x0n
x923:填充了 n 个字符,最后一个填充字符就是 \x0n,其余的是 \x00
iso7816:第一个填充字符为 \x80,其余的是 \x00
解密时也需要去除填充,需要填充的加密算法使用的填充方式都是一致的
from Crypto.Util.Padding import pad, unpad
pad( 明文, 填充长度, 填充模式 )
unpad( 解密后的密文, 填充长度, 填充模式 )

from Crypto.Cipher import DES
obj = DES.new( key, DES.MODE_CBC, iv )
obj.encrypt( 明文 )
obj2.decrypt( 密文 ) # 加密解密不能用同一个对象

3DES:
对数据进行 3 次 DES 加密
有两种密钥模式:
2-key:长度 16 字节
3-key(更安全):长度 24 字节
偏移量长度:8 字节
模式(不同模式存在本质区别):
ECB:需要密钥
CBC:需要密钥(key)、偏移量(iv)
比 DES 安全,但速度和安全性仍慢于 AES

AES:
使用最广泛的对称加密算法
分组加密:每次处理 16 字节的明文
密钥长度:
AES-128:128 位密钥(16 字节)
AES-192:192 位密钥(24 字节)
AES-256:256 位密钥(32 字节)
偏移量长度:16 字节
模式(不同模式存在本质区别):
ECB:需要密钥
CBC:需要密钥(key)、偏移量(iv)

RSA:
使用最广泛的非对称加密算法
公钥(Public Key):公开用于加密
私钥(Private Key):保密用于解密
密钥长度:
128、256、384、512 字节
能够加密的明文长度有限,短于密钥长度
计算量大,计算速度慢
同样的密钥和明文,得到不一样的密文

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
public_key = RSA.import_key( key )
obj = PKCS1_OAEP.new( public_key )
obj.encrypt( 明文 )

from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend
from base64 import b64decode, b64encode

public_key_der = b64decode(公钥)
public_key = serialization.load_der_public_key(public_key_der, backend=default_backend())
密文 = public_key.encrypt(
明文,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)

浏览器调试工具使用

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
可能无法打开 f12:
右上角三个点 -> 更多工具 -> 开发者人员工具
开发者工具右上角三个点 -> 停靠位置 -> 停靠到单独窗口

网络(Network):
保留日志(Preserve log):当页面重新加载时,是否清除网络日志(抓包抓到的数据包)
禁用缓存(Disable cache):已经请求加载过的文件是否使用本地的缓存数据,而不重新向服务端请求
筛选器(Filter):筛选 url 中包含指定字符的请求
左上角红色按钮:停止抓包
左上角白色按钮:清除网络日志
ctrl + f:在所有请求中进行搜索,无法搜索到请求体中的数据
请求分类:
Fetch/XHR:由网站js代码发送的请求
文档:页面加载的第一个 HTML

控制台(Console):
用户消息:由js代码触发的打印信息
错误:console.error
警告:console.warn
信息:console.log
详细信息:console.debug
呼出悬停控制台界面:
1. ctrl + shift + f
2. 右上角三个点 -> 搜索

源代码(Sources):
在这个页面调试js代码
代码段(Snippets):编写并运行多段js代码

寻找数据来源

1
2
3
4
1. HTML 数据,设置中禁用 JavaScript 进行验证
2. 在所有网络请求中搜索
3. 筛选请求时机
4. 响应可能有加密或者编码

识别加密参数

1
2
3
4
5
6
7
大前提一:请求头、请求体、请求参数、cookie 中的参数都可能对请求造成影响
大前提二:除了浏览器自动携带的部分请求头,所有参数都由服务端自定义,作用只能靠猜

1. 明确目标
2. 观察哪些参数对目标有影响
3. 观察参数是否是动态的
4. 观察参数是来自其他请求还是通过 js 生成

定位加密算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
全局搜索:
网站一般会压缩 js 代码,搜索时需要注意是否要包含空格
只看 js 文件中的搜索结果
假设搜索参数 sign:
sign
sign:
sign=
\bsign\b
sign 一般是签名的意思,也就是一个防止篡改的摘要值,可以根据长度搜索对应算法关键词
遇到类似 base64 编码的数据可以先解码看看
搜索 encrypt(加密)、decrypt(解密)等相关关键字

调用堆栈:
描述函数间的调用链,每调用一个函数就加深一层,回到上一层就知道是哪里调用了当前函数
注意:
1. 只在代码运行起来时,才有调用堆栈,调用堆栈是根据当前运行到的某一段代码进行描述的
2. 一个加密函数可能多个地方都在使用

最简单的定位技巧:顺着调用堆栈不断向上寻找是否包含需要的加密参数,当某个堆栈下一层包含加密参数,上一层不包含加密参数,那么大概率就是在这一层生成的

在 py 调用 js 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
三种方式:
1. 第三方模块
2. 命令行调用
3. API 接口

execjs:
安装模块:pip install pyexecjs2
不要安装:pip install pyexecjs(有编码问题,如果已经安装了,卸载:pip uninstall pyexecjs)

js = execjs.compile( js代码 )
结果 = js.call( "函数名称", 函数参数 )

运行原理:调用 node.js 去执行给定的 js 代码
误解:将 js 代码翻译成 py 代码进行执行

execjs 模块内部会使用到 process 对象,如果删除了 process 就会报错