JS逆向:由 words 、sigBytes 引发的一系列思考与实践

【作者主页】:小鱼神1024

【擅长领域】:JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等

在做JS逆向时,你是否经常看到 wordssigBytes 这两个属性呢,比如:

{words: [2003644449, 1081552243, 594308145, 1718382376], sigBytes: 16
}

如果发现了它们的踪迹,那恭喜你了。基本可以确定,网站是使用 crypto-js 库中某一个加密算法实现加密的。

words 、sigBytes 它们是什么

crypto-js 这个流行的 JavaScript 加密库中,wordssigBytesCryptoJS.lib.WordArray 对象的两个重要属性。

words 数组是 WordArray 对象的主体,它用于存储加密数据。在 crypto-js 中,一个 word 是由32位(或者说是4个字节)组成的,因此每个元素在 words 数组中实际上可以表示4个字节的数据。

在计算机系统中,一个32位的整数可以存储从-2,147,483,648到2,147,483,647的整数值(如果是有符号整数),或者从0到4,294,967,295(如果是无符号整数)。这种表示范围是因为32位可以表示2^32(即4,294,967,296)个不同的值。

补充

在计算机和信息技术中,32位(4字节)是一个常用的数据大小单位,用来表示数据的存储或处理容量。具体来讲:

  • 1位(bit):是计算机数据的最基本单位,代表一个二进制数字,即0或1。
  • 1字节(Byte):通常由8位组成,是基本的数据存储单位。1字节可以表示0到255(或者在二进制中是00000000到11111111)之间的一个数值。
  • 32位:则相当于4字节(4 Bytes),因为1字节是8位,所以4字节就是4 * 8 = 32位。

如何生成 WordArray 对象

两种解决办法:

通过 CryptoJS.enc.Utf8.parse 生成

如果已知utf8编码的明文,则可以使用 CryptoJS.enc.Utf8.parse 方法生成 WordArray 对象。

const CryptoJS = require("crypto-js");// 生成 WordArray 对象
const keyWordArray = CryptoJS.enc.Utf8.parse("wm0!@w-s#ll1flo(");
console.log(keyWordArray);
通过 CryptoJS.lib.WordArray.create 生成
const CryptoJS = require("crypto-js");
const words = [2003644449, 1081552243, 594308145, 1718382376]; // 32位整数数组(words数组)
const sigBytes = 16;// 使用 CryptoJS 转换字节数组到 WordArray
const keyWordArray = CryptoJS.lib.WordArray.create(words, sigBytes);console.log(keyWordArray);

如何将 WordArray 转化为 UTF-8 字符串

const CryptoJS = require("crypto-js");
const wordArrayBytes = {words: [ 2003644449, 1081552243, 594308145, 1718382376 ],sigBytes: 16
};// 将 WordArray 转换为 UTF-8 字符串
const key = CryptoJS.enc.Utf8.stringify(wordArrayBytes);
console.log(key);// wm0!@w-s#ll1flo(

如何将 WordArray 转化为十六进制Hex

const CryptoJS = require("crypto-js");
const wordArrayBytes = {words: [ 2003644449, 1081552243, 594308145, 1718382376 ],sigBytes: 16
};// 将 WordArray 转换为十六进制Hex
const key = CryptoJS.enc.Hex.stringify(wordArrayBytes);
console.log(key);// 776d302140772d73236c6c31666c6f28

那如何判断一个字符串是否为有效的十六进制字符串,可以检查以下几点:

  • 字符串长度:十六进制字符串的长度应该是偶数。

  • 字符范围:十六进制字符串只能包含 0-9 以及 a-f 和 A-F 这些字符。

可以编写一个函数来实现这个逻辑:

// 判断一个字符串是否为十六进制字符串的函数
function isHexString(str) {// 检查长度是否为偶数if (str.length % 2 !== 0) {return false;}// 使用正则表达式检查字符串是否只包含合法的十六进制字符(0-9, a-f, A-f)const hexRegex = /^[0-9a-fA-F]+$/;return hexRegex.test(str);
}// 示例测试
const testHexString1 = "776d302140772d73236c6c31666c6f28";
const testHexString2 = "1a2b3g4d5e"; // 包含非法字符 'g'
const testHexString3 = "123";        // 长度为奇数console.log(isHexString(testHexString1)); // 输出:true
console.log(isHexString(testHexString2)); // 输出:false
console.log(isHexString(testHexString3)); // 输出:false

