js逆向——返回加密数据的处理

一、确定爬取目标

今日受害者网站:https://jzsc.mohurd.gov.cn/data/company

我们的目标是爬取该网站列表页当中的企业信息

二、数据抓包分析

首先刷新网页进行抓包,观察一下数据在哪个数据包当中被返回(获取返回数据)

结果发现返回的都是一串看不懂的密文

搜索数据包中的文字也没有找到任何信息

​ 

这说明我们收到的响应数据是加密后的结果,返回客户端之后由客户端解密拿到明文字符串展示在前端当中,所以我们才能在网页看到有效的信息

因此现在就需要对返回的响应数据进行解密(decrypt)

三、js逆向分析

我们直接搜索decrypt,在所有可疑的位置打上断点

注意:像css/axios//jquery/react这种可以直接pass;但是app/chunk不能pass

另外一个js文件也不能放过

​ 

然后我们重新刷新网页,来再次发送请求

这样就定位到了解密的关键位置,不难发现是使用AES-CBC模式进行的加密解密

此外还有2处断点是具体的加密细节,可以直接忽略(取消断点)

​ 

既然是AEC-CBC进行加密解密

那么我们就重点关注3个参数:密文(n),密钥(f),iv(m)

a = d.a.AES.decrypt(n, f, {
    iv: m,
    mode: d.a.mode.CBC,
    padding: d.a.pad.Pkcs7
}

往上观察发现密钥和iv都是直接静态给出了

​ 

但是我们在使用之前需要验证一下值是否正确

结果发现密钥的值竟然和js代码当中给的不一致!

​ 

我们往下翻,原来是在执行解密之前key的值被重新修改了.......

看来还是得小心点留个心眼啊😂😂😂

​ 

这样数据解密的步骤我们已经分析完了,现在还有一个问题就是我怎么知道返回的7个XHR数据包哪一个是列表页展示的、我们希望得到的目标数据呢?

其实很简单.....

法一:你可以一个个爬取尝试(排除法)

而且几个过小/过大的文件肯定不是,剩下几个慢慢排除即可

法二:

在调试窗口观察到目标数据之后,点击网络面板,观察哪个数据接口返回了数据

其实根据名字也能猜个大概,因为我们想要要获取列表页数据,那么list?pg=0,pgsz=15就显得很可疑了😂😂

现在我们还需要确认一件事,就是这个网站使用的AES-CBC是不是标准的AES算法,这将决定我们需不需要扣代码

不妨在在线加密解密网站测试一下

能够正确解密,说明使用的是标准的AES算法

而且通过观察,js代码没有特别需要扣的地方,都是使用的库当中自带函数

function b(t) {
            var e = d.a.enc.Hex.parse(t)
              , n = d.a.enc.Base64.stringify(e)
              , a = d.a.AES.decrypt(n, f, {
                iv: m,
                mode: d.a.mode.CBC,
                padding: d.a.pad.Pkcs7
            })
              , r = a.toString(d.a.enc.Utf8);
            return r.toString()
        }

四、代码编写

下面就是愉快的编码环节了

第一步是正确拿到返回的加密数据

import execjs
from loguru import logger
import requests

headers = {
    "Connection": "keep-alive",
    "Pragma": "no-cache",
    "Cache-Control": "no-cache",
    "accessToken": "",
    "timeout": "30000",
    "sec-ch-ua-mobile": "?0",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Core/1.94.253.400 QQBrowser/12.6.5678.400",
    "v": "231012",
    "Accept": "application/json, text/plain, */*",
    "sec-ch-ua-platform": "^\\^Windows^^",
    "Sec-Fetch-Site": "same-origin",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Dest": "empty",
    "Referer": "https://jzsc.mohurd.gov.cn/data/company",
    "Accept-Language": "zh-CN,zh;q=0.9"
}
url = "https://jzsc.mohurd.gov.cn/APi/webApi/dataservice/query/comp/list"
params = {
    "pg": "0",
    "pgsz": "15",
    "total": "0"
}
response = requests.get(url, headers=headers, params=params)

print(response.text)
print(response)

然后在vscode当中脱离浏览器环境编写解密的js代码,并完成测试

var CryptoJS = require('crypto-js');

function decrypt_aes_cbc(arg_key,arg_iv,arg_text){
    // utf-8串转word数组
    var key = CryptoJS.enc.Utf8.parse(arg_key);
    // 16进制数转word数组
    var e = CryptoJS.enc.Hex.parse(arg_text);
    // 将e转为base64字符串
    var n = CryptoJS.enc.Base64.stringify(e);
    var a = CryptoJS.AES.decrypt(n, key, {
        iv: CryptoJS.enc.Utf8.parse(arg_iv),
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return a.toString(CryptoJS.enc.Utf8).toString()
}
//测试
//console.log(decrypt_aes_cbc('Dt8j9wGw%6HbxfFn','0123456789ABCDEF',"5588a9e126c91a28cc2f6813e37933690edae8a89a2a40f9ee87399a0fa435be5ec9333788e282e9b9cf6c5b7b8d92dcf0f09279c62908f4d6ce72a06dd3e9f2"))

最后在pycharm当中调用js代码即可

with open('aes_cbc.js','r',encoding='utf-8') as f:
    ctx = execjs.compile(f.read())
decry_data = ctx.call('decrypt_aes_cbc','Dt8j9wGw%6HbxfFn','0123456789ABCDEF',response_string)
print("解密后的数据为:",decry_data)

结果报错:

1)UnicodeDecodeError: 'gbk' codec can't decode byte 0xac in position 62: illegal multibyte sequence

2)AttributeError: 'NoneType' object has no attribute 'replace'

为此只需要在execjs库的上面添加如下代码,确保js代码执行时编码环境为utf-8:

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

 

再次运行,顺利拿到解密后的列表数据 

 

完整代码:

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

headers = {
    "Connection": "keep-alive",
    "Pragma": "no-cache",
    "Cache-Control": "no-cache",
    "accessToken": "",
    "timeout": "30000",
    "sec-ch-ua-mobile": "?0",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Core/1.94.253.400 QQBrowser/12.6.5678.400",
    "v": "231012",
    "Accept": "application/json, text/plain, */*",
    "sec-ch-ua-platform": "^\\^Windows^^",
    "Sec-Fetch-Site": "same-origin",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Dest": "empty",
    "Referer": "https://jzsc.mohurd.gov.cn/data/company",
    "Accept-Language": "zh-CN,zh;q=0.9"
}
url = "https://jzsc.mohurd.gov.cn/APi/webApi/dataservice/query/comp/list"
params = {
    "pg": "0",
    "pgsz": "15",
    "total": "0"
}
response = requests.get(url, headers=headers, params=params)
response_string = response.text
# print(response.text)
# print(response)

with open('aes_cbc.js','r',encoding='utf-8') as f:
    ctx = execjs.compile(f.read())
decry_data = ctx.call('decrypt_aes_cbc','Dt8j9wGw%6HbxfFn','0123456789ABCDEF',response_string)
print("解密后的数据为:",decry_data)

ok,今天的你就到此为止吧,我们下期再见~ 

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值