JS逆向进阶篇【去哪儿旅行登录】【下篇-逆向Bella参数JS加密逻辑Python生成】

目录:

  • 每篇前言:
  • 引子——本篇目的
  • 1、 代码混淆和还原
    • (1)单独替换:
    • (2)整个js文件替换:
  • 2、算法入口分析
  • 3、 深入分析
      • (0)整体分析:
      • (1)_0x4dd553:
      • (2)_0x15c356:
      • (3)_0x4fb8ac:
      • (4)_0x34877a:
      • (5)_0x5ad2bb:
      • (6)_0xc21476:
      • (7)_0x34c54c:
      • (8)至此,结束:
  • 4、Python还原算法
  • 5、整体测试

每篇前言:

  • 🏆🏆作者介绍:【孤寒者】—CSDN全栈领域优质创作者、HDZ核心组成员、华为云享专家Python全栈领域博主、CSDN原力计划作者

  • 🔥🔥本文已收录于爬虫进阶+实战系列教程专栏:《爬虫进阶+实战系列教程》
  • 🔥🔥热门专栏推荐:《Python全栈系列教程》、《爬虫从入门到精通系列教程》、《爬虫进阶+实战系列教程》、《Scrapy框架从入门到实战》、《Flask框架从入门到实战》、《Django框架从入门到实战》、《Tornado框架从入门到实战》、《前端系列教程》。
  • 📝​📝本专栏面向广大程序猿,为的是大家都做到Python全栈技术从入门到精通,穿插有很多实战优化点。
  • 🎉🎉订阅专栏后可私聊进一千多人Python全栈交流群(手把手教学,问题解答); 进群可领取Python全栈教程视频 + 多得数不过来的计算机书籍:基础、Web、爬虫、数据分析、可视化、机器学习、深度学习、人工智能、算法、面试题等。
  • 🚀🚀加入我一起学习进步,一个人可以走的很快,一群人才能走的更远!

在这里插入图片描述

引子——本篇目的

在中篇其实已经通过补浏览器环境的方式破解出了bella参数,但是如果补浏览器环境不行的时候该怎么办呢?
这就得一点点扣js了!
所以,本篇文章来扣一波!
逆向分析出bella参数的加密算法,全部基于Python来实现!!!

1、 代码混淆和还原

分析这个js文件,有很多形如_0x5a69('0x0')_0x5a69('0x1')这样的代码,其实这些最终得到的就是一个个对应的字符串。

很入门级的代码混淆,这时候要做的就是尝试解混淆,将这个js文件中这样的代码替换为对应的字符串,这样看着就会直观了!
在这里插入图片描述

上述红框部分就是代码混淆的实现逻辑;而下述整个try部分就是整个加密逻辑。

【很明显,这些最终的字符串,即上图红框中那个大数组中的值,都经过base64加密~】

(1)单独替换:

解这种混淆也非常简单,我们直接执行对应的函数,如_0x5a69('0x0')就可以得到其对应的字符串:
在这里插入图片描述

在这里插入图片描述
pyexecjs执行:
在这里插入图片描述

(2)整个js文件替换:

只需要处理整个try部分代码(放到js3.js文件中)即可~

import re

import execjs

with open('js2.js', 'r') as f:
    JS = execjs.compile(f.read())
    res = JS.call("_0x5a69", "0x0")
    print(res)

with open("js3.js", 'r', encoding='utf-8') as f1, open("js4.js", 'w', encoding='utf-8') as f2:
    for line in f1:
        match_list = re.findall(r"_0x5a69\(.*?\)", line)
        if not match_list:
            f2.write(line)
            continue
        for mt_str in match_list:
            try:
                # arg = str(re.findall(r"\('(.*?)'\)", mt_str)[0])
                arg = mt_str[9: -2]
                line = line.replace(mt_str, f'"{JS.call("_0x5a69", arg)}"')
            except Exception as e:
                pass

        f2.write(line)
        print(line.strip())

