华为商城秒杀时加密验证 device_data 的算法研究
前言
- 之前华为商城放出 Mate60 手机时, 想给自己和家人抢购一两台,手动刷了好几天无果后,决定尝试编写程序,直接发送 POST 请求来抢。
- 通过抓包和简单重放发送后,始终不成功。仔细研究,发现 Cookie 中有一个名为 device_data 的数据比较可疑,看起来是加密后的 base64, 很有可能服务器是使用这些值进行了验证。于是决定研究一下,看是否可以破解并用于秒杀。
- 最后虽然研究出加密算法,并尝试用于秒杀,但由于仍然有其他的限制,暂时放弃,并将相关信息开源。
- 华为的兄弟姐妹们需要辛苦更改算法了 😃
查找 device_data 的生成位置
通过搜索,定位到名为 cp_20230815/…/ars_event.js 的文件,里面有对 ‘device_data’ 赋值的操作,那么加密算法就在这里了。
补充信息: 最新版本的地址已经是 cp_20231215/…/ars_event.js, 但内容没有改变。
算法分析
初看 ars_event.js , 是进行过混淆的, 简直和天书一样, 而且以前也没怎么用过 js, 不知道怎么下手。不过好在知道 js 的所有源码都在里面, 只要肯花一点时间, 必然是能解析出来的。
于是开始分析, 此处省略一万字。。。
功夫不负有心人,断断续续经过一两周的时间,总算把算法反推出来,并且编写了 java 代码进行验证和 POST 秒杀。虽然事后证明,服务器还有其他验证方式没有破解(比如 IP、UID 验证?),并发10个线程请求, 有4个给我返回非法请求。。。)
算法解释
- device_data 的数据分两部分:
- 最前面的
*2k
常量 + 从479752
中选出的两个数(间隔 3) - 要加密的字符串先 base64, 按 4 个字符进行编码
- 然后在前面加上 8 个随机字符
- 最后再按 8 个字符为一组的方式编码.
- 最前面的
源码
- 因为源码是从 js 中反推出来的, 主要是为了满足和原有的算法一致, 命名和写法上就比较乱.
/*** device_data 的加密/解密 算法* https://res.vmallres.com/cp_20230815/js/common/risk/ars_event.js* https://res.vmallres.com/cp_20231215/js/common/risk/ars_event.js*/
@Slf4j
public class ArsEventCrack {//获取华为 function _0x272042 函数中,对字符串加密时采用的位置索引列表public LinkedList<Integer> GetEncodeStringIndexArray(int strLength, int blockSize){//最后4字节保持原样int charArrayLength = strLength - 4;//String[] charArray = strInput.substring(0, strInput.length() - 4).split("");LinkedList<LinkedList<Integer>> tmpArray = new LinkedList<>();for(int i = 0; i < blockSize; i++){tmpArray.add(new LinkedList<>());for(int j = 0; j < charArrayLength; j++){int _tmpVal_1 = j * 2 * (blockSize - 1) + i;int _tmpVal_2 = 0;if (_tmpVal_1 < charArrayLength){tmpArray.get(i).add(_tmpVal_1);}if (i != 0) {_tmpVal_2 = j * 2 * (blockSize - 1) - i;if (_tmpVal_2 < charArrayLength && _tmpVal_2 > 0) {tmpArray.get(i).add(_tmpVal_2);}}if(_tmpVal_1 > charArrayLength || _tmpVal_2 > charArrayLength){break;}}}//log.info("tmpArray={}", tmpArray);//排序和删除重复数据List<List<Integer>> sortedDistinctLists = tmpArray.stream().map(linked -> linked.stream().sorted().distinct().collect(Collectors.toList())).collect(Collectors.toList());LinkedList<Integer> allPositions = new LinkedList<>();sortedDistinctLists.forEach(integers -> allPositions.addAll(integers));return allPositions;}public String EncodeString(String strInput, int blockSize){LinkedList<Integer> allPositions = GetEncodeStringIndexArray(strInput.length(), blockSize);StringBuilder sb = new StringBuilder();for (Integer pos : allPositions) {sb.append(strInput.charAt(pos));}sb.append(strInput.substring(strInput.length() - 4));String result = sb.toString();//log.info("result={}", result);return result;}public String repeatString(String str, int times){if (times <= 1){return str;}StringBuilder sb = new StringBuilder();for (int i = 0 ; i < times; i++){sb.append(str);}return sb.toString();}public String DecodeString(String strInput, int blockSize){//除了最后4个字节的,生成指定长度, 然后获取随机字符串位置索引, 并对应替换.int encodeLength = strInput.length() - 4;char[] chars = repeatString("0", encodeLength).toCharArray();LinkedList<Integer> allPositions = GetEncodeStringIndexArray(strInput.length(), blockSize);int index = 0;for (Integer pos : allPositions) {chars[pos] = strInput.charAt(index);index++;}String strResult = String.copyValueOf(chars) + strInput.substring(encodeLength);return strResult;}//再次调用就会恢复public String BlockString(String strInput, int blockSize){String strResult = "";String strWithoutLast4 = strInput.substring(0, strInput.length()-4);int blockCount = strWithoutLast4.length() / blockSize;for (int i = blockSize; i > 0; i--){String tmp = strInput.substring((i - 1)*blockCount, i*blockCount);strResult += tmp;}strResult += strInput.substring(strInput.length()-4);return strResult;}public String DecodeDeviceDataString(String strEncodedDeviceData){//去除前面的 *2k47 一类的随机开头String remove2KHeader = strEncodedDeviceData.substring(5);//第一次解码String outerDecode = DecodeString(remove2KHeader, 8);//解码出来, 前面8个字节是随机值String firstUnBlock = BlockString(outerDecode, 4);String realFirstBlock = firstUnBlock.substring(8);//再次解码,此时解出来的就是 base64String innerDecode = DecodeString(realFirstBlock, 4);String strOriginal = new String(Base64.getDecoder().decode(innerDecode));return strOriginal;}public String Get2kHeader(){//*2k92String strInput = "479752";int randomIndex = new Random(System.currentTimeMillis()).nextInt(3);return "*2k" + strInput.charAt(randomIndex) + strInput.charAt(randomIndex + 3);}public String EncodeDeviceDataString(String strEncodedDeviceData){String strBase64 = Base64.getEncoder().encodeToString(strEncodedDeviceData.getBytes());String innerEncode = RandomStringUtils.randomAlphanumeric(8) + EncodeString(strBase64, 4);String strBlocked = BlockString(innerEncode, 4);String strEncodeDeviceData = Get2kHeader() + EncodeString(strBlocked, 8);return strEncodeDeviceData;}@Testpublic void testAstEventCrack(){log.info("Get2kHeader={}", Get2kHeader());String strOriginal = "ABCDEFGHIJKLMNOPQRSTWVXYZabcdefghijklmnopqrstuvwxyz123456789";int blockSize = 8;String strEncoded = EncodeString(strOriginal, blockSize);String strDecoded = DecodeString(strEncoded, blockSize);log.info("strEncoded={}, strDecoded={}", strEncoded, strDecoded);Assert.assertEquals(strOriginal, strDecoded);String strBlocked = BlockString(strOriginal, blockSize);log.info("strBlocked={}", strBlocked);String strUnblocked = BlockString(strBlocked, blockSize);log.info("strUnblocked={}", strUnblocked);Assert.assertEquals(strOriginal, strUnblocked);}@Testpublic void TestDeviceData(){if(true){//只解密String strEncodedDeviceData = "*2k75xxxxxx"; // 此处输入通过 F12 或抓包获取的 device_data 字符串,运行后即可解密String strDeviceData = DecodeDeviceDataString(strEncodedDeviceData);log.info("strDeviceData: {}", strDeviceData);}if(false){//加密后再解密String strOriginalData = ""; //输入想要加密的字符串//String strOriginalData = GetDeviceFingerPrint() + "_" + "[object Object]";String strEncode = EncodeDeviceDataString(strOriginalData);String strDecode = DecodeDeviceDataString(strEncode);log.info("strDecode:{}", strDecode);Assert.assertEquals(strOriginalData, strDecode);}}
}
相关文章:

