【爬虫JS混淆分析】某网站票房响应数据加密(含JS补环境调用与Python解密算法)

文章目录

  • 1. 接口分析
  • 2. 断点调试
  • 3. 扣解密JS代码
  • 4. 补环境调用接口
  • 5. Python实现解密算法

【作者主页】:吴秋霖
【作者介绍】:Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作!
【作者推荐】:对JS逆向感兴趣的朋友可以关注《爬虫JS逆向实战》,对分布式爬虫平台感兴趣的朋友可以关注《分布式爬虫平台搭建与开发实战》
还有未来会持续更新的验证码突防、APP逆向、Python领域等一系列文章

在这里插入图片描述

在这里插入图片描述

1. 接口分析

  打开网站,随意点击票房板块抓包查看接口请求,响应数据是加密的,也是本次需要分析解决的

在这里插入图片描述
在这里插入图片描述

这里直接根据请求的堆栈去分析,或者也可以通过搜索加密接口的方式找到JS文件,如下两种方式均可:

在这里插入图片描述

在这里插入图片描述

2. 断点调试

通过请求堆栈跟踪来到PostAPI代码处,在此处挂上断点再次刷新页面,如下所示:

在这里插入图片描述

1 == (e = "{" == e[0] ? JSON.parse(e) : JSON.parse(webInstace.shell(e))).Status || 200 == e.Code ? r(e.Data) : 200 == e.code ? r(e.data) : a(e.Msg)

这里可以把这段代码拿出来分析一下,大概的逻辑是e的第一个字符是{就调用JSON.parse解析,显然这个地方逻辑有点多余,数据本身就是加密的!可能为了适配不同场景的可能

另外一个分支就是调用webInstace.shell去解密加密之后的数据e

我们能够直接通过数据接口拿到加密后的数据,接口请求的参数也均没有加密,那么要做的就是直接分析webInstace.shell方法实现对数据的解密

3. 扣解密JS代码

鼠标移动至webInstace.shell方法处,点击对应JS调用跳转找到加密函数的具体实现代码,如下所示:

在这里插入图片描述

解密方法经过了OB混淆,第一种咱们可直接复制webDES方法,执行这个方法,根据提示补齐所有缺失的属性即可

在这里插入图片描述

这里直接将webDES.min.js?v=1.0.0的整个JS代码全部扣下来

4. 补环境调用接口

JS解密代码扣下来后,编写Python请求接口测试代码调用扣下来的JS解密方法进行测试,代码实现如下:

import execjs
import requests

def fetch_data_from_api():
    url = "https://www.endata.com.cn/API/GetData.ashx"
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
    }
    data = {
        "year": 2023,
        "MethodName": "BoxOffice_GetYearInfoData"
    }
    response = requests.post(url, headers=headers, data=data)
    response.raise_for_status()
    return response.text

def execute_js_code(ori_data):
    with open("endata.js", 'r') as f:
        js_code = f.read()
    return execjs.compile(js_code).call("webInstace.shell", ori_data)

def main():
    try:
        encipher_data = fetch_data_from_api()
        
        result = execute_js_code(encipher_data)
        print(result)
    except requests.RequestException as e:
        print(f"请求异常:{e}")
    except Exception as e:
        print(f"执行 JavaScript 代码时发生错误:{e}")

if __name__ == "__main__":
    main()

运行上面Python代码出现如下异常错误:

execjs._exceptions.ProgramError: ReferenceError: navigator is not defined

异常提示JS代码中存在一个navigator属性,是环境属性,作用是用来检测请求是否为浏览器发送!这个在控制台根据混淆的JS代码调试一下,再手动补上

在这里插入图片描述

如上图显示可以看到navigator检测有UserAgent字段,一行代码,放到先前扣下来的JS代码最开始位置即可,如下所示:

