目录
-
-
- 编码与加密与混淆
-
- 1、编码
-
- 2、基于编码的反爬虫设计
-
- (1)Base64编码反爬
- (2)MD5反爬
- (3)对称加密与AES
- (4)非对称加密与RSA
- 3、JavaScript代码混淆
-
- (1)正则替换之变量名替换
- (2)正则替换之进制替换
- (3)代码编码之Base64
- (4)代码编码之AAEncode
- (5)代码编码之JJEncode
- (6)代码复杂化之访问符
- (7)代码复杂化之Packer
- 4、混淆代码的还原
- 5、混淆原理
- 6、前端禁止事件
-
- (1)禁止鼠标事件
- (2)禁止键盘事件
- (3)其他debug时,使用while 1启动无限debug,达到干扰爬虫工程师调试代码的目的
编码与加密与混淆
1、编码
(1)ASCII编码
- ASCII码默认使用7位二进制数来表示所有大写字母、小写字母、数字(0~9)、标点符号和特殊的控制符
(2)Base64编码
- Base64基于64个可打印字符来表示8位二进制数据,用于解决不可打印的字符(如非英文的字符)在网络传输过程中造成的乱码现象
2、基于编码的反爬虫设计
(1)Base64编码反爬
- Base64编码时所用的对照表是固定的,也就是说它的编码过程是可逆的。这意味着我们只需要将编码的流程倒置,就能得到解码的方法
- 一般带有“==”符号或者“=”符号的字符串时,可能就是Base64编码字符串后得到的结果,按照Base64解码规则进行倒推,得到原字符
from base64 import b64decode
code = ["d3d3Lmh1YXdlaS5jb20=", "d3d3Lmp1ZWppbi5pbQ=="]
for c in code:
s = b64decode(c).decode("utf8")
print(s)
- 难点:Base64编码和解码时都是将原本8位的二进制数转换成6位的二进制数。如果我们改动位数,将其设置为5位或者4位,那么就可以实现新的编码规则,这时如果还是使用原先的方法进行解码,就可能解码不成功。改动位数,将其设置为5位或者4位
(2)MD5反爬
- MD5是一种广泛使用的散列函数,它能够将任意长度的消息转换成128位的消息摘要。与Base64编码不同的是,MD5是不可逆的,这意味着我们可以将字符串转换为MD5值,但无法将MD5值换成MD5值转换成原字符串。
- 相比Base64编码,MD5运算过程要复杂很多。由于MD5在运算过程中使用了补位、追加和移位等操作,所以他人无法从输出结果倒推出输入字符,这个特性被称为“不可逆”。
- MD5可以对任意长度的消息进行运算,输出固定位数(128位)的结果,这个特性被称为“压缩”
(3)对称加密与AES
- 加密和解密时使用同一个密钥的加密方式叫做对称加密,使用不同密钥的是非对称加密
- 对称加密
加密_密钥A -- --
解密_密钥A -- --
明文
密文
加密_密钥A _公钥 -- --
解密_密钥B_私钥 -- --
明文
密文
- 相对于非对称加密来说,对称加密的速度更快,速度的优势使得它更适合大量数据加密的场景,常见的堆成加密算法有DES、3DES、BLOWFISH、RC5和AES等
- AES加密过程中的操作是可逆的,关键在于密钥
from Crypto.Cipher import AES
aes1 = AES.new('63f09k56nv2b10cf', AES.MODE_CBC, "01pv928nv2i5ss68")
message = "Hi I am from the earth number 77"
print(f"待加密消息{message}")
cipher_text = aes1.encrypt(message)
aes2 = AES.new('63f09k56nv2b10cf', AES.MODE_CBC, "01pv928nv2i5ss68")
plaint_text = aes2.decrypt(cipher_text)
print(f"密文{cipher_text}")
print(f"明文{plaint_text.decode('utf8')}")
(4)非对称加密与RSA
- 在不传递密钥的情况下实现加密和解密操作,与对称加密不同的是,这种方式需要用到两个密钥:公钥和私钥。
- 公钥和私钥是一对,如果用该公钥对数据进行加密,那么只有用对应的私钥才能够解密数据,反之亦然;用的最多的是RSA,可逆
from Crypto import Random
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
message = 'async'
rsa = RSA.generate(1024, Random.new().read)
private_key = rsa.exportKey()
public_key = rsa.publickey().exportKey()
print(private_key.decode('utf8'))
print(public_key.decode('utf8'))
with open('private.pem', 'wb') as f:
f.write(private_key)
with open('public.pem', 'wb') as f:
f.write(public_key)
with open('public.pem', 'r') as f:
pub = f.read()
pubkey = RSA.importKey(pub)
cipher = PKCS1_v1_5.new(pubkey)
c = base64.b64encode(cipher.encrypt(message.encode('utf8'))).decode('utf8')
with open('private.pem', 'r') as f:
pri = f.read()
prikey = RSA.importKey(pri)
cipher = PKCS1_v1_5.new(prikey)
m = cipher.decrypt(base64.b64decode(c), 'error').decode('utf8')
print(f'消息原文:{message}\n消息密文:{c}\n解密结果:{m}')
3、JavaScript代码混淆
- 网页打开速度:即资源加载速度和页面渲染速度;
- 代码压缩:资源加载速度的常用优化方法是压缩,压缩的对象包括HTML文档、图片和JavaScript文件等。压缩后的文件体积相对较小,不仅有利于网络传输,还能起到保护代码的作用。这是因为在压缩过程中会删除注释、空格和换行符等元素,将多个JavaScript文件合并以及缩短变量名。
- 随着浏览器和各大IDE推出代码格式化功能,文件压缩对代码的保护就被削弱了;为了更好地保护代码,开发者想到了代码混淆这样的办法
- 常见的混淆方法:正则替换、代码编码和代码复杂化等
(1)正则替换之变量名替换
- 用简短的字母替换方法中的变量名,也可以在此基础上增加空格和换行符的删除操作,原代码如下
function transToDict(name, age, vip){
result = "{name:" + name + ", age:" + age + ", isVIP:" + vip + ",}";
console.log(result);
return result;
}
(2)正则替换之进制替换
- 除了能解析Base64编码外,浏览器还能解析十六进制的字符。原代码如下
var ins = 1 + 2, ss = "abc";
function pack(a, b){
return a + b + ss;
};
var pp = pack(ins, 6);
console.log(pp);
(3)代码编码之Base64
- 浏览器会自动解析Base64编码后的内容,切换到Console可以看到输出结果
(4)代码编码之AAEncode
- 代码混淆不仅是替换一些字符,一些奇怪的编码也能起到保护代码的作用。常见的编码方法有AAEncode和JJEncode。
- AAEncode能将JavaScript代码转换为颜文字,而JJEncode则将代码转换为“$”符号、"_“符号和”+"符号。
(5)代码编码之JJEncode
- JJEncode与AAEncode的编码原理相似,但输出的符号不同。
(6)代码复杂化之访问符
- 有些混淆方法则是改变对象的访问符,如将原本用",“访问对象的方式改为用”[]"访问,在此基础上,加入十六进制和Unicode混淆手段
(7)代码复杂化之Packer
- Packer是代码混淆中常用的方法,它能将原代码变得复杂,降低可读性
4、混淆代码的还原
- 混淆并不是加密,混淆后的代码肯定能够在浏览器中执行,否则混淆就失去了意义。所以说,混淆的代码可以被还原,只不过不同的混淆手段在还原时耗费的时间不同,
- 代码还原有一定的规律,例如正则替换这种混淆方法通常会将数字和字母转换为十六进制
- 在调用Base64编码后得到的代码时,需要使用eval关键字
eval(atob("basefahuh=="))
- AAEncode 和JJEncode的编码结果可以直接运行,不需要再代码前加eval。这两种编码的还原方式很简单,只需要用到console shell工具,需要删除最后一组括号内的信息
- Packer的还原和Base64一样,需要使用eval关键字,只需要用到console shell工具即可看到原代码
5、混淆原理
- 混淆功能的关键是:抽象语法树(abstract syntax tree,简称AST)
- JavaScript编译器的目的是将JavaScript代码编译为机器码,而混淆的处理结果仍然是JavaScript代码
- 混淆器在代码编译过程中修改语法树,并将输出结果改为JavaScript,了解混淆器对代码的处理流程后,就可以动手编写混淆器了
- 可以借助UglifyJS实现对代码的分析和对语法树的修改,并输出混淆结果