WordArray整数数组、Hex十六进制、ByteArray字节数组、UTF-8字符串之间的相互转换

// 判断一个数组是否为字节数组(每个元素为0到255之间的整数)
function isByteArray(arr) {// 首先检查是否为数组if (!Array.isArray(arr)) {return false;}// 检查每个元素是否为0到255之间的整数for (let i = 0; i < arr.length; i++) {if (typeof arr[i] !== 'number' || arr[i] < 0 || arr[i] > 255 || !Number.isInteger(arr[i])) {return false;}}return true;
}// 将字节数组转换为十六进制字符串的函数
function bytesToHex(bytes) {if (!isByteArray(bytes)) {throw new Error('输入必须是字节数组');}return bytes.map(byte => byte.toString(16).padStart(2, '0')).join('');
}// 将十六进制字符串转换为字节数组的函数
function hexToBytes(hex) {let bytes = [];for (let c = 0; c < hex.length; c += 2) {bytes.push(parseInt(hex.substr(c, 2), 16));}return bytes;
}// 将32位整数数组(words数组)转换为字节数组的函数
function wordsToBytes(words) {let bytes = [];for (let i = 0; i < words.length; i++) {let word = words[i];bytes.push((word >> 24) & 0xFF);bytes.push((word >> 16) & 0xFF);bytes.push((word >> 8) & 0xFF);bytes.push(word & 0xFF);}return bytes;
}// 将字节数组转换为UTF-8字符串的函数
function bytesToStringUTF8(bytes) {if (!isByteArray(bytes)) {throw new Error('输入必须是字节数组');}return new TextDecoder('utf-8').decode(new Uint8Array(bytes));
}let key = { words: [2003644449, 1081552243, 594308145, 1718382376], sigBytes: 16 };// 将key的words数组转换为字节数组
let keyBytes = wordsToBytes(key.words);// 将字节数组转换为十六进制字符串,测试使用
let keyHex = bytesToHex(keyBytes);// 打印key和iv的十六进制字符串
console.log("十六进制Hex:", keyHex);// 将十六进制字符串还原为字节数组,以确保转换过程的无损失和准确性,测试使用
let keyBytesFromHex = hexToBytes(keyHex);// 将字节数组转换为UTF-8字符串,得到最终的字符串表示
let keyString = bytesToStringUTF8(keyBytes);// 打印key和iv的UTF-8字符串表示
console.log("字符串UTF-8:", keyString);

用 Uint8Array 生成字节数组

Uint8Array 的特点包括以下几点:

  • 无符号整数:Uint8Array 存储的是无符号的 8 位整数(0 到 255),因此每个元素的取值范围是从 0 到 255。这意味着每个元素可以表示一个字节的数据,适用于处理字节数据和二进制数据。

  • 定长数组:Uint8Array 是一种定长数组,它的长度在创建时就已经确定,并且不能动态改变。这有助于提高操作效率和性能,尤其在处理大量数据时非常有用。

// 用 Uint8Array 生成字节数组
const uint8Array = Uint8Array.from([119, 109, 48, 33, 64,119, 45, 115, 35, 108,108, 49, 102, 108, 111,40
]);// 使用 TextDecoder 将 bytes 转换为字符串
const textDecoder = new TextDecoder();
const decodedString = textDecoder.decode(uint8Array);console.log(decodedString);// wm0!@w-s#ll1flo(

为什么 AES、DES等加密时,要先转化 WrodArray

// AES 加密
function encrypt(word) {// 转化为 WordArrayconst iv = CryptoJS.enc.Utf8.parse("0102030405060708");// 转化为 WordArrayconst key = CryptoJS.enc.Utf8.parse('wm0!@w-s#ll1flo(');const encrypted = CryptoJS.AES.encrypt(word, key, {iv,mode: CryptoJS.mode.CBC,padding: CryptoJS.pad.Pkcs7})return encrypted.toString();
}

