爬虫JS逆向时Python的加密解密,编码(MD5,AES,DES,RSA,base64,UrlEncode等)

如果遇到问题可以留言,欢迎技术交流。交换意见

一,Python中运行JS代码

1-1  解决中文乱码或者报错问题

import subprocess
from functools import partial
subprocess.Popen = partial(subprocess.Popen, encoding='utf-8')
import execjs

1-2 常用函数



print(execjs.get().name) # 获取js代码执行环境

res = execjs.eval(js)  # 执行一段js代码

#先编译
jj = execjs.compile("""  
    function an(a, b){
        return a + b    
    }
""")
# call() 运行代码中的xxx函数. 后续的参数是xxx的参数
ret = jj.call("an", 10, 20)

#读取js文件
f = open("01.js",mode="r",encoding="utf-8")
js_code = f.read()
# 执行js代码函数
js = execjs.compile(js_code)
js.call(函数)

obj = re.compile(r"window\._INIT_STATE__ = (?P.*?);",re.S) # 正则表达式
code = obj.search(js_code).group("code") #匹配正则表达式
print(type(code))  #这里的code是字符串

# execjs这个库会把运行的结果自动转化成对象
result = execjs.eval(code)# 拿到的是一个JS代码运行的结果
print(result) #是个字典,Python会自动转换成字典
print(type(result)) # dict

execjs._exceptions.ProgramError: Error: Malformed UTF-8 data
js报错了,返回的数据两端有引号或者双引号,需要去掉

二,CryptoJS加密特征(逆向时)

function b(a, b) {
        var c = CryptoJS.enc.Utf8.parse(b)
          , d = CryptoJS.enc.Utf8.parse("0102030405060708")
          , e = CryptoJS.enc.Utf8.parse(a)
          , f = CryptoJS.AES.encrypt(e, c, {
            iv: d,
            mode: CryptoJS.mode.CBC
        });
        return f.toString()
    }


#转换为UTF8
CryptoJS.enc.Utf8
# 转换为base64
CryptoJS.enc.Base64

三,md5加密解密

3-1 不加盐

from hashlib import md5

obj = md5()
obj.update("alex".encode("utf-8"))
# obj.update("wusir".encode('utf-8'))  # 可以添加多个被加密的内容

bs = obj.hexdigest()
print(bs)

3-2 加盐


# 加盐
from hashlib import md5
salt = "我是盐.把我加进去就没人能破解了"

obj = md5(salt.encode("utf-8"))  # 加盐
obj.update("alex".encode("utf-8"))

bs = obj.hexdigest()
print(bs)

3-3 傻系列加密

sha1,sha256,sha512用法跟md5一样

from hashlib import sha1, sha256
sha = sha256(b'salt')
sha.update(b'alex')
print(sha.hexdigest())

四,URLencode

from urllib.parse import quote, unquote, quote_plus, unquote_plus, urlencode

# 单独编码字符串 用 quote
wq = "米饭怎么吃"
print(quote(wq))  # %E7%B1%B3%E9%A5%AD%E6%80%8E%E4%B9%88%E5%90%83
print(quote(wq, encoding="gbk")) # %C3%D7%B7%B9%D4%F5%C3%B4%B3%D4

# 多个数据统一进行编码 用urlencode ,比如字典进行编码
dic = {
    "wq": "米饭怎么吃",
    "new_wq": "想怎么吃就怎么吃"
}

print(urlencode(dic))  # wq=%E7%B1%B3%E9%A5%AD%E6%80%8E%E4%B9%88%E5%90%83&new_wq=%E6%83%B3%E6%80%8E%E4%B9%88%E5%90%83%E5%B0%B1%E6%80%8E%E4%B9%88%E5%90%83
print(urlencode(dic, encoding="utf-8"))  # 也可以指定字符集

# 一个完整的url编码过程
base_url = "http://www.baidu.com/s?"
params = {
    "wd": "大王"
}

url = base_url + urlencode(params)
print(url)  # http://www.baidu.com/s?wd=%E5%A4%A7%E7%8E%8B

#解码
s = "http://www.baidu.com/s?wd=%E5%A4%A7%E7%8E%8B"
print(unquote(s))  # http://www.baidu.com/s?wd=大王

print(quote("a /b/c=", safe=""))  # 传递safe=""  可以保持和浏览器一致
print(quote_plus("a /b/c="))

五,base64

