声明

本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除!

逆向目标

  • 目标:Steam 登录

  • 主页:aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2lu

  • 接口:aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2luL2RvbG9naW4v

  • 逆向参数:

    Form Data:

    password: MzX419b8uvaNe//lkf+15sx6hnLD/L1BX......
    captchagid: 5718995253934681478
    rsatimestamp: 374533150000

逆向过程

抓包分析

来到 Steam 的登录页面,随便输入一个账号密码登录,抓包定位到登录接口为 aHR0cHM6Ly9zdG9yZS5zdGVhbXBvd2VyZWQuY29tL2xvZ2luL2RvbG9naW4v ,POST 请求,Form Data 里,donotcache 是 13 位时间戳,密码 password 被加密处理了,captchagidrsatimestamp 不知道是什么,captcha_text 是验证码:

我们注意到登录请求的上面,还有一个 getrsakey 的请求,很明显和 RSA 加密有关,应该是获取 key 之类的参数,可以看到其返回值类似于:

{
"success":true,
"publickey_mod":"b1ae3215684fd66207415e39810dcbda75c143dc8c4497994db51591ed5bd17dbaf75e1e......",
"publickey_exp":"010001",
"timestamp":"288093900000",
"token_gid":"c304e76a58481ad12"
}

这里可以发现登录请求的 rsatimestamp 参数就是这里的 timestamp,其他参数在后面会用到。

XHR 断点定位

本次案例我们使用 XHR 断点来定位加密的位置,首先了解一下什么是 XHR,XHR 全称 XMLHttpRequest,XHR 可以在不重新加载页面的情况下更新网页、在页面已加载后从服务器请求、接收数据,是 Ajax 的基础,属于 Ajax 特殊的请求类型,利用浏览器控制台可以过滤 XHR 请求。

既然是 XHR 断点,那么这种方法就只能用于 XHR 请求,这也是这种方法的缺点,通过 XHR 断点,定位到的位置通常在加密处理完成之后,已经准备发送请求了,这样的优点是我们可以跟踪栈,能比较容易地找到加密的地方。

XHR 断点定位有两种方法,第一种是找到发送请求的 URL 之后,截取 URL 的一部分,在 Source 面板下,右侧 XHR/fetch Breakpoints 里添加你截取的 URL,如下图所示,已成功断下:

第二种方法,在 Network 面板,点击 XHR 过滤 XHR 请求,在 Initiator 项里可以看到调用的 JS,鼠标移到 JS 上,可以看到调用栈,点击第一个,即可进入到发送请求的地方,定位到的位置和第一种方法是一样的。这种方法需要注意的是,XHR 过滤不一定准确,但是只要是 Initiator 项里可以看到 JS,就说明可以跟进去进行调试,如果是通过 Form 表单或者其他方式发送的请求,Initiator 项会显示 Other,此时就不能通过这种方法进行调试。

参数逆向

前面 XHR 的两种方法,无论使用哪一种,定位到的位置都是一样的,查看右侧 Call Stack,即调用栈,一步一步往上查看调用的函数,在 login.js 里面,可以找到语句 var encryptedPassword = RSA.encrypt(password, pubKey);,非常明显的 RSA 加密:

可以关键代码改写一下,方便本地调试:

function getEncryptedPassword(password, results) {
var pubKey = RSA.getPublicKey(results.publickey_mod, results.publickey_exp);
password = password.replace(/[^\x00-\x7F]/g, '');
var encryptedPassword = RSA.encrypt(password, pubKey);
return encryptedPassword
}

找到加密的位置后,就可以埋下断点,取消 XHR 断点,重新进行调试,可以看到 results 就是前面 getrsakey 请求返回的数据:

RSA.getPublicKeyRSA.encrypt 分别是 rsa.js 里面 RSA 的 getPublicKeyencrypt 方法:

将整个 rsa.js 复制下来进行本地调试,会提示 BigInteger 未定义,鼠标放上去会看到是用到了 jsbn.js 里面的方法,如果一个一个函数去扣的话会比较麻烦,直接将整个 jsbn.js 文件代码复制下来即可:

完整代码

GitHub 关注 K 哥爬虫,持续分享爬虫相关代码!欢迎 star !https://github.com/kgepachong/

以下只演示部分关键代码,不能直接运行!完整代码仓库地址:https://github.com/kgepachong/crawler/

JavaScript 加密关键代码架构

navigator = {};

var dbits;

// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary & 0xffffff) == 0xefcafe); // (public) Constructor
function BigInteger(a, b, c) {} // return new, unset BigInteger
function nbi() {} // am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment. // am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i, x, w, j, c, n) {} // 此处省略 N 个函数 var RSAPublicKey = function ($modulus_hex, $encryptionExponent_hex) {}; var Base64 = {}; var Hex = {}; var RSA = {}; function getEncryptedPassword(password, results) {
var pubKey = RSA.getPublicKey(results.publickey_mod, results.publickey_exp);
password = password.replace(/[^\x00-\x7F]/g, '');
var encryptedPassword = RSA.encrypt(password, pubKey);
return encryptedPassword
} // 测试样例
// var results = {
// publickey_exp: "010001",
// publickey_mod: "b1c6460eb07d9a6a9de07e2d7afbbe36f30b7196a4a13b7f069e8bc6be3217fe368df46ee506ad4bbaf4190a13d3937b7cc19d081fa40c3cb431d94956804b2c80aad349fa9f95254c899d905aaaab54e7bbe95159b400fde541ec6828df76f0c7a226b38651853f6cdc67dc46e7fc3253d819e0ece8aae8551a27ebbb9f8a579ba1c4f52b69fc6605c8e11b0c00e32043c7675e268815f491be48ee644670d2d632077f8ff09d7a4928e5187d6e33279760f23b0b72a4e2928154f87326e5a57541b91862b3916e4972313ad764608d9628793eef3a0a8dcdd1ab6b908d32f56f830262fd33ed6b441e6b1e0c945508461e9c083cb10d8069f9539ca70fdd33",
// success: true,
// timestamp: "370921200000",
// token_gid: "3d1df3e102d1a1d2"
// }
//
// console.log(getEncryptedPassword("12345678", results))

