js逆向-哈希算法

前言

静看光阴荏苒,借我喑哑无言

⚠️声明:本文所涉及的爬虫技术及代码仅用于学习、交流与技术研究目的,禁止用于任何商业用途或违反相关法律法规的行为。若因不当使用造成法律责任,概与作者无关。请尊重目标网站的robots.txt协议及相关服务条款,共同维护良好的网络环境。

1.MD5

1.1简介

简介:全称 MD5 消息摘要算法,又称哈希算法、散列算法,由美国密码学家罗纳德·李维斯特设计,于 1992 年作为 RFC 1321 被公布,用以取代 MD4 算法。摘要算法是单向加密的,也就是说明文通过摘要算法加密之后,是不能解密的。摘要算法的第二个特点密文是固定长度的,它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。之所以叫摘要算法,它的算法就是提取明文重要的特征。所以,两个不同的明文,使用了摘要算法之后,有可能他们的密文是一样的,不过这个概率非常的低。

MD5(Message Digest Algorithm 5)是一个广泛使用的 哈希算法,其主要目的是将任意长度的输入数据(例如文本、文件等)转换成一个固定长度的 128位哈希值(通常以 32 字符的十六进制表示)。尽管 MD5 在许多领域得到了广泛应用,但它在现代安全场景中已不再足够安全,特别是由于它容易受到 碰撞攻击

MD5 的特点:

  1. 固定长度的输出:无论输入数据的大小如何,MD5 输出的哈希值始终是 128 位(16 字节)的固定长度,通常以 32 个十六进制字符表示。
  2. 不可逆性:MD5 是单向的,意味着无法从哈希值反推出原始数据(理想情况下)。但随着计算能力的提升,它已被证明不再适用于高度安全的应用。
  3. 碰撞性:理论上,MD5 是设计为产生唯一哈希值的,但由于存在碰撞攻击(即两个不同的输入数据产生相同的哈希值),它不再被认为是“安全的”哈希函数。
  4. 速度:MD5 处理速度较快,适合用于较小的数据量的哈希计算。
  5. 广泛应用:曾经广泛用于文件完整性验证、密码存储等,但由于其安全性问题,许多应用现在已经转向更安全的哈希算法,如 SHA-256。

MD5 的常见用途:

  1. 数据完整性检查:通过计算文件或消息的 MD5 哈希值,可以确保数据在传输或存储过程中没有被篡改。如果哈希值匹配,则数据未改变;否则,数据可能已被修改。
  2. 密码存储(不推荐):过去,许多系统使用 MD5 存储密码的哈希值,但现在由于 MD5 的碰撞攻击问题,这种做法已被弃用。现代系统通常使用更强大的哈希算法,如 bcrypt、scrypt 或 Argon2。
  3. 数字签名:在某些应用中,MD5 也被用于生成数字签名的摘要,尤其是在较老的系统中。

1.2JavaScript实现MD5

安装命令

npm install crypto-js  --save

image-20250301010027913

代码

var cryptojs  = require('crypto-js')
function test_md5(str){
    return cryptojs.MD5(str).toString()
}

console.log(test_md5('peng'))

image-20250301010344668

1.3Python实现实现MD5

MD5哈希视为字符串,而是将其视为十六进制数, MD5哈希长度为128位,通常由32个十六进制数字表示。

代码

import hashlib

def md5_test(str):
    md5 = hashlib.md5()
    md5.update(str.encode('utf-8'))
    return md5.hexdigest()  # 返回 MD5 的十六进制哈希值

if __name__ == '__main__':
    print(md5_test('peng'))

image-20250301010742076

2.MyToken网站逆向解密

地址:https://www.mytokencap.com/

2.1分析过程

检查请求头,好像没有加密字段

image-20250301011940607

发现请求中codetimestamp会改变

pages=1%2C1&sizes=100%2C100&subject=market_cap&language=zh_CN&legal_currency=USD&code=a9a9ca3adc8c8a459abfaa12fd8cb6d2&timestamp=1740763224060&platform=web_pc&v=0.1.0&mytoken=

