我们学过Python就已经基本掌握了爬虫的技术,那么为啥还要js逆向呢?这里有必要梳理一下js逆向的主要原理。
在Web系统发展早期,Js在Web系统中承担的职责并不多,Js文件比较简单,也不需要任何的保护。随着Js文件体积的增大和前后端交互增多,为了加快http传输速度并提高接口的安全性,出现了很多的压缩工具和混淆加密工具。
代码混淆的本质是对于代码标识和结构的调整,从而达到不可读不可调用的目的,常用的混淆有字符串、变量名混淆,比如把字符串转换成_0x,把变量重命名等,从结构的混淆包括控制流平坦化,虚假控制流和指令替换,代码加密主要有通过veal方法去执行字符串函数,通过escape()等方法编码字符串、通过转义字符加密代码、自定义加解密方法(RSA、Base64、AES、MD5等),或者通过一些开源的工具进行加密。
另外目前市面上比较常见的混淆还有ob混淆(obfuscator),特征是定义数组,数组位移。不仅Js中的变量名混淆,运行逻辑等也高度混淆,应对这种混淆可以使用已有的工具ob-decrypt或者AST解混淆或者使用第三方提供的反混淆接口。 例如翻看网站的Javascript源代码,可以发现很多压缩或者看不太懂的字符,如javascript文件名被编码,文件的内容被压缩成几行,变量被修改成单个字符或者一些十六进制的字符——这些导致我们无法轻易根据Javascript源代码找出某些接口的加密逻辑。
简单说,爬虫获取数据的过程就是爬虫工作者和网站之间的一场攻防战,网站希望真实的用户来访问,不希望爬虫来访问,所以对爬虫设置了重重障碍,而所用的手段,主要就是利用JS进行处理,那么JS逆向就是突破网站在JS中设置的重重障碍,拿到想要的数据的过程。
知己知彼百战不殆,我们只有了解网站在反爬方面采用的是哪些手段,才能有针对性地想办法逆向突破。下面我们就从压缩、混淆、加密/解密 三个方面来梳理一下目前大部分网站的主要反爬手段。
1、压缩
Javascript压缩即去除JavaScript代码中不必要的空格、换行等内容或者把一些可能公用的代码进行处理实现共享,最后输出的结果都压缩为几行内容,代码的可读性变得很差,同时也能提高网站的加载速度。
如果仅仅是去除空格、换行这样的压缩方式,其实几乎是没有任何防护作用的,因为这种压缩方式仅仅是降低了代码的直接可读性。因为我们有一些格式化工具可以轻松将JavaScirpt代码变得易读,比如利用IDE、在线工具或Chrome浏览器都能还原格式化的代码。
这里举一个最简单的JavaScript压缩示例。原来的JavaScript代码是这样的:
function echo(stringA, stringB){
const name = "Germey";
alert("hello " + name);
}
压缩之后就变成这样子:
function echo(d,c){const e="Germey";alert("hello "+e)};
可以看到,这里参数的名称都被简化了,代码中的空格也被去掉了,整个代码也被压缩成了一行,代码的整体可读性降低了。
目前主流的前端开发技术大多都会利用webpack、Rollup等工具进行打包。webpack、Rollup会对源代码进行编译和压缩,输出几个打包好的JavaScript文件,其中我们可以看到输出的JavaScript文件名带有一些不规则的字符串,同时文件内容可能只有几行,变量名都用一些简单字母表示。这其中就包含JavaScript压缩技术,比如一些公共的库输出成bundle文件,一些调用逻辑压缩和转义成冗长的几行代码,这些都属于JavaScript压缩,另外,其中也包含了一些很基础的JavaScript混淆技术,比如把变量名、方法名替换成一些简单的字符,降低代码的可读性。
但整体来说,JavaScript压缩技术只能在很小的程度上起到防护作用,想要真正的提高防护效果,还得依靠JavaScript混淆和加密技术。
2、混淆
JavaScript混淆完全是在JavaScript上面进行的处理,它的目的就是使得JavaScript变得难以阅读和分析,大大降低代码的可读性,是一种很实用的JavaScript保护方案。
JavaScript混淆技术主要有以下几种。
变量名混淆:将带有含义的变量名、方法名、常量名随机变为无意义的类乱码字符串,降低代码的可读性,如转换成单个字符或十六进制字符串。
字符串混淆:将字符串阵列化集中并可进行MD5或Base64加密存储,使代码中不出现明文字符串,这样可以避免使用全局搜索字符串的方式定位到入口。
对象键名替换:针对JavaScript对象的属性进行加密转化,隐藏代码之间的调用关系。
控制流平坦化:打乱函数原有代码的执行流程及函数调用关系,使代码逻辑变得混乱无序。
无用代码注入:随机在代码中插入不会被执行到的无用代码,进一步使代码看起来更加混乱。
调试保护:基于调试器特征,对当前运行环境进行检查,加入一些debugger语句,使其在调试模式下难以顺利执行JavaScript代码。
多态变异:使JavaScript代码每次被调用时,将代码自身立刻自动发生变异,变为与之前完全不同的代码,即功能完全不变,只是代码形式变异,以此杜绝代码被动态分析和调试。
域名锁定:使JavaScript代码只能在指定域名下执行。
代码自我保护:如果对JavaScript代码进行格式化,则无法执行,导致浏览器假死。
特殊编码:将JavaScript完全编码为人不可读的代码,如表情符号、特殊表示内容、等等。
总之,以上方案都是JavaScript混淆的实现方式,可以在不同程度上保护JavaScript代码。
在前端开发中,现在JavaScript混淆的主流实现使javascript-obfuscator和terser这两个库。它们都能提供一些代码混淆功能,也都有对应的webpack和Rollup打包工具的插件。利用它们,我们可以非常方便地实现页面的混淆,最终输出压缩和混淆后的JavaScript代码,使得JavaScript代码的可读性大大降低。
下面我们以javascript-obfuscator为例来介绍一些代码混淆的实现,了解了实现,那么我们自然就对混淆的机制有了更加深刻的认识。
javascript-obfuscator的官方介绍内容如下:
链接:Javascript Obfuscator - Protects JavaScript code from stealing and shrinks size - 100% Free
它是支持ES8的免费、高效的JavaScript混淆库,可以使得JavaScript代码经过混淆后难以被复制、盗用、混淆后的代码具有和原来的代码一模一样的功能。
当然首先要装好nodeJS,然后新建一个文件夹,比如js-ob然后进入该文件夹,初始化工作空间:
npm init
这里会提示我们输入一些信息,然后创建package.json文件,这就完成了项目的初始化了。
接下来,我们来安装javascript-obfuscator这个库:
npm i -D javascript-obfuscator
稍等片刻,即可看到本地js-ob文件下生成了一个node_modules文件夹,里面就包含了javascript-obfuscator这个库,这就说明安装成功了。
2.1 用javascript-obfuscator库实现“控制流平坦化”
新建main.js文件,其内容如下:
const code = `
let x = '1' + 1
console.log('x', x)
`
const options = {
compact: false,
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
这里我们定义了两个变量:一个是code,即需要被混淆的代码;另一个是混淆选项options,是一个Object。接下来,我们引入了javascript-obfuscator这个库,然后定义了一个方法,给其传入code和options来获取混淆之后的代码,最后控制台输出混淆后的代码。
node main.js
输出结果如下:
看到了吧,那么简单的代码,被我们混淆成了这个样子,其实这里我们就是设定了“控制流平坦化”选项。整体看来,代码的可读性大大降低了,JavaScript调试的难度也大大加强了。
2.2 javascript-obfuscator实现变量名混淆
变量名混淆可以通过在javascript-obfuscator中配置identifierNamesGenerator参数来实现。我们通过这个参数可以控制变量名混淆的方式,如将其设置为hexadecimal,则会将变量名替换为十六进制形式的字符串。该参数的取值如下。
hexadecimal:将变量名替换为十六进制形式的字符串,如0xabc123。
mangled:将变量名替换为普通的简写字符,如a,b,c等。
该参数的默认值为:hexadecimal
我们将该参数改为:mangled 来试一下:
const code = `
let x = '1' + 1
console.log('x', x)
`
const options = {
compact: true,
identifierNamesGenerator: 'mangled'
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
运行结果如下:另外,我们还可以通过设置identifiersPrefix参数来控制混淆后的变量前缀,示例如下:
const code = `
let x = '1' + 1
console.log('x', x)
`
const options = {
identifiersPrefix: 'kk',
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
运行结果如下:
可以看到,混淆后的变量前缀加上了我们自定义的字符串kk。
另外,renameGlobals这个参数还可以指定是否混淆全局变量和函数名称,默认值为false。示例如下:
const code = `
var $ = function(id){
return document.getElementById(id);
};
`
const options = {
renameGlobals: true,
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
运行结果如下:
可以看到,这里我们声明了一个全局变量$,在renameGlobals设置为true之后,这个变量也被替换了。如果后文用到了这个变量,可能就会有找不到定义的错误,因此这个参数可能导致代码执行不通。
如果我们不设置 renameGlobals 或者将其设置为false,结果如下:
可以看到,最后还是有$的声明,其全局名称没有被改变。
2.3 javascript-obfuscator字符串混淆
字符串混淆,就是将一个字符串声明放到一个数组里面,使之无法被直接搜索到。这可以通过stringArray参数来控制,默认为true。
此外,我们还可以通过rotateStringArray参数来控制数组化后结果的元素顺序,默认为true。
示例如下:
const code = `
var a = 'helloworld'
`
const options = {
stringArray: true,
rotateStringArray: true,
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
运行结果如下:
另外,我们还可以使用unicodeEscapeSequence这个参数对字符串进行Unicode转码,使之更加难以辨认,示例如下:
const code = `
var a = 'hello world'
`
const options = {
compact: false,
unicodeEscapeSequence: true
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options)
运行结果如下:
可以看到,这里字符串被数字化和Unicode化,非常难以辨认。
在很多JavaScript逆向过程中,一些关键的字符串可能会作为切入点来查找加密入口,用了这种混淆之后,如果有人想通过全局搜索的方式搜索hello这样的字符串找加密入口,也就没法搜到了。
2.4 代码自我保护
我们可以通过设置selfDefending参数来开启代码自我保护功能。开启之后混淆后的JavaScript会强制以一行显示。如果我们将混淆后的代码进行格式化或者重命名,该段代码将无法执行。
示例如下:
const code = `
console.log('hello world')
`
const options = {
selfDefending: true
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
运行结果如下:
如果我们将上述代码放到控制台,它的执行结果和之前是一模一样的,没有任何问题。
如果我们将其进行格式化,然后粘贴到浏览器控制台里面,浏览器会直接卡死无法运行。这样如果有人对代码进行了格式化,就无法正常对代码进行运行和调试,从而起到了保护作用。
2.5 无用代码注入
无用代码即不会被执行的代码或对上下文没有任何影响的代码,注入之后可以对现有的JavaScript代码的阅读形成干扰。我们可以使用deadCodeInjection参数开启这个选项,其默认值为false。
示例:
const code = `
console.log(c);
console.log(a);
console.log(b);
`
const options = {
compact: false,
deadCodeInjection: true
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
这种混淆方式通过混入一些特殊的判断条件并加入一些不会被执行的代码,可以对代码起到一定的干扰作用。
2.6 对象键名替换
如果是一个对象,可以使用transformObjectKeys来对对象的键值进行替换,示例如下:
const code = `
(function(){
var object = {
foo: 'test1',
bar: {
baz: 'test2'
}
};
})();
`
const options = {
compact: false,
transformObjectKeys: true
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
可以看到,Object的变量名被替换为了特殊的变量,代码的可读性变差,这样我们就不好直接通过变量名进行搜寻了,这也可以起到一定的防护作用。
2.7 禁用控制台输出
const code = `
console.log('hello world')
`
const options = {
disableConsoleOutput: true
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
运行结果如下:
此时,我们如果执行这段代码,发现是没有任何输出的,这里实际上就是将console的一些功能禁用了。
2.8 调试保护
我们知道,如果Javascript代码中加入关键字debugger关键字,那么执行到该位置的时候,就会进入断点调试模式。如果在代码多个位置都加入debugger关键字,或者定义某个逻辑来反复执行debugger,就会不断进入断点调试模式,原本的代码就无法顺畅执行了。这个过程可以称为调试保护,即通过反复执行debugger来使得原来的代码无法顺畅执行。
其效果类似于执行了如下代码:
setInterval(() => {debugger;}, 3000)
如果我们把这段代码粘贴到控制台,它就会反复执行debugger语句,进入断点调试模式,从而干扰正常的调试流程。
在javascript-obfuscator中,我们可以使用debugProtection来启用调试保护机制,还可以使用debugProtectionInterval来启用无限调试(debug),使得代码在调试过程中不断进入断点模式,无法顺畅执行。配置如下:
const options = {
debugProtection: true,
}
混淆后的代码会跳到debugger代码的位置,使得整个代码无法顺畅执行,对JavaScript代码的调试形成干扰。
2.9 域名锁定
我们还可以通过控制domainLock来控制JavaScript代码只能在特定域名下运行,这样就可以降低代码被模拟或者盗用的风险。
示例如下:
const code = `
console.log('hello world')
`
const options = {
domainLock: ['kk.com']
}
const obfuscator = require('javascript-obfuscator')
function obfuscate(code, options) {
return obfuscator.obfuscate(code, options).getObfuscatedCode()
}
console.log(obfuscate(code, options))
这里我们使用domainLock指定了一个域名kk.com,也就是设置了一个域名白名单,混淆后的代码结果如下:
这段代码就只能在指定的域名kk.com下运行,不能在其他网站运行。这样的话,如果一些相关JavaScript代码被单独剥离出来,想在其他网站运行或者使用程序模拟运行的话,运行结果只有失败,这样就可以有效降低代码被模拟或盗用的风险。
2.10 特殊编码
另外,还有一些特殊的工具包(jjencode,aaencode等)他们可以对代码进行混淆和编码。
链接:JSON在线 | JSON解析格式化—SO JSON在线工具
示例如下:
var kk = 100
使用jjencode工具的结果:
使用aaencode工具的结果:
可以看到,通过这些工具,原本非常简单的代码被转化为一些几乎完全不可读的代码,但实际上运行效果还是相同的。这些混淆方式比较另类,看起来虽然没有什么头绪,但实际上找到规律是非常好还原的,并没有真正达到强力混淆的效果。
关于这种混淆代码的解码方法,一般直接复制到控制台运行或者用解码工具进行转换,如果运行失败,就需要按分号分割语句,逐行调试分析源码。
以上便是对JavaScript混淆方式的介绍和总结。总的来说经过混淆的JavaScript代码的可读性大大降低,同时其防护效果也大大增强。
3、常见的编码和加密
我们在爬取网站的时候,会遇到一些需要分析接口或URL信息的情况,这时会有各种各样的类似加密的情形。
某个网站的URL带有一些看不太懂的长串加密参数,要抓取就得必须懂得这些参数是怎么构造的,否则我们连完整的URL都构造不出来,更不用说爬取了。
在分析某个网站的Ajax接口时,可以看到接口的一些参数也是加密的,Request Headers 里面也可能带有一些加密参数,如果不知道这些参数的具体构造逻辑,就没法直接用程序来模拟这些Ajax请求。
常见的编码有base64、unicode、urlencode编码,加密有MD5、SHA1、HMAC、DES、RSA等。
3.1 base64
base64是一种基于64个可打印ASCLL字符对任意字节数据进行编码的算法,其在编码后具有一定意义的加密作用。在逆向过程中经常会碰到base64编码(不论是Js逆向还是安卓逆向)。
浏览器提供了原生的base64编码、解码方法,方法名就是btoa和atob如下图所示:
在python中使用base64:
import base64
print(base64.b64encode('hello'.encode()))
print(base64.b64decode('aGVsbG8='.encode()))
运行结果:
b'aGVsbG8='
b'hello'
unicode和urlencode比较简单,unicode是计算机中字符集、编码的一项业界标准,被称为统一码、万国码,表现形式一般以“\u" 或者 "&#"开头。urlencode是URL编码,也称作百分号编码用于把URL中的符号进行转换。
3.2 MD5
MD5消息摘要算法(英文:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5加密算法是不可逆的,所以解密一般都是通过暴力穷举方法,以及网站的接口实现解密。
python代码实现加密:
import hashlib
pwd = "123"
# 生成MD5对象
m = hashlib.md5()
# 对数据进行加密
m.update(pwd.encode('utf-8'))
# 获取密文
pwd = m.hexdigest()
print(pwd)
解密工具:md5在线解密破解,md5解密加密
3.3 SHA1
SHA1(Secure Hash Algorithm)安全哈希算法主要适用于数字签名标准里面定义的数字签名算法,SHA1比MD5的安全性更强。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。
一般在没有高度混淆的Js代码中,SHA1加密的关键词就是sha1。
Python实现代码:
import hashlib
sha1 = hashlib.sha1()
data1 = "hello"
data2 = "world"
sha1.update(data1.encode("utf-8"))
sha1_data1 = sha1.hexdigest()
print(sha1_data1)
sha1.update(data2.encode("utf-8"))
sha1_data2 = sha1.hexdigest()
print(sha1_data2)
运行结果如下:
aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
6adfb183a4a2c94a2f92dab5ade762a47889a5a1
解密工具:哈希在线加密|MD5在线解密加密|SHA1在线解密加密|SHA256在线解密加密|SHA512在线加密|GEEKAPP开发者在线工具
运行如下:
3.4 HMAC
HMAC全称:散列消息鉴别码。HMAC加密算法是一种安全的基于加密hash函数和共享密钥的消息认证协议。实现原理是用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。
python实现代码:
new(key,msg=None,digestmod)方法
创建哈希对象
key和digestmod参数必须指定,key和msg(需要加密的内容)均为bytes类型,digestmod指定加密算法,比如‘md5’,'sha1'等
对象digest()方法:返回bytes类型哈希值
对象hexdigest()方法:返回十六进制哈希值
import hmac
import hashlib
key = "hello".encode()
text = "world".encode()
m = hmac.new(key, text, hashlib.sha256)
print(m.digest())
print(m.hexdigest())
运行结果:
b'\xf1\xac\x97\x02\xeb_\xaf#\xca)\x1aM\xc4m\xed\xde\xee*x\xcc\xda\xf0\xa4\x12\xbe\xd7qL\xff\xfb\x1c\xc4'
f1ac9702eb5faf23ca291a4dc46deddeee2a78ccdaf0a412bed7714cfffb1cc4
3.5 DES
DES全称:数据加密标准(Data Encryption Standard),属于对称加密算法。DES是一个分组加密算法,典型的DES以64位为分组对数据加密,加密和解密用的是同一个算法。它的密钥长度是56位(因为每个第8位都用作奇偶校验),密钥可以是任意的56位数,而且可以任意时候改变。
Js逆向时,DES加密的搜索关键词有DES、mode、padding等。
python实现代码:
# pyDes需要安装
from pyDes import des, CBC, PAD_PKCS5
import binascii
# 秘钥
KEY = 'mykey001'
def des_encrypt(s):
"""
DES 加密
:param s: 原始字符串
:return: 加密后字符串,16进制
"""
secret_key = KEY
iv = secret_key
k = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5)
en = k.encrypt(s, padmode=PAD_PKCS5)
return binascii.b2a_hex(en).decode()
def des_decrypt(s):
"""
DES 解密
:param s: 加密后的字符串,16进制
:return: 解密后的字符串
"""
secret_key = KEY
iv = secret_key
k = des(secret_key, CBC, iv, pad=None, padmode=PAD_PKCS5)
de = k.decrypt(binascii.a2b_hex(s), padmode=PAD_PKCS5)
return de.decode()
text = 'helloworld'
secret_str = des_encrypt(text)
print(secret_str)
clear_str = des_decrypt(secret_str)
print(clear_str)
运行结果如下:
59672a0579c71be59a05e2a0c9f59dcb
helloworld
3.6 AES
AES全程:高级加密标准,在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。
AES也是对称加密算法,如果能够获取到密钥,那么就能对密文解密。
Js逆向时,AES加密的搜索关键词有AES、mode、padding等。
python代码实现:
import base64
from Crypto.Cipher import AES
# AES
# 需要补位,str不是16的倍数那就补足为16的倍数
def add_to_16(value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value) # 返回bytes
# 加密方法
def encrypt(key, text):
aes = AES.new(add_to_16(key), AES.MODE_ECB) # 初始化加密器
encrypt_aes = aes.encrypt(add_to_16(text)) # 先进行aes加密
encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8')
return encrypted_text
# 解密方法
def decrypt(key, text):
aes = AES.new(add_to_16(key), AES.MODE_ECB) # 初始化加密器
base64_decrypted = base64.decodebytes(text.encode(encoding='utf-8'))
decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8').replace('\0', '') # 执行解密密并转码返回str
return decrypted_text
mykey = 'mykey'
text = "helloworld"
jiami_str = encrypt(mykey,text)
print(jiami_str)
jiemi_str = decrypt(mykey,jiami_str)
print(jiemi_str)
运行结果:
3lA29BaY0DLuTayjAfplUA==
helloworld
3.7 RSA
RSA全称:Rivest-Shamir-Adleman, RSA加密算法是一种非对称加密算法,在公开密钥加密和电子商业中RSA被广泛使用,它被普遍认为是目前最优秀的公钥方案之一。RSA是第一个能同时用于加密和数字签名的算法,它能够抵抗目前为止已知的所有密码攻击。
注意Js代码中的RSA常见标志setPublickey。
Python代码实现:
import base64
from Crypto.Cipher import PKCS1_v1_5
from Crypto import Random
from Crypto.PublicKey import RSA
# ------------------------生成密钥对------------------------
def create_rsa_pair(is_save=False):
"""
创建rsa公钥私钥对
:param is_save: default:False
:return: public_key, private_key
"""
f = RSA.generate(2048)
private_key = f.exportKey("PEM") # 生成私钥
public_key = f.publickey().exportKey() # 生成公钥
if is_save:
with open("crypto_private_key.pem", "wb") as f:
f.write(private_key)
with open("crypto_public_key.pem", "wb") as f:
f.write(public_key)
return public_key, private_key
def read_public_key(file_path="crypto_public_key.pem") -> bytes:
with open(file_path, "rb") as x:
b = x.read()
return b
def read_private_key(file_path="crypto_private_key.pem") -> bytes:
with open(file_path, "rb") as x:
b = x.read()
return b
# ------------------------加密------------------------
def encryption(text: str, public_key: bytes):
# 字符串指定编码(转为bytes)
text = text.encode("utf-8")
# 构建公钥对象
cipher_public = PKCS1_v1_5.new(RSA.importKey(public_key))
# 加密(bytes)
text_encrypted = cipher_public.encrypt(text)
# base64编码,并转为字符串
text_encrypted_base64 = base64.b64encode(text_encrypted).decode()
return text_encrypted_base64
# ------------------------解密------------------------
def decryption(text_encrypted_base64: str, private_key: bytes):
# 字符串指定编码(转为bytes)
text_encrypted_base64 = text_encrypted_base64.encode("utf-8")
# base64解码
text_encrypted = base64.b64decode(text_encrypted_base64)
# 构建私钥对象
cipher_private = PKCS1_v1_5.new(RSA.importKey(private_key))
# 解密(bytes)
text_decrypted = cipher_private.decrypt(text_encrypted, Random.new().read)
# 解码为字符串
text_decrypted = text_decrypted.decode()
return text_decrypted
if __name__ == "__main__":
# 生成密钥对
# create_rsa_pair(is_save=True)
# public_key = read_public_key()
# private_key = read_private_key()
public_key, private_key = create_rsa_pair(is_save=False)
# 加密
text = "helloworld"
text_encrypted_base64 = encryption(text, public_key)
print("密文:", text_encrypted_base64)
# 解密
text_decrypted = decryption(text_encrypted_base64, private_key)
print("明文:", text_decrypted)
运行结果:
密文: 0MIWBjjJsTU/i3x0+LQj+bpLuMByVsI+McjZJn54KDUH5fqhDK/gFj09qVb9+nzawMJdanNw05nFg0adTNU6a3Jrc0YUt0DA94mdvZBJujqpKRmuxe4G7cVwoNdG3nctyfzJxpe13HppXhWBKf3j6shi2U90L0y8QSFoN1jjUYUfwPg797eUy/Ibo9gSGrTeWZxuKbynAGwmCTg0BSpmJlDvJaEYpkq9UqHE4MPuRQRVPNFKRqao5z1wEmjqDTvu2d7q25b63M74LUiEEE1frnyo1gT+Q34GawGUfkf/77Tz2ma1RVdLGcwB9ctYQ2yjHHsTvm5yYiKD2Q04xIpNfw==
明文: helloworld
自此我们梳理了如今主要网站的一些主要的反爬手段,下一步就是针对这些手段采取措施,进行js逆向工作了。