js逆向实战之烯牛数据请求参数加密和返回数据解密

sbhglqy 2024-10-01 08:39:03 阅读 67

声明:本篇文章仅用于知识分享

实战网址:https://www.xiniudata.com/industry/newest?from=data

请求参数加密

  1. 访问网址,往下翻翻,可以看到触发了如下的数据包,请求参数进行了加密。

    image

  2. 全局搜索<code>list_industries_by_sort地址,有四处,都位于同一个文件中。

    image

    随便点一个看看,可以看到有<code>payload关键字。

    image

    打断点,刷新界面触发断点,一步一步往下调试,可以看到<code>payload传给了j.a.fetch()函数。

    image

  3. 定位<code>j.a.fetch()函数。

    image

    打断点,跳转进来。可以看到明显给<code>payload和sig赋值的语句。

    image

    最关键的就是如下这行代码。

<code>var f = Object(u.c)(Object(u.d)(JSON.stringify(s.payload))), p = Object(u.e)(f);

涉及到一个变量和三个函数,一个一个看。

(1)变量s.payload:直接输出看即可。

image

(2)函数<code>Object(u.c):控制台输出,定位。

image

(3)函数<code>Object(u.d):控制台输出,定位。

image

(4)函数<code>Object(u.e):控制台输出,定位。

image

上面三个函数的代码可以不用看,直接调用js代码就可以了。

4. 目前的js代码如下。

<code>s = {

"payload": {

"sort": 1,

"start": 20,

"limit": 20

}

}

function e1(e) {

if (null == e)

return null;

for (var t, n, r, o, i, a, u, c = "", l = 0; l < e.length;)

o = (t = e.charCodeAt(l++)) >> 2,

i = (3 & t) << 4 | (n = e.charCodeAt(l++)) >> 4,

a = (15 & n) << 2 | (r = e.charCodeAt(l++)) >> 6,

u = 63 & r,

isNaN(n) ? a = u = 64 : isNaN(r) && (u = 64),

c = c + _keyStr.charAt(o) + _keyStr.charAt(i) + _keyStr.charAt(a) + _keyStr.charAt(u);

return c

}

function e2(e) {

if (null == (e = _u_e(e)))

return null;

for (var t = "", n = 0; n < e.length; n++) {

var r = _p.charCodeAt(n % _p.length);

t += String.fromCharCode(e.charCodeAt(n) ^ r)

}

return t

}

function sig(e) {

return md5(e + _p).toUpperCase()

}

var f = e1(e2(JSON.stringify(s.payload)))

, p = sig(f);

console.log(f);

console.log(p);

运行提示_u_e is not defined,去找一下补到代码中。

image

运行提示<code>_p is not defined,去找_p

image

运行提示<code>md5 is not defined,这里需要导入crypto-js库,可以通过npm install crypto-js命令下载。

const CryptoJS = require("crypto-js");

function sig(e) {

return CryptoJS.MD5(e + _p).toUpperCase()

}

运行提示CryptoJS.MD5(...).toUpperCase is not a function,是因为在转换为大写之前需要先用toString函数转为字符串。

完整代码如下:

// query_parameter_encrypt.js

const CryptoJS = require("crypto-js");

s = {

"payload": {

"sort": 1,

"start": 0,

"limit": 20

}

}

function _u_e(e) {

if (null == e)

return null;

e = e.replace(/\r\n/g, "\n");

for (var t = "", n = 0; n < e.length; n++) {

var r = e.charCodeAt(n);

r < 128 ? t += String.fromCharCode(r) : r > 127 && r < 2048 ? (t += String.fromCharCode(r >> 6 | 192),

t += String.fromCharCode(63 & r | 128)) : (t += String.fromCharCode(r >> 12 | 224),

t += String.fromCharCode(r >> 6 & 63 | 128),

t += String.fromCharCode(63 & r | 128))

}

return t

}

var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

, _p = "W5D80NFZHAYB8EUI2T649RT2MNRMVE2O";

function e1(e) {

if (null == e)

return null;

for (var t, n, r, o, i, a, u, c = "", l = 0; l < e.length;)

o = (t = e.charCodeAt(l++)) >> 2,

i = (3 & t) << 4 | (n = e.charCodeAt(l++)) >> 4,

a = (15 & n) << 2 | (r = e.charCodeAt(l++)) >> 6,

u = 63 & r,

isNaN(n) ? a = u = 64 : isNaN(r) && (u = 64),

c = c + _keyStr.charAt(o) + _keyStr.charAt(i) + _keyStr.charAt(a) + _keyStr.charAt(u);

return c

}

function e2(e) {

if (null == (e = _u_e(e)))

return null;

for (var t = "", n = 0; n < e.length; n++) {

var r = _p.charCodeAt(n % _p.length);

t += String.fromCharCode(e.charCodeAt(n) ^ r)

}

return t

}

function sig(e) {

return CryptoJS.MD5(e + _p).toString().toUpperCase();

}

function generate(s) {

var payload = e1(e2(JSON.stringify(s.payload))), sign = sig(payload);

return [payload, sign];

}

console.log(generate(s));

运行结果如下。

image

成功,接下来就是编写python代码获取数据了。

<code>import requests

import json

import execjs

url = "https://www.xiniudata.com/api2/service/x_service/person_industry_list/list_industries_by_sort"