5-1 base64编码解码

import base64

bs = "我要吃饭".encode("utf-8")
# 把字节转化成b64
print(base64.b64encode(bs).decode())

# 把b64字符串转化成字节
s = "5oiR6KaB5ZCD6aWt"
print(base64.b64decode(s).decode("utf-8"))

5-2 base64解码时报错问题

#base64报错问题
import base64

s = "ztKwrsTj0b0"
bb = base64.b64decode(s)
print(bb)

此时运行出现以下问题
Traceback (most recent call last):
  File "D:/PycharmProjects/rrrr.py", line 33, in 
    bb = base64.b64decode(s)
  File "D:\Python38\lib\base64.py", line 87, in b64decode
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding

# 解决办法

s = "ztKwrsTj0b0"
s += ("=" * (4 - len(s) % 4))
print("填充后", s)
bb = base64.b64decode(s).decode("gbk")
print(bb)

# 注意事项
由于标准的Base64编码后可能出现字符+和/,但是这两个字符在URL中就不能当做参数传递,所以就出现了Base64URL,下面是它们的区别:

1. Base64编码后出现的+和/在Base64URL会分别替换为-和_
2. Base64编码中末尾出现的=符号用于补位,这个字符和queryString中的key=value键值对会发生冲突,所以在Base64URL中=符号会被省略,去掉=后怎么解码呢?因为Base64是把3个字节变为4个字节,所以,Base64编码的长度永远是4的倍数,因此,需要加上=把Base64字符串的长度变为4的倍数,就可以正常解码了。

我们的应对方案:
在处理base64的时候.如果遇到了没有+和/的情况. 可以采用下面的方案来替换掉+和/
b64 = base64.b64decode(mi, b"-_")

六,AES加解密

6-1 AES加密

1-1 第一种

# AES加密
from Crypto.Cipher import AES
import base64
"""
长度
    16: *AES-128*
    24: *AES-192*
    32: *AES-256*

MODE 加密模式.常见的 
    ECB  可以没有iv
    CBC	 需要iv的
"""
# 创建加密器 注意秘钥和iv必须是16个字节
aes = AES.new( key= b"alexissbalexissb", mode=AES.MODE_CBC, iv=b"0102030405060708") # 分别是秘钥,模式,iv
data = "我吃饭了"

# 加密的内容必须是字节,所以先进行编码
data_bs = data.encode("utf-8")

# 需要加密的数据必须是16的倍数
# 填充规则: 缺少数据量的个数 * chr(缺少数据量个数)
pad_len = 16 - len(data_bs) % 16
data_bs += (pad_len * chr(pad_len)).encode("utf-8")
# 再对编码后的字节进行加密
bs = aes.encrypt(data_bs)
#用base64对结果进行编码
result = base64.b64encode(bs).decode()

1-2 第二种:

用pad对字节进行填充达到规定的长度

# AES加密
from Crypto.Cipher import AES
import base64
from Crypto.Util.Padding import pad
"""
长度
    16: *AES-128*
    24: *AES-192*
    32: *AES-256*

MODE 加密模式.常见的 
    ECB  可以没有iv
    CBC	 需要iv的
"""
# 创建加密器 注意秘钥和iv必须是16个字节
aes = AES.new( key= b"alexissbalexissb", mode=AES.MODE_CBC, iv=b"0102030405060708") # 分别是秘钥,模式,iv
data = "我吃饭了"

# 加密的内容必须是字节,所以先进行编码
data_bs = data.encode("utf-8")

# 需要加密的数据必须是16的倍数
# 用pad工具进行填充
data_bs = pad(data_bs,16)
# 再对编码后的字节进行加密
bs = aes.encrypt(data_bs)
#用base64对结果进行编码
result = base64.b64encode(bs).decode()
print(result)

1-3 加密结果转换为base64

# 转换成base64 bs是AES加密得到的字节
result = base64.b64encode(bs).decode()

#转换成16进制
import binascii
res = binascii.b2a_hex(bs).decode()

# 也可以转换成16进制,跟上面一个效果一样
bs.hex()

6-2 AES解密

from Crypto.Util.Padding import pad,unpad