转化为32位二进制数据再进行加密是出于以下几个主要原因:

  • 加密算法的要求:几乎所有的现代加密算法,包括对称加密(如AES)和非对称加密(如RSA),在底层操作时都是以二进制数据形式工作的。这是因为加密算法在设计时需要操作位(bit)和字节(byte),而不是高级语言中的文本或数字等类型。因此,将数据转换为二进制形式是执行这些算法的必要步骤。

  • 安全性增强:通过转换为二进制数据,可以确保在加密过程中原始数据的内容不会因其格式或结构而泄露信息。此外,某些加密方式(如块加密)要求数据以特定大小的块来处理。将数据预先转化为二进制形式,可以更容易地按块对数据进行操作和填充。

  • 性能优化:直接对二进制数据进行操作比对高级数据类型(如字符串或对象)进行操作要有效率得多。加密和解密操作通常需要大量的数据处理工作,特别是在数据量大或需求高性能的应用场景中。预先将数据转换为二进制格式,可以最大限度地减少处理时间和资源消耗。

创作不易,动动您发财的小手,点赞关注一波,支持我创作更多对您有帮助的文章!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/863159.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习300问】135、决策树算法ID3的局限性在哪儿?C4.5算法做出了怎样的改进?

ID3算法是一种用于创建决策树的机器学习算法&#xff0c;该算法基于信息论中的信息增益概念来选择最优属性进行划分。信息增益是原始数据集熵与划分后数据集熵的差值&#xff0c;熵越小表示数据集的纯度越高。有关ID3算法的详细步骤和算法公式在我之前的文章中谈到&#xff0c;…

探索 Electron:将 Web 技术带入桌面应用

Electron是一个开源的桌面应用程序开发框架&#xff0c;它允许开发者使用Web技术&#xff08;如 HTML、CSS 和 JavaScript&#xff09;构建跨平台的桌面应用程序&#xff0c;它的出现极大地简化了桌面应用程序的开发流程&#xff0c;让更多的开发者能够利用已有的 Web 开发技能…

关于Java依赖版本升级的相关问题解决(持续更新)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

VMware Workstation 安装 Centos 虚拟机

1. 下载 VMware Workstation 直接上网找官网下载即可 2. 下载 Centos 镜像 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 3.打开 VMware 创建虚拟机 3.1点击创建虚拟机 3.2 选择自定义安装 3.3 选择使用 Workstation 的版本 版本越高兼容性越低但性能越好&#xff0c;一…

Linux磁盘分区方案

如下&#xff1a; /boot 分区&#xff1a;存放Linux系统启动有关程序&#xff0c;建议大小100MB。 /usr 分区&#xff1a;存放Linux系统中的应用程序&#xff0c;数据较多&#xff0c;建议大于3GB。 /var 分区&#xff1a;存放Linux系统中经常变化的数据及日志文件&#xff0c…

智慧校园-实训管理系统总体概述

智慧校园实训管理系统&#xff0c;专为满足高等教育与职业教育的特定需求而设计&#xff0c;它代表了实训课程管理领域的一次数字化飞跃。此系统旨在通过革新实训的组织结构、执行流程及评估标准&#xff0c;来增强学生的实践操作技能和教师的授课效率&#xff0c;为社会输送具…

Leetcode 701:二叉搜索树的插入操作

给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和要插入树中的值 value &#xff0c;将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 &#xff0c;新值和原始二叉搜索树中的任意节点值都不同。 //遍历二叉搜索树&#xff0c;遇到空结点&#…

python如何把一个函数的返回值,当成这个函数的参数值

python如何把一个函数的返回值&#xff0c;当成这个函数的参数值 1. 递归调用 递归是一种函数自己调用自己的方法。在递归调用中&#xff0c;你可以将前一次调用的返回值作为下一次调用的参数。 def recursive_function(x):# 函数逻辑if 条件满足:return 结果else:return rec…

用mn查看单例模式符号表——动态加载的动态链接库中的单例

nm - display name list(symbol table) 问题 有一个疑问由来已久&#xff0c;单例分单线程安全&#xff0c;多线程安全。那么如果动态加载链接库&#xff0c;单例还能保证只存在唯一实例吗&#xff1f; 于是&#xff0c;打算写代码验证一下 验证 1. 代码准备 总共有三个文…

数据结构-分析期末选择题考点(图)