# 这里面最重要的就是content-type参数,否则获取不到数据

headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "

"Chrome/129.0.0.0 Safari/537.36",

# "cookie": "btoken=82SLKQK1ESJ7A9NZ1C3ZAKREOC0I3AAC",

# "referer": "https://www.xiniudata.com/industry/newest?from=data",

# "origin": "https://www.xiniudata.com",

"content-type": "application/json"}

# 生成payload和sig

s = {

"payload": {

"sort": 1,

"start": 0,

"limit": 20

}

}

file = open("query_parameter_encrypt.js", mode="r")code>

exec_code = file.read()

exec_js = execjs.compile(exec_code)

payload, sig = exec_js.call("generate", s)

data = {

"payload": payload,

"sig": sig,

"v": 1

}

# 注意需要转换为json数据传输

resp = requests.post(url, headers=headers, data=json.dumps(data))

print(json.loads(resp.text))

运行结果如下。

image

返回数据解密

  1. 一般来说对于返回数据的解密,常规操作是搜索<code>interceptors,但是很可惜不适用于该网站。那就得想其他的办法。

    image

  2. 我们知道该数据包采用的是JSON格式数据,解密出来后肯定需要进行JSON解析,所以可以搜索<code>JSON.parse关键字。

    image

    地方比较多,需要进行筛选。如果<code>JSON.parse是该函数的第一行代码就铁定不是,因为是需要先解密再进行JSON解析的。点击几个,发现了一处跟加密逻辑很像的代码。(其实一般情况下加密逻辑和解密逻辑是写在一起的)

    image

  3. 打断点,一行一行调试。

    image

    可以看到<code>s的值就是返回的数据,说明我们没找错地方。

    image

    <code>y的值是明文了,所以解密逻辑肯定跟Object(u.a)Object(u.b)相关。

    找到这两个函数的定义。

    image

    image

  4. 接下来就跟加密逻辑一样了,把相关的js代码复制出来,把需要用到的变量和参数都补齐。

<code>// response_data_decrypt.js

var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="

, _p = "W5D80NFZHAYB8EUI2T649RT2MNRMVE2O";

function d1(e) {

var t, n, r, o, i, a, u = "", c = 0;

for (e = e.replace(/[^A-Za-z0-9\+\/\=]/g, ""); c < e.length;)

t = _keyStr.indexOf(e.charAt(c++)) << 2 | (o = _keyStr.indexOf(e.charAt(c++))) >> 4,

n = (15 & o) << 4 | (i = _keyStr.indexOf(e.charAt(c++))) >> 2,

r = (3 & i) << 6 | (a = _keyStr.indexOf(e.charAt(c++))),

u += String.fromCharCode(t),

64 != i && (u += String.fromCharCode(n)),

64 != a && (u += String.fromCharCode(r));

return u

}

function _u_d(e) {

for (var t = "", n = 0, r = 0, o = 0, i = 0; n < e.length;)

(r = e.charCodeAt(n)) < 128 ? (t += String.fromCharCode(r),

n++) : r > 191 && r < 224 ? (o = e.charCodeAt(n + 1),

t += String.fromCharCode((31 & r) << 6 | 63 & o),

n += 2) : (o = e.charCodeAt(n + 1),

i = e.charCodeAt(n + 2),

t += String.fromCharCode((15 & r) << 12 | (63 & o) << 6 | 63 & i),

n += 3);

return t

}

function d2(e) {

for (var t = "", n = 0; n < e.length; n++) {

var r = _p.charCodeAt(n % _p.length);

t += String.fromCharCode(e.charCodeAt(n) ^ r)

}

return t = _u_d(t)

}

function decrypt(e){

return d2(d1(e));

}

e = '返回的加密数据';

// console.log(decrypt(e));

运行结果如下。

image

成功。

5. 写python代码,将加密逻辑和解密逻辑进行融合。

<code>from functools import partial

import subprocess

subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")code>

import requests

import json

import execjs

url = "https://www.xiniudata.com/api2/service/x_service/person_industry_list/list_industries_by_sort"

headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "

"Chrome/129.0.0.0 Safari/537.36", # "cookie": "btoken=82SLKQK1ESJ7A9NZ1C3ZAKREOC0I3AAC",

# "referer": "https://www.xiniudata.com/industry/newest?from=data",

# "origin": "https://www.xiniudata.com",

"content-type": "application/json"}

# 生成payload和sig

s = {"payload": {"sort": 1, "start": 0, "limit": 20}}

file1 = open("query_parameter_encrypt.js", mode="r")code>

exec_code1 = file1.read()

exec_js1 = execjs.compile(exec_code1)

payload, sig = exec_js1.call("generate", s)

data = {"payload": payload, "sig": sig, "v": 1}

resp = requests.post(url, headers=headers, data=json.dumps(data))

# 得到返回的响应数据

response_data = json.loads(resp.text)['d']

# print(response_data)

# 调用解密逻辑

file2 = open("response_data_decrypt.js", mode="r")code>

exec_code2 = file2.read()

exec_js2 = execjs.compile(exec_code2)

ming_str = exec_js2.call('decrypt', response_data)

print(ming_str)

运行结果如下。

image

跟页面上显示的对应。

image

大功告成。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。