# base64编码后的密文
s = '9noPO0fcQizMbPkXcVOTDg=='
# 创建解密器
aes = AES.new(key= b"alexissbalexissb", mode=AES.MODE_CBC, iv=b"0102030405060708")
# 首先把base64编码转换成正常的字节
data = base64.b64decode(s)
res = aes.decrypt(data)
# 明文有可能有问题,因为字节是填充过得
# 用unpad 去除填充的内容,注:需要导入unpad
res = unpad(res,16)
# 得到明文
mingwen = res.decode("utf-8")
print(mingwen)

七,DES加密解密

7-1 DES加密

from Crypto.Cipher import DES
from Crypto.Util.Padding import pad,unpad
import base64
mingwen = '艾尼在学爬虫'

# DES key 是 8个字节
# iv 在CBC 模式下使用 长度8个字节
des = DES.new(key=b'aininora', mode=DES.MODE_CBC,iv=b'ainiaini')

# 明文进行编码
data = mingwen.encode('utf-8')
# 对编码后的字节进行填充
r = pad(data,8)
# 对填充后的字节进行des加密后的字节
res = des.encrypt(r)
# 对加密后的字节进行base64编码
base64_res = base64.b64encode(res).decode()
print(base64_res)

7-2 DES解密

from Crypto.Cipher import DES
from Crypto.Util.Padding import pad,unpad
import base64

#base64密文
miwen = 't4TYyzRnIkmVmI81n+cdsVQfprHN5AtG'
#转换成base64字节
base64_byt = base64.b64decode(miwen)
# 创建解密器对象
des = DES.new(key=b'aininora', mode=DES.MODE_CBC,iv=b'ainiaini')
# 进行解密
r = des.decrypt(base64_byt)
# 得到的结果去掉填充部分
data = unpad(r,8)
# 对得到的字节进行解码
mingwen = data.decode('utf-8')
print(mingwen)

注:DES3 与AES,DES加密,解密规则一样,都是兄弟

注:js里的秘钥可以超过8位,js默认去前八位进行解密(JavaScript语言里)

八,RSA加解密

8-1 RSA加密特征(逆向时) 

setMaxDigits,
RSAKeyPair,
encryptedString

8-2 RSA生成秘钥和公钥

#生成和处理秘钥的
from Crypto.PublicKey import RSA
# 加密和解密
from Crypto.Cipher import PKCS1_v1_5

import base64

# 生成私钥
#bits = 2048 是秘钥的长度
rsa_key = RSA.generate(bits=2048)
# 把秘钥导出来
# 秘钥的本质是字节

# export_key 有个参数叫format,默认为PEM,背后的含义是把秘钥转换成了base64
key = rsa_key.export_key().decode()

with open('./rsa_miyao.pem',mode='w',encoding='utf-8') as f:
	f.write(key)


# 把format换成DER,拿到的是字节
# key = rsa_key.export_key(format='DER')

# 把字节手动转换成base64
# result = base64.b64encode(key).decode()


#生成公钥
public_key = rsa_key.public_key()
# 把公钥导出来
p_key = public_key.export_key()

with open('./rsa_gongyao.pem',mode='wb') as f:
	f.write(p_key)

 8-3 RSA加密

3-1 第一种

#加密解密
from Crypto.Cipher import PKCS1_v1_5
#加载key
from Crypto.PublicKey import RSA

import base64

# 导入的key可以是PEM格式的秘钥,或者是直接形式的秘钥也可以
# 读取秘钥
f = open('./rsa_gongyao.pem',mode='r',encoding='utf-8')
# 拿到已经生成好的秘钥
pub_key = f.read()
f.close()

mingwen = "我要好好学习爬虫"

rsa_key = RSA.importKey(pub_key)
# 生成加密对象
rsa = PKCS1_v1_5.new(key=rsa_key)
# 对明文进行编码,处理成字节
mingwen_bs = mingwen.encode('utf-8')
# 对明文字节进行加密,得到密文字节
mi_bs = rsa.encrypt(mingwen_bs)
# 转换成base64
base_64 = base64.b64encode(mi_bs).decode()
#rsa每次加密后的结果可能不一样
print(base_64)

# 如果用了没有填充的算法,那每一次算出来的结果固定的
# 如果同一个明文反复计算结果是一样的那么考虑用js来完成逆向工作

3-2 第二种

// JSEncrypt
// setPublicKey

// 用的是固定的第三方库,库的名字叫jsencrypt
// 但是这个库只能在浏览器环境使用
// 我们用的是Node环境,所以不能直接是哟和


// 我们需要换一个库, 名字叫 node-jsencrypt
// 安装 npm install node-jsencrypt

