Bootstrap

js逆向-某某威客signature参数解密

js逆向实战之一品威客signature参数解密

url: aHR0cHM6Ly93d3cuZXB3ay5jb20vbG9naW4uaHRtbA==

分析过程

输入用户名和密码,看触发的流量包。

image

  1. signature参数明显是被加密过的,接下来就是去寻找加密的过程。关键词搜索signature

    image

    有两处,第二处是个固定值不需要看,关注点在第一处。点进去看对应的代码,并打断点,重新登录,触发该断点。image编辑编辑

  2. 查看该行代码所有变量的值。

    image

    总共是4个关注点,Object(f.a)是个函数,UMl.j ? l.g : l.c是传给Object(f.a)函数的三个变量。先看三个变量中的l.j ? l.g : l.cl.j为false,所以会取l.cimage编辑编辑再看U变量。image

  3. 编辑 编辑

    U的定义如下:

    image

    其中需要考虑的变量只有TimestempNonceStrApp-Id,其余值都是固定的。
    App-Id的值最容易得到,由于l.j为false,故App-Id的值为l.b的值。
    Timestemp的值就是个时间戳。
    NonceStr的变量稍微麻烦点,由TimestempObject(h.e)()拼接而成。看Object(h.e)()是什么。
     

    image

    该函数本质上就是生成一个随机字符串并截取字符串中的第3位到第8位,既然是个随机函数,所以该值取什么无所谓。最后看M变量。image编辑编辑

  4. 其中的username和password和code就是我们输入的账号密码和图片验证码。refer可以理解成一个固定值,不用变。变量看完了,最后来看Object(f.a)函数。image编辑编辑

  5. 看到函数中的变量都跟arguments有关,先看arguments是什么。
     

    image

    arguments是个字典,第一个键值对中的值就是U变量,第二个键值对中的值就是M变量,第三个键值对中的值是个定值。那么变量datae很容易就能得到。
    n = e + f(data) + f(t) + e中需要知道f函数的作用和变量t是什么。
     

    image

    变量t跟变量U是一致的。
    函数f的实现如下。
     

    image

    如果我们看不懂具体代码是什么意思,可以将结果输出看看。
     

    image

    大概就是一个字符串的拼接。
    最后看dv函数。
     

    image

    image

    d函数是一个md5算法,v算法是一个AES加密,只要知道了密钥、偏移量和加密模式即可。
     

    image

    上图中显示的key和iv都是字节,想要看到字符串类型的话可以采用toString函数。

    image

  6. 一切加密过程都知道了,把相关代码复制,完整代码如下。

// 引入 CryptoJS 库
var CryptoJS = require("crypto-js");

// 主函数
function test(t, arguments) {
    // 设置默认参数值
    var data = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {};  
    var e = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : "a75846eb4ac490420ac63db46d2a03bf";

    // 构建加密字符串
    var n = e + f(data) + f(t) + e;
    
    // 对字符串进行 MD5 加密
    n = d(n);
    
    // 对 MD5 加密结果进行 AES 加密
    n = v(n);

    return n;
}

// r 对象,包含一个用于检查类型的函数
var r = {};
r.a = function(e) {
    return typeof e;  // 返回类型
}

// 将对象转换为排序后的字符串
function f(t) {
    var e = "";
    
    // 遍历对象的所有属性并按字典序排序
    Object.keys(t).sort().forEach(function(n) {
        e += n + (typeof t[n] === "object" 
            ? JSON.stringify(t[n], function(t, e) {
                // 将数值转换为字符串
                return typeof e === "number" ? String(e) : e;
              }).replace(/\//g, "\\/") 
            : t[n]);
    });
    
    return e;
}

// 加密所需的密钥和 IV(初始向量)
var l = {
    "key": {
        "words": [
            1717059670,
            2034454870,
            1987077226,
            944915297
        ],
        "sigBytes": 16
    },
    "iv": {
        "words": [
            0, 0, 0, 0
        ],
        "sigBytes": 16
    }
};

// 使用 MD5 算法进行哈希加密
function d(data) {
    return CryptoJS.MD5(data).toString();
}

// 使用 AES 算法进行加密
function v(data) {
    return function(data) {
        // 使用 CBC 模式和 PKCS7 填充方式进行加密
        return CryptoJS.AES.encrypt(data, l.key, {
            iv: l.iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        }).toString();
    }(data);
}

测试一下。

// 请求的参数 t
const t = {
    "App-Ver": "",
    "Os-Ver": "",
    "Device-Ver": "",
    "Imei": "",
    "Access-Token": "",
    "Timestemp": 1713833424,
    "NonceStr": "1713833424aoi0s",
    "App-Id": "4ac490420ac63db4",
    "Device-Os": "web"
};

// 请求的参数 arguments
const arguments = {
    "0": {
        "App-Ver": "",
        "Os-Ver": "",
        "Device-Ver": "",
        "Imei": "",
        "Access-Token": "",
        "Timestemp": 1713833424,
        "NonceStr": "1713833424aoi0s",
        "App-Id": "4ac490420ac63db4",
        "Device-Os": "web"
    },
    "1": {
        "username": "111111",
        "password": "111111",
        "code": "qwrd",
        "hdn_refer": "https://www.epwk.com/"
    },
    "2": "a75846eb4ac490420ac63db46d2a03bf"
};

// 调用 test 函数并打印结果
console.log(test(t, arguments));

结果如下:
 

image

可以得到正确的加密结果。

如果想要在python中运行,需要加入下述代码:

import time
import execjs

# 定义请求参数
app_version = ""
os_version = ""
device_version = ""
imei = ""
access_token = ""
app_id = "4ac490420ac63db4"
device_os = "web"

# 获取当前时间戳
timestamp = int(time.time())

# 构造 NonceStr,确保每次请求的唯一性
nonce_str = "{}fj1e".format(timestamp)

# 构建请求参数 t
request_params = {
    "App-Ver": app_version,
    "Os-Ver": os_version,
    "Device-Ver": device_version,
    "Imei": imei,
    "Access-Token": access_token,
    "Timestemp": timestamp,
    "NonceStr": nonce_str,
    "App-Id": app_id,
    "Device-Os": device_os
}

# 构建额外的参数 arguments
arguments = {
    "0": request_params,
    "1": {
        "username": "111111",
        "password": "123456",
        "code": "zyp6",
        "hdn_refer": "https://www.epwk.com/"
    },
    "2": "a75846eb4ac490420ac63db46d2a03bf"
}

# 读取并执行解密的 JavaScript 文件
with open("解密.js", mode="r", encoding="utf-8") as f:
    exec_js = f.read()

# 编译并执行 JavaScript 代码
exec_code = execjs.compile(exec_js)

# 调用 JavaScript 中的 test 函数,传入参数并打印结果
result = exec_code.call("test", request_params, arguments)
print(result)

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;