我是梦中传彩笔 欲书花叶寄朝云 目录 图的常见考点&#xff08;一&#xff09;图的概念题 图的常见考点&#xff08;二&#xff09;图的邻接矩阵、邻接表 图的常见考点&#xff08;三&#xff09;拓扑排序 图的常见考点&#xff08;四&#xff09;关键路径 图的常见考点&#x…

c语言实现贪吃蛇小游戏

源码 /** * FileName: snakec* Author:PowerKing * Version&#xff1a;V1.0* Date:2024.6.28* Description: 贪吃蛇小游戏*/#include <curses.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h>/*贪吃蛇游戏 */#define UP 1…

c++ 递归

递归函数是指在函数定义中调用自身的函数。C语言也支持递归函数。 下面是一个使用递归函数计算阶乘的例子&#xff1a; #include <iostream> using namespace std;int factorial(int n) {// 基本情况&#xff0c;当 n 等于 0 或 1 时&#xff0c;阶乘为 1if (n 0 || n…

S32K3 工具篇2:如何在S32DS中使用Segger JLINK下载

S32K3 工具篇2&#xff1a;如何在S32DS中使用Segger JLINK下载 一&#xff0c; S32DS中JLINK下载1.1 Segger JLINK 驱动1.2 S32DS JLINK驱动路径配置1.3 S32DS JLINK debug configuration1.4 S32DS JLINK debug S32K3板子结果 二&#xff0c; JLINK驱动实现S32K344代码下载2.1 …

mysql数据库备份-使用自带工具mysqldump备份工具,mysqlimport/source导入工具

MysqlLimport介绍 MySCLimport是MysQL数据库中的一个命令行工具&#xff0c;用于将数据从外部文件导入MySQL数据库中的表中。它可以导入多种格式的数据&#xff0c;包括CSV、TXT、XML和SQL文件等。本文将介绍MySQLimport的用法&#xff0c;并通过代码示例演示如何使用它来导入…

高考落幕,暑期西北行,甘肃美食等你来尝

高考结束&#xff0c;暑期来临&#xff0c;西北之旅成为许多人的热门选择。而来到甘肃&#xff0c;除了领略壮丽的自然风光和深厚的历史文化&#xff0c;甘肃特产和传统面点以其独特的风味和传统的制作工艺也为游客们带来了一场地道的甘肃美食体验。 平凉的美食&#x…

c++ try 函数

在C中&#xff0c;try函数用于捕获和处理异常。以下是一个演示try函数的示例&#xff1a; #include <iostream> #include <stdexcept>double division(int x, int y) {if (y 0) {throw std::runtime_error("Divide by zero error");}return x / y; }in…

005-GeoGebra基础篇-GeoGebra的点

新手刚开始操作GeoGebra的时候一般都会恨之入骨&#xff0c;因为有些操作不进行学习确实有些难以凭自己发现。 目录 一、点的基本操作1. 通过工具界面添加点2. 关于点的选择&#xff08;对象选择通用方法&#xff09;&#xff08;1&#xff09;选择工具法&#xff08;2&#xf…

Vue3使用jsbarcode生成条形码,以及循环生成条形码

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;我是前端菜鸟的自我修养&#xff01;今天给大家分享Vue3使用jsbarcode生成条形码&#xff0c;以及循环生成条形码&#xff0c;介绍了JsBarcode插件的详细使用方法&#xff0c;并提供具体代码帮助大家深入理解&#xff0c;彻…

【Docker】集群容器监控和统计 CAdvisor+lnfluxDB+Granfana的基本用法

集群容器监控和统计组合&#xff1a;CAdvisorlnfluxDBGranfana介绍 CAdvisor&#xff1a;数据收集lnfluxDB&#xff1a;数据存储Granfana&#xff1a;数据展示 ‘三剑客’ 安装 通过使用compose容器编排&#xff0c;进行安装。特定目录下新建文件docker-compose.yml文件&am…

日志分析-windows系统日志分析

日志分析-windows系统日志分析 使用事件查看器分析Windows系统日志 cmd命令 eventvwr 筛选 清除日志、注销并重新登陆&#xff0c;查看日志情况 Windows7和Windowserver2008R2的主机日志保存在C:\Windows\System32\winevt\Logs文件夹下&#xff0c;Security.evtx即为W…