当前位置: 首页 > ai >正文

[JS逆向] 喜马拉雅登录案例

博客配套代码发布于github:喜马拉雅登录 (欢迎顺手Star一下⭐)

相关知识点:webpack 补环境

相关爬虫专栏:JS逆向爬虫实战  爬虫知识点合集  爬虫实战案例 逆向知识点合集


此案例目标为逆向成功对应的参数,并成功返回对应请求。

一、爬取工作准备

进入网址:喜马拉雅登录,再点击右上角那个小电脑,进入密码登录界面。

手机号输入123456789,密码输入123456,点开F12,再点击登录,就可以开始我们的表演了。

一看就知道这个就是我们要的数据,再看载荷:

能看出我们大致要破解的四个参数:account,nonce,password,signature.

老规矩,直接复制这个数据包到curl to python,并将其复制到py运行查看结果(步骤在以往JS逆向案例有,不懂的直接看往期操作即可)

可以看到运行结果应是这个。如果并非这个结果说明参数传递错误。

二、逆向入口分析

直接encrypt搜,搜到七个,分析发现只有下面四个长得像,依次打上断点。

只剩这俩地方断的住。

试着从第二个开始找,我们直接选择最下层的这个匿名,进去查看下:巧了不是,account,password,nonce都看到了,这儿还有这么完美的四大参数,不是这我吃。现在再挨个分析即可。

account:

account是n,n是→这里getEncryptPwd点进去的确可以进入到下层栈然后接着找,再补环境,但未免落了下成。往上再捋捋看看a的位置在哪。一直翻翻翻,终于看到:看到a=r(19),webpack与加载器,看到这就很开心了,后面可以直接一套公式流webpack过掉,轻轻又松松啊。

password:

password是i,i是→跟上面的account近乎一个样,也是webpack,处理近乎一致。

nonce:

nonce是t,t是→

呃,是传入参数。而且这个t还是个一直变动的值。找不到上下层堆栈。

别急:当你看到检测参数较多而且某个值似乎无法通过溯源找到适,可以看看其他的数据包:

果然,是从另一个数据包获得的nonce,那我们只需要通过单独向这个数据包发请求获得nonce即可。

signature:

siganture是e,e是→可以将其简化,即:

var e = a.getSignature({
                account: n,
                password: i,
                nonce: t
            })

乍一看,也是用了a加载器,直接像上面的账密一样...不行!

这里有个坑,而且非常隐蔽,笔者在这里就捣鼓了好久才知晓原因。

如果你用的是加载器的方式得到,它最后作为参数是错误的。原因极大概率是因为其获取方式与常规加载器的逻辑不同。这里说一下此处的分析思路应如何。

正常来讲是会去想到直接去用加载器,但用前一定先在控制台打印观察下。此处可以看到打印出来格式为哈希算法,则:尽可能别用加载器,加密算法的逻辑就单独用算法方式解决,两者获取可能会冲突。

点进a.getSiganture看对应层逻辑:

 

这里选中的是e.toUpperCase()),能看到它是一个很长的由上方拼接获取的字符

同时我们再把这一串打印进控制台:

发现没有:在同一时间线(同个ajax请求)下,获得的两个算法最终答案相近,但后者(加密算法)才对。

同时这里能通过分析哈希算法长度,即40位,基本确定其为sha1算法。

但还是有点不放心。我们这里是想直接用sha1的加密逻辑得到,但不确定它有没有加盐(魔改)算法,到SHA1加密网络转换器里,将e.toUpeerCase()的东西输入进去看下(记得去掉引号)

与上述答案一致。那么就可以放心直接用算法了,至此这个逻辑基本完成。

综上,所有参数分析完毕后可以开始我们的正式操作了。

三、破解逆向

1.补webpack

这段就老生常谈的公式了,尽量简化描述:

(如需更详细的步骤请参考我往期写的webpack处理公式)

