关于python:JS-逆向百例PEDATA-加密资讯以及-zlibgunzipSync-的应用

12次阅读

共计 4969 个字符,预计需要花费 13 分钟才能阅读完成。

关注微信公众号:K 哥爬虫,继续分享爬虫进阶、JS/ 安卓逆向等技术干货!

申明

本文章中所有内容仅供学习交换,抓包内容、敏感网址、数据接口均已做脱敏解决,严禁用于商业用途和非法用处,否则由此产生的所有结果均与作者无关,若有侵权,请分割我立刻删除!

逆向指标

  • 指标:某投资畛域 SAAS 零碎 PEDATA MAX 资讯,返回后果加密
  • 主页:aHR0cHM6Ly9tYXgucGVkYXRhLmNuL2NsaWVudC9uZXdzL25ld3NmbGFzaA==
  • 接口:aHR0cHM6Ly9tYXgucGVkYXRhLmNuL2FwaS9xNHgvbmV3c2ZsYXNoL2xpc3Q=
  • 逆向参数:申请返回的加密后果,data: "L+o+YmIyNDE..."

抓包剖析

咱们在首页,点击查看全副 24 小时资讯,往下拉,资讯是以 Ajax 模式加载的,咱们选中开发者工具 XHR 进行筛选,很容易找到一个 list 申请,其返回值 data 是一串通过加密后的字符串,exor 不晓得是啥,然而前面可能有用,ts 是工夫戳,如下图所示:

Payload 里的参数没有什么特地的,就是一些翻页信息,咱们再看看申请 header,这里留神 CookieHTTP-X-TOKEN 两个参数,拜访这个页面须要登录账号,一般来说,Cookie 是用来标识不同用户的,但通过 K 哥测试发现,此案例中,这个 HTTP-X-TOKEN 参数才是用来辨认用户的,所以不须要 Cookie 也行,轻易提一嘴,Cookie 中咱们常常看到有 Hm_lvt_xxxHm_lpvt_xxx 是用于百度联盟广告的数据统计的,与爬虫无关。

加密逆向

咱们留神到返回的是一个字典,在获取到加密数据后,必定会有一个取值的过程,所以咱们间接搜寻键,搜寻 exor 后果只有一个:

这里 e.data 就是返回的字典,e.data.datae.data.exor 顺次取加密值和 exor,这里就能够猜想是将加密值取出来进行解密操作了,咱们在此函数结尾处也打个断点,看看这段代码执行结束后,data 的值是否变成了明文:

不出所料,Object(p["y"])(e.data.data, e.data.exor) 这段代码就是解密函数了,Object(p["y"]) 其实是调用了 M 办法,跟进去看看:

传入的 t 和 n 别离是加密值和 exor,最初返回的 JSON.parse(c) 就是解密后果:

要害代码:

function M(t, n) {var a = L(Object(s["a"])(), n)
    , r = Y(B(t), a)
    , c = o.a.gunzipSync(e.from(r)).toString("utf-8");
    return JSON.parse(c)
}

挨个函数扣下来,简略的就不讲了,其中 Object(s["a"]),选中它,其实是调用了 c 办法,跟进 c 办法,实际上是取了 loginToken,这个 loginToken 就是咱们后面剖析的申请头中的 HTTP-X-TOKEN,蕴含了你的登录信息。

拓展常识:window.localStorage 属性用于在浏览器中存储键值对模式的数据,localStoragesessionStorage 相似,区别在于:localStorage 中的数据能够长期保留,没有过期工夫,直到被手动删除。sessionStorage 的数据仅保留在以后会话中,在敞开窗口或标签页之后将会删除这些数据。

再往下看,有个 o.a.gunzipSync(),先放一下,先看看传入的参数 e.from(r),跟进看可能看不出来什么,间接比照 re.from(r),会发现都是 Uint8Array 的数据,截然不同的,如下图所示:

再来看看 o.a.gunzipSync(),实际上调用的是 chunk-vendors.js 里的匿名函数,不晓得这个 JS 不要紧,咱们留神到 chunk-vendors.js 外面的代码有超过 14 万行,再加上这个奇怪的名字,什么模块供应商,不难想到这是一个零碎或者第三方生成的 JS,事实上它是 vue 应用程序构建过程中创立的文件,对于咱们爬虫工程师来讲,粗犷的将其了解为相似 jquery.js 一样的货色也行,咱们个别是不会去扣 jquery.js 外面的代码的,同样这个 chunk-vendors.js 也不可能傻傻的去扣。

咱们重点看看这个函数名,gunzipSync,其余不意识,但意识 zip 吧,能够联想到应该与压缩无关,不理解同样不要紧,间接使出百度大法:

这间接给出了 nodejs 外面的实现办法,用的是 zlib 模块,轻易找个示例看看用法:

