本教程仅供学习交流使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,请各学员自觉遵守相关法律法规。
小节目标:
- 了解 算法实现方法
- 熟悉 定位加密位置
- 熟悉 网页加密的实现
一. 非对称简介
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)
和私有密钥(privatekey)
。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
- 常见非对称加密算法
RSA
、DSA
。 - 非对称加密算法私钥由数据接收方持有,不会在网络上传递,保证了密钥的安全。
- 非对称加密算法通常比对称加密算法计算复杂,性能消耗高。
- 非对称加密算法可用于数字签名。
注意:
- 使用时都是使用公钥加密使用私钥解密,公钥可以公开,私钥自己保留。
- 算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,而使加密解密速度慢于对称加密
二. 非对称特征
常见JavaScript调试算法
- 搜索关键词
new JSEncrypt()
,JSEncrypt
等,一般会使用JSEncrypt
库,会有 new 一个实例对象的操作; - 搜索关键词
setPublicKey
、setKey
、setPrivateKey
、getPublicKey
等,一般实现的代码里都含有设置密钥的过程。
RSA
的私钥、公钥、明文、密文长度也有一定对应关系,也可以从这方面初步判断:
私钥长度 | 公钥长度 | 明文长度 | 密文长度 |
---|---|---|---|
428 | 128 | 1~53 | 88 |
812 | 216 | 1~117 | 172 |
1588 | 392 | 1~245 | 344 |
2.1 JavaScript 实现
// npm install node-rsa --save
// 引用 node-rsa 加密模块
var NodeRSA = require('node-rsa');
function rsaEncrypt() {
pubKey = new NodeRSA(publicKey,'pkcs8-public');
var encryptedData = pubKey.encrypt(text, 'base64');
return encryptedData
}
function rsaDecrypt() {
priKey = new NodeRSA(privatekey,'pkcs8-private');
var decryptedData = priKey.decrypt(encryptedData, 'utf8');
return decryptedData
}
var key = new NodeRSA({b: 512}); //生成512位秘钥
var publicKey = key.exportKey('pkcs8-public'); //导出公钥
var privatekey = key.exportKey('pkcs8-private'); //导出私钥
var text = "I love Python!"
var encryptedData = rsaEncrypt()
var decryptedData = rsaDecrypt()
console.log("公钥:\n", publicKey)
console.log("私钥:\n", privatekey)
console.log("加密字符串: ", encryptedData)
console.log("解密字符串: ", decryptedData)
2.2 Python 实现
模块:rsa
import rsa
import base64
def rsa_encrypt(pu_key, t):
# 公钥加密
rsas = rsa.encrypt(t.encode("utf-8"), pu_key)
return base64.b64encode(rsas)
def rsa_decrypt(pr_key, t):
# 私钥解密
rsas = rsa.decrypt(base64.b64decode(t), pr_key).decode("utf-8")
return rsas
if __name__ == "__main__":
public_key, private_key = rsa.newkeys(512) # 生成公钥、私钥
print('公钥:', public_key)
print('私钥:', private_key)
text = 'I love Python!' # 加密对象
encrypted_str = rsa_encrypt(public_key, text)
print('加密字符串:', encrypted_str)
decrypted_str = rsa_decrypt(private_key, encrypted_str)
print('解密字符串:', decrypted_str)
三. 案例实战1
1. 采集目标
- 目标:中国移动登录
- 主页:https://login.10086.cn/html/login/email_login.html
- 接口:https://login.10086.cn/login.htm
2.表单逆向分析
1. 加密位置定位
- 关键字搜索
JSEncrypt
,encrypt
,password
2. 代码分析
- 账号密码都是由encrypt来进行加密的,所以我们只需要扣encrypt核心代码就好
- 进入函数观察代码,可以看出当前代码是一个RSA的加密算法,我们可以直接扣关键代码
3. 逆向代码
- JavaScript工具包使用
var JSEncrypt = require('jsencrypt')
function encrypt(_0x32033c) {
var _0x283d00 = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgDq4OqxuEisnk2F0EJFmw4xKa5IrcqEYHvqxPs2CHEg2kolhfWA2SjNuGAHxyDDE5MLtOvzuXjBx/5YJtc9zj2xR/0moesS+Vi/xtG1tkVaTCba+TV+Y5C61iyr3FGqr+KOD4/XECu0Xky1W9ZmmaFADmZi7+6gO9wjgVpU9aLcBcw/loHOeJrCqjp7pA98hRJRY+MML8MK15mnC4ebooOva+mJlstW6t/1lghR8WNV8cocxgcHHuXBxgns2MlACQbSdJ8c6Z3RQeRZBzyjfey6JCCfbEKouVrWIUuPphBL3OANfgp0B+QG31bapvePTfXU48TYK0M5kE+8LgbbWQIDAQAB';
var _0x1defd6 = new JSEncrypt();
_0x1defd6['setPublicKey'](_0x283d00);
var _0x4bd6d3 = _0x1defd6['encrypt'](_0x32033c);
return _0x4bd6d3;
}
console.log(encrypt('12345'));
四.案例实战2
1.采集目标
- 目标:苏宁登录
- 主页:https://passport.suning.com/ids/login
- 接口:https://passport.suning.com/ids/login
2.表单逆向分析
1.加密位置定位
- 搜索关键字password2
- 根据启动器定位
- 很直观就能看出为非对称加密方式(使用公钥加密)
2.代码分析
- 通过标准算法库可以直接进行还原定位到公钥的位置观察公钥的生成方式
3.逆向代码
- JavaScript标准库实现
var JSEncrypt = require('jsencrypt')
var loginPBK="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQComqoAyvbCqO1EGsADwfNTWFQIUbm8CLdeb9TgjGLcz95mAo204SqTYdSEUxFsOnPfROOTxhkhfjbRxBV4/xjS06Y+kkUdiMGFtABIxRQHQIh0LrVvEZQs4NrixxcPI+b1bpE0gO/GAFSNWm9ejhZGj7UnqiHphnSJAVQNz2lgowIDAQAB";
var encrypt = new JSEncrypt();
encrypt.setPublicKey(loginPBK);
var pwd2 = encrypt.encrypt('12341');
console.log(pwd2);
- 扣代码实现方式
- 扣代码的方式可以先把全部的代码都拿下来,分析那些代码是我们想要的
五. 案例实战3
1. 逆向目标
- 目标:中国观鸟记录中心
- 主页:http://birdreport.cn/home/activity/page.html
- 接口:https://api.birdreport.cn/front/activity/search
2.逆向分析
1.加密位置分析
- 关键字定位数据位置,请求头有
Requestid
sign
, 定位到文件加密的位置
- 上面还有个b.data感觉像是请求的表单数据的生成位置,跟的时候也需要留意一下
2.代码分析
- Timestamp:用的是时间戳生成
- Requestid:是有d获取的调用的getUuid方法
- sign:是把请求体明文数据,Requestid和Timestamp拼接做md5加密
- 运算代码时我们就能看到b.data,就是对明文的请求体内容做加密处理的操作
- 大致就能推断出是非对称加密,encrypt是加密对象,可以直接尝试标准算法来进行加密,扣代码的方式也行
3. 响应数据解密
- 响应比较好分析直接定位到ajax的请求成回调就行\
- 跟进a.parseData就行
- 最终根据发现为AES解密方法
4.逆向代码
- js 算法还原
var CryptoJS = require('crypto-js')
var JSEncrypt = require('jsencrypt')
function getUuid() {
var s = [];
var a = "0123456789abcdef";
for (var i = 0; i < 32; i++) {
s[i] = a.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = "4";
s[19] = a.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23];
var b = s.join("");
return b
}
beforeSend = function (key) {
var paramPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB";
var encrypt = new JSEncrypt();
encrypt.setPublicKey(paramPublicKey);
var c = Date.parse(new Date());
var d = getUuid();
var e = key;
b = encrypt.encrypt(e);
// console.log(b)
var f = CryptoJS.MD5(e + d + c).toString();
return {
"timestamp": c,
'requestId': d,
'sign': f,
'data': b
}
}
console.log(beforeSend('{"limit":"20","page":"9"}'));
- 响应数据解密
var CryptoJS = require('crypto-js');
decode = function (a) {
var key = '3583ec0257e2f4c8195eec7410ff1619';
var iv = 'd93c0d5ec6352f20'
var b = CryptoJS.enc.Utf8.parse(key);
var c = CryptoJS.enc.Utf8.parse(iv);
var d = CryptoJS.AES.decrypt(a, b, {
iv: c,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return d.toString(CryptoJS.enc.Utf8)
}
bb = ""
console.log(decode(bb))
- python代码
import requests
import execjs
import pymongo
import json
class GuanNiao:
def __init__(self):
self.con = pymongo.MongoClient()
self.db = self.con['spiders']['gn']
self.headers = {
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Origin": "http://birdreport.cn",
"Pragma": "no-cache",
"Referer": "http://birdreport.cn/",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "cross-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
"requestId": "aec16658d1b45d43a4dbd99df2c41d09",
"sec-ch-ua": "^\\^Chromium^^;v=^\\^118^^, ^\\^Google",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "^\\^Windows^^",
"sign": "2ac365e6886dd970d1a5700ccb846b98",
"timestamp": "1697548343000"
}
self.url = "https://api.birdreport.cn/front/activity/search"
self.data = '{"limit":"20","page":"%s"}'
def get_data(self, page):
with open('demo1.js', 'r', encoding='utf-8')as f:
js_code = f.read()
js = execjs.compile(js_code)
res = js.call('beforeSend', self.data % page)
self.headers['sign'] = res['sign']
self.headers['timestamp'] = str(res['timestamp'])
self.headers['requestId'] = res['requestId']
response = requests.post(self.url, headers=self.headers, data=res['data'])
return response.json()
def parse_data(self, response):
with open('demo2.js', 'r', encoding='utf-8')as f:
js_code = f.read()
js = execjs.compile(js_code)
result = js.call('decode', response['data'])
for i in json.loads(result):
item = {}
item['address'] = i['address']
item['serial_id'] = i['serial_id']
item['timebegin'] = i['timebegin']
item['timeend'] = i['timeend']
self.save_data(item)
def save_data(self, item):
print(item)
self.db.insert_one(item)
def main(self):
for i in range(1, 10):
res = self.get_data(i)
self.parse_data(res)
if __name__ == '__main__':
gn = GuanNiao()
gn.main()
结语
以上就是关于js逆向技术中的非对称加密算法全部内容了,欢迎同学们在评论区讨论交流,有任何js逆向、数据采集相关需求也可以V后台regentwan与我联系哟~