image-20250301012053574

可以尝试使用关键字搜索,ctrl + shift +f搜索code=

找到了 t.data.code = n,,感觉很像,打个断点调试一下

image-20250301012342260

这个没有进断点,我们使用XHR/提取断点,然后单步调试

image-20250301013025967

单步调试定位到这里发现code赋值了

并且尝试里面每个方法发现(0, c.xZ)是加密的方法

r.__generator)(this, (function(r) {
                    return t = s.length > 1 && void 0 !== s[1] && s[1],
                    n = (0,
                    c.Fk)((0,
                    l.Z)({}, e, (0,
                    c.xZ)())),
                    i = "".concat(null !== (a = "https://api.mytokenapi.com") && void 0 !== a ? a : "http://localhost:3300/api", "/ticker/currencyranklist").concat(t ? "seo" : "", "?").concat(n),
                    [2, (0,
                    c.lq)(i)]
                }
                ))

image-20250301015945206

跳转到c.xZ

image-20250301020112892

发现这里进行了code赋值,但是a()返回了个方法

在此处代码断点,并继续跳转到该方法的实现

code: a()(t + "9527" + t.substr(0, 6)),

image-20250301020155367

返回的是个方法,在此处继续断点,然后跳转到实现

: function() {
            return e
}

image-20250301020804746

这里就能发现通过e.wordsToBytes(s(t, n)) e.bytesToHex(r)进行加密

 t.exports = function(t, n) {
                if (void 0 === t || null === t)
                    throw new Error("Illegal argument " + t);
                var r = e.wordsToBytes(s(t, n));
                return n && n.asBytes ? r : n && n.asString ? i.bytesToString(r) : e.bytesToHex(r)
            }

image-20250301020947232

2.2实现

核心代码就是对当前时间戳进行了处理,并且加盐了("9527" + t.substr(0, 6))

image-20250301021504729

python只要模拟实现时间戳的md5即可

import time
import hashlib
import requests

url = 'https://api.mytokenapi.com/ticker/currencyranklist?pages=2%2C1&sizes=100%2C100&subject=market_cap&language=en_US&legal_currency=USD&code={}&timestamp={}&platform=web_pc&v=0.1.0&mytoken='
t1 = str(int(time.time() * 10000))
va = t1 + '9527' + t1[0:6]
md5 = hashlib.md5()
md5.update(va.encode('utf-8'))
code = md5.hexdigest()
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36'
}
res = requests.get(url.format(code, t1), headers=headers)
print(res.json())

image-20250301021812974

3.SHA

3.1简介

SHA(Secure Hash Algorithm)是由 美国国家安全局(NSA) 设计的一个系列加密哈希函数,它广泛用于信息安全领域,包括数据完整性验证、数字签名等。SHA 用于生成定长的哈希值(摘要),无论输入数据的大小如何,它的输出都是固定的长度。

SHA 的版本很多,其中最常用的是 SHA-1SHA-256SHA-3

SHA 的主要版本:

  • SHA-1

    • 输出长度:160位(20字节)

    • SHA-1 生成一个长度为160位的哈希值。曾被广泛用于数字签名、证书、消息完整性等场景。

    • 安全性问题:由于 SHA-1 易受碰撞攻击(两个不同的输入可能产生相同的哈希值),已经不再被认为是安全的。

    • 弃用:大部分现代应用已经停止使用 SHA-1,转而使用更强大的 SHA-2 或 SHA-3。

  • SHA-2

    • 输出长度:256位(SHA-256)、512位(SHA-512)

    • SHA-2 是 SHA-1 的继任者,具有更高的安全性,广泛应用于各类加密协议和数字证书中。

    • SHA-256:最常见的 SHA-2 变体,广泛用于区块链(如比特币)和安全传输(如 HTTPS)中。

    • SHA-512:SHA-2 的另一种变体,输出长度为 512 位,通常在需要更高安全级别的场合使用。

  • SHA-3

    • 输出长度:可以是224位、256位、384位、512位等

    • SHA-3 是基于 Keccak 算法的全新设计,与 SHA-2 不同,并且提供了更强的安全性。

    • 它作为 SHA 系列的最新成员,比 SHA-1 和 SHA-2 更为强大,并且设计上具有更强的抗碰撞和抗预图像攻击的能力。