global.navigator = {'userAgent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.     0 Safari/537.36"}

这个navigator也可以选择不补它,直接把JS混淆内代码删除即可,删除如下代码即可:

if (!navigator || !navigator[_0x2246('0x36', 'OMJW')])
	return '';

最后我们再次运行上面Python代码,测试结果如下:

在这里插入图片描述

5. Python实现解密算法

  JS代码文件目前有2000+行,且都是混淆的,其中有大部分无效可以弃用的操作逻辑,这里我们可以尝试调试分析JS混淆代码的逻辑,再使用Python重新实现!真正的核心解密算法在下面这部分代码:

var webDES = function() {
    var _0x4da59e = {
        'bUIIa': function _0x2a2af9(_0x779387, _0x4a4fec) {
            return _0x779387 + _0x4a4fec;
        }
    };
    var _0x9843d3 = function(_0x29d556, _0xcc6df, _0x3d7020) {
        if (0x0 == _0xcc6df)
            return _0x29d556[_0x2246('0x254', '4VZ$')](_0x3d7020);
        var _0x48914b;
        _0x48914b = '' + _0x29d556[_0x2246('0x255', 'GL3Q')](0x0, _0xcc6df);
        return _0x48914b += _0x29d556['substr'](_0x4da59e[_0x2246('0x256', 'DK[&')](_0xcc6df, _0x3d7020));
    };
    this[_0x2246('0x257', 'nArV')] = function(_0xa0c834) {
        var _0x51eedc = {
            'pKENi': function _0x2f627(_0x5b6f5a, _0x440924) {
                return _0x5b6f5a === _0x440924;
            },
            'wnfPa': 'ZGz',
            'VMmle': '7|1|8|9|5|2|3|6|0|4',
            'GKWFf': function _0x1a4e13(_0x40cfde, _0x16f3c2) {
                return _0x40cfde == _0x16f3c2;
            },
            'MUPgQ': function _0x342f0d(_0x19038b, _0x4004d6) {
                return _0x19038b >= _0x4004d6;
            },
            'hLXma': function _0x55adaf(_0x45a871, _0x161bdf) {
                return _0x45a871 + _0x161bdf;
            },
            'JdOlO': function _0x13e00a(_0x5899a9, _0x4bb34d) {
                return _0x5899a9 + _0x4bb34d;
            },
            'qrTpg': function _0x1198fb(_0x55b317, _0x22e1db, _0x1b091a) {
                return _0x55b317(_0x22e1db, _0x1b091a);
            },
            'pdmMk': function _0xe2b022(_0x4af286, _0x4c2fd4) {
                return _0x4af286 - _0x4c2fd4;
            },
            'xVKWW': function _0x1094a3(_0x5f3627, _0x2a0ac5, _0x3ad2e5) {
                return _0x5f3627(_0x2a0ac5, _0x3ad2e5);
            }
        };
        if (_0x51eedc[_0x2246('0x258', '@1Ws')](_0x2246('0x259', 'E&PI'), _0x51eedc['wnfPa'])) {
            this['_append'](a);
            return this[_0x2246('0x25a', 'GL3Q')]();
        } else {
            var _0x492a62 = _0x51eedc[_0x2246('0x25b', '&59Q')][_0x2246('0x25c', ')q#9')]('|')
              , _0x356b01 = 0x0;
            while (!![]) {
                switch (_0x492a62[_0x356b01++]) {
                case '0':
                    _0x554c90 = _grsa_JS[_0x2246('0x25d', 'E&PI')]['decrypt']({
                        'ciphertext': _grsa_JS['enc'][_0x2246('0x25e', 'sy^o')]['parse'](_0xa0c834)
                    }, _0x2cf8ae, {
                        'iv': _0x554c90,
                        'mode': _grsa_JS[_0x2246('0x16c', 'O^50')][_0x2246('0x25f', 'Who^')],
                        'padding': _grsa_JS[_0x2246('0x260', '7IfV')][_0x2246('0x261', 'E&PI')]
                    })[_0x2246('0x1c', 'yY#5')](_grsa_JS['enc'][_0x2246('0x262', ']2BX')]);
                    continue;
                case '1':
                    if (_0x51eedc[_0x2246('0x263', 'Jsmq')](null, _0xa0c834) || _0x51eedc[_0x2246('0x264', '!2eC')](0x10, _0xa0c834['length']))
                        return _0xa0c834;
                    continue;
                case '2':
                    _0xa0c834 = _0x9843d3(_0xa0c834, _0x2cf8ae, 0x8);
                    continue;
                case '3':
                    _0x2cf8ae = _grsa_JS[_0x2246('0x265', 'RQ2o')][_0x2246('0x266', '3j7z')][_0x2246('0x267', 'RQ2o')](_0x554c90);
                    continue;
                case '4':
                    return _0x554c90[_0x2246('0x268', 'cs*4')](0x0, _0x51eedc[_0x2246('0x269', 'MVsm')](_0x554c90[_0x2246('0x26a', '0J6f')]('}'), 0x1));
                case '5':
                    _0x554c90 = _0xa0c834[_0x2246('0x26b', 'UwHa')](_0x2cf8ae, 0x8);
                    continue;
                case '6':
                    _0x554c90 = _grsa_JS[_0x2246('0x26c', '4VZ$')]['Utf8']['parse'](_0x554c90);
                    continue;
                case '7':
                    if (!navigator || !navigator[_0x2246('0x26d', '0I#o')])
                        return '';
                    continue;
                case '8':
                    var _0x554c90 = _0x51eedc[_0x2246('0x26e', 'Yb4P')](_0x51eedc[_0x2246('0x26f', 'BQ5p')](parseInt, _0xa0c834[_0x51eedc[_0x2246('0x270', 'Z2VK')](_0xa0c834['length'], 0x1)], 0x10), 0x9)
                      , _0x2cf8ae = _0x51eedc[_0x2246('0x271', 'yY#5')](parseInt, _0xa0c834[_0x554c90], 0x10);
                    continue;
                case '9':
                    _0xa0c834 = _0x9843d3(_0xa0c834, _0x554c90, 0x1);
                    continue;
                }
                break;
            }
        }
    }
    ;
}

先还原_0x2246方法,控制台依次查看几个关键的调用都是啥,还原混淆后的内容,梳理清晰逻辑,过程很简单,就是很枯燥~没办法,做爬虫它就是这样,你挑的嘛!偶像!

在这里插入图片描述

包括混淆代码中的16进制都在控制台打印查看一下,进行还原,如下所示:

在这里插入图片描述

接下来对函数this[_0x2246(‘0x257’, ‘nArV’)]进行还原,控制台可以看到它就是shell方法,同时可以看到_0x51eedc定义了很多变量与函数的对应关系,方便调用

在这里插入图片描述

整体逻辑总的来说就是一些字符转进制操作、切片赋值、编码转字节,最终Python解密算法代码实现如下:

from Crypto.Cipher import DES
import binascii
import json

def extract_data(data, start_index, length):
    """从数据中提取特定部分并返回"""
    if start_index == 0:
        return data[length:]
    return data[:start_index] + data[(start_index + length):]

def decrypt_data(encrypted_data):
    """解密数据并返回 JSON 对象"""
    # 计算解密时使用的索引和长度
    last_char_index = int(encrypted_data[-1], 16) + 9
    char_at_last_index = int(encrypted_data[last_char_index], 16)

    # 提取数据和密钥
    intermediate_data = extract_data(encrypted_data, last_char_index, 1)
    key = encrypted_data[char_at_last_index:char_at_last_index + 8]
    intermediate_data = extract_data(encrypted_data, char_at_last_index, 8)

    # 将密钥和数据转换为字节
    key_bytes = key.encode('UTF-8')
    intermediate_data_bytes = binascii.a2b_hex(intermediate_data)

    # 使用 DES 解密
    des_cipher = DES.new(key=key_bytes, mode=DES.MODE_ECB)
    decrypted_result = des_cipher.decrypt(intermediate_data_bytes).decode('utf-8')

    # 从解密结果中提取有效的 JSON 字符串并返回
    json_end_index = decrypted_result.rindex('}') + 1
    json_string = decrypted_result[:json_end_index]
    return json.loads(json_string)

最后测试了一下两种解密方案调用速度,Python调用的速度确实要远高于扣下来的JS臃肿代码,所以说当JS代码过多的时候,能够实现加解密的逻辑可以尝试实现算法,不然直接调用JS的话效率会比较低下,附运行图如下所示:

Python解密算法
在这里插入图片描述

JS混淆原生算法
在这里插入图片描述

  好了,到这里又到了跟大家说再见的时候了。创作不易,帮忙点个赞再走吧。你的支持是我创作的动力,希望能带给大家更多优质的文章

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习——决策树(三)

【说明】文章内容来自《机器学习——基于sklearn》,用于学习记录。若有争议联系删除。 1、案例一 决策树用于是否赖床问题。 采用决策树进行分类,要经过数据采集、特征向量化、模型训练和决策树可视化4个步骤。 赖床数据链接:https://pan…

12.26ARM作业

三个按键中断&#xff0c;控制对应灯亮灭 main.c #include "key_it.h"void delay(int ms){int i,j;for(i0;i<ms;i){for(j0;j<2000;j);}}int main(){all_led_init();key1_it_config();key2_it_config();key3_it_config();while(1){printf("do main...\n&…

认识K8S的基本概念和原理

K8S&#xff1a;Kubernetes8个字母省略就是k8s 自动部署&#xff0c;自动扩展和容器化部署的应用程序的一个开源系统 k8s是负责自动化运维管理多个容器化程序的集群。是一个功能强大的容器编排工具。分布式和集群化的方式进行容器管理。 k8s的版本&#xff1a;1.15或1.18。使…

【Hive_05】企业调优1(资源配置、explain、join优化)

1、 计算资源配置1.1 Yarn资源配置1.2 MapReduce资源配置 2、 Explain查看执行计划&#xff08;重点&#xff09;2.1 Explain执行计划概述2.2 基本语法2.3 案例实操 3、分组聚合优化3.1 优化说明&#xff08;1&#xff09;map-side 聚合相关的参数 3.2 优化案例 4、join优化4.1…

K8s攻击案例:组件未授权访问导致集群入侵

K8s集群往往会因为组件的不安全配置存在未授权访问的情况&#xff0c;如果攻击者能够进行未授权访问&#xff0c;可能导致集群节点遭受入侵。比较常见的的组件未授权访问漏洞&#xff0c;主要包括 API Server 未授权访问、kubelet 未授权访问、etcd 未授权访问、kube-proxy 不安…

实现 Spring Boot 项目热重载,无需重启,省时省力

实现 Spring Boot 项目热重载&#xff0c;无需重启&#xff0c;省时省力 插件介绍 通过 JRebel 插件 实现 Spring Boot 项目热重载。类似于前端 Webpack 热重载功能 无需重启&#xff0c;省时省力 安装插件 打开 IDEA 在插件中搜索&#xff1a;JRebel 安装成功后重启IDEA …

总结js中遍历对象属性的方法

方法介绍 1、 forin循环&#xff1a;遍历对象自身的和原型链上的可枚举属性。 2、Object.getOwnPropertySymbols()方法&#xff1a;返回一个数组&#xff0c;包含对象自身的所有Symbol类型的属性。 3、 Object.getOwnPropertyNames()方法&#xff1a;返回一个数组&#xff0…

【51单片机系列】DS1302时钟模块

本文是关于DS1302时钟芯片的相关介绍。 文章目录 一、 DS1302时钟芯片介绍二、DS1302的使用2.1、DS1302的控制寄存器2.2、DS1302的日历/时钟寄存器2.3、片内RAM2.4、DS1302的读写时序 三、SPI总线介绍四、DS1302使用示例 一、 DS1302时钟芯片介绍 DS1302是DALLAS公司推出的涓流…

【验证概括 SV的数据类型_2023.12.18】

验证概括 验证的过程是保证芯片实现符合规格说明书&#xff08;Specification&#xff0c;spec&#xff09;的过程 验证的两项任务&#xff1a; RTL sim&#xff1a;前仿真&#xff0c;验证功能 GLS-Gate (Level Simulation)&#xff1a;后仿真&#xff0c;验证功能和时序 验…

html table+css实现可编辑表格

要实现可编辑的 HTML 表格&#xff0c;你可以使用 JavaScript 和 HTML5 的 contenteditable 属性。 <!DOCTYPE html> <html> <head><style>table {border-collapse: collapse;width: 100%;}th, td {border: 1px solid black;padding: 8px;text-align:…

基于采样的自动驾驶规划算法 - PRM,RRT,RRT*,CL-RRT

本文将讲解PRM&#xff0c;RRT&#xff0c;RRT*自动驾驶规划算法原理&#xff0c;不正之处望读者指正 0 前言 机器人运动规划的基本任务&#xff1a;从开始位置到目标位置的运动 &#xff08;1&#xff09;如何躲避构型空间出现的障碍物 &#xff08;2&#xff09;如何满足机器…

SpringBoot集成opencc4j实现繁体中文转为简体中文

背景 繁体中文转为简体中文的需求非常常见&#xff0c;特别是在中文语境下的文本处理和翻译应用中。有很多现成的工具和库可以实现这个功能&#xff0c;比如 OpenCC 、 HanLP 等。从网上下载的 MySQL 版诗词数据库中的诗词数据都是繁体字&#xff0c;这里使用 SpringBoot 集成…

echarts自定义鼠标移上去显示,自定义图例,自定义x轴显示

提示&#xff1a;记录一下echarts常用配置,以免后期忘记 1.自定义鼠标移上去效果 tooltip: { show: true, trigger: "axis", axisPointer: { type: "shadow",//默认自定义效果 }, // //自定义鼠标移上去效果 formatter: (v) > { console.log("打印…

【Git-IDEA】在 IDEA 中使用 Git(clone、pull、push、merge、建立本地分支与远程分支的连接)

【Git-IDEA】在 IDEA 中使用 Git&#xff08;clone、pull、push、merge、建立本地分支与远程分支的连接&#xff09; 1&#xff09;Gitee2&#xff09;配置 Git3&#xff09;初始化本地仓库4&#xff09;连接远程仓库5&#xff09;clone5.1.方式一5.2.方式二 6&#xff09;分支…

C++面试宝典第10题:绘制各种图形

题目 我们需要编写一个图形相关的应用程序,并处理大量图形(Shape)信息,图形有矩形(Rectangle)、正方形(Square)、圆形(Circle)等种类。应用程序需要计算这些图形的面积,并且可能需要在某个设备上进行显示(使用在标准输出上打印信息的方式作为示意)。 (1)请使用面…

网安面试三十道题(持续更新)

91 mof提权 ## 是mysql的提权方式&#xff0c;在Linux下不能用&#xff0c;就是利用了 c:/windows/system32/wbem/mof/目录下的nullevt.mof文件&#xff0c;每分钟都会在一个特定的时间去执行一次的特征 sql语句&#xff1a; ## 通过shell上传这个文件&#xff0c;通过sql语句写…

利用Milvus Cloud和LangChain构建机器人:一种引人入胜且通俗易懂的方法

一、引言 机器人已经深入我们的日常生活&#xff0c;从家庭服务到工业生产&#xff0c;再到医疗和运输等领域。然而&#xff0c;这些机器人往往需要复杂的算法和数据处理技术才能有效地执行任务。在这个过程中&#xff0c;人工智能&#xff08;AI&#xff09;和机器学习&#…

uniapp 手持弹幕全端实现(微信/QQ小程序 + APP)

见下述效果图,本文话少纯干货 代码实现 <template><view class="main"

网络编程第五天

IO多路复用实现TCP服务器和客户端 运行结果&#xff1a; select实现TCP服务器&#xff1a; #include <myhead.h>#define PORT 8888 //1024~49151 #define IP "192.168.170.126" //ifconfig本机IPint deal_cli_connect(int sfd,struct sockaddr_in *pcin…

【产品经理】Axure原型工具教程

笔记为项目总结笔记&#xff0c;若有错误欢迎指出哟~ Axure原型工具教程 Axure简介原型图分类常用操作常用原件常用交互母版常用设备分辨率 Axure简介 Axure是一款专业的原型设计与交互设计软件&#xff0c;可以帮助用户快速创建高保真的原型和交互设计。Axure支持多种常见的交…