上述代码try的作用:

  • 这个js代码这么多,上述简单的替换逻辑肯定会有报错的时候,但是报错又没事,何必一个个扣报错,然后完善逻辑呢?
    等下面具体分析这部分js代码的时候,如果分析到了未替换成功的代码块,手动单独替换下不更方便吗?

2、算法入口分析

直接分析处理后的js文件即可~
在这里插入图片描述
在这里插入图片描述

分析,形参_0x29235d接收的是slideToken:
在这里插入图片描述
但是上一个调用栈其实是传过来了两个参数:
在这里插入图片描述
这就是JavaScript的一个神奇之出,当传给js函数的参数多于js函数的形参时并不会报错,而且在函数内部可以通过arguments加索引的方式取得对应的参数:

var _0x5d0961 = arguments["length"] > 0x1 && arguments[0x1] !== undefined ? arguments[0x1] : '';

所以上述这个三元运算就会赋值{v: 2}_0x5d0961

继续分析:
在这里插入图片描述
执行try的时候,第一句delete删除参数_0x29235d中的Bella,第二句_0x398212(_0x29235d, _0x5d0961)就是生成Bella的函数;
如果捕获到异常,就直接返回_0x2e61cb,而这是个固定值,我直接将Bella写死为这个值,也是没问题的!
【为啥会有这种奇怪的代码,直接写死就可以,这不就是个漏洞吗?
这是因为核心加密逻辑可能需要读取各种浏览器参数,而浏览器千千万,有些浏览器也许不适配,那么就会报错,但是不能影响到用户使用,所以就有了这个try】
下面深入剖析Bella的加密逻辑:

3、 深入分析

(0)整体分析:

在这里插入图片描述
分析都在注释:
在这里插入图片描述

在这里插入图片描述

(1)_0x4dd553:

var _0x4dd553 = _0x318499(_0x481ca8);

入参是:{slideToken: "80ed997a295a75f57e6d8cbe88d77a8b"}
结果是:

{
    "bParam": "80ed997a295a75f57e6d8cbe88d77a8b",
    "keyArray": [
        "slideToken"
    ]
}

(2)_0x15c356:

var _0x15c356 = (0x0, _0x5b06ff["default"])();

解读:
var _0x15c356 = _0x5b06ff["default"]();

对应js函数:

