js逆向系列——入门篇
对于偏好研究数据请求过程中协议破解的朋友而言,js逆向是必不可少的环节。本文简单介绍了逆向过程使用到的工具、逆向流程以及一些简单案例。
一、常用工具
| 名称 | 作用 | 用法 |
|---|---|---|
| chrome dev tools | 调试web程序 | 查看数据请求流程、调试js等 |
| postman | 接口测试 | 复制curl请求,自定义请求头、请求参数,模拟发包 |
| hashlib | 计算散列值 | python包,可计算MD5/SHA1/SHA256/SHA512/shake_128/... |
| Crypto | 加解密 | pip install cryptodome,AES/DES/3DES/RSA/... |
| curlconverter | curl转脚本 | curlconverter.com/ |
| jsnice | js反混淆 | www.jsnice.org |
| spidertools | 爬虫工具库 | spidertools.cn/ |
| decode_obfuscator | 解ob混淆 | tool.yuanrenxue.cn/decode_obfu… |
| JStillery | js反混淆 | mindedsecurity.github.io/jstillery/ |
| JSbeautifier | js反混淆 | beautifier.io/ |
二、逆向流程
1. 数据获取流程
一个直观的数据获取流程如下:
graph TD
创建socket --> 建立tcp连接 --> 构造请求头/payload --> 发送请求数据 --> 接收响应数据 --> 解析响应数据
2. 逆向流程
常规逆向流程包含如下步骤:
graph TD
发现加密参数or加密响应内容 --> 检索加密函数位置 --> 分析加密函数 --> 复现加解密逻辑 --> 模拟实际请求
(1)发现加密参数or加密响应内容
在构造请求头时,有时需要携带需要验证的参数或者payload,部分参数在客户端生成。
解析响应数据时,有时会发现原始的响应数据为密文。
(2)检索加密函数位置
包含关键词检索法和跟栈分析方法。
常用的关键词包含
- 加密参数key
- JSON.parse(
- decrypt(
- btoa(
- base64
- hmac
(3)分析加密函数
包含静态分析法和动态分析法。
(4)复现加解密逻辑
可以直接把相关的js代码扣下来,也可以根据加解密逻辑复现。
复现加解密逻辑后,可以直接构造加密参数,模拟请求;也可以解密密文形式的响应内容,得到原始数据。
(5)模拟实际请求
python运行js的方法包含:
- 使用pyexecjs库运行js
- 调用系统命令运行node
3. 数据获取流程与逆向流程对应关系
逆向与数据获取流程的关系,可以简单对应成如下形式
timeline
title 逆向流程与数据获取流程对应关系
创建socket
建立tcp连接
构造请求头/payload : 构造加密参数
发送请求数据 : 模拟实际请求
接收响应数据 : 检索加密函数位置
: 分析加密函数
: 扣包含加密逻辑的js代码
解析响应数据 : 解析加密响应内容
三、案例
1. aHR0cHM6Ly9sb2dpbjEuc2NyYXBlLmNlbnRlci8=
网页如下,是一个登陆页面
填写用户名密码,提交可以发现请求的数据包
通过POST方法,发送了一个token参数,但是token加密了。通过关键词token检索,可以定位到XHR发包位置。也可以跟栈,下断查看token生成位置。
token为e,e是this.form转字符串后传入c.encode生成。在此处下断
先查看传入参数,是我们填充的明文,再查看c.encode
分析之,encode函数包含e,r两个参数,此时只赋值了e,那么接下来的三目表达式跟到_encode(String(e)),e为之前传入的字符串形式的账密,此处再次做了字符串转换后,调用_encode函数处理字符串。
分析作用域,知buffer为undefined,此时_encode函数为btoa(utob(e))
utob是unicode转utf-8的函数,btoa是base64编码函数,由此确定该加密逻辑实际是将序列化的用户名和密码用base64编码,进而通过接口发送请求。
2. aHR0cHM6Ly93d3cuc3BpZGVyZGVtby5jbi9hdXRoZW50aWNhdGlvbi9wcm90b2J1Zl9jaGFsbGVuZ2UvP2NoYWxsZW5nZV90eXBlPXByb3RvYnVmX2NoYWxsZW5nZQ==
这是来自靶场的一道题目,网页如下
可以看到POST请求的payload以及响应内容均为密文
此处关注响应内容的解密。题目已经提示protobuf,且每次翻页前,都会请求challenge.proto文件,该文件定义了protobuf明文的数据结构
参考protobuf编码的文档【zhuanlan.zhihu.com/p/192089514…】,可以直接把响应内容复制到在线的protobuf解密工具【protobuf-decoder.netlify.app/】中,再自己编写proto文件解码。
下面看密文的payload,可以通过下xhr断点先找发包位置,再找加密位置
发包函数在一个严重混淆的js文件中,同时也可以在当前作用域中看到请求数据的明文内容。xhr发包函数传入的值finalBuffer是一个uint8array,这个就是编码的payload
由于在.proto数据结构文件中已经看到了ChallengeRequest的定义,那么此处直接构造ChallengeRequest,并测试生成的payload
接下来就是请求数据的构造,多次测试发现challengetype是定值,另外timestamp是时间戳,page为请求页码,实际只需要构造signature即可
格式化js,查看加密逻辑,但是由于js中的常量存在零宽、换行等字符,此处简单分析即可,不做反混淆,检索signature
可以清楚看到,先使用md_sign['OooO'](timestampStr)计算得到signature,然后赋值给requestData的signature字段。
下面复现md_sign['OooO']
代码量不是很多,可以直接在控制台打印函数,然后在本地node环境中测试。本题并没有针对node环境检测的部分,复现之后,测试生成的signature。
四、总结
主要介绍了js逆向的一般流程,从数据获取的过程中,针对加密的请求参数、响应内容,通过关键词检索、跟栈等手法能够跟进到加密逻辑的位置,进而手动构造出加密参数、响应解析函数。