将安全君呀设为"星标⭐️"
第一时间收到文章更新
声明: 安全君呀 公众号文章中的技术只做研究之用,禁止用来从事非法用途,如有使用文章中的技术从事非法活动,一切后果由使用者自负,与本公众号无关。
文章声明:本篇文章内容部分选取网络,如有侵权,请告知删除。
在进行 js 逆向时,常遇人类难以直接理解之物,眼前多是加密后的密文。
一. MD5
MD5 乃极为常见的摘要(hash)算法,其优势在于小巧、速度快且极难被破解,故而仍是众多国内互联网公司选用的密码摘要算法。
-
此算法不可逆,可见摘要算法并非加密逻辑。
-
相同内容所算出的摘要必然相同。
-
不同内容(即便仅有细微差异)
md5的python实现:
from hashlib import md5
obj = md5()
obj.update("alex".encode("utf-8"))
# obj.update("wusir".encode('utf-8')) # 可以添加多个被加密的内容
bs = obj.hexdigest()
print(bs)
当把密文置于网页之中,会察觉部分网站看似能够直接解密。实则不然,此并非真正对 MD5 予以直接解密,而是运用 “撞库” 手段。即该网站预先储存了海量的 MD5 值。
待需要展开查询之际,仅需一条 select 语句即可达成查询目的,此即为所谓的撞库情形。
至于如何有效规避撞库:在进行 MD5 计算时可添加盐值。一旦加盐之后,撞库便会变得极为困难。
from hashlib import md5
salt = "我是盐.把我加进去就没人能破解了"
obj = md5(salt.encode("utf-8")) # 加盐
obj.update("alex".encode("utf-8"))
bs = obj.hexdigest()
print(bs)
无论是 sha1、sha256 还是 md5,均隶属于摘要算法范畴,本质上都是在对 hash 值进行计算,仅在散列程度方面存在差异。这类算法具备一个显著特性,即它们属于散列操作而非加密行为。并且,鉴于 hash 算法不可逆的特质,故而不存在所谓解密的逻辑。
二. URLEncode和Base64
在我们访问一个url的时候总能看到这样的一种url
https://www.sogou.com/web?query=%E5%90%83%E9%A5%AD%E7%9D%A1%E8%A7%89%E6%89%93%E8%B1%86%E8%B1%86&_asf=www.sogou.com&_ast=&w=01019900&p=40040100&ie=utf8&from=index-nologin&s_from=index&sut=3119&sst0=1630994614300&lkt=0%2C0%2C0&sugsuv=1606978591882752&sugtime=1630994614300
在浏览器当中明明能够清晰看到中文内容,可一旦进行复制操作,又或者是在抓包工具里查看时,呈现出来的却全是那种“%”字符。那么这个“%”究竟是怎么回事呢?难道也是一种加密方式吗? 实际上,浏览器会自动执行urlencode操作,针对我们所请求的url展开编码处理。而这种编码所遵循的规则就被称作百分号编码,它是特意为url(统一资源定位符)量身定制的一套编码规则。 以下是一个url的完整构成部分:
scheme://host:port/dir/file?p1=v1&p2=v2#anchor
http://www.baidu.com/tieba/index.html?name=alex&age=18
参数: key=value
服务器可以通过key拿value
倘若在参数里出现了诸如“=”这类特殊符号,比如我们想要给服务器传递“a=b=c”这样的参数,那势必会致使整个URL出现歧义。 所以呢,需要将url中的参数部分先转变成字节形式,接着再把每个字节进一步转化成两个十六进制的数字,并且在前面添加上“%”。
这看起来似乎挺复杂的,不过在python当中,却能够直接一次性搞定。
from urllib.parse import urlencode, unquote, 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
# 多个数据统一进行编码
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=大王
Base64 其实并不难理解。一般而言,被加密后的内容呈现为字节形式,而我们的密文是用于传输的。 然而,在 http 协议下传输字节会是一件极为棘手的事情。与之相比,如果传输的是字符串则要容易掌控得多。 就在这样的情境下,Base64 顺势诞生了。由 26 个大写字母、26 个小写字母、10 个数字以及 2 个特殊符号(“+”与“/”)共同构建起了一套类似 64 进制的计算逻辑体系,这便是 Base64。
import base64
bs = "我要吃饭".encode("utf-8")
# 把字节转化成b64
print(base64.b64encode(bs).decode())
# 把b64字符串转化成字节
s = "5oiR6KaB5ZCD6aWt"
print(base64.b64decode(s).decode("utf-8"))
注意, b64处理后的字符串长度. 一定是4的倍数. 如果在网页上看到有些密文的b64长度不是4的倍数. 会报错
例如
import base64
s = "ztKwrsTj0b0"
bb = base64.b64decode(s)
print(bb)
此时运行出现以下问题
Traceback (most recent call last):
File "D:/PycharmProjects/rrrr.py", line 33, in <module>
bb = base64.b64decode(s)
File "D:Python38libbase64.py", line 87, in b64decode
return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
解决的思路在于,依据 base64 长度的特定要求,其字符串长度务必是 4 的倍数,若不足则通过填充的方式予以补足便可。
import base64
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、DES、3DES 等。接下来,我们将重点讨论 AES 和 DES 这两种算法。 如果想要亲自体验 AES 加密和解密的过程,可以访问测试网站:https://the-x.cn/cryptography/Aes.aspx 。在这个网站上,你可以输入明文、密钥等信息,选择相应的加密模式和参数,进行 AES 加密和解密操作,从而更直观地感受对称加密的神奇之处。
3.1 AES
pip install pycrypto => 很多人装不上....
pip install pycryptodome => 可以装..
安装之后依然不好用.. 发现只有crypto 而Crypto用不了.
去模块的安装目录. 改一下文件名, 把小写的c改成大写的C
# AES加密
from Crypto.Cipher import AES
"""
长度
16: *AES-128*
24: *AES-192*
32: *AES-256*
MODE 加密模式.
常见的ECB, CBC
以下内容来自互联网~~
ECB:是一种基础的加密方式,密文被分割成分组长度相等的块(不足补齐),然后单独一个个加密,一个个输出组成密文。
CBC:是一种循环模式,前一个分组的密文和当前分组的明文异或或操作后再加密,这样做的目的是增强破解难度。
CFB/OFB:实际上是一种反馈模式,目的也是增强破解的难度。
FCB和CBC的加密结果是不一样的,两者的模式不同,而且CBC会在第一个密码块运算时加入一个初始化向量。
"""
aes = AES.new(b"alexissbalexissb", mode=AES.MODE_CBC, IV=b"0102030405060708")
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)
print(bs)
AES解密
from Crypto.Cipher import AES
aes = AES.new(b"alexissbalexissb", mode=AES.MODE_CBC, IV=b"0102030405060708")
# 密文
bs = b'xf6zx0f;GxdcB,xcclxf9x17qSx93x0e'
result = aes.decrypt(bs) # 解密
print(result.decode("utf-8"))
3.2 DES
# DES加密解密
from Crypto.Cipher import DES
# key: 8个字节
des = DES.new(b"alexissb", mode=DES.MODE_CBC, IV=b"01020304")
data = "我要吃饭".encode("utf-8")
# # 需要加密的数据必须是16的倍数
# # 填充规则: 缺少数据量的个数 * chr(缺少数据量个数)
pad_len = 8 - len(data) % 8
data += (pad_len * chr(pad_len)).encode("utf-8")
bs = des.encrypt(data)
print(bs)
# 解密
des = DES.new(key=b'alexissb', mode=DES.MODE_CBC, IV=b"01020304")
data = b'6HXxfaxb2Rxa8rxa3xedxbdx00xdb}xb0xb9'
result = des.decrypt(data)
print(result.decode("utf-8"))
四.非对称加密:公钥与私钥的加密“魔法”
在密码学的奇妙领域中,非对称加密以其独特的加密和解密方式脱颖而出。与对称加密不同,非对称加密所使用的加密和解密密钥并非同一把,而是需要两把截然不同的钥匙,即一个公钥和一个私钥。 其加密过程如同一场精心编排的信息传递舞蹈。首先,公钥会被发送至客户端,这就像是在信息传递的舞台上搭建起了一座桥梁。发送端拿到公钥后,便利用它对需要传输的数据进行加密处理,随后将加密后的数据发送给接收端。而接收端则持有与之对应的私钥,这把私钥就像是一把独一无二的魔法钥匙,只有它能够解开由公钥加密的数据。由于私钥仅仅存放在接收端一方,犹如被珍藏在一个极其隐秘的宝库之中,所以即便加密的数据在传输过程中不幸被他人截获,没有私钥的解密“咒语”,这些数据也如同被施了魔法的神秘卷轴,无法被窥探其中的奥秘。 在众多非对称加密算法的璀璨星空中,RSA、DSA 等都是耀眼的明星。在这里,我们将聚焦于其中最为常见的一种加密方案——RSA 加密,一同领略它在信息安全领域所施展的强大“魔法”。
4.1 RSA加密解密
4.1.1 创建公钥和私钥
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
from Crypto import Random
import base64
# 随机
gen_random = Random.new
# 生成秘钥
rsakey = RSA.generate(1024)
with open("rsa.public.pem", mode="wb") as f:
f.write(rsakey.publickey().exportKey())
with open("rsa.private.pem", mode="wb") as f:
f.write(rsakey.exportKey())
4.1.2 加密
# 加密
data = "我要吃饭了"
with open("rsa.public.pem", mode="r") as f:
pk = f.read()
rsa_pk = RSA.importKey(pk)
rsa = PKCS1_v1_5.new(rsa_pk)
result = rsa.encrypt(data.encode("utf-8"))
# 处理成b64方便传输
b64_result = base64.b64encode(result).decode("utf-8")
print(b64_result)
4.1.3 解密
data = "e/spTGg3roda+iqLK4e2bckNMSgXSNosOVLtWN+ArgaIDgYONPIU9i0rIeTj0ywwXnTIPU734EIoKRFQsLmPpJK4Htte+QlcgRFbuj/hCW1uWiB3mCbyU3ZHKo/Y9UjYMuMfk+H6m8OWHtr+tWjiinMNURQpxbsTiT/1cfifWo4="
# 解密
with open("rsa.private.pem", mode="r") as f:
prikey = f.read()
rsa_pk = RSA.importKey(prikey)
rsa = PKCS1_v1_5.new(rsa_pk)
result = rsa.decrypt(base64.b64decode(data), gen_random)
print(result.decode("utf-8"))
原文始发于微信公众号(安全君呀):【JS逆向渗透系列03】一篇看懂Python各种加密逻辑
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论