SHA 的用途:

  • 数据完整性验证

    • 用于确保数据在传输或存储过程中没有被篡改。例如,当你下载一个文件时,可以查看它的 SHA 哈希值,下载完成后计算该文件的 SHA 哈希值,验证是否一致。
  • 数字签名

    • 在数字签名中,哈希值用于生成消息摘要,确保消息的完整性和不可篡改性。
  • 密码学应用

    • SHA 常用于加密协议中,例如 TLS/SSL、区块链等,确保信息的安全传输。
  • 密码存储

    • 用于存储密码的哈希值,防止密码泄露。特别是在存储用户密码时,一般将密码通过 SHA(或结合盐值)进行哈希,再进行存储。

SHA 的优点:

  1. 快速:SHA 算法通常处理速度较快,适合大规模的数据处理。
  2. 不可逆:哈希算法是单向的,从哈希值无法还原回原始数据。
  3. 唯一性:理想情况下,不同的输入数据会产生不同的哈希值。
  4. 稳定性:SHA 系列具有高稳定性和良好的抗碰撞性,尤其是 SHA-256 和 SHA-3。

SHA 的缺点

  • SHA-1 不安全:已经被证明存在碰撞攻击,不适合用于现代安全应用。
  • 性能开销:SHA-2 尽管提供更强的安全性,但相对于 SHA-1 来说,计算开销较大,可能影响性能。
  • SHA-3 的复杂性:相比 SHA-2,SHA-3 的设计较为复杂,且对硬件的要求较高。
  • 理论碰撞风险:尽管 SHA-2 和 SHA-3 提供强大的抗碰撞性,但随着计算能力的提升,未来某些攻击的潜力仍是不可忽视的。
  • 计算成本高:对于大量数据的处理,SHA 算法可能消耗更多的计算资源,影响系统性能。

3.2JavaScript实现SHA

代码

crypto-js开箱即用

var crypto = require('crypto-js')

function get_sha1(str) {
    return crypto.SHA1(str).toString()
}

function get_sha224(str) {
    return crypto.SHA224(str).toString()
}

function get_shas56(str) {
    return crypto.SHA256(str).toString()
}

function get_sha512(str) {
    return crypto.SHA512(str).toString()
}

console.log(get_sha1('peng'))
console.log(get_sha224('peng'))
console.log(get_shas56('peng'))
console.log(get_sha512('peng'))

image-20250301022935500

3.3Python实现实现SHA

代码

hashlib开箱即用

import hashlib

def get_sha1(str):
    sha1 = hashlib.sha1()
    sha1.update(str.encode('utf-8'))
    return sha1.hexdigest()

def get_sha224(str):
    sha224 = hashlib.sha224()
    sha224.update(str.encode('utf-8'))
    return sha224.hexdigest()

def get_sha256(str):
    sha256 = hashlib.sha256()
    sha256.update(str.encode('utf-8'))
    return sha256.hexdigest()

def get_sha512(str):
    sha512 = hashlib.sha512()
    sha512.update(str.encode('utf-8'))
    return sha512.hexdigest()

print(get_sha1('peng'))
print(get_sha224('peng'))
print(get_sha256('peng'))
print(get_sha512('peng'))

image-20250301023455815

4.红人点集网站逆向解密

地址:http://www.hh1024.com/#/login?redirect=%2FrealTimeLiving

好像网站有点问题登录不上

image-20250301180319353

5.HMAC

5.1简介

HMAC(Hash-based Message Authentication Code,基于哈希的消息认证码)是一种用于数据完整性校验和身份认证的加密技术。它结合了哈希函数(如 SHA-256、SHA-512)和密钥,确保消息在传输过程中未被篡改。

