【Python 逆向某1688】 今天做一下1688的逆向分析,仅供学习
逆向日期:2024.12.16
使用工具:Python,Node.js
本章知识:逆向分析1688的sign参数,并用python实现调用并采集公开数据
文章难度:低等(没难度)
文章全程已做去敏处理!!! 【需要做的可联系我】
AES解密处理(直接解密即可)(crypto-js.js 标准算法):在线AES加解密工具
仅供学习!仅供学习!仅供学习!
今天来逆向分析sign参数,并采集公开数据,给大家进行演示,仅供学习,文章全部已去敏,无需担心
1、打开某某网站(使用文章开头的AES在线工具解密):
99Lp55WUF/d7WIKTTODahbNWMWI/iO0K9bnAJp5+P0Y=
2、首先随便搜索一个产品,将页面下拉到最底部,然后F12打开控制台,点击第二页,抓到的接口可以进行查看,有商品数据的是正确的即可,然后我们打开接口可以看到有很多的载荷信息,其中【sign】就是我们要分析逆向的参数,其他参数都不需要逆向,只需要分析即可
3、好,接下来就是怎么去找这个加密位置,我们可以全局搜索一下【sign】,发现关键字太多,直接放弃
4、 我们要分析一下,他每次调用接口都会使用到【appkey】,而且这个值是固定的,那我们就用他的值去全局搜索试试,一下就搜索到了,而且只有两个关键字,是不是很方便
5、点击进去看了一下,我就知道这个就是加密的位置,我们直接打上断点,然后去翻一下页面,翻到第三页看看会不会断住
6、点击第三页,发现断住了,然后我们看图分析一下,其他都没什么好解释的,直接看【d】参数,这个就是sign,这个是sign就是将【r.token + "&" + c + "&" + s + "&" + n.data】这几个字符串合并,然后用ms5进行加密的来的,可以看下面的两个图
7、知道了他是怎么加密的了,那我们就直接本地模拟了,我就不扣代码了,他是标准的MD5算法,可以看下面的图片,全部转到本地js里,然后导入md5包,运行发现结果一样,这就成功了。
8、接下来我们要看一下他的【token】是哪里来的,经过我的分析,发现他的token是通过【_m_h5_tk】里提取的,他的这个参数是在cookie里保存的,所以大家要注意,可能图片里的这两个参数也要作为cookie使用,记得留一手
9、我们现在已经分析了全部,接下来就要全部打包并用python实现调用并生成sign。看下面两个图,一个是js打包后的,将js的代码全部整理成接口,可以用python直接嗲用接口进行获取sign,不仅方便,可以省去很多麻烦。另一个图片是python整理后的,整合为了类,直接看图看注释吧,比较直观
10、好了,接下来就要获取公开数据了,首先我们要先模拟一下,看他都需要什么参数,经过我的测试发现,他不需要headers,但需要cookie,cookie里只要有下面两个参数即可。然后我还发现请求后的数据不是字典格式的,很烦,还要正则提取,我都已经够懒了,怎么可能会提取呢,这很好处理,只需要将【type】【dataType】修改为json即可,下次请求的数据就是字典格式的数据了(为了测试,我修改了参数,所以请求的数据是无效的)
11、好了,我们将python代码进行再次融合,然后再运行一下看看怎么样,成功获取数据,截图截不全,先凑合看吧
12、接下来看动图,代码就这么多,没啥难度(此接口的调用不要太快,使用Ip池的情况下才可以并发,防止出现问题)
好了今天就到这里了 ,代码如下
【sign接口.js】
// 安装 crypto-js 包
// npm install crypto-js --save
const CryptoJS = require('crypto-js')
// npm install express --save
const express = require('express');
// MD5加密
function MD5(text){return CryptoJS.MD5(text).toString()}
const app = express();
app.use(express.json());
function sign_encryption(token,data,time_c){
// appkey 固定值,写死即可
s = '12574478'
// 13位时间戳
c = time_c
// 将字典类目数据转为字符串类型数据 如果对面传递的字典已经经过了处理,则这里就不需要在处理
// data = JSON.stringify(data)
sign = MD5(token + "&" + c + "&" + s + "&" + data)
return sign
}
// 使用post接口
app.post('/', (req, res) => {
const {token, data, time_c} = req.body;
// 加密数据
sign_data = sign_encryption(token, data, time_c)
console.log('生成的sign:',sign_data)
// 返回数据
res.status(200).json({code: 1, data: sign_data});
});
app.listen(4000, () => {
console.log('Node.js 服务监听端口 127.0.0.1:4000');
});
【sign请求.py】 注意,代码经过base64去敏,不需要的可以自行整改
import json
import random
import time
import requests
import base64
# base64 解密
def base64_decode(str):
# print(base64.b64decode('MTIz'.encode()))
return base64.b64decode(str).decode('utf-8')
# 这个是链接被base64加密了,此步骤只用于去敏,无其他作用
URL = base64_decode('aHR0cHM6Ly9oNWFwaS5tLjE2ODguY29tLw==')
class response_1688:
def __init__(self, data_dict):
# 载荷数据 将数据转为字符串 字典转字符串的时候,一定要加 separators=(',', ':'),用于去除间隔,如果不去除间隔,他的值也是错误的
self.data_dict = json.dumps({
"appId": 32517,
"params": json.dumps(data_dict, separators=(',', ':'))
}
, separators=(',', ':'))
print(f'载荷数据处理:{self.data_dict}')
# 用空的令牌请求获取新令牌 结果 {'_m_h5_tk': '3815a4dc1cfca5bbdbcae6eac7e15659_1734327147972', '_m_h5_tk_enc': '63a2009c320191df2d747d64baf11b5c'}
self.cookie = self._m_h5_tk()
print(f'获取Cookie:{self.cookie}')
# 从令牌里提取token
self.token = self.cookie['_m_h5_tk'].split('_')[0]
print(f'获取token:{self.token}')
# 生成13位时间戳(公用)
self.time_c = int(time.time() * 1000)
print(f'生成时间戳:{self.time_c}')
# 生成获取sign值 返回 {'code': 1, 'data': '6d2310b6ddc3c501ccc3785f82c6505d'}
self.sign = self.sign_encryption()
print(f'生成并获取sign:{self.sign}')
# 请求数据
self.data1688 = self.mtop_relationrecommend_wirelessrecommend_recommend_v20()
# 获取 _m_h5_tk 和 _m_h5_tk_enc (公用)
def _m_h5_tk(self):
'''
:return: {'_m_h5_tk':'值','_m_h5_tk_enc':'值'}
可以作为cookie使用,也可以提取token
'''
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
}
url = URL + "h5/mtop.relationrecommend.wirelessrecommend.recommend/2.0/"
params = {
"jsv": "2.7.4",
"appKey": "12574478"
}
response = requests.get(url, headers=headers, params=params)
_m_h5_tk_Dict = {key: value for key, value in response.cookies.items() if key in ['_m_h5_tk', '_m_h5_tk_enc']}
# 获取 _m_h5_tk 和 _m_h5_tk_enc
# 可以直接用作cookies
return _m_h5_tk_Dict
# 生成sign
def sign_encryption(self):
"""
生成sign值
:return:
"""
headers = {'Content-Type': 'application/json'}
url = "http://127.0.0.1:4000/"
data = {
'token': self.token, # token
'time_c': self.time_c, # 13位时间戳
'data': self.data_dict # 字典数据
}
try:
response = requests.post(url, headers=headers, data=json.dumps(data))
if response.status_code == 200:
return response.json()
except:
pass
# 返回错误代码
return {'code': -1}
# 请求数据
def mtop_relationrecommend_wirelessrecommend_recommend_v20(self) -> dict:
'''
接口:mtop.relationrecommend.WirelessRecommend.recommend/2.0
当前接口版本:2.7.4
:return:
'''
if self.sign['code'] != 1:
return {'msg': 'sign生成错误', 'code': -1}
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
}
url = URL + "h5/mtop.relationrecommend.wirelessrecommend.recommend/2.0/"
params = {
"jsv": "2.7.4",
"appKey": "12574478",
"t": str(self.time_c),
"sign": self.sign['data'],
"api": "mtop.relationrecommend.WirelessRecommend.recommend",
"v": "2.0",
"jsonpIncPrefix": "reqTppId_32517_getOfferList",
"type": "json",
"dataType": "json",
"callback": "mtopjsonpreqTppId_32517_getOfferList8",
"data": self.data_dict
}
response = requests.get(url, headers=headers, cookies=self.cookie, params=params).json()
return response
for i in range(10):
data_dict = {
"beginPage": i, # 第几页
"pageSize": 100, # 每页获取多少数据 我设置为100,官方默认是60,我们可以不遵守
"method": "getOfferList",
"pageId": ''.join(random.choices('012QWERTYUIOPASDFGHJKLZXCVBNM3445qwertyuiopasdfghjklzxcvbnm6789', k=48)),
# 不知道这个是什么,直接用随机代替,防止封锁
"verticalProductFlag": "pcmarket",
"searchScene": "pcOfferSearch",
"charset": "GBK",
"spm": "a260k.home2024.searchbox.0",
"keywords": "鞋子" # 关键字搜索商品
}
# 实例化并获取生成sign值,并请求数据
data = response_1688(data_dict=data_dict)
print('当前页面有多少条数据:',len(data.data1688['data']['data']['OFFER']['items']))
# 提取数据
for data_dict in data.data1688['data']['data']['OFFER']['items']:
offerId = data_dict['data']['offerId']
title = data_dict['data']['title']
# 这个是链接被base64加密了,此步骤只用于去敏,无其他作用
pageurl = base64_decode('aHR0cHM6Ly9kZXRhaWwuMTY4OC5jb20vb2ZmZXIv')
print(f'{pageurl}{offerId}.html',title)
作者:小木_.