微软tts 下载音频按钮(tampermonkey脚本)的实现思路
微软tts 下载音频按钮(tampermonkey脚本)的实现思路
目标网站
https://azure.microsoft.com/zh-cn/services/cognitive-services/text-to-speech/#features
- 目的是解决微软官方的网页版demo,不能直接下载转换后的MP3文件
效果
image-20220319101032760.png
适合阅读的人群
有一定的JS逆向基础的人群
- 会使用chrome开发者工具进行抓包调试和断点调试
如果只是想要实现
微软tts 下载音频
可以直接安装tampermonkey扩展后安装脚本即可,安装完成后会在声音合成后出现下载音频
的按钮,脚本地址https://greasyfork.org/zh-CN/scripts/441531-%E5%BE%AE%E8%BD%AFtts-%E4%B8%8B%E8%BD%BD%E6%8C%89%E9%92%AE
理解本篇教程你将会对以下知识点有更深刻的理解
- tampermokey 脚本的注入时机,需要尽可能早的注入时使用
@run-at document-start
- hook 的简单使用
- 函数或变量的导出,比如导出到
window
方便后续使用,JS扣代码常用 - 使用tampermokey 脚本 下载二进制文件的方法
- websocket的简单了解
0x01
首先在目标网站https://azure.microsoft.com/zh-cn/services/cognitive-services/text-to-speech/#features
点击几次播放
抓几次包发现以下的请求是用来传输合成的音频的
wss://eastus.tts.speech.microsoft.com/cognitiveservices/websocket/v1?Authorization=bearer%20eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJyZWdpb24iOiJlYXN0dXMiLCJzdWJzY3JpcHRpb24taWQiOiI2MWIxODBlMmJkOGU0YWI2OGNiNmQxN2UxOWE5NjAwMiIsInByb2R1Y3QtaWQiOiJTcGVlY2hTZXJ2aWNlcy5TMCIsImNvZ25pdGl2ZS1zZXJ2aWNlcy1lbmRwb2ludCI6Imh0dHBzOi8vYXBpLmNvZ25pdGl2ZS5taWNyb3NvZnQuY29tL2ludGVybmFsL3YxLjAvIiwiYXp1cmUtcmVzb3VyY2UtaWQiOiIvc3Vic2NyaXB0aW9ucy9jMjU1ZGYzNi05NzRjLTQ2MGEtODMwYi0yNTE2NTEzYWNlYjIvcmVzb3VyY2VHcm91cHMvY3MtY29nbml0aXZlc2VydmljZXMtcHJvZC13dXMyL3Byb3ZpZGVycy9NaWNyb3NvZnQuQ29nbml0aXZlU2VydmljZXMvYWNjb3VudHMvYWNvbS1zcGVlY2gtcHJvZC1lYXN0dXMiLCJzY29wZSI6InNwZWVjaHNlcnZpY2VzIiwiYXVkIjoidXJuOm1zLnNwZWVjaHNlcnZpY2VzLmVhc3R1cyIsImV4cCI6MTY0NzU5NDk1MCwiaXNzIjoidXJuOm1zLmNvZ25pdGl2ZXNlcnZpY2VzIn0.PjYuGtrhh9RgkaH3iPYwPxBwtUDaWCjIBT_iQbPJf7g&X-ConnectionId=F4D719576F88485997E628643E905B76
有两个参数Authorization
和X-ConnectionId
前者在网页的源码中,后者是uuid4
,因为我们用tampermonkey脚本所以不需要搞定这两个参数的生成
image-20220318171347035.png
这个还是get
请求,但该请求后改变传输协议为websockets
,发送需要转换的文本后,服务器返回了一堆Binary Message
(二进制消息),这些就是合成的声音。
image-20220318171615746.png
0x02
因为网页版会对合成的声音进行播放,所以前端肯定有对这些数据进行处理,我们只需要拿到前端处理完成的二进制文件就可以了。那问题就是处理后的二进制文件存哪里了?
可以进行简单的断点调试,没有思路就直接这个进去下一个断点,一步步往上找找看。
image-20220318172825273.png
在这里可找到websocket的onmessage
image-20220318204848429.png
这里l.provWebsocketClient
Websocket的一个实例
image-20220318205340359.png
继续往上追一层找到了每一次处理Binary消息的地方。
image-20220318204950954.png
0x03
但是这么找很难找到最后binary传输完成后的完整音频数据。
这里再换一个方法,可以看到websocket通讯的最后有一个含有Path:turn.end
的包,尝试在上面同一个js文件中搜一下 turn.end
然后下断点。
image-20220318215256773.png
可以发现定到了这里
image-20220318220501114.png
这里好多case 一开始以为是控制流平坦化,但并不是,而且变量名是有意义的,稍微仔细一点就可以看到这样一个synthesisCompleted
。
image-20220318220913937.png
而且有一个if的判断,synthesisCompleted
翻译过来是合成完成,打一个断点,确实是在websockets传输完成后会断住,里面的g.privAudioData
猜测就是合成的mp3。
image-20220318221113258.png
做一下实验,把这个二进制数据下载下载听一下,发现确实就是我们要的mp3
window.URL.createObjectURL(new Blob([g.privAudioData], { type: 'audio/mp3' }));