HMAC:全称散列消息认证码、密钥相关的哈希运算消息认证码,于 1996 年提出,1997 年作为 RFC 2104 被公布,HMAC 加密算法是一种安全的基于加密 Hash 函数和共享密钥的消息认证协议,它要求通信双方共享密钥 key、约定算法、对报文进行 Hash 运算,形成固定长度的认证码。通信双方通过认证码的校验来确定报文的合法性。

百度百科:https://baike.baidu.com/item/hmac/7307543?fr=aladdin

HMAC 的特点

  1. 抗篡改:攻击者即使截获了消息,若不知道密钥,就无法伪造正确的 HMAC。
  2. 防重放:结合时间戳或随机数(nonce)使用,可以防止重放攻击。
  3. 可用于 API 认证:HMAC 常用于 API 请求的身份认证,比如 AWS、支付宝等接口。
  4. 比数字签名快:HMAC 只依赖哈希函数,而数字签名(如 RSA 签名)需要额外的公私钥计算。

5.2 JavaScript实现HMAC

代码

var crypto = require('crypto-js')

function get_hmac1(str, key) {
    return crypto.HmacSHA1(str, key).toString()
}

function get_hmac224(str, key) {
    return crypto.HmacSHA224(str, key).toString()
}

function get_hmac256(str, key) {
    return crypto.HmacSHA256(str, key).toString()
}
function get_hmac512(str, key) {
    return crypto.HmacSHA512(str, key).toString()
}

var str = 'peng'
var key = 'askjdhjkas'
console.log(get_hmac1(str,key))
console.log(get_hmac224(str,key))
console.log(get_hmac256(str,key))
console.log(get_hmac512(str,key))

image-20250301181627897

5.3Python实现HMAC

代码

keystr值一样,确定和js生成的一样

import hmac
import hashlib

def get_hmacsha1(key, str):
    return hmac.new(key.encode('utf-8'), str.encode('utf-8'), hashlib.sha1).hexdigest()


def get_hmacsha224(key, str):
    return hmac.new(key.encode('utf-8'), str.encode('utf-8'), hashlib.sha224).hexdigest()


def get_hmacsha256(key, str):
    return hmac.new(key.encode('utf-8'), str.encode('utf-8'), hashlib.sha256).hexdigest()


def get_hmacsha512(key, str):
    return hmac.new(key.encode('utf-8'), str.encode('utf-8'), hashlib.sha512).hexdigest()


str = 'peng'
key = 'askjdhjkas'
print(get_hmacsha1(key, str))
print(get_hmacsha224(key, str))
print(get_hmacsha256(key, str))
print(get_hmacsha512(key, str))

image-20250301182746608

6.企查查网站逆向

地址:https://www.qcc.com/

6.1分析过程

登录后搜索小米

image-20250301183256655

搜索后返回的没有找到api,返回的是一个页面

那就尝试点击分页

在请求头下找到了一个类似加密的键值对

https://www.qcc.com/api/search/searchMulti

image-20250301183630060

右键复制(以cURL(base)格式复制)整个请求

