Home 初始JS逆向-小试牛刀
Post
Cancel

初始JS逆向-小试牛刀

前言

终于,经过半个月的js学习,现在开始了JS逆向之路,按照计划本周也就是9月7号前需要完成一个JS逆向案例报告(接口分析),当然哈,这个计划可能会根据我的学习进度改变。在b站找了个小课看看,虽然老师花了15分钟卖课,但质量感觉还行,至少我可以跟着上手,后续如何尽情期待~

定义

学习之前,首先了解了一下JS逆向是什么,总结一下就是分析前端JavaScript代码,破解网站核心逻辑(数据加密、接口签名、动态渲染等)这样爬虫就可以模拟合法请求获取数据。 具体来说,一般学习的重点是接口参数加密,数据动态渲染,环境监测对抗,验证码逻辑破解,如果可以的话,将在之后的学习中针对上述几个点都进行练习(虽然目标是安卓逆向!)

学习重点

  • 通信方式
  • 签名生成
  • 消息摘要加密算法
  • 定位加密+算法还原
  • 实战

通信方式

本次实验的网站采用wss => websocket 动态 ajax 渲染 => 实时更新页面内容

  • 通信层:浏览器和服务器用 wss (WebSocket secure) 建立长连接,持续收发数据。
  • 展示层:前端用 动态 AJAX 渲染把这些实时数据更新到网页,不用整页刷新。

Websocket / wss

一种浏览器和服务器之间的全双工通信协议。

  • HTTP 通信是「一次请求对应一次响应」,完了就断开。
  • WebSocket 建立连接后,客户端和服务端可以互相主动发送消息,保持实时通信。

ws:// 与 wss://

  • ws://:明文的 WebSocket(类似 http://)。
  • wss://:加密的 WebSocket,基于 TLS/SSL(类似 https://)。

动态 ajax 渲染

AJAX(Asynchronous JavaScript and XML):浏览器端用来向服务器异步请求数据的方式。 页面不用整页刷新,只把新数据更新到页面上。

签名生成

比较典型的两类签名核心差异:

  • 参数签名(每次都需要更新):随请求参数变化而变。用于保证完整性(参数未被篡改)与身份/权限(握有密钥的一方才算合法)。

  • 时效性 code(基于时间戳):随时间窗口变化而变。用于保证时效性与防重放。

参数签名

(还没学待更新)

时效性 code(基于时间戳)

这类 code 只依赖时间相关信息(有时再加 nonce/deviceId),在一个时间窗口内有效,超时失效。

通用流程

  1. 取当前时间戳 ts(10 位秒或 13 位毫秒)。
  2. 按约定拼接原串(常见:ts + 固定串 + ts子串,或 method+path+ts+nonce 等)。
  3. 选择摘要算法(MD5/SHA256/HMAC-*)生成 code。
  4. 携带 ts(和 nonce)一起发给后端。
  5. 服务端校验:
    • now - ts<= 窗口(例如 30s/60s)
    • 选用相同算法重算比对
    • 可选:校验 nonce 未重复(防重放)

消息摘要加密算法

如何快速判断加密算法,可以根据算法特性判断:

  • md5 => 输出 32 位

  • sha1 => 输出 40 位

  • sha256 => 输出 64 位

  • hmacMD5 => 输出 32 位,需要 秘钥

实战定位加密+算法还原

先根据payload查找加密参数,这里用code举例

关键词搜索

可以搜索 code:,code=

调用堆栈分析

  1. 调试面板Initiator栈堆列
  2. 分区,断点
  3. 点击匿名堆栈断点,查看作用域,注意特殊控制流结构,查看相关函数和参数
  4. 还可以通过堆栈的文件地址
  5. 排除参数消失(加密)的地方
  6. 通过控制台逐个定位

hook技术

问题:一个数据在被加密之前,会做什么处理?

常见操作:

  • 转化为JSON字符串
  • JSON.stringify() => 加密前处理
  • JSON.parse() => 解密后处理

(AI)Mytoken实战

本次实战以MYtoken网站作为操作对象

网站是实时更新的,所以打开检查就可以到数据会刷新

点击网络可以看到面板有很多请求,点进去预览发现有很多请求