选中加载器,刷新页面并进去里面,全选复制粘贴过来并简单处理掉里面的html元素,并在顶部写个window=global。再把刷新进去位置的函数(function i(r)),设置个console.log('r:::',r)与window.loader=i

最后再将传入代码写作:

一般如果webpack里是r('GSGX')这种还能依靠直接搜'GSGX'复制粘贴过来,但如果是像上图这种r(19)的webpack,就得通过源函数来进入了:

选中r(19):进入里面这个栈并全局复制到mod01即可(对应的是数组,不用分析半天究竟是谁)

缺这玩意儿就全局搜并全部复制(注意这个搜索的是var xx,切记把这个var去掉不然没法上升为全局变量),再给个mod02补上,并require过来(注意require的顺序):

看到这里就能确定webpack环节结束,接下来到补环境阶段。

2.补环境

却啥补啥,补到此时:

有点怪,好像不太好补。那就把我们的监控器递出来吧↓

(监控器具体用法在文章 [逆向知识] 补环境 -- 让本地逆向如鱼得水 处有讲,建议在这里看一看)

function setProxy(proxyObjArr) {for (let i = 0; i < proxyObjArr.length; i++) {const handler = `{get: function(target, property, receiver) {console.log("方法:", "get  ", "对象:", "${proxyObjArr[i]}", "  属性:", property, "  属性类型:", typeof property, ", 属性值:", target[property], ", 属性值类型:", typeof target[property]);return target[property];},set: function(target, property, value, receiver) {console.log("方法:", "set  ", "对象:", "${proxyObjArr[i]}", "  属性:", property, "  属性类型:", typeof property, ", 属性值:", value, ", 属性值类型:", typeof target[property]);return Reflect.set(...arguments);}}`;eval(`try {${proxyObjArr[i]};${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});} catch (e) {${proxyObjArr[i]} = {};${proxyObjArr[i]} = new Proxy(${proxyObjArr[i]}, ${handler});}`);}
}setProxy(['window', 'document','canvas'])

document补全,看到:

很明显知道canvas少了个toDataURL,加进去即可。

同样,其他补环境逻辑也大致相同,陆续为setProxy加东西即可,只有一个点要注意下:

大大的userAgent,这玩意儿就不要再传空值或空func,直接控制台打印个丢过来不好吗

这个点也是不太好处理,但只要全局搜就可以了,搜到发现隶属于document对象,仿写过来就行。同理,后面好几个环境也可以靠这样补。

最终成功补完没再报错,将setProxy的代理与之前console.log的r都注释掉,

这个是window对象,也补上环境就消失了,

最终输出:账户与密码的逻辑搞定。

3.py与js代码互传

首先我们要确定py与js互传的方式。分为execjs与subprocess。正好这里都会用到。execjs用于构建环境较简单,没有很多浏览器原生的bom/dom,api或更复杂的模拟环境这些;subprocess则用于如之前代码中复杂的环境模拟,最终输出。

(1) execjs

它的使用方式相较非常简单,直接传递即可。这里我们以siganture的获得举例

cryptoJs = require("crypto-js")function y(t) {var e = "", r = Object.keys(t).sort((function (t, e) {return (t = t.charCodeAt(0)) - (e = e.charCodeAt(0))})), n = r.length;return r.forEach((function (r, o) {var i = t[r];e += "".concat(r, "=").concat(i),o < n - 1 && (e += "&")})),e
}function getSignature() {var t = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}, e = y(t) + "&" + "WEB-V1-PRODUCT-E7768904917C4154A925FBE1A3848BC3E84E2C7770744E56AFBC9600C267891F";return e.toUpperCase(),cryptoJs.SHA1(e.toUpperCase()).toString()
}

这个signature在js中的构建逻辑,而在py中,我们只需要通过如下的代码即可获取对应js生成的代码。