curl 'https://www.qcc.com/api/search/searchMulti' \
  -H 'accept: application/json, text/plain, */*' \
  -H 'accept-language: zh-CN,zh;q=0.9' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -H 'cookie: acw_tc=1a0c384a17408235448871640e0043676097a7e8a43ee701aa2dc819edc970; QCCSESSID=c88eeaa956741c981aeb5fe8a4; qcc_did=1ea4af44-65a0-4d83-932a-4ba76bc0bf83; tfstk=gBJtNMbkX20iXeWoIAG3n9vXrDnnxKKwsF-7nZbgGeLpV3zGG10NkBLX81b_Ic5vJnT-bi0wnlRer3wMIfW0HOWVh40oEYmwbtWb47X6iN-BYiihfECyG8WVh40h6LOrYt8rV0861HidmibffEs6Aw_FX-6f5saQOg_CltTfG9tC2gbffs1_ODIVRZ6fhEtINCEOol_YHQ1BjHk8I92bhpIO1Xx1R-sepGCOPh9Th-Z5X1QWfw3yy25FOn5v3oyAMhOyugT_5qQ2pH96NUg3sNORD3AvfAwCId-6Ns9SuStNINBBCQEbhHBOSEpv8P3dPd-BU91nF0KBLFxwpnqjhMYlRhJ1HYiPBO_1pMJm8R_vOI9G_tzskZJpAdOA4SpkeZw_raI01DnLgS51YplVGuHs0NSPvam3tSPVtMSdrDnLgS51YMQoxYV4g6jF.' \
  -H 'da4728817277230574d7: 4870626981a425631fb457af4b1d6125bc76a10162a138f3bbc082d0c11c65bfccd34d3d8c29930d3aa122e519b3c1ea1c41d84003cd09b95c828e9fc06fb08e' \
  -H 'origin: https://www.qcc.com' \
  -H 'pragma: no-cache' \
  -H 'priority: u=1, i' \
  -H 'referer: https://www.qcc.com/web/search?key=%E5%B0%8F%E7%B1%B3' \
  -H 'sec-ch-ua: "Not A(Brand";v="8", "Chromium";v="132", "Google Chrome";v="132"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Windows"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: same-origin' \
  -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36' \
  -H 'x-pid: fe2c13049497d88ff04a56fc39f9739e' \
  -H 'x-requested-with: XMLHttpRequest' \
  --data-raw '{"searchKey":"小米","pageIndex":2,"pageSize":20}'

image-20250301184147067

接下来就要找到给请求头加密的地方

我这里直接尝试使用XHR/提取断点

/api/search/searchMulti

image-20250301184448956

header肯定在发送请求前创建成功的,所以我们在调用堆栈中向之前代码单步调试

image-20250301184529487

前面俩个堆栈方法中请求的header已经加密成了,接着就看到了异步代码,所以要在异步代码打上断点,继续单步调试

image-20250301184732408

发现在这段代码结束后,请求头有了加密

看代码我们一开始也可以使用关键字定位,ctrl + shift + f搜索headers[

i.default = [function(e) {
                var t, n;
                return null !== (t = e.custom) && void 0 !== t && t.originConfig || (e.headers["X-Requested-With"] = "XMLHttpRequest",
                null !== (n = e.custom) && void 0 !== n && n.functionName && (e.headers["x-function-name"] = encodeURIComponent(e.custom.functionName)),
                window.pid && (e.headers["x-pid"] = window.pid),
                e.withCredentials = !0,
                (0,
                a.default)(e)),
                e
            }

image-20250301190629884

定位到是由(0,a.default)(e)进行加密的,继续跳转到实现

image-20250301191634890

我们发现在这段代码中进行数据加密赋值

可以看到e.headers[i] = u,说明i是请求头键,u是请求头值

 // 源代码
 i.default = function(e) {
                var t = e.url.replace(e.baseURL, "")
                  , n = a.default.stringify(e.params || {});
                n && (t += (-1 === t.indexOf("?") ? "?" : a.default.options.delimiter || "&") + n),
                t = t.toLowerCase();
                var i = (0,
                o.default)(t, e.data)
                  , u = (0,
                r.default)(t, e.data, (0,
                s.default)());
                e.headers[i] = u
            }

image-20250301192802840

首先解析i,发现i的赋值(0, o.default)(t, e.data),跳转到 o.default实现

var i = (0, o.default)(t, e.data)

image-20250301193710714

return (0, a.default)(t + n, (0, o.default)(t)).toLowerCase().substr(8, 20)发现此方法还有2个自定义参数a.defaulto.default

这里只能一步一步继续看,我们按顺序继续跟踪o.default,因为会先执行里面的代码

然后我们重写这个方法

// 源代码
i.default = function() {
                var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}
                  , t = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase()
                  , n = JSON.stringify(e).toLowerCase();
                return (0,
                a.default)(t + n, (0,
                o.default)(t)).toLowerCase().substr(8, 20)
            }
            
// 修改后
aa = function () {
    var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}
        , t = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase()
        , n = JSON.stringify(e).toLowerCase();
    return (0,
        cc)(t + n, (0,
        bb)(t)).toLowerCase().substr(8, 20)
}