华为商城秒杀时加密验证 device_data 的算法研究
前言 之前华为商城放出 Mate60 手机时, 想给自己和家人抢购一两台,手动刷了好几天无果后,决定尝试编写程序,直接发送 POST 请求来抢。通过抓包和简单重放发送后,始终不成功。仔细研究,发现 Cookie 中有一个名为 devic…...

Wrk压测发送Post请求的正确姿势
一、Wrk简介 wrk 是一个能够在单个多核 CPU 上产生显著负载的现代 HTTP 基准测试工具。它采用了多线程设计,并使用了像 epoll 和 kqueue 这样的可扩展事件通知机制。此外,用户可以指定 LuaJIT 脚本来完成 HTTP 请求生成、响应处理和自定义报告等功能。 …...
【管理篇 / 登录】❀ 06. macOS下使用USB配置线登录 ❀ FortiGate 防火墙
【简介】飞塔防火墙上都会配有CONSOLE接口,包装里都会配置一根USB配置线,通过这个接口和这根线,我们可以用命令的方式登录飞塔防火墙。随着苹果电脑的普及,我们来学习如何在macOS中使用USB配置线登录飞塔防火墙。 早期飞塔防火墙包…...
linux系统shell语言的自动化交互
自动化交互 自动化交互expect交互expect用法 sshpass概念shhpass的脚本批量拷贝文件批量传递秘钥批量修改密码 自动化交互 expect交互 yum -y install expect tcl tcl-devel //安装expect交互工具expect用法 用法: 1)#!/usr/bin/expect //定义脚本执行的shell 2)set …...
HarmonyOS ArkTS 三方库的基本使用(十六)
如何获取三方库 目前提供了两种途径获取开源三方库: 1、通过访问Gitee网站开源社区获取 在Gitee中,搜索OpenHarmony-TPC仓库,在tpc_resource中对三方库进行了资源汇总,可以供开发者参考。 2、通过OpenHarmony三方库中心仓获取 …...
Spring boot封装rocket mq 教程
1、rocket mq版本 5.1.3 2、pom引入rocket mq依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-client-java</artifactId><version>5.0.4</version></dependency> 3、发送MQ消息工具类 impor…...

