爬取某省公共资源交易中心

通过抓包数据可知

这个data是我们所需要的数据,但是已经通过加密隐藏起来了

分析

  1. 首先这是个json文件,我们可以用请求参数一个一个搜

    但是由于我们已经知道了这是个json

    所以我们可以直接偷个懒
  2. 直接搜索json.parse



然后还看见了state 刚好也是返回的值



3. 根据上述请求参数我们看见了ts是一个时间戳,

然后往周围找找



所以可以判断这是个AES加密

基本这个函数就是AES加密的,我们把代码抠出来就能直接运行了,但是抠出来之后发现

这就是个常规AES加密,所以我们直接通过python改写

Python代码

# -*- coding: utf-8 -*-
# @Time : 2022/7/13 16:39
# @Author : lzc
# @Email : hybpjx@163.com
# @Software: PyCharm
import base64
import datetime
import hashlib
import json
import time
import requests
from Cryptodome.Cipher import AES class PublicResourceCenter: item = {}
session = requests.session() list_url = '(为保证网站安全性)' detail_post_url = "(为保证网站安全性)" API = APIInvoke() secret_key = 'BE45D593014E4A4EB4449737660876CE'[0:32] # 密钥 iv = "A8909931867B0425"[0:16] # 初始向量 # 需要补位,str不是16的倍数那就补足为16的倍数
def add_to_16(self, value):
while len(value) % 16 != 0:
value += '\0'
return str.encode(value) # 解密方法
def aes_decrypt(self, key, t, iv):
aes = AES.new(self.add_to_16(key), AES.MODE_CBC, self.add_to_16(iv)) # 初始化加密器
base64_decrypted = base64.decodebytes(t.encode(encoding='utf-8')) # 优先逆向解密 base64 成 bytes
decrypted_text = str(aes.decrypt(base64_decrypted), encoding='utf-8').replace('\0', '') # 执行解密密并转码返回str
return decrypted_text def less_data(self, less_data):
now = datetime.datetime.now()
# 在获取超过当前时间30天的时间
delta = datetime.timedelta(days=less_data)
n_days = now - delta
return str(n_days).split(' ')[0] def get_sign_data(self, sign):
# md5 加密
portal_sign = hashlib.md5(sign.encode(encoding='utf-8')).hexdigest() return portal_sign def timestamp(self, shijian):
s_t = time.strptime(shijian, "%Y-%m-%d %H:%M:%S")
mkt = int(time.mktime(s_t))
return mkt def parse(self):
padding = lambda detail_decrypted_str: detail_decrypted_str[:detail_decrypted_str.rfind("}") + 1] for url in range(1, 6):
ts = int(time.time() * 1000)
# 封装请求体
js_data = {"pageNo": url, "pageSize": 20, "total": 5679, "AREACODE": "", "M_PROJECT_TYPE": "",
"KIND": "GCJS",
"GGTYPE": "1", "PROTYPE": "", "timeType": "6", "BeginTime": f"{self.less_data(180)} 00:00:00",
"EndTime": f"{self.less_data(0)} 23:59:59", "createTime": [], "ts": ts}
# 伪装sign
sign = f'3637CB36B2E54A72A7002978D0506CDFBeginTime{js_data["BeginTime"]}createTime[]EndTime{js_data["EndTime"]}GGTYPE{js_data["GGTYPE"]}KIND{js_data["KIND"]}pageNo{js_data["pageNo"]}pageSize{js_data["pageSize"]}timeType{js_data["timeType"]}total{js_data["total"]}ts{js_data["ts"]}'
# 执行公用的方法 返回值是未经过加密的 数据
encrypted_str = self.encrypted_str_get(self.list_url, js_data, sign)
# 执行AES解密的方法 把 key 和IV 传入
decrypted_str = self.aes_decrypt(self.secret_key, encrypted_str, self.iv)
js_text = json.loads(padding(decrypted_str))
for data in js_text['Table']:
self.item['title_name'] = data['NAME'].replace("<br>", "")
self.item['title_date'] = data.get('TM')
self.item[
'title_url'] = f"https://ggzyfw.fujian.gov.cn/web/index.html#/business/detail?cid={data['M_ID']}&type={data['KIND']}"
if self.item['title_date'] is None:
self.item['title_date'] = time.strftime('%Y-%m-%d %H:%M:%S')
# 封装请求体
# 1657709230
# 执行js代码
payload = {"m_id": data['M_ID'], "type": data['GGTYPE'], "ts": int(time.time() * 1000)}
# 伪装详情页的sign sign = "3637CB36B2E54A72A7002978D0506CDFm_id216262ts1657708358505type1"
detail_sign = f"3637CB36B2E54A72A7002978D0506CDFm_id{payload['m_id']}ts{payload['ts']}type{payload['type']}"
# 执行公用的方法 返回值是未经过加密的 数据
encrypted_str = self.encrypted_str_get(self.detail_post_url, payload, detail_sign)
# 执行AES解密的方法 把 key 和IV 传入
detail_decrypted_str = self.aes_decrypt(self.secret_key, encrypted_str, self.iv)
content_html = json.loads(padding(detail_decrypted_str))
self.item['content_html'] = content_html.get("Contents", "")
self.API.data_update(self.item)
self.close() def encrypted_str_get(self, url, js_data, sign):
portal_sign = self.get_sign_data(sign)
headers = {
'portal-sign': portal_sign,
'Content-Type': 'application/json;charset=UTF-8',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/103.0.0.0 Safari/537.36',
} response = self.session.post(url, headers=headers, json=js_data) encrypted_str = response.json() return encrypted_str['Data'] def close(self):
self.session.close() if __name__ == '__main__':
pr = PublicResourceCenter()
pr.parse()