image-20250301194142465

跳转到o.default

image-20250302000735790

这个方法有1个自定义的变量a,我们通过控制台输出,感觉他应该是一个固定变量

这个方法大部分是js方法,比较简单

重写这个方法

// 原代码
i.default = function() {
                for (var e = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase(), t = e + e, n = "", i = 0; i < t.length; ++i) {
                    var o = t[i].charCodeAt() % a.default.n;
                    n += a.default.codes[o]
                }
                return n
            }
// 修改后
bb = function () {
    a = {
        "n": 20,
        "codes": {
            0: 'W',
            1: 'l',
            2: 'k',
            3: 'B',
            4: 'Q',
            5: 'g',
            6: 'f',
            7: 'i',
            8: 'i',
            9: 'r',
            10: 'v',
            11: '6',
            12: 'A',
            13: 'K',
            14: 'N',
            15: 'k',
            16: '4',
            17: 'L',
            18: '1',
            19: '8'
        }
    }
    for (var e = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase(), t = e + e, n = "", i = 0; i < t.length; ++i) {
        var o = t[i].charCodeAt() % a.n;
        n += a.codes[o]
    }
    return n
}

image-20250302000920380

上面方法完成后,我们接着单步调试回到 return (0,a.default)(t + n, (0, o.default)(t)).toLowerCase().substr(8, 20)

接着需要调试a.default

image-20250302001248583

我们进入到a.default实现,打上断点,单步调试到此处,此处也是个方法跳转,接着跳转到实现

i.default = function(e, t) {
                return (0,
                a.default)(e, t).toString()
            }

image-20250301194414048

我们发现这段代码进行了HMAC加密,参数就是我们的请求地址(/api/search/searchmulti) + 请求参数({"searchkey":"小米","pageindex":1,"pagesize":20})

_createHmacHelper: function(t) {
                            return function(e, n) {
                                return new p.HMAC.init(t,n).finalize(e)
                            }
                        }

image-20250302002018370

地址:https://toolgg.com/hmacsha512/

我们需要验证这个方法是否是固定的某种hmac算法,我们直接打开在线加解密的网站,进行HmacSHA512HmacSHA1HmacSHA224HmacSHA256验证,只能一个个尝试,这里发现这个网站是使用的HmacSHA512算法

image-20250302003709144

所以这个方法的意思就是:

  • (0, o.default)(t):对请求地址进行字符转换
  • (0, a.default)(t + n, (0, o.default)(t)):请求地址进行字符转换后作为盐,在对请求地址 + 请求参数进行HMAC512加密

image-20250302004418716

调试完 var i = (0, o.default)(t, e.data) ,

接着就是u = (0,r.default)(t, e.data, (0,s.default)());

image-20250302004518102

接着调试s.default,跳转到实现

image-20250302004759082

这个方法就是执行了js脚本,获取window.tid

ctrl + shift + f全局搜索window.tid

这个一眼就能看到最后一个就是赋值

var _default = _exports.default = function _default() {
                var list = ["w", "i", "n", "d", "o", "w", ".", "t", "i", "d"];
                return eval(list.join(""))
            }

image-20250302005021803

跳转到搜索,获取到 window.tid

image-20250302005150174

window的值一般在首页都能搜索到,正常情况我们需要请求首页获取到该值,因为是本地测试,这里可以固定

image-20250302005241658

s.default就是获取页面的 window.tid

接着调试 r.default,跳转到该实现

image-20250302005424288

我们发现此方法有2个自定义方法a.defaulto.default

