JS逆向实战:死磕某蓝厂社区AES+RSA混合加密,从混淆代码到Python还原全流程
2026/6/30 9:21:31 网站建设 项目流程

前言

在做数据采集和安全研究的过程中,我们经常会遇到各种前端加密防护。最近在对某“蓝厂”(懂的都懂,国内头部手机厂商)社区进行协议分析时,发现其登录及核心业务接口的请求参数不再是简单的MD5签名,而是采用了AES加密数据 + RSA加密AES密钥的混合加密方案。

这种方案在金融级应用和大型厂商中越来越常见。很多初学者在遇到这种情况时,往往因为找不到密钥生成逻辑或者无法处理非对称加密而卡壳。本文将从零开始,记录一次完整的逆向分析过程,包括加密点定位、混淆代码还原、算法识别以及最终的Python模拟实现。

⚠️ 免责声明
本文所涉及的技术仅用于安全研究与学习交流,请勿用于任何非法用途。文中出现的网址、接口均已做脱敏处理。尊重知识产权,遵守相关法律法规。

一、 抓包分析与加密特征识别

1.1 接口观察

打开浏览器开发者工具(F12),切换到Network面板,触发登录操作。我们可以观察到关键的login请求,其Form Data中包含两个核心加密字段:

  • encData: 一串很长的Base64编码字符串,目测是加密后的业务数据。
  • encKey: 另一串Base64字符串,长度固定,疑似RSA加密后的密文。
  • encVer: 版本号标识,如 “1_1_2”。

1.2 响应数据

不仅请求加密,响应体也是一串密文。这说明服务端和客户端约定了一套完整的加解密协议。如果我们不能还原这套协议,就无法正常解析返回的业务数据。

蓝厂服务器浏览器/脚本蓝厂服务器浏览器/脚本生成随机AES密钥AES加密(明文数据) ->> encDataRSA公钥加密(AES密钥) ->> encKeyPOST /login (encData, encKey)RSA私钥解密(encKey) ->> AES密钥AES解密(encData) ->> 明文数据业务处理 & AES加密响应返回加密响应体AES解密响应体 ->> 明文结果

二、 加密入口定位

面对压缩混淆过的JS代码,全局搜索是最直接的手段。

2.1 关键字搜索

我们在Sources面板中全局搜索encData。由于代码经过Webpack打包和混淆,变量名可能变成了单字母。但字符串常量通常不会被完全改变。

搜索后发现约4处匹配。通过观察上下文,我们发现一处赋值逻辑:

e.encData=t.ciphertext,e.encKey=n,e.encVer="1_1_2"

这与我们抓包看到的结构完全一致!在此处打下断点,重新触发登录,成功断住。

2.2 调用栈回溯

断住后,查看右侧Call Stack(调用栈)。我们需要找到加密函数的“源头”。逐层向上点击,观察Scope中的变量值,直到找到一个函数,它的入参包含了我们的明文密码和手机号,而出参正是加密后的对象。

这个函数就是我们要逆向的核心加密入口

三、 算法还原与代码剥离

3.1 识别加密库

在核心加密函数内部,我们可以看到大量特征代码。通过搜索CryptoJSAES等关键字,确认该站点使用的是标准的crypto-js库。

但关键在于:AES的密钥是如何生成的?RSA的公钥写在哪里?

3.2 提取RSA公钥

在加密函数附近,我们发现了一个硬编码的长字符串,以MIGfMA0GCSqGSIb3DQEBAQUAA4GN...开头。这是标准的PKCS#1格式RSA公钥的Base64编码。将其保存下来,后续Python复现时需要用到。

3.3 梳理加密逻辑

通过单步调试,我们还原出如下加密流程:

  1. 生成一个随机的16字节字符串作为AES密钥(aesKey)。
  2. 使用AES-CBC模式,以aesKey为密钥,对JSON序列化后的表单数据进行加密,得到encData
  3. aesKey转为Hex字符串,使用预置的RSA公钥进行加密,得到encKey
  4. 组装最终请求体。

💡 踩坑提示
在扣取JS代码时,不要试图把整个webpack模块都搬走。只提取加密相关的纯函数,并手动补齐navigatorwindow等必要的环境变量即可。过多的环境依赖会导致Node.js运行报错。

四、 Python协议还原

逆向的终点不是看懂JS,而是用Python稳定地模拟请求。以下是核心还原代码(已脱敏):

importjsonimportbase64fromCrypto.CipherimportAES,PKCS1_v1_5fromCrypto.PublicKeyimportRSAfromCrypto.Randomimportget_random_bytesfromCrypto.Util.PaddingimportpadclassLanChangCrypto:def__init__(self):# 从JS中提取的RSA公钥self.rsa_public_key="""-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...(脱敏)...IDAQAB -----END PUBLIC KEY-----"""defencrypt(self,plain_data:dict)->dict:# 1. 生成随机AES密钥aes_key=get_random_bytes(16)# 2. AES加密业务数据plain_text=json.dumps(plain_data,separators=(',',':'))cipher_aes=AES.new(aes_key,AES.MODE_CBC,iv=aes_key)# 注意:该站IV与Key相同encrypted_data=cipher_aes.encrypt(pad(plain_text.encode(),AES.block_size))enc_data=base64.b64encode(encrypted_data).decode()# 3. RSA加密AES密钥rsa_key=RSA.import_key(self.rsa_public_key)cipher_rsa=PKCS1_v1_5.new(rsa_key)enc_key=base64.b64encode(cipher_rsa.encrypt(aes_key.hex().encode())).decode()return{"encData":enc_data,"encKey":enc_key,"encVer":"1_1_2"}# 测试crypto=LanChangCrypto()payload=crypto.encrypt({"phone":"13800138000","password":"test123"})print(payload)

4.1 关键细节对齐

在还原过程中,最容易出错的是Padding方式IV向量

  • 通过JS调试发现,该站AES使用的是Pkcs7填充(Python的pycryptodome默认也是此填充)。
  • IV并没有单独生成,而是直接复用了AES Key本身。这一点如果不通过动态调试,很难从静态混淆代码中直接看出来。

五、 响应数据解密

请求搞定后,响应解密就简单了。因为我们自己生成了AES密钥,所以只需要用同一个密钥对响应体进行AES-CBC解密即可。

需要注意的是,服务端返回的密文可能包含额外的校验位或头部信息,解密前可能需要先截取特定长度的字节。建议先在浏览器中断点观察解密函数的输入输出,确认密文的实际格式。

六、 总结与思考

本次某蓝厂社区的逆向实战,核心难点不在于算法本身(都是标准算法),而在于:

  1. 混合加密的定位:如何在海量混淆代码中快速找到AES和RSA的结合点。
  2. 细节参数的确认:IV是否等于Key?Padding模式是什么?公钥格式是否需要转换?这些都必须通过动态调试验证,不能靠猜。
  3. 环境最小化:扣代码时保持克制,只取必要逻辑,避免陷入补环境的泥潭。

给新手的建议:
不要过度依赖AST还原或自动化补环境框架。对于这种标准加密库的场景,手动调试+理解原理才是最快的路径。当你能够看着混淆代码就在脑海中勾勒出数据流向时,才算真正入了逆向的门。


🔗 参考资料

  • CryptoJS官方文档
  • PyCryptodome使用指南
  • 《JavaScript逆向开发实战》

如果这篇文章对你有帮助,欢迎点赞、收藏、关注三连支持!有任何问题欢迎在评论区交流讨论。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询