Java Swing手搓童年坦克大战游戏(I)
前言 业余偶尔对游戏有些兴趣,不过这样的时代,硬件软件飞速进步,2D游戏画面都无比精美,之前的8bit像素游戏时代早就过去了,不过那时候有许多让人印象深刻的游戏比如魂斗罗、超级玛丽、坦克大战(Battle City)等等。 学…...
【DevOps-04]】Operate阶段工具
一、简要说明 安装Docker安装Docker-compose二、安装Docker 官网地址:https://www.docker.com文档地址:Docker Docs仓库地址:https://hub.docker.com1、Docker相关网站 官方网站Get Docker | Docker Docs...

力扣2807.在链表中插入最大公约数
思路:遍历链表,对于每一个结点求出它与下一个结点的最大公约数并插入到俩个结点之间 代码: /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}…...
开始刷Leetcode之前你需要知道的 - The basic is all you need
数据结构:列表,哈希表,集合,栈,堆,链表,二叉树,图 入门算法:递归,排序算法,二分法,bfs,dfs list/array 列表常见操作&am…...
【PostgreSQL】模式Schema
PostgreSQL 数据库集群包含一个或多个命名数据库。角色和一些其他对象类型在整个集群中共享。与服务器的客户端连接只能访问单个数据库中的数据,该数据库在连接请求中指定。 数据库包含一个或多个命名schema,而这些schema又包含表。schema还包含其他类型…...
JavaScript实现的复杂功能:自动生成带水印的图片
#程序员的崩溃瞬间 在本文中,我们将讨论一个JavaScript实现的复杂功能,该功能可以自动为图片添加水印。这个功能在许多场景中都非常有用,例如,如果你想保护你的图片版权,或者你想在你的网站上显示自定义的水印。 一、…...

图神经网络|8.2 图卷积的计算基本方法
不同于一般的神经网络,网络层数的并不用特别多。 原因是只需要少数次数迭代后(当迭代次数为图上的直径?任意两点最短距离的最大值?),某节点便可获取得到图上所有的节点。 通俗的理解是,在社会中…...

equals()与hashCode()方法详解
java.lang.Object类中有两个非常重要的方法: 1 2 public boolean equals(Object obj) public int hashCode() Object类是类继承结构的基础,所以是每一个类的父类。所有的对象,包括数组,都实现了在Object类中定义的方法。 回到…...

六、基于Flask、Flasgger、marshmallow的开发调试
基于Flask、Flasgger、marshmallow的开发调试 问题描述调试方法一调试方法二调试方法三 问题描述 现在有一个传入传出为json格式文件的,Flask-restful开发的程序,需要解决如何调试的问题。 #!/usr/bin/python3 # -*- coding: utf-8 -*- # Project :…...

TypeScript 从入门到进阶之基础篇(三) 元组类型篇
系列文章目录 TypeScript 从入门到进阶系列 TypeScript 从入门到进阶之基础篇(一) ts基础类型篇TypeScript 从入门到进阶之基础篇(二) ts进阶类型篇TypeScript 从入门到进阶之基础篇(三) 元组类型篇TypeScript 从入门到进阶之基础篇(四) symbol类型篇 持续更新中… 文章目录 …...
现代CPU的多种运行模式
目前的CPU大多是支持X86-64技术的兼容CPU,这包括AMD64以及Intel的IA32E(后被正式命名为EM64T,Extended Memory 64 Technology),因为AMD64先出,而EM64T与AMD64完全兼容,所以也统一称为AMD64技术。…...

Python PDF处理模块pypdf库详解
概要 PDF(Portable Document Format)是一种常见的文档格式,广泛用于存储和共享文本和图像数据。在 Python 中,有许多库可以用于处理 PDF 文件,其中之一就是 PyPDF。PyPDF 是一个功能强大的库,它允许你读取…...

C++上位软件通过LibModbus开源库和西门子S7-1200/S7-1500/S7-200 PLC进行ModbusTcp 和ModbusRTU 通信
前言 一直以来上位软件比如C等和西门子等其他品牌PLC之间的数据交换都是大家比较头疼的问题,尤其是C上位软件程序员。传统的方法一般有OPC、Socket 等,直到LibModbus 开源库出现后这种途径对程序袁来说又有了新的选择。 Modbus简介 Modbus特点 1 &#…...

PLSQL Developer 15安装和oracle客户端安装
文章目录 前言一、PLSQL Developer1.下载2.安装 二、oracle客户端1.下载2.环境变量 三、使用1. oci2. 连接3. 配置文件 总结 前言 oracle是经常使用的数据库,PLSQL Developer是众多产品中比较不错的一款工具,接下来我们来介绍PLSQL Developer的安装和使…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...