华为商城秒杀时加密验证 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的安装和使…...
Steam库存管理革命:5分钟掌握批量操作终极指南
Steam库存管理革命:5分钟掌握批量操作终极指南 【免费下载链接】Steam-Economy-Enhancer 中文版:Enhances the Steam Inventory and Steam Market. 项目地址: https://gitcode.com/gh_mirrors/ste/Steam-Economy-Enhancer Steam Economy Enhancer…...
ISE FPGA开发全流程实战:从代码到比特流的经典设计指南
1. 项目概述与核心价值如果你正准备踏入FPGA开发的大门,或者已经用了一段时间的Vivado,想看看业界另一个主流工具ISE(Integrated Software Environment)到底怎么玩,那这个系列的内容就是为你准备的。ISE是Xilinx&#…...
ssm基于Java的试题库管理系统(10030)
有需要的同学,源代码和配套文档领取,加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码(前后端源代码SQL脚本)配套文档(LWPPT开题报告/任务书)远程调试控屏包运行一键启动项目&…...
硅与锗PN结实战对比:手把手测量导通电压VF与温度系数
硅与锗PN结实战对比:手把手测量导通电压VF与温度系数 在电子工程实践中,PN结的特性测量是理解半导体器件行为的基础。硅(Si)和锗(Ge)作为两种经典半导体材料,其PN结在导通电压(VF)和温度特性上表现出显著差异。本文将带领读者通过实际测量&a…...
从电赛A题到实战:手把手教你搞定单相交流电子负载的SPWM控制与功率因数调节
从电赛A题到实战:手把手教你搞定单相交流电子负载的SPWM控制与功率因数调节 在电子设计竞赛中,单相交流电子负载的设计一直是极具挑战性的题目。它不仅考验参赛者对电力电子技术的理解,更要求具备将理论转化为实际电路的能力。本文将从硬件选…...
ARM RAS架构中ERR<n>FR寄存器解析与应用
1. ARM RAS架构与错误记录机制概述 在服务器和关键任务计算领域,硬件可靠性直接决定了系统的可用性水平。ARMv8/v9架构中的RAS(Reliability, Availability, Serviceability)扩展提供了一套完整的硬件错误处理机制,其核心是通过一组专用寄存器实现错误检测…...
ElevenLabs俄文语音合成私有化部署终极方案(含Docker镜像+俄语ASR对齐校验工具链)
更多请点击: https://intelliparadigm.com 第一章:ElevenLabs俄文语音合成私有化部署的背景与价值 随着全球本地化需求激增,俄语市场对高质量、低延迟、高隐私保障的语音合成(TTS)服务提出迫切要求。ElevenLabs 以其卓…...
LibreCAD符号库创建终极指南:快速构建您的专业CAD图库
LibreCAD符号库创建终极指南:快速构建您的专业CAD图库 【免费下载链接】LibreCAD LibreCAD is a cross-platform 2D CAD program. It can read DXF and write DXF/PDF/SVG files, with basic support for DWG reading. It supports point/line/circle/ellipse/parab…...
实战指南:如何高效部署VoiceFixer语音修复系统,从噪声消除到低分辨率增强全解析
实战指南:如何高效部署VoiceFixer语音修复系统,从噪声消除到低分辨率增强全解析 【免费下载链接】voicefixer General Speech Restoration 项目地址: https://gitcode.com/gh_mirrors/vo/voicefixer VoiceFixer是一款基于深度学习的通用语音修复工…...
告别裸机延时!ESP32-C3/ESP32-S3用RMT外设精准驱动WS2812B灯带(Arduino/IDF双平台教程)
ESP32-C3/ESP32-S3 RMT外设驱动WS2812B灯带实战指南 当你的灯光项目从十几颗WS2812B升级到上百颗时,GPIO模拟驱动方式很快就会遇到瓶颈——闪烁、卡顿、颜色失真,这些问题的根源在于时序精度不足。ESP32系列芯片内置的RMT(Remote Control&…...