var {JSEncrypto} = require('node-jsencrypt')
var o = new JSEncrypt  
o.setPublicKey('xxxxxxxxxxxxxxxxxxxxxxxx')
r = o.encrypt("加密的内容")

// 可以直接把网站的内容拿过来

8-4 RSA解密

4-1 第一特征

1,如果用了没有填充的算法,那每一次算出来的结果固定的

2,如果同一个明文反复计算结果是一样的那么考虑用js来完成逆向工作

// 特征:
// "010001" -> 这是16进制 -> 65537
// setMaxDigits
// RSAKeyPair
// encryptedString

const {
	setMaxDigits,
	RSAKeyPair,
	encryptedString
} = require("./Ras加密");

function c(a,b,c){
    var d,e;
    return setMaxDigits(131),
        d = new RSAKeyPair(b,"",c),
        e = encryptedString(d,a)
}

 注:./RSA加密 这个文件内容过长,如果需要请留言,我单独分享

例如:网易云案例  

const CryptoJS = require("crypto-js")
var window = this

const {
	setMaxDigits,
	RSAKeyPair,
	encryptedString
} = require("./Ras加密");
!function() {
    function a(a) {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; a > d; d += 1)
            e = Math.random() * b.length,
            e = Math.floor(e),
            c += b.charAt(e);
        return c
    }
    function b(a, b) {
        var c = CryptoJS.enc.Utf8.parse(b)
          , d = CryptoJS.enc.Utf8.parse("0102030405060708")
          , e = CryptoJS.enc.Utf8.parse(a)
          , f = CryptoJS.AES.encrypt(e, c, {
            iv: d,
            mode: CryptoJS.mode.CBC
        });
        return f.toString()
    }
    function c(a, b, c) {
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b,"",c),
        e = encryptedString(d, a)
    }
    function d(d, e, f, g) {
        var h = {}
          , i = a(16);
        return h.encText = b(d, g),
        h.encText = b(h.encText, i),
        h.encSecKey = c(i, e, f),
        h
    }
    function e(a, b, d, e) {
        var f = {};
        return f.encText = c(a + e, b, d),
        f
    }
    window.asrsea = d,
    window.ecnonasr = e
}();

    var params = {
        csrf_token: "",
        encodeType: "aac",
        ids: "[1325905146]",
        level: "standard"
    }
    var second = '010001'
    var third = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
    var forth = '0CoJUm6Qyw8W8jud'


function fn(params) {
       return window.asrsea(JSON.stringify(params),second,third,forth)

4-2  RSA普通解密

#加密解密
from Crypto.Cipher import PKCS1_v1_5
#加载key
from Crypto.PublicKey import RSA

import base64

f = open('./rsa_miyao.pem',mode='r',encoding='utf-8')
# 拿到已经生成好的秘钥
pri_key = f.read()
f.close()
# 拿到秘钥
rsakey = RSA.importKey(pri_key)
# 生成解密器对象
rsa = PKCS1_v1_5.new(key=rsakey)
miwen = 'mmf28CJEtFU2Y6C/qx10xoaRmsiY2at3LBjHR5DFdnG9V+5sGPFaMGDGM4OBVWKKJNuSFZgGL9Y409mbh32IKRL4TZYnc0RvJH/0t38d7AmnqnHAyTRUvpKlPzzJg559md6BcTA/ZpYZ4WAtXRuysMvuPTdlRvog2ceGJDXURajU3KyzHXFA9Hc+AamVL75D+YKrOB6n9YeV7n4+DK5mqouNlLp6Plee39vYBzN0IKkzyD6RatmVVUIxJCsUJmeJgIdnBGEuRA9bGNOG3VQa7NF/syWjiRNbKYz+KZHx+RtQ9GuzmPhtJbjh8anPeR2kzNwgfD1HiKhIBDQKVQH/eA=='
#对密文进行base64解码转换成字节
base64_2bs = base64.b64decode(miwen)
#对拿到的字节进行rsa解密得到明文字节
# 解密第二个参数给个None,意思是解密时出错了返回None
mingwen_bs = rsa.decrypt(base64_2bs,None)
#对明文字节进行解码得到明文
mingwen = mingwen_bs.decode('utf-8')
print(mingwen)

如果遇到问题可以留言,欢迎技术交流。交换意见

你可能感兴趣的:(Python爬虫,Python,javascript,前端,开发语言)