var zlib = require('zlib');
var input = "Nidhi";
var gzi = zlib.gzipSync(input);
var decom = zlib.gunzipSync(new Buffer.from(gzi)).toString();

console.log(decom);

进一步学习,咱们能够晓得 zlib.gunzipSync() 办法是 zlib 模块的内置应用程序编程接口,用于应用 Gunzip 解压数据块。传入的数据能够是 Buffer、TypedArray、DataView、ArrayBuffer、string 类型,在官网文档中咱们能够看到更新历史外面,在 v8.0.0 当前,传入的数据就反对 Uint8Array 了:

联合后面咱们对 r 值的剖析,所以在 nodejs 里,间接把 r 值传入到 zlib.gunzipSync() 办法里就能够了,将用到的 L、V、B 三个办法扣进去,而后配合 zlib 库,改写一下就能拿到解压后的数据了:

function getDecryptedData(encryptedData, exor, loginToken) {var a = L(loginToken, exor);
    var r = Y(B(encryptedData), a)
    var decryptedData = zlib.gunzipSync(r).toString();
    return decryptedData
}

残缺代码

GitHub 关注 K 哥爬虫,继续分享爬虫相干代码!欢送 star!https://github.com/kgepachong/

以下只演示局部要害代码,不能间接运行! 残缺代码仓库地址:https://github.com/kgepachong…

JavaScript 加密代码

/* ==================================
# @Time    : 2021-12-31
# @Author  : 微信公众号:K 哥爬虫
# @FileName: main.js
# @Software: PyCharm
# ================================== */

var zlib = require('zlib');

function L(e, t) {if ("1" == t)
        return [7, 65, 75, 31, 71, 101, 57, 0];
    for (var n = [], a = 0, r = t.length; a < r; a += 2)
        n.push(e.substr(1 * t.substr(a, 2), 1).charCodeAt());
    return n
}

function Y(e, t) {for (var n, a = new Uint8Array(e.length), r = 0, c = e.length; r < c; r++)
        n = t[r % t.length],
            a[r] = e[r].charCodeAt() ^ n;
    return a
}

function B(e) {
    var t, n, a, r, c, u, i, o = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", s = "", f = 0;
    e = e.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    while (f < e.length)
        r = o.indexOf(e.charAt(f++)),
            c = o.indexOf(e.charAt(f++)),
            u = o.indexOf(e.charAt(f++)),
            i = o.indexOf(e.charAt(f++)),
            t = r << 2 | c >> 4,
            n = (15 & c) << 4 | u >> 2,
            a = (3 & u) << 6 | i,
            s += String.fromCharCode(t),
        64 != u && (s += String.fromCharCode(n)),
        64 != i && (s += String.fromCharCode(a));
    return s
}

function getDecryptedData(encryptedData, exor, loginToken) {var a = L(loginToken, exor);
    var r = Y(B(encryptedData), a)
    var decryptedData = zlib.gunzipSync(r).toString();
    return decryptedData
}

Python 示例代码

# ==================================
# --*-- coding: utf-8 --*--
# @Time    : 2021-12-31
# @Author  : 微信公众号:K 哥爬虫
# @FileName: main.py
# @Software: PyCharm
# ==================================


import execjs
import requests

news_est_url = "脱敏解决,残缺代码关注 GitHub:https://github.com/kgepachong/crawler"
login_token = "token 换成你本人的!"
headers = {
    "Accept": "application/json, text/plain, */*",
    "Content-Type": "application/json",
    "Host": "脱敏解决,残缺代码关注 GitHub:https://github.com/kgepachong/crawler",
    "HTTP-X-TOKEN": login_token,
    "Origin": "脱敏解决,残缺代码关注 GitHub:https://github.com/kgepachong/crawler",
    "Referer": "脱敏解决,残缺代码关注 GitHub:https://github.com/kgepachong/crawler",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
}


def get_decrypted_data(encrypted_data, exor):
    with open('pedata_decrypt.js', 'r', encoding='utf-8') as f:
        pedata_js = f.read()
    decrypted_data = execjs.compile(pedata_js).call('getDecryptedData', encrypted_data, exor, login_token)
    return decrypted_data


def get_encrypted_data():
    data = {
        "type": "","module":"LP","page":
            {
                "currentPage": 1,
                "pageSize": 10
            }
    }
    response = requests.post(url=news_est_url, headers=headers, json=data).json()
    encrypted_data, exor = response["data"], response["exor"]
    return encrypted_data, exor


def main():
    encrypted_data, exor = get_encrypted_data()
    decrypted_data = get_decrypted_data(encrypted_data, exor)
    print(decrypted_data)


if __name__ == '__main__':
    main()

正文完
 0