那接着调试o.default,我们需要重写这个方法

ccHmacSHA512加密

bb是进行字符转换

 // 原代码
 i.default = function() {
                var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}
                  , t = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : ""
                  , n = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase()
                  , i = JSON.stringify(e).toLowerCase();
                return (0,
                a.default)(n + "pathString" + i + t, (0,
                o.default)(n))
            }
 
 // 修改后
 // cc是
 ee = function () {
    var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}
        , t = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : ""
        , n = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase()
        , i = JSON.stringify(e).toLowerCase();
    return (0,
        cc)(n + "pathString" + i + t, (0,
        bb)(n))
}

image-20250302005804782

跳转到o.default

image-20250302005930585

我们发现此方法就是对参数进行字符转换,刚才我们已经重写过了

这里需要固定a.default.codes变量,其他都是js方法

image-20250302010007318

接着跳转到a.default实现

image-20250302010151703

这里也是方法跳转,继续跳转到实现

image-20250302010247095

发现这里也是进行HmacSHA512加密的地方,并且是规范的HmacSHA512

刚才我们通过在线加解密的网站验证了

我们可以通过crypto-js实现HmacSHA512

var crypto = require('crypto-js')
cc = function (text, key) {
    return crypto.HmacSHA512(text, key).toString()
}

到这里调试完了,js逆向的代码也完成了,接着进行代码测试

image-20250302011246821

6.2js逆向代码

var crypto = require('crypto-js')

aa = function () {
    var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}
        , t = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase()
        , n = JSON.stringify(e).toLowerCase();
    return (0,
        cc)(t + n, (0,
        bb)(t)).toLowerCase().substr(8, 20)
}

bb = function () {
    a = {
        "n": 20,
        "codes": {
            0: 'W',
            1: 'l',
            2: 'k',
            3: 'B',
            4: 'Q',
            5: 'g',
            6: 'f',
            7: 'i',
            8: 'i',
            9: 'r',
            10: 'v',
            11: '6',
            12: 'A',
            13: 'K',
            14: 'N',
            15: 'k',
            16: '4',
            17: 'L',
            18: '1',
            19: '8'
        }
    }
    for (var e = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase(), t = e + e, n = "", i = 0; i < t.length; ++i) {
        var o = t[i].charCodeAt() % a.n;
        n += a.codes[o]
    }
    return n
}

cc = function (text, key) {
    return crypto.HmacSHA512(text, key).toString()
}

dd = function () {
    //  var list = ["w", "i", "n", "d", "o", "w", ".", "t", "i", "d"];
    //                 return eval(list.join(""))
    // window.tid
    // <script>
    //     window.pid = 'abd354f89325103e1bf47f162fee26c0';
    //     window.tid = '026bf665b0abdeff61cb1b3c32fd6dd5'
    // </script>
    return '026bf665b0abdeff61cb1b3c32fd6dd5'
}

ee = function () {
    var e = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}
        , t = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : ""
        , n = (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : "/").toLowerCase()
        , i = JSON.stringify(e).toLowerCase();
    return (0,
        cc)(n + "pathString" + i + t, (0,
        bb)(n))
}

get_hmacsha512_header = function (t, e) {
    return {'key': aa(t, e), 'val': ee(t, e, dd())}
}

// // 测试代码
// var t = '/api/search/searchmulti',
//     e = {"searchkey": "小米", "pageindex": 1, "pagesize": 20},
//     key = '026bf665b0abdeff61cb1b3c32fd6dd5'
// console.log(aa(t,e))
// // u = (0,  r.default)(t, e.data, (0, s.default)());
// // r.default('/api/search/searchmulti',{"searchkey": "小米", "pageindex": 1, "pagesize": 20},'026bf665b0abdeff61cb1b3c32fd6dd5')
// console.log(ee(t,e,dd()))
// console.log(ee(t,e,key))
//
// console.log(get_hmacsha512_header('/api/search/searchmulti',{"searchkey": "小米", "pageindex": 1, "pagesize": 20}))