function _0x9beb5a() {
            var _0x2bb251 = document[_0x5a69('0xf9')](_0x5a69('0xfa'));
            var _0x4ba4c2 = _0x2bb251 && _0x2bb251[0x0] && _0x2bb251[0x0][_0x5a69('0xfb')];
            var _0x5a5787 = document['getElementsByName'](_0x5a69('0xfc'));
            var _0x4030ec = _0x5a5787 && _0x5a5787[0x0] && _0x5a5787[0x0][_0x5a69('0xfb')];
            var _0x5a2ba8 = document[_0x5a69('0xfd')];
            return [{
                'key': _0x5a69('0xfe'),
                'value': document['referrer'][_0x5a69('0xb7')]('?')[0x0][_0x5a69('0xd4')](/^http(s)?:\/\//, '')[_0x5a69('0xff')](0x0, 0x14) || ''
            }, {
                'key': _0x5a69('0x100'),
                'value': _0x89ccfe()
            }, {
                'key': _0x5a69('0x101'),
                'value': window['shirley'] || _0xd6c5e7[_0x5a69('0x8a')]['string']
            }, {
                'key': _0x5a69('0xfd'),
                'value': _0x5a2ba8 && _0x5a2ba8['slice'](-0xa) || _0xd6c5e7[_0x5a69('0x8a')][_0x5a69('0xcb')]
            }, {
                'key': _0x5a69('0xfa'),
                'value': _0x4ba4c2 && _0x4ba4c2[_0x5a69('0x1f')](-0xa) || _0xd6c5e7[_0x5a69('0x8a')]['string']
            }, {
                'key': _0x5a69('0xfc'),
                'value': _0x4030ec && _0x4030ec[_0x5a69('0xff')](0x0, 0x14) || _0xd6c5e7['default'][_0x5a69('0xcb')]
            }, {
                'key': _0x5a69('0x102'),
                'value': window[_0x5a69('0xd0')][_0x5a69('0x102')][_0x5a69('0xd4')](/^http(s)?:\/\//, '')[_0x5a69('0xff')](0x0, 0x14) || _0xd6c5e7[_0x5a69('0x8a')][_0x5a69('0xcb')]
            }, {
                'key': _0x5a69('0x103'),
                'value': _0x1916ab() || _0xd6c5e7[_0x5a69('0x8a')][_0x5a69('0xcb')]
            }];
        }

在这里插入图片描述

在这里插入图片描述
分析piccolo:

在这里插入图片描述

在这里插入图片描述
生成_0x113a55中随机16个字符拼接而成的字符串~

此参数结果:

[
    {
        "key": "referer",
        "value": ""
    },
    {
        "key": "piccolo",
        "value": "5879##420347A09BF3F723##1708962151096"
    },
    {
        "key": "shirley",
        "value": "unknown"
    },
    {
        "key": "title",
        "value": "unknown"
    },
    {
        "key": "keywords",
        "value": "unknown"
    },
    {
        "key": "description",
        "value": "unknown"
    },
    {
        "key": "host",
        "value": "user.qunar.com"
    },
    {
        "key": "scriptSrc",
        "value": [
            "qimgs.qunarzz.co",
            "q.qunarzz.com/ho"
        ]
    }
]

(3)_0x4fb8ac:

此参数结果:

{
    "referer": "",
    "piccolo": "5879##420347A09BF3F723##1708962151096",
    "shirley": "unknown",
    "title": "unknown",
    "keywords": "unknown",
    "description": "unknown",
    "host": "user.qunar.com",
    "scriptSrc": [
        "qimgs.qunarzz.co",
        "q.qunarzz.com/ho"
    ]
}

(4)_0x34877a:

var _0x34877a = (0x0, _0xd98ca5["default"])();

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

生成_0x24b4d9中随机21个字符拼接而成的字符串~
此参数结果:

dWre6Dts7TLT1O2GkSeTZ

(5)_0x5ad2bb:

var _0x5ad2bb = _0x4dd553["bParam"] + _0x34877a + JSON["stringify"](_0x4dd553['keyArray']);

唯一注意的就是JSON["stringify"](_0x4dd553['keyArray'])的值是["slideToken"]

此参数结果:

3d8b8860db2bf9a6aef7f872979113dedWre6Dts7TLT1O2GkSeTZ["slideToken"]

(6)_0xc21476:

var _0xc21476 = _0x5625ac['default']["signature"](_0x5ad2bb);

在这里插入图片描述

很明显是将入参_0x5ad2bb使用sha1加密,最终将结果转换为十六进制字符串。
加密秘钥16进制表示为:
在这里插入图片描述
Python实现:

import binascii
import hmac
from hashlib import sha1

key_str = '68386673614b337771652b696f4d7673'
key = binascii.a2b_hex(key_str)
data = 'b363b3f511bf0e3e556c29cb56dcf4c9hLHNl1SVxE8UR2ONt1-N9["slideToken"]'

hmac_code = hmac.new(key, data.encode('utf-8'), sha1)
res = hmac_code.hexdigest()
print(res)

此参数结果:

df95bd703794a4cc96e611da8a53b87f2aadcb40

(7)_0x34c54c:

var _0x34c54c = (0x0, _0x38cd63["default"])(JSON["stringify"](_0x4fb8ac), _0x3d0fe0["default"]);

等价于:

var _0x34c54c = _0x38cd63["default"](JSON["stringify"](_0x4fb8ac), _0x3d0fe0["default"])
  • 函数第一个参数:
    将上述得到的_0x4fb8ac序列化;
  • 函数第二个参数:
    多次测试为固定值:
[
    "B6F1YrNm+OA=sw",
    "n8xbeLlzQ",
    "p5M02SUHt/dog",
    "cyfj-9kPKu",
    "EX7VWaqJi",
    "3CIGDRhTv4"
]

分析此函数:
在这里插入图片描述
看看_0x21da9c函数:
在这里插入图片描述
内部又调用_0x2d06ba函数,先来看看第一个参数:

_0x1414cd["charCodeAt"]()['toString'](0x2)

获得传进来的参数的unicode编码,toString后得到对应二进制字符串。

在这里插入图片描述
多次调试可发现_0x2d06ba函数的作用就是对传进来的第一个参数左边补0达到八位。

继续看_0x26cf34函数:
在这里插入图片描述
在这里插入图片描述
上述使用js列表的some方法,会对列表中的每个值执行给定函数,但凡有一个返回True则整体为True。

window["location"]["href"]
获取浏览器当前url。

再.include("x")就是判断浏览器当前url中是否含有x。

在这里插入图片描述

(8)至此,结束:

拓展:js字符串和数组拼接知识点~
在这里插入图片描述

在JavaScript中,当你使用 + 运算符连接一个字符串和一个数组时,数组会被转换为字符串,并与原始字符串连接。这个过程被称为字符串拼接。

在上图中,"GuHanZhe" 是一个字符串,["Cool"] 是包含一个字符串元素的数组。当它们相加时,JavaScript会将数组转换为字符串,然后进行字符串拼接。这导致了结果 'GuHanZheCool'

4、Python还原算法

import binascii
import hmac
import json
import random
import string
import time
from hashlib import sha1
from urllib.parse import quote_plus


def hmac_sha1(data_string):
    key_str = "68386673614b337771652b696f4d7673"
    key = binascii.a2b_hex(key_str)
    # data_string = 'b363b3f511bf0e3e556c29cb56dcf4c9hLHNl1SVxE8UR2ONt1-N9["slideToken"]'
    hmac_code = hmac.new(key, data_string.encode('utf-8'), sha1)
    return hmac_code.hexdigest()


def _0x26cf34(_0x4800e6, _0x572efb):
    _0x5ee954 = ['qunar', 'tujia']
    _0x403118 = "B6F1YrNm+OA=sw"
    _0x501116 = "n8xbeLlzQ"
    _0x3b5454 = "p5M02SUHt/dog"
    _0x251b31 = "cyfj-9kPKu"
    _0x222d7d = "EX7VWaqJi"
    _0x47c310 = "3CIGDRhTv4"

    _0x3af887 = "".join(_0x572efb) + (_0x403118 + _0x501116 + _0x3b5454 + _0x251b31 + _0x222d7d + _0x47c310);
    _0x3af887 = list(_0x403118 + _0x501116 + _0x3b5454 + _0x251b31 + _0x222d7d + _0x47c310)

    _0x4800e6 = quote_plus(_0x4800e6)
    while len(_0x4800e6) % 0x3 != 0x0:
        _0x4800e6 += '\x20'

    _0x933810 = [format(ord(i), "0>8b") for i in _0x4800e6]

    _0x2e4d85 = []
    for i in range(0, len(_0x933810), 3):
        end_index = min(i + 3, len(_0x933810))
        group = _0x933810[i: end_index]
        _0x2e4d85.append(group)

    _0x2a779b = ''
    for _0x28ee6c in _0x2e4d85:
        _0x572061 = "".join(_0x28ee6c)
        _0x351bb8 = []
        for j in range(0, len(_0x572061), 6):
            end_idx = min(j + 6, len(_0x572061))
            _0x351bb8.append(_0x572061[j: end_idx])

        new_0x351bb8 = []
        for _0x2a9721 in _0x351bb8:
            _0x2a9721 = "".join(["0" if char == "1" else "1" for char in _0x2a9721])
            idx = int(_0x2a9721, base=2)
            part = _0x3af887[idx]
            new_0x351bb8.append(part)

        _0x2a779b += "".join(new_0x351bb8)
    return _0x2a779b


def run():
    slide_token = "d8cc3d473f4d895b3af483e122f1ddc061ccb250"

    _0x4dd553 = {"KeyArray": ["slideToken"], "bParam": slide_token}

    total_string = string.digits + string.ascii_letters
    random_string = "".join([random.choice(total_string) for _ in range(16)])

    _0x4fb8ac = {
        "referer": "",
        "piccolo": f"{random.randint(1000, 9999)}##{random_string}##{int(time.time() * 1000)}",
        "shirley": "unknown",
        "title": "unknown",
        "keywords": "unknown",
        "description": "unknown",
        "host": "user.qunar.com",
        "scriptSrc": ["qimgs.qunarzz.co", "q.qunarzz.com/ho"]
    }

    total_string = '-_zyxwvutsrqponmlkjihgfedcba9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA'
    _0x34877a = "".join([random.choice(total_string) for _ in range(21)])

    _0x5ad2bb = slide_token + _0x34877a + json.dumps(_0x4dd553['KeyArray'])

    _0xc21476 = hmac_sha1(_0x5ad2bb)

    _0x4fb8ac["sign"] = _0xc21476
    _0x4fb8ac["randomNum"] = _0x34877a
    _0x4fb8ac["t"] = int(time.time() * 1000)

    _0x572efb = [
        "B6F1YrNm+OA=sw",
        "n8xbeLlzQ",
        "p5M02SUHt/dog",
        "cyfj-9kPKu",
        "EX7VWaqJi",
        "3CIGDRhTv4"
    ]
    _0x34c54c = _0x26cf34(json.dumps(_0x4fb8ac, separators=(',', ':')), _0x572efb)

    june_v = "1683616182042"
    _0x112026 = june_v + '##' + _0xc21476 + '##' + _0x34c54c + '##' + _0x34877a + '##' + 'slideToken'
    print(_0x112026)


if __name__ == '__main__':
    run()

在这里插入图片描述

5、整体测试

很简单~

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

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

相关文章

前后端分离项目Docker部署指南(上)

目录 前言 一.搭建局域网 1.搭建net-ry局域网,用于部署若依项目 2.注意点 二.安装redis 创建目录 将容器进行挂载 ​编辑 测试是否安装成功 ​编辑 三. 安装MySQL 创建文件夹 上传配置文件并且修改 .启动MySQL容器服务 充许远程连接 四.部署后端 使用…

linux 交叉编译curl(+openssl)

一、交叉编译openssl 参考博客:点击跳转 二、交叉编译curl 1、源码下载 地址:点击跳转 2、配置 CPPFLAGS"-I/home/gui/gui/openssl/build_arm/include" LDFLAGS"-L/home/gui/gui/openssl/build_arm/lib" LIBS"-ldl" \ …

Android之Handler原理解析与问题分享

一、Handler运行原理剖析 1.关系剖析图 如果把整个Handler交互看做一个工厂,Thread就是动力MessageQueue是履带Looper是转轴Loooper的loop方法就是开关,当调用loop方法时整个工厂开始循环工作,处理来自send和post提交到MessageQueue的消息&a…

使用Javassist 在android运行时生成类

序言 最近在写框架,有一个需求就是动态的生成一个类,然后查阅了相关文献,发现在android中动态生成一个类还挺麻烦。因次把一些内容分享出来,帮助大家少走弯路。 方案一 DexMaker DexMaker 是一个针对 Android 平台的库&#xf…

游戏引擎用什么语言开发上层应用

现在主流的游戏引擎包括: 1、Unity3D,C#语言,优点在于支持几乎所有平台 丹麦创立的一家公司,现已被微软收购。在中国市场占有率最高,也是社群很强大,一般解决方案也能在网上找到,教程丰富。物理…

.md转pdf

1、使用vscode安装Markdown PDF Markdown PDF 打开预览转pdf,同目录下自动生成pdf文件

稀碎从零算法笔记Day5-LeetCode:多数元素

题型:数组、计数、排序、STL函数、查找众数 链接:169. 多数元素 - 力扣(LeetCode) 来源:LeetCode 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 题目描述 给定一个大小为 n …

30分钟做200多张报表的金蝶云星空BI方案来了

曾经一张报表都要做好久,但现在,200多张的BI数据分析报表只需30分钟就能完成!BI智能数据分析的高效性在这一刻具象化了。奥威-金蝶云星空BI方案,一套注册、下载、执行,即见效果的标准化BI数据分析方案。 30分钟&#…

【S32DS报错】-7-程序进入HardFault_Handler,无法正常运行

【S32K3_MCAL从入门到精通】合集: S32K3_MCAL从入门到精通https://blog.csdn.net/qfmzhu/category_12519033.html 问题背景: 在S32DS IDE中使用PEmicro(Multilink ACP,Multilink Universal,Multilink FX&#xff09…

智能驾驶规划控制理论学习06-基于优化的规划方法之数值优化基础

目录 一、优化概念 1、一般优化问题 2、全局最优和局部最优 二、无约束优化 1、无约束优化概述 2、梯度方法 通用框架 线性搜索 回溯搜索 3、梯度下降 基本思想 实现流程 ​4、牛顿法 基本思想 实现流程 5、高斯牛顿法 6、LM法(Le…

java数据结构与算法刷题-----LeetCode637. 二叉树的层平均值

java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 广度优先2. 深度优先 解题思路:时间复杂度O(n)&am…

网络基础(二)

目录 再谈"协议" 序列化 JSON 网络版计算器 HTTP协议 认识URL urlencode和urldecode HTTP协议格式 telnet指令 stat函数 struct stat类型 stringstream类型 wget指令 HTTP的方法 HTTP的状态码 传输层 再谈端口号 端口号范围划分 认识知名端口号(W…

深度学习_16_权重衰退调整过拟合

所谓过拟合即模型复杂度较高,但用于训练数据集过于简单,最后导致模型将过多无用渣质作为学习对象 这个在上篇 深度学习_15_过拟合&欠拟合 已经详细介绍,以下便不再赘述。 上篇提到要想解决过拟合现象可以试着降低模型复杂度&#xff0c…

Python web框架fastapi中间件与CORS详细教学

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 所属专栏:Fastapi 景天的主页:景天科技苑 文章目录 fastapi中间件与CORS1、中间件1.创建中间件方法2.中间件里面添加响应头…

抖音视频评论批量采集软件|视频下载工具

《轻松搞定!视频评论批量采集软件,助您高效工作》 在短视频这个充满活力和创意的平台上,了解用户评论是了解市场和观众心声的重要途径之一。为了帮助您快速获取大量视频评论数据,我们推出了一款操作便捷、功能强大的软件&#xff…

第一弹:Flutter安装和配置

目标: 1)配置Flutter开发环境 2)创建第一个Flutter Demo项目 Flutter中文开发者网站: https://flutter.cn/ 一、配置Flutter开发环境 Flutter开发环境已经提供集成IDE开发环境,因此需要配置开发环境的时候&#xf…

【STM32】STM32学习笔记-读写内部FLASH 读取芯片ID(49)

00. 目录 文章目录 00. 目录01. FLASH概述02. 读写内部FLASH接线图03. 读写内部FLASH相关API04. 读写内部FLASH程序示例05. 读写芯片ID接线图06. 读写芯片ID程序示例07. 程序示例下载08. 附录 01. FLASH概述 STM32F10xxx内嵌的闪存存储器可以用于在线编程(ICP)或在程序中编程(…

Spark中读parquet文件是怎么实现的

背景 最近在整理了一下 spark对Parquet的写文件的过程,也是为了更好的理解和调优Spark相关的任务, 因为对于Spark来说,任何一个事情都不是独立的存在的,比如说parquet文件的rowgroup设置的大小对读写的影响,以及parqu…

数据删除

目录 数据删除 删除员工编号为 7369 的员工信息 删除若干个数据 删除公司中工资最高的员工 Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 数据删除 删除数据就是指删除不再需要的数据 delete from 表名称 [where 删…

Java架构之路-架构应全面了解的技术栈和工作域

有时候我在想这么简单简单的东西,怎么那么难以贯通。比如作为一个架构师可能涉及的不单单是技术架构,还包含了项目管理,一套完整的技术架构也就那么几个技术栈,只要花点心思,不断的往里面憨实,总会学的会&a…
最新文章