js_compile = execjs.compile(open('sign.js',encoding='utf8').read())
data = {'account': enc_account,'password': enc_password,'nonce': nonce_val}
signature = js_compile.call('getSignature',data)

其中data是传递进去的参数,'getSignature'则是用于选择这个文件的这个函数。

(2) subprocess

其使用相较就复杂很多,这里也是分别放js与py文件对应代码:

function get_all(account, password) {function get_account() {return (0, a.getEncryptPwd)(account)}function get_pwd() {return (0, a.getEncryptPwd)(password)}return {account:get_account(),pwd:get_pwd(),}
}act = process.argv[2]
pwd = process.argv[3]
const ret = get_all(act,pwd)
res = JSON.stringify(ret)
console.log(res)process.exit()

在js文件中,通过process.argv获取传递过来的原始账密并传递给get_all,再生成后打印到控制台上。py文件再通过控制台的消息对应接收过来。本质上可以理解为双方是靠终端来接收各自的数据。

all_ = subprocess.check_output(f'node get_sign.js "{account}" "{password}"') # 分别获得传递
all_ = all_.decode('utf8').strip()
# 通过json传递取出对应值
js_data = json.loads(all_)
account = js_data['account']
pwd = js_data['pwd']

注意:这里我用的是json数据来互传。因为js传递过来的数据是个长字符串,用json格式才方便将其转化为字典格式以提取。

四、完整逻辑构建

如上,我们基本已完成了相关具体逻辑与所有代码的构建。

最终我们将其模块化打包输出:

if __name__ == '__main__':account = '123456789'password = '123456'cur_nonce = get_nonce()enc_account,enc_pwd = get_encrypt(account, password)signature = get_signature(enc_account,enc_pwd,cur_nonce)ret = login(enc_account,enc_pwd,cur_nonce,signature)print(ret)

答案获取成功,逆向完成。

由于代码量过大,本文并未将所有代码都放上来。但可以看我的github开源代码,来进一步全面分析。

📌 项目代码 + 后续案例合集 全部发布在 GitHub 仓库中,持续更新中,欢迎收藏!

http://www.xdnf.cn/news/14713.html

相关文章:

  • [面试] js手写题-树转数组
  • Objective-c把字符解析成字典
  • C语言常用转换函数实现原理
  • Docker 入门教程(九):容器网络与通信机制
  • React-Find 一款能快速在网页定位到源码的工具,支持React19.x/next 15
  • 【AI时代速通QT】第四节:Windows下Qt Creator调试指南
  • 【c/c++3】类和对象,vector容器,类继承和多态,systemd,stdboost
  • 「Java案例」输出24个希腊字母
  • 双指针的用法
  • Vue 3 Teleport 特性
  • 人工智能之数学基础:如何判断正定矩阵和负定矩阵?
  • 矩阵的逆 线性代数
  • LRU缓存设计与实现详解
  • Spring Cloud:服务监控与追踪的高级实践
  • C# 合并两个byte数组的几种方法
  • 零基础学习RabbitMQ(5)--工作模式(1)
  • C/C++数据结构之动态数组
  • ali PaddleNLP docker
  • vue-31(Nuxt.js 中的数据获取:asyncData和fetch)
  • XIP (eXecute In Place)
  • Spring AI Alibaba Nacos 集成实践
  • 【C++ 基础】 C++ 与 C 语言差异面试题(附大厂真题解析)
  • 【智能协同云图库】智能协同云图库第三弹:基于腾讯云 COS 对象存储—开发图片模块
  • 【Linux高级全栈开发】2.3.1 协程设计原理与汇编实现2.3.2 协程调度器实现与性能测试
  • 原型设计Axure RP网盘资源下载与安装教程共享
  • 【记录】服务器多用户共享Conda环境——Ubuntu24.04
  • 进阶向:Django入门,从零开始构建一个Web应用
  • Word之电子章制作——1
  • kubectl exec 原理
  • 力扣第73题-矩阵置零