6.3python调用代码

右键复制(以cURL(base)格式复制)整个请求

image-20250302015222661

地址:https://www.spidertools.cn/#/curl2Request

这个网站可以快速帮我们转换headers

image-20250302015308054

代码

主要是js逆向比较耗时,代码就是调用js生成加密headers,然后带上headers发送post请求

import json
import time

import execjs
import requests


class QiCCSpider:
    def __init__(self):
        self.headers = {
            "accept": "application/json, text/plain, */*",
            "accept-language": "zh-CN,zh;q=0.9",
            "cache-control": "no-cache",
            "content-type": "application/json",
            "da4728817277230574d7": "4870626981a425631fb457af4b1d6125bc76a10162a138f3bbc082d0c11c65bfccd34d3d8c29930d3aa122e519b3c1ea1c41d84003cd09b95c828e9fc06fb08e",
            "origin": "https://www.qcc.com",
            "pragma": "no-cache",
            "priority": "u=1, i",
            "referer": "https://www.qcc.com/web/search?key=%E5%B0%8F%E7%B1%B3",
            "sec-ch-ua": "\"Not A(Brand\";v=\"8\", \"Chromium\";v=\"132\", \"Google Chrome\";v=\"132\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
            "x-pid": "fe2c13049497d88ff04a56fc39f9739e",
            "x-requested-with": "XMLHttpRequest"
        }
        self.cookies = {
            "acw_tc": "1a0c384a17408235448871640e0043676097a7e8a43ee701aa2dc819edc970",
            "QCCSESSID": "c88eeaa956741c981aeb5fe8a4",
            "qcc_did": "1ea4af44-65a0-4d83-932a-4ba76bc0bf83",
            "tfstk": "gBJtNMbkX20iXeWoIAG3n9vXrDnnxKKwsF-7nZbgGeLpV3zGG10NkBLX81b_Ic5vJnT-bi0wnlRer3wMIfW0HOWVh40oEYmwbtWb47X6iN-BYiihfECyG8WVh40h6LOrYt8rV0861HidmibffEs6Aw_FX-6f5saQOg_CltTfG9tC2gbffs1_ODIVRZ6fhEtINCEOol_YHQ1BjHk8I92bhpIO1Xx1R-sepGCOPh9Th-Z5X1QWfw3yy25FOn5v3oyAMhOyugT_5qQ2pH96NUg3sNORD3AvfAwCId-6Ns9SuStNINBBCQEbhHBOSEpv8P3dPd-BU91nF0KBLFxwpnqjhMYlRhJ1HYiPBO_1pMJm8R_vOI9G_tzskZJpAdOA4SpkeZw_raI01DnLgS51YplVGuHs0NSPvam3tSPVtMSdrDnLgS51YMQoxYV4g6jF."
        }
        self.url = 'https://www.qcc.com/api/search/searchMulti'
        js_code = open('12.企查查.js', encoding='utf-8').read()
        self.js = execjs.compile(js_code)

    def get_data(self):
        for i in range(1, 3):
            # e = {"searchkey": "小米", "pageindex": i, "pagesize": 20}
            e = {"searchKey": "小米", "pageIndex": i, "pageSize": 20}
            js_result = self.js.call('get_hmacsha512_header', '/api/search/searchMulti', e)
            self.headers[js_result['key']] = js_result['val']
            print(js_result)
            print(self.headers)
            r = requests.post(self.url, headers=self.headers, cookies=self.cookies, json=e)
            print(r.text)
            time.sleep(1)


if __name__ == '__main__':
    spider = QiCCSpider()
    spider.get_data()

image-20250302021620615

📌 创作不易,感谢支持!
每一篇内容都凝聚了心血与热情,如果我的内容对您有帮助,欢迎请我喝杯咖啡☕,您的支持是我持续分享的最大动力!

wxzf
posted @ 2025-04-09 16:08  peng_boke  阅读(137)  评论(0)    收藏  举报