首页 > 其他分享 >【JS逆向百例】某赚网 WebSocket 套 Webpack 逆向分析

【JS逆向百例】某赚网 WebSocket 套 Webpack 逆向分析

时间:2024-10-21 15:48:32浏览次数:7  
标签:function 逆向 exports return JS ws WebSocket 连接

7xC1bf.png

声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!

本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在公众号【K哥爬虫】联系作者立即删除!

前言

近期有粉丝私信,提到了某网站抓不到包的问题,之前还有不少新手粉丝提到不会 webpack。经过分析,发现这个网站属于 ws 协议,同时还是一个简单的 webpack,正好借此案例,解答粉丝们的疑惑:

7x4cGY.jpg

7xCPRc.png

逆向目标

  • 目标:某赚网

  • 地址:aHR0cDovL3dhcC54dXp1YW4uY24vIy9ob21l

抓包分析

进入首页,随机选择某个商品点击接单,然后进入订单列表,会发现有不同的订单在一直刷新,会发现 /GetPendingOrderStatus 这个数据包在不断的下发:

7x4Rv7.jpg

同时该数据是密文状态,经过断点调试发现这个接口并不是我们需要的,那么它的数据应该是从何而来呢?大胆猜测应该是走了不同的协议,因为这种实时获取数据的接口,一般大概率都是走的 ws 协议, 避免了每次通信都需要重新建立连接的开销。

再次刷新列表页,我们发现有一个 ws 通信的数据包 /api/market

7x4YnI.jpg

同时在消息接收里面,不断加载服务器给我们返回的消息,同样这个消息也是密文的状态,大胆猜测这个应该就是我们需要的列表数据:

7x4tLV.jpg