定位核心请求

  • 预览(Preview)里能直接看到页面上的列表数据:currency_id / name / symbol / rank …,而不是图片、css 或静态 aboutus.json。
  • URL 里有 currency_last_price、currencyranklist、pages=1、sizes=100、subject=market_cap 等明显的业务参数;
  • 滚动或切换页签后,这个请求才发出(瀑布图上时间点对应用户操作),符合“数据拉取”的触发时机。

点击发现确实里面有当前页面的100条数据(截图略)

右键这个请求->复制->https://curlconverter.com/->生成请求代码->复制到pycharm->运行代码->成功获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests

headers = {
    'accept': '*/*',
    'accept-language': 'zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6',
    'cache-control': 'no-cache',
    'origin': 'https://mytokencap.com',
    'pragma': 'no-cache',
    'priority': 'u=1, i',
    'referer': 'https://mytokencap.com/',
    'sec-ch-ua': '"Not;A=Brand";v="99", "Google Chrome";v="139", "Chromium";v="139"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'cross-site',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
}

params = {
    'pages': '3,1',
    'sizes': '100,100',
    'subject': 'market_cap',
    'language': 'en_US',
    'legal_currency': 'USD',
    'code': '0133da2e2af4f7085250f993d311ad75',
    'timestamp': '1756799034781',
    'platform': 'web_pc',
    'v': '0.1.0',
    'mytoken': '',
}

response = requests.get('https://api.mytoken.info/ticker/currencyranklist', params=params, headers=headers)

查看请求的载荷,发现code是一个32位的加密数,根据之前的技巧,推测可能是由MD5算法加密完成

定位code加密函数,搜索法得到的結果太多不易筛查,选择堆栈和hook方法

通过堆栈分区,debugger断点调试和加密参数分析得到结果,具体如图:

  • 断点作用域发现控制流语句(截图略)
  • 该堆栈之前断点查看发现code加密,查看密文什么时候变成明文,或者有单独的进行赋值
  • 将分析出来code加密语句发送给ai让其分析:code: s()(e + “9527” + e.substr(0, 6))
  • 让ai实现这个加密算法在windows11环境里有python3.9实现这个加密
  • 放入之前写好的请求代码里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#算法还原
# Wss =>websocket
# 动态ajax渲染 =>货币网站 => 实时更新签名:
#   签名:
#       时效性
#       每次都是需要新的 密文 =>明文(请求参数(分页参数))=>对应的JS代码进行加密
#       code:密文 =>明文(时间戳)=>对应的JS代码进行加密


import requests
import hashlib
import time
import argparse
from typing import Optional

def gen_code(e_str) :
    """
    生成签名 code:
      code = MD5( timestamp + "9527" + timestamp[:6] )
    :param ts_ms: 13位毫秒时间戳(字符串)。不传则用当前时间。
    :return: 32位小写hex MD5
    """

    raw = e_str + "9527" + e_str[:6]
    return hashlib.md5(raw.encode("utf-8")).hexdigest()

headers = {
    'accept': '*/*',
    'accept-language': 'zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6',
    'cache-control': 'no-cache',
    'origin': 'https://mytokencap.com',
    'pragma': 'no-cache',
    'priority': 'u=1, i',
    'referer': 'https://mytokencap.com/',
    'sec-ch-ua': '"Not;A=Brand";v="99", "Google Chrome";v="139", "Chromium";v="139"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'cross-site',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
}

# 获取当前13位时间按戳
e = int(time.time()*1000)
e_str = str(e)
code = gen_code(e_str)

params = {
    'pages': '4,1',#分页参数
    'sizes': '100,100',
    'subject': 'market_cap',
    'language': 'en_US',
    'legal_currency': 'USD',
    # 因为code在变化,所以现在要看一下code怎么来的
    'code': code,#变化密文 =>明文(时间戳)=>对应JS代码进行加密 (因为是时效性,所以只包含时间戳)
    'timestamp': '1756798889200',#13位时间戳
    'platform': 'web_pc',
    'v': '0.1.0',
    'mytoken': '',
}

print(params)
# response = requests.get('https://api.mytoken.info/ticker/currencyranklist', params=params, headers=headers)
# print(response.json())

hook方法使用了其他网站做示范,这里通过分析发现加密需要进行JSON.stringify转变,所以和ai对话:在浏览器控制台中编写一个hook脚本,当进行JSON。stringify转变就debugger 然后将代码复制到控制台中运行,就可以得到加密位置。

This post is licensed under CC BY 4.0 by the author.