Python 登录关键代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*- import time import execjs
import requests
from PIL import Image index_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
login_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
get_rsa_key_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
render_captcha_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler'
refresh_captcha_url = '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler' headers = {
'Host': '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler',
'Origin': '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler',
'Referer': '脱敏处理,完整代码关注 GitHub:https://github.com/kgepachong/crawler',
'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
session = requests.session() def get_cookies():
response = session.get(url=index_url, headers=headers)
cookies = response.cookies.get_dict()
print(cookies)
return cookies def get_captcha(cookies):
# 首先获取 gid
data = {'donotcache': str(int(time.time() * 1000))}
refresh_captcha_response = session.post(url=refresh_captcha_url, data=data, cookies=cookies, headers=headers)
gid = refresh_captcha_response.json()['gid'] # 携带 gid 获取验证码
params = {'gid': gid}
render_captcha_response = session.get(url=render_captcha_url, params=params, cookies=cookies, headers=headers) with open('code.png', 'wb') as f:
f.write(render_captcha_response.content)
image = Image.open('code.png')
image.show()
captcha = input('请输入验证码: ')
return captcha, gid def get_rsa_key(username, cookies):
data = {
'donotcache': str(int(time.time() * 1000)),
'username': username
}
response = session.post(url=get_rsa_key_url, data=data, cookies=cookies, headers=headers).json()
print(response)
return response def get_encrypted_password(password, rsa_key_dict):
with open('encrypt.js', 'r', encoding='utf-8') as f:
steampowered_js = f.read()
encrypted_password = execjs.compile(js).call('getEncryptedPassword', password, rsa_key_dict)
print(encrypted_password)
return encrypted_password def login(username, encrypted_password, cookies, rsa_key_dict, captcha, gid):
data = {
'donotcache': str(int(time.time() * 1000)),
'password': encrypted_password,
'username': username,
'twofactorcode': '',
'emailauth': '',
'loginfriendlyname': '',
'captchagid': gid,
'captcha_text': captcha,
'emailsteamid': '',
'rsatimestamp': rsa_key_dict['timestamp'],
'remember_login': False,
# 'tokentype': '-1'
}
print(data)
response = session.post(url=login_url, data=data, cookies=cookies, headers=headers)
print(response.text) def main():
username = input('请输入登录账号: ')
password = input('请输入登录密码: ') # 获取 cookies
cookies = get_cookies() # 获取验证码和 gid
captcha, gid = get_captcha(cookies) # 获取 RSA 加密所需 key 等信息
rsa_key_dict = get_rsa_key(username, cookies) # 获取加密后的密码
encrypted_password = get_encrypted_password(password, rsa_key_dict) # 携带 用户名、加密后的密码、cookies、验证码等登录
login(username, encrypted_password, cookies, rsa_key_dict, captcha, gid) if __name__ == '__main__':
main()

【JS 逆向百例】XHR 断点调试,Steam 登录逆向的更多相关文章

  1. 异步加载的JS如何在chrome浏览器断点调试?

    我们常常利用chrome强大的控制台Sources下面进行代码断点调试,但是通过$.getScript等异步加载JS的方式在Sources里面就是找不到,那如何进行debug断点调试呢? 方案一: 在 ...

  2. 谷歌浏览器遇到js报错自动进行断点调试,如何关闭

    转载于csdn 附地址 http://blog.csdn.net/microcosmv/article/details/60793882 留备用.

  3. Visual Studio Code 调试 nodejs (断点调试、自动重启、进程调试)

    学习链接: https://cnodejs.org/topic/5a9661ff71327bb413bbff5b https://github.com/nswbmw/node-in-debugging ...

  4. js断点调试心得

    虽然网上已经有多的数不清的调试教程了,但仍然没有发现哪篇文章写的通俗易懂,索性自己尝试写写自己的一些使用习惯或者说是心得,希望对那些还不是很懂得使用断点调试的孩子有一些帮助(大神请无视~). 1.断点 ...

  5. Chrome JS断点调试技巧

    Chrome调试折腾记_(2)JS断点调试技巧 技巧一:格式化压缩代码 技巧二:快速跳转到某个断点的位置 技巧三:查看断点内部的作用范围[很实用] 技巧4:监听事件断点 技巧5:DOM及 XHR监听跳 ...

  6. 【调试】JS断点调试

    1.断点调试是啥?难不难? 断点调试其实并不是多么复杂的一件事,简单的理解无外呼就是打开浏览器,打开sources找到js文件,在行号上点一下罢了.操作起来似乎很简单,其实很多人纠结的是,是在哪里打断 ...

  7. JS 断点调试心得

    1.断点调试是啥?难不难? 断点调试其实并不是多么复杂的一件事,简单的理解无外呼就是打开浏览器,打开sources找到js文件,在行号上点一下罢了.操作起来似乎很简单,其实很多人纠结的是,是在哪里打断 ...

  8. JS断点调试,必备的javaScript的debug调试技巧

    1.断点调试是啥?难不难? 断点调试其实并不是多么复杂的一件事,简单的理解无外呼就是打开浏览器,打开sources找到js文件,在行号上点一下罢了.操作起来似乎很简单,其实很多人纠结的是,是在哪里打断 ...

  9. 使用VSCode 断点调试 js项目,html页面

    一.效果目的 1.在VSCode里,直接F5打开html页面,并且可以在编辑器里,进行断点调试js代码: 二.工具准备 1.VSCode 软件 2.一个js项目 3.VSCode 上装一个插件:Deb ...

  10. 浏览器断点调试js

    说了一些 Chrome 开发者工具的技巧,其实并没有涉及到开发者工具最核心的功能之一:断点调试.断点可以让程序运行到某一行的时候,把程序的整个运行状态进行冻结.你可以清晰地看到到这一行的所有的作用域变 ...

随机推荐

  1. 在探索的道路上持续“做对”,火山引擎A/B测试成为这家企业数字基建

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群   最近,乐刻的"百城万店"战略在行业激起了许多讨论.在传统健身馆经营承压.服务业难标准化的语 ...

  2. Solon 开发进阶,四、启动参数说明

    Solon 开发进阶 一.插件扩展机制 二.体外扩展机制 三.常用配置说明 四.启动参数说明 五.全局异常订阅 启动参数,在应用启动后会被静态化(为了内部更高效的利用).比如,想通过体外扩展加载配置, ...

  3. SpringBoot Admin OFFLINE

    java.util.concurrent.TimeoutException message Did not observe any item or terminal signal within 100 ...

  4. C++11实用特性1

    1 原始字面量 有时候在输出一个路径字符串时,编译器会将其中的部分内容识别成转义字符进行输出,可以用R "xxx(原始字符串)xxx"其中()两边的字符串可以省略.原始字面量R可以 ...

  5. c#-微软

    1)使用c#编写第一个程序简介: c#编程语言允许你构建多种类型的应用程序,例如: 用于捕获,分析和处理数据的业务应用程序 可从Web浏览器访问的动态Web应用程序 2D和3D游戏 金融和科研应用程序 ...

  6. AtCoder Beginner Contest 163 (6/6)

    比赛链接:Here AB水题, C - management 题意:给一棵 \(N(2\le N\le2e5)\)​ 个节点的有根树,求每个节点的儿子数. 思路:由于输入直接给的是每个节点的父节点,直 ...

  7. jdk1.8(java8)新特性

    借鉴之:https://blog.csdn.net/qq_28410283/article/details/80601495 Lambda 表达式: //1.Lambda 表达式,也可称为闭包,它是推 ...

  8. vue 文件路径获取文件名

    例如: url 是//resource//20220819//kfz//调试.zip转换后结果为 调试.zip//文件路径获取文件名 getFileName(url) { let name = &qu ...

  9. JVM简单概述

    一.内存模型&分区 Java虚拟机在运行Java程序时,会管理着一块内存区域:运行时数据区 在运行时数据区里,会根据用途进行划分为以下模块: 1.Java虚拟机栈 2.本地方法栈 3.Java ...

  10. Kafka 面试题

    1. 为什么要使用 Kafka,为什么要使用消息队列 1.使用消息队列的目的: 服务解耦 流量削峰 异步通信 在早期的 web 应用程序开发中,当请求量突然上来了时候,我们会将要处理的数据推送到一个队 ...