JS逆向实战3——AESCBC 模式解密的更多相关文章

  1. JS逆向实战6-- x轴 y轴 过点触验证码

    点触验证码是一种常见的反爬手段 解决方案有两种:一种是直接解决,这需要深度学习机器学习等图像处理技术,以大量的数据训练识别模型,最终达到模型足矣识别图片中的文字提示和应该点击的区域之间的对应关系. 这 ...

  2. JS逆向实战8——某网实战(基于golang-colly)

    其实本章算不上逆向教程 只是介绍golang的colly框架而已 列表页分析 根据关键字搜索 通过抓包分析可知 下一页所请求的参数如下 上图标红的代表所需参数 所以其实我们真正需要的也就是Search ...

  3. JS逆向实战7-- 某省在线审批网站params 随机生成

    参数分析 我们首先通过抓包 发现这个就是我们所需要的数据 然后我们通过fidder 发起请求 结果: 通过我们反复测试 发现这个params的参数是每次请求中都会变化的 断点查找 我们通过 这个t参数 ...

  4. JS逆向实战5--JWT TOKEN x_sign参数

    什么是JWT JWT(JSON WEB TOKEN):JSON网络令牌,JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑的自包含的方式在不同实体之间安全传输信息(JSON格式).它是在Web环境下 ...

  5. JS逆向实战4--cookie——__jsl_clearance_s 生成

    分析 网站返回状态码521,从浏览器抓包来看,浏览器一共对此地址请求了三次(中间是设置cookie的过程): 第一次请求:网站返回的响应状态码为 521,响应返回的为经过 混淆的 JS 代码:但是这些 ...

  6. JS逆向实战2--cookie-AcwScV2加密—某招标信息网

    cookies的获取 首先拿到第一次访问原链接 拿到acw_tc的值,然后放到session中去 再用这个session再次访问原链接拿到js加载的加密的真实数据.用了一些反混淆. 最后获取这个数据中 ...

  7. JS逆向实战1——某省阳光采购服务平台

    分析 其实这个网站基本没有用到过什么逆向,就是简单的图片base64加密 然后把连接变成2进制存成文件 然后用ocr去识别即可 !! 注意 在获取图片连接 和对列表页发起请求时一定要用一个请求,也就是 ...

  8. 网络爬虫之记一次js逆向解密经历

    1 引言 数月前写过某网站(请原谅我的掩耳盗铃)的爬虫,这两天需要重新采集一次,用的是scrapy-redis框架,本以为二次爬取可以轻松完成的,可没想到爬虫启动没几秒,出现了大堆的重试提示,心里顿时 ...

  9. Geth控制台使用及Web3.js使用实战

    在开发以太坊去中心化应用,免不了和以太坊进行交互,那就离不开Web3. Geth 控制台(REPL)实现了所有的web3 API及Admin API, 使用好 Geth 就是必修课.结合Geth命令用 ...

随机推荐

  1. 基于Go语言的xmind读写库,我主要用来把有道云笔记思维导图转为xmind

    项目地址 xmind 基于go语言的xmind接口 使用方法参考: example 本库主要加载xmind文件为json结构,保存文件时也用的json结构而不是xml结构 本库只做了最基本的主题添加功 ...

  2. 微软Azure配置中心 App Configuration (一):轻松集成到Asp.Net Core

    写在前面 在日常开发中,我这边比较熟悉的配置中心有,携程Apollo,阿里Nacos(配置中心,服务治理一体) 之前文章: Asp.Net Core与携程阿波罗(Apollo)的第一次亲密接触 总体来 ...

  3. python与pycharm的安装与“试用”

    python与pycharm的安装与"试用" 一.python解释器安装与启动 python解释器的安装 1.打开文件安装包运行页面 #python3.8 2.选择Customiz ...

  4. 给博客添加个充电按钮(仿B站)

    今天我准备吧B站的充电按钮移植到本博客,开始- 上代码: HTML <html> <head> <link href="./space.8.f69f7d6f8f ...

  5. Taurus.MVC 微服务框架 入门开发教程:项目部署:5、微服务应用程序发布到Docker部署(下)。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...

  6. ARC125E - Snack (网络流)

    题面 有 N N N 种糖果, M M M 个小孩子,第 i i i 种糖果有 A i A_i Ai​ 个,第 i i i 个孩子不能有超过 B i B_i Bi​ 个同种类型的糖果,第 i i i ...

  7. dotnet 设计规范 · 抽象定义

    严格来说,只有一个类被其他的类继承,那么这个类就是基类.在很多时候,基类的定义是提供足够的抽象和通用方法和属性.默认实现.在继承关系中,基类定义在上层抽象和底层自定义之间. 他们充当抽象实现的实现帮助 ...

  8. 第七十七篇:ref引用(在vue中引用组件实例)

    好家伙, 为方便理解, 我们先来写一个经典自增一按钮, 再加上一个count清零按钮, Left.vue组件中: <template> <div > <h1>我是L ...

  9. 流媒体协议扫盲(rtp/rtcp/rtsp/rtmp/mms/hls)

    RTP           参考文档 RFC3550/RFC3551 Real-time Transport Protocol)是用于Internet上针对多媒体数据流的一种传输层协议.RTP协议详细 ...

  10. 【读书笔记】C#高级编程 第四章 继承

    (一)继承的类型 1.实现继承和接口继承 在面向对象的编程中,有两种截然不同的继承类型:实现继承和接口继承. 实现继承:表示一个类型派生于一个基类型,它拥有该基类型的所有成员字段和函数.在实现继承中, ...