6、前端禁止事件
(1)禁止鼠标事件
属性 |
描述 |
onclick |
当用户点击某个对象时调用的事件句柄 |
oncontextmenu |
在用户点击鼠标右键打开上下文菜单时触发 |
ondbclick |
当用户双击某个对象时调用的事件与句柄 |
onmousedown |
鼠标按钮按下 |
onmouseenter |
当鼠标指针移动到元素时触发 |
onmousemove |
鼠标被移动 |
onmouseover |
鼠标移到某元素之上 |
onmouseout |
鼠标从某元素移开 |
onmouseup |
鼠标按键被松开 |
- 开发者可以通过JavaScript在HTML中注册oncontextmenu事件检测鼠标右键,当检测到鼠标右键被按下时重置事件或返回false,这样就能够实现鼠标右键的检测和禁用,进而限制开发者工具的唤起操作;但是ctr+shift+I可以
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<p>hello,todayp>
<script type="text/javascript">
document.oncontextmenu = function(){
event.returnValue = false
}
script>
body>
html>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<p>hello,todayp>
<script type="text/javascript">
document.oncopy = function(){
event.returnValue = false
}
script>
body>
html>
(2)禁止键盘事件
事件 |
值 |
描述 |
VK_F5 |
0x74(116) |
F5键 |
VK_F12 |
0x7B(123) |
F12键 |
VK_DELETE |
0x2E(46) |
Delete键 |
VK_PAGE_UP |
0x21(33) |
Page up键 |
VK_PAGE_DOWN |
0x22(34) |
Page down键 |
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<p>用户无法通过F12键唤起开发者工具p>
<script type="text/javascript">
document.onkeydown = function(){
if (window.event && window.event.keyCode == 123){
event.returnValue=false
}
event.returnValue = false
}
script>
body>
html>
(3)其他debug时,使用while 1启动无限debug,达到干扰爬虫工程师调试代码的目的