其中协议头中的参数,是 ws 特有的:

  • Upgrade: websocket:表明这是 WebSocket 类型请求;

  • Sec-WebSocket-Key:Sec-WebSocket-Key 则是用于握手协议的密钥,是 Base64 编码的 16 字节随机字符串。

    服务器的 WebSocket 回应如下:

    HTTP/1.1 101 Switching Protocols
    Connection: Upgrade
    Upgrade: websocket
    Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
    Sec-WebSocket-Origin: null
    Sec-WebSocket-Location: ws://example.com/
    

    服务器同样用 Connection 字段通知浏览器,需要改变协议。Sec-WebSocket-Accept 字段是服务器在浏览器提供的 Sec-WebSocket-Key 字符串后面,添加 RFC6456(http://tools.ietf.org/html/rfc6455)标准规定的 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 字符串,然后再取 SHA-1 的哈希值。浏览器将对这个值进行验证,以证明确实是目标服务器回应了 WebSocket 请求。

  • Sec-WebSocket-Location 字段表示进行通信的 WebSocket 网址。

完成握手以后,WebSocket 协议就在 TCP 协议之上,开始传送数据。

WebSocket 回顾

关于 WebSocket 在往期文章有过介绍,不过时间久远,可能部分新粉不知道:

协议对比

HTTP

  • 协议类型:无状态的请求-响应型协议。
  • 通信方式:单向通信,客户端发起请求,服务器返回响应。
  • 持久连接:支持通过 HTTP Keep-Alive 持久连接,允许在一个连接内进行多次请求和响应。

WebSocket

  • 协议类型:全双工通信协议。
  • 通信方式:双向通信,客户端和服务器可以随时发送消息。
  • 持久连接:使用单个 TCP 连接保持长时间连接,避免了每次通信都需要重新建立连接的开销。

连接方式对比

HTTP

  • 连接建立:每次请求需要建立一个新的连接。
  • 连接关闭:服务器在发送完响应后关闭连接,客户端需要为每个新请求重新建立连接。

WebSocket

  • 连接建立:初始连接通过 HTTP 建立,然后升级为 WebSocket 协议,连接建立后保持开放状态。
  • 连接关闭:连接可以由客户端或服务器随时关闭,且不需要为每次通信重新建立连接。

简单一句话总结,就是 WS 是长连接,HTTP 是短连接。

逆向分析

WebSocket 流程分析

再次刷新页面,按下 F12 选择 ws 选项,可以看到有关 WebSocket 协议的发包:

7x4zxL.jpg

进入堆栈查看,从第一个堆栈进入:

7x44zJ.jpg

我们找到 initWebSocket 与 websocketonopen 的地方:

7x4MQe.jpg

我们发现在调用 open 建立连接时,会向服务器发送一段 y.a.encryptDes(r()(t)) 数据,所以我们构造这个 ws 请求就必须完成里面加密参数的生成。

参数加密分析

上面分析可知,ws 向服务器发送了一段密文数据,该数据是由 y.a.encryptDes 生成的,所以我们需要将 y 函数导出,经过分析 y 是一个 webpack 打包的一个模块:

7x4fvP.jpg

在该处打上断点,重新刷新列表页,成功在此断住,还是进入 a 中将分发器扣下来,导出到 windows:

window = global;
!function(e) {
    var f = window.webpackJsonp;
    window.webpackJsonp = function(c, b, n) {
        for (var r, t, o, i = 0, u = []; i < c.length; i++)
            t = c[i],
            a[t] && u.push(a[t][0]),
            a[t] = 0;
        for (r in b)
            Object.prototype.hasOwnProperty.call(b, r) && (e[r] = b[r]);
        for (f && f(c, b, n); u.length; )
            u.shift()();
        if (n)
            for (i = 0; i < n.length; i++)
                o = d(d.s = n[i]);
        return o
    }
    ;
    var c = {}
      , a = {
        113: 0
    };
    function d(f) {
        if (c[f])
            return c[f].exports;
        var a = c[f] = {
            i: f,
            l: !1,
            exports: {}
        };
        console.log(f)
        return e[f].call(a.exports, a, a.exports, d),
        a.l = !0,
        a.exports
    }window.kk=d;
    d.e = function(e) {
        var f = a[e];
        if (0 === f)
            return new Promise(function(e) {
                e()
            }
            );
        if (f)
            return f[2];
        var c = new Promise(function(c, d) {
            f = a[e] = [c, d]
        }
        );
        f[2] = c;
        var b = document.getElementsByTagName("head")[0]
          , n = document.createElement("script");
        n.type = "text/javascript",
        n.charset = "utf-8",
        n.async = !0,
        n.timeout = 12e4,
        d.nc && n.setAttribute("nonce", d.nc),
        n.src = d.p + "static/js/" + e + "." + {
            0: "8400f3beade260525146",
        }[e] + ".js";
        var r = setTimeout(t, 12e4);
        function t() {
            n.onerror = n.onload = null,
            clearTimeout(r);
            var f = a[e];
            0 !== f && (f && f[1](new Error("Loading chunk " + e + " failed.")),
            a[e] = void 0)
        }
        return n.onerror = n.onload = t,
        b.appendChild(n),
        c
    }
    ,
    d.m = e,
    d.c = c,
    d.d = function(e, f, c) {
        d.o(e, f) || Object.defineProperty(e, f, {
            configurable: !1,
            enumerable: !0,
            get: c
        })
    }
    ,
    d.n = function(e) {
        var f = e && e.__esModule ? function() {
            return e.default
        }
        : function() {
            return e
        }
        ;
        return d.d(f, "a", f),
        f
    }
    ,
    d.o = function(e, f) {
        return Object.prototype.hasOwnProperty.call(e, f)
    }
    ,
    d.p = "/",
    d.oe = function(e) {
        throw console.error(e),
        e
    }
}({})

将我们所需的 u46b 模块找到,还是将断点断在 return e[f].call(a.exports, a, a.exports, d) 处,控制台输出 e["u46b "] 即可找到相关模块的位置。

自动扣 webpack 可以在分发器的位置断住,将分发器改写为:

window.code = '';
a=function (c) {
        if (f[c])
            return f[c].exports;
        var d = f[c] = {
            i: c,
            l: !1,
            exports: {}
        };
        console.log(c)
window.code += c + ':' + e[c] + ',\r\n'
        return e[c].call(d.exports, d, d.exports, a),
        d.l = !0,
        d.exports
    }

全部流程走完以后,复制 window.code 即可将全部模块导出,最后运行发现报错,提示语法错误:

7xC059.jpg

经过分析可知,源码中模块的命名存在不规范的情况,如果我们直接引用的话就会报语法错误:

7xCmI4.jpg
7xCL0h.jpg

那么我们自动扣 webpack 的脚本就需要修改一下,修改后代码如下:

window.code = '';
a=function (c) {
        if (f[c])
            return f[c].exports;
        var d = f[c] = {
            i: c,
            l: !1,
            exports: {}
        };
        console.log(c)
window.code += '"' + c + '"' + ':' + e[c] + ',\r\n'
        return e[c].call(d.exports, d, d.exports, a),
        d.l = !0,
        d.exports
    }

不懂的小伙伴可以参考往期文章,【JS逆向百例】某点数据逆向分析,多方法详解,最终效果如下:

7x4oP3.jpg

Python 实现 WebSocket 请求

创建 WebSocket 连接

import websocket

# 创建 WebSocket 连接
ws = websocket.WebSocket()
ws.connect("wss://example.com")

发送连接请求

import websocket

# 创建 WebSocket 连接
ws = websocket.WebSocket()
ws.connect("wss://example.com")

# 发送连接请求
ws.send("************************")

处理响应数据

import websocket

# 创建 WebSocket 连接
ws = websocket.WebSocket()
ws.connect("wss://example.com")

# 发送连接请求
ws.send("*************************")

# 接收和处理响应数据
while True:
    response = ws.recv()
    if response:
        print(response)
        # 在这里添加对响应数据的处理逻辑
    else:
        break

而我们这个案例向服务器 send 数据建立连接的时候需要发送密文数据,同时不断接收服务器返回的数据也是密文数据,所以我们需要在此基础上进行修改。

经过测试发现,当 send 内容错误,服务器依旧可以给我们返回数据,但是间隔时间很长,且数据内容都一致,没有列表数据:

7xC96j.jpg

只有完美构造 send 参数,我们才会接收到完整的数据,所以这可以用来检验我们的参数构造是否正确:

7xCTam.jpg

最终代码如下:

import execjs
import asyncio
import websockets

from loguru import logger

with open('pack.js', 'rb') as f:
    js = f.read().decode()
ctx = execjs.compile(js)

def des_decrypt(word):
    decode_word = ctx.call('des_decrypt', word)
    return decode_word

def des_encrypt(word):
    encode_word = ctx.call('des_encrypt', word)
    return encode_word

async def hello():
    url = "ws://脱敏处理"
    headers = {}
    async with websockets.connect(url, extra_headers=headers.items()) as websocket:
        encrypt_msg = Des_encode(word)
        # encrypt_msg="12345678900000000000000000000000"
        await websocket.send(encrypt_msg)
        while True:
            response = await websocket.recv()
            logger.success(f"密文:{response},明文:{des_decrypt(response)}")

asyncio.get_event_loop().run_until_complete(hello())

标签:function,逆向,exports,return,JS,ws,WebSocket,连接
From: https://www.cnblogs.com/ikdl/p/18489638

相关文章

  • 洛谷 P1197 [JSOI2008] 星球大战 做题记录
    我不会做摧毁,于是反着做,就变成了合并连通块,倒序加边即可,时间复杂度\(O((n+m)\alpha(n))\)。(大抵是吧点击查看代码#include<bits/stdc++.h>#definemem(aqwqawa,bqwqawa)memset((aqwqawa),(bqwqawa),sizeof(aqwqawa))#definem0(aqwqawa)memset((aqwqawa),0,sizeof(aqwqaw......
  • 探秘 Day.js:轻量级日期处理神器来袭
    Day.js:轻量级的日期处理库在JavaScript开发中,经常需要处理日期和时间。而Day.js就是一个强大且轻量级的日期处理库,它提供了简洁、直观的API,让日期处理变得轻松高效。本文将详细介绍Day.js是什么,以及如何安装和使用它。一、Day.js是什么Day.js是一个极简的JavaS......
  • 如何使用WebSockets
    使用WebSockets你需要遵循以下步骤:一、理解WebSockets与传统HTTP的差异;二、选择合适的库和框架;三、建立WebSocket服务器;四、构建WebSocket客户端;五、确保连接的安全性。在开始使用WebSockets前,我们首先需要明白其背后的设计理念和技术特点。一、理解WebSockets与传统HTTP的差......
  • Nuxt.js 应用中的 build:done 事件钩子详解
    title:Nuxt.js应用中的build:done事件钩子详解date:2024/10/21updated:2024/10/21author:cmdragonexcerpt:build:done是Nuxt.js的一个生命周期钩子,它在Nuxt应用的打包构建器完成运行后被调用。这个钩子为开发者提供了一个在构建过程结束后执行特定逻辑的机会,可......
  • 基于node.js+vue基于Android的星座运势查询系统(开题+程序+论文)计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于星座运势查询系统的研究,现有研究主要以网页形式为主,专门针对基于Android平台开发星座运势查询系统的研究较少。在国内外,星座文化都有一定的受众群体......
  • 基于node.js+vue化妆品网站的设计与实现(开题+程序+论文)计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于化妆品网站的设计与实现这一课题,现有研究主要集中在网站的视觉设计或部分功能模块方面,如化妆品网站设计模板的开发,以及特定功能(如产品展示、订购等)......
  • 基于node.js+vue河北省科技馆展品设备巡检系统的设计与实现(开题+程序+论文)计算机毕业
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于科技馆展品设备巡检系统的研究,现有研究多以科技馆的整体运营管理或个别展品的维护为主1。专门针对河北省科技馆展品设备巡检系统全面且系统的研究较......
  • 基于node.js+vue基于Android的学生考勤APP(开题+程序+论文)计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容一、选题背景关于学生考勤管理的研究,现有研究主要以传统方式(如纸质签到等)为主,专门针对基于Android平台开发学生考勤APP的研究较少。在国内外,虽然考勤管理一直是教育......
  • SpringBoot 使用WebSocket
    WebSocket是一种网络通信协议,提供了在单个TCP连接上进行全双工通信的能力。这意味着客户端和服务器可以同时发送和接收数据,而不需要等待对方的回应。SpringBoot提供了对WebSocket的自动配置和简化的编程模型,使得在SpringBoot应用程序中集成WebSocket变得相对简单。需要引入......
  • [实时计算flink]动态CEP中规则的JSON格式定义
    本文为您介绍CEP中规则的JSON格式相关信息。目标人群客户风控平台开发人员:对FlinkCEP较熟悉的平台研发人员应能快速学习本格式,并根据自身平台需求判断是否需要进一步封装。客户风控策略人员:只熟悉具体策略但缺乏Java经验的同学,在熟悉CEP概念的基础上,也可快速上手本格式的使......