【WEB安全-某云音乐爬取-反调试绕过,js调用栈回溯,web算法逆向】此文章归类为:WEB安全。
本文以某云音乐爬虫案例为基础,详细探讨了从工具准备到目标数据爬取的全过程,包括爬取目标的需求分析、关键数据的识别以及加密参数的解析和破解。在学习其他案例是发现很多爬虫文章都是莫名其妙的获得某些数据,不知道前因和后果,所以这里就讲逆向和调试的细节分享出来!包括利用源代码搜索、静态堆栈调用回溯、动态调试以及动态堆栈调用等技术手段,最终锁定目标加密函数的调用位置。
本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与文章作者无关,本文只做模拟不做任何真实对象的爬取!
在正式开始之前,先确认以下工具和环境已准备就绪:
新手在接触爬虫时最难也是最痛苦的阶段就是绕过反爬虫和反调试了,目前总结一下爬虫过程种遇到的主要反爬手段和难点技术,这里主要做汇总分析,想了解详细原理的可以移步相关链接!
下面是目前主流的反调试和反爬虫手段:
1.无限 Debugger 技术
这些方法的核心思想是通过动态插入 debugger
来中断代码执行,增加调试的复杂度。通常采用这些技术的组合与变形,使反调试手段更加隐蔽且有效。
debugger
语句。eval
创建 debugger
。Function
动态创建 debugger
。constructor
、eval
、setInterval
等,在原生功能中添加debugger。2.阻止开启浏览器开发者工具
通过检测浏览器的行为或使用工具强行禁用开发者工具,这些方法可以有效防止爬虫或调试行为,尤其在页面载入和交互过程中增加了监测和干扰。
F12
快捷键。disable-devtool
插件来强制禁用开发者工具。3.前端代码混淆技术
前端代码混淆技术通过减少代码可读性、隐藏关键数据、以及分发资源的方式,阻碍逆向工程和爬虫的成功率,提高了安全性和防护力度。
浏览器反爬虫手段汇总:
我们为什么要绕过反调试?因为原生浏览器才是最好的js调试环境和算法逆向工具!不然为什么这么多的网页都来进行反爬虫反调试?而且市面上的最主流的调试工具都是浏览器?而不像Linux,Windows逆向那样市面上存在五花八门各种各样的调试器?下面就来讲解绕过反调试技术的核心原理!
目前,反调试手段主要通过检测浏览器的行为来进行防护。然而,这些反调试代码通常是从服务器端传输到浏览器的,这意味着这些代码必须先到达浏览器才能执行。关键问题在于,在从服务器传输到浏览器的过程中,反调试代码无法被执行,但相关的流量依然可以被第三方的抓包工具截获并转发。下面是流量的流程交互图:
这是正常的浏览器和服务器交互:
这个是爬虫工作者一般的浏览器交互图:
因此,尽管开发者工具被禁用,但是由于反调试代码通常嵌入在 JavaScript 或 HTML 中,仍然可以通过抓包工具获取这些前端页面代码进行分析。我们可以使用中间的代理工具Charles,Fiddler,等抓包工具,替换相关的 JavaScript 和 HTML 代码,从而逐步一次次的绕过目标页面中的各种反调试手段。这样,就能利用浏览器自带的调试工具和抓包工具进行逆向分析和爬虫操作。这种方法有效地避开了反调试措施,助力爬虫技术的实施。
下面是相关案例,都可以通过替换原有js代码来进行绕过反调试:
在Web安全领域,反调试技术常被用于防止攻击者通过开发者工具对网页进行调试,从而保护网页中的敏感逻辑和代码不被轻易篡改或分析。然而,随着攻击者技术的不断提升,绕过这些反调试措施的方法也层出不穷。以下是对几种常见绕过反调试技术的策略进行理论化总结与拓展。
案例一展示了使用disable-devtool
插件阻止用户打开开发者工具的情况。针对此类反调试技术,攻击者和防御者可以采取以下策略:
eval
和Function
构造函数的反调试案例二展示了使用eval
或Function
构造函数执行无限debugger
语句的反调试技术。这些技术通常会导致代码难以阅读和调试。攻击者可以通过以下方式绕过:
eval
调用点。eval
或Function
调用的上下文。eval
和Function
:通过修改网页代码,将eval
和Function
替换为其他不触发反调试机制的函数调用方式。案例三展示了将反调试代码嵌入到开源框架(如jQuery)中的情况。这种策略增加了识别和绕过反调试措施的难度。攻击者可以采取以下策略:
实验网站:https://spiderbuf.cn/playground/h04
在实际操作中,攻击者需要综合考虑目标网页的复杂性、安全性以及自身的技术能力。以下是一些实用的替换策略:
在Web爬虫和数据抓取领域,通过抓取数据包来获取目标资源的URL是一种常见且有效的策略。以下是对这一策略的理论化总结与拓展,包括目标确定、数据包分析、关键数据识别以及自动化实现等方面。
首先,需要明确要抓取的目标网页和具体的资源(如音乐、视频、图片等)。在本案例中,目标是抓取某云音乐平台上的音乐资源URL。明确目标后,可以开始分析资源的加载方式,如通过Ajax请求加载资源等。首先找到要爬取的目标网页:
找到目标先确定单首音乐的爬取方式才可以确定完整的方法!
首先分析一下资源的加载方式:当我点击播放按钮后音乐开始播放!!由此可以得出某云的资源加载方式是ajax!
分析接下来的需求,先开启抓包然后筛选出需要的url资源目标链接!
开始抓包!!!本次并无任何反调试主要是算法逆向!!
使用浏览器开发者工具或第三方抓包工具(如 Wireshark、Fiddler)捕获网络数据包后,可通过分析请求 URL、方法和请求头筛选出与目标资源相关的 HTTP 请求,重点关注 XHR 数据包,其中通常包含以 JSON 格式传递的关键数据(如资源 URL)。
开启F12后在点击音乐播放就可以发现很多的数据包,由于这种音乐格式的数据包大部分都是通过json格式的数据中藏有mp4链接来传递数据,所以首先就是选中XHR这里的数据包进行分析!
分析 XHR 数据包的响应内容时,可通过预览响应快速定位关键字段,解析 JSON 格式数据提取目标资源 URL,并通过直接访问或工具测试验证 URL 的有效性。
一个个的手工看,可以发现一个我们需要的资源链接:
可以看见update,weblog,get,v1等等的HTTP的请求数据包,这些数据包中都有可能藏有关键数据,但是更多还是靠经验,没有经验的话就直接一个个看:
主要是看数据预览部分,来查看数据!我们就可以得到一个音频链接:http://m804.music.126.net/20241113011636/c53d40244287ed16507ad686b6a83e86/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/28481676823/4af4/3b82/de3c/082fc537ce73819afdeb6694703f398a.m4a
或者直接在媒体中也可以找到,但是我们是需要自动化,这样就算是拿到了也没用:
所以抓包到这里也就可以确定要分析的数据包了!目标url:https://music.163.com/weapi/song/enhance/player/url/v1
在XHR数据包中,需要仔细分析响应内容以识别关键数据。这通常涉及以下几个步骤:
在现代的 Web 应用程序中,为了防止爬虫和恶意攻击,开发者采用了各种加密与混淆技术来保护传输中的敏感数据。与传统的明文传输相比,加密的请求参数和复杂的加密逻辑能够有效增加爬虫抓取的难度,提高服务器安全性。本文将通过具体案例分析一个加密的 Web 请求,并探讨如何对其中的加密参数进行逆向分析。
csrf_token
、params
和encSecKey
是三个关键的请求参数。csrf_token
:通常用于防止跨站请求伪造(CSRF)攻击。它可能是一个随机生成的字符串,每次请求都会变化。params
和encSecKey
:这两个参数很可能包含加密后的数据。加密的目的可能是为了保护数据的机密性,防止被未经授权的用户或爬虫轻易获取。根据数据包分析出其中的关键点:
请求 URL 示例:
`https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=a440a8d7bdb5da6fa2c9..`.
从请求的结构可以看出,参数并不是直接明文传输,而是经过加密处理。这种做法有效地减少了爬虫直接访问数据的可能性,因为即便获取到了请求 URL 和参数,参数的值仍然需要解密才能获得真实信息。
csrf_token:
请求方式:POSTparams 和 encSecKey:
这两个参数显然是经过加密的,其中 params
应该是包含请求数据的加密字符串,而 encSecKey
很可能是加密过程中使用的密钥或中间结果。这种加密方式的目的是增强安全性,防止爬虫工具直接通过抓包获取有效数据。
在该请求中,你希望获取一条指定的音乐资源,但是并未能直接在 URL 中找到对应的唯一音乐 ID。传统上,网络请求通常是明文传输所需的数据(如音乐的唯一 ID),但是随着批量爬虫的增加,网站通常会采用加密措施来提高请求的复杂性,从而增加爬虫抓取的难度。
通过观察请求中的加密参数,我们可以推测,某些参数(如 params
和 encSecKey
)可能包含了关键的加密信息,其中可能包括音乐资源的唯一 ID。
通过获取该URL的三个请求参数,我们可以肯定这三个参数中就是由音乐的唯一id和一些参数加密而来的,所以我们要做的就是一步步逆向算法了!:
csrf_token: a440a8d7bdb5da6fa2c9d17ba16a3b91...
params: G2O0AhGQTQOP2fgkeXwkPg+F/zGvL...
encSecKey: 2dae29e53a8fd629e09d7fa6497f...
确定逆向目标:我们需要寻找这三个参数了解他们的加密方式和产生原理!
寻找参数加密位置是逆向工程的关键步骤,通常需结合多种方法。源代码分析虽繁琐,但在缺乏线索时仍有效。静态堆栈回溯可忽略无关调用,逐步定位加密函数。动态调试则通过运行时断点观察程序行为,筛选目标请求。一旦捕获目标,动态堆栈回溯能进一步确定加密算法位置。这些方法需根据实际情况灵活运用,以提高分析效率。寻找着写参数的加密位置有几种方法:
可以通过源代码搜索目标位置,这种方法虽然繁琐,但适用于没有明确思路的情况。具体步骤如下:
encrypt
、aes
、rsa
等。发现这些方法后可以一个个的下断点看我们请求的api到底停止在哪一个代码位置,这样比较麻烦,等于大海捞针,如果没有思路可以用这个方法,推荐第二个方法!
调用栈回溯,根据调用栈来寻找目标位置,这里俗语静态的堆栈调用:
这些函数的调用过程中就藏有我们所需要的加密算法:
XMLHttpRequest.send
等原生框架调用,专注于自定义函数调用。通过调用栈回溯,可以快速定位到可能包含加密算法的函数。例如:
send()
函数XMLHttpRequest.send() - Web API | MDN,即发送数据包的位置。在此处设置断点,可以进一步追踪目标参数的加密位置。Js前端动态调试锁定指定请求包发送时候的数据:
XMLHttpRequest.send
函数处设置断点。操作开始:
下一个断点在send函数我们可以发现,此时的url并不是我们要爬取的目标,而是其他api请求,所以我们需要一个个的放行这些我们不需要的api请求,找到我们的目标请求!刷新页面重新点击音乐播放按钮就可以一个个的筛选断点获取我们的目标api的js处理请求!
成功找到发送目标请求的数据包的上下文!!!可以开始下面的步骤了!
找到并分析目标API请求中的参数加密位置。静态的堆栈调用回溯和动态调试相结合,能够快速定位加密算法,提高逆向分析的效率。
找到之后就可以开始动态调试的栈回溯了!动态的堆栈调用回溯!
观察一下此时的参数,可以发现:
我们需要你逆向的参数已经被加密了也就意味着这个函数调用的上一层调用之中可能存在加密算法继续观看动态函数调用栈!
分别看每次调用时候的变量的值来判断是否存在加密算法:
发现这里的函数存在参数组装:
1 2 3 4 5 6 7 8 9 10 11 12 13 | b6f.bzu9l = function () { this .Az2x(); t6n.be6Y( "/api/song/enhance/player/url/v1" , { type: "json" , query: { ids: JSON.stringify([ this .cx7q.id]), level: DEFAULT_LEVEL, encodeType: DEFAULT_ENCODETYPE }, onload: this .bOl0x.f6b( this ), onerror: this .bOl0x.f6b( this ) }) } |
在上面其他的几次调用中都存在,这两个参数:
params: G2O0AhGQTQOP2fgkeXwkPg+F/zGvL...
encSecKey: 2dae29e53a8fd629e09d7fa6497f...
由此可以判断,t6n.be6Y
函数负责发送请求,并且参数 params
和 encSecKey
在此函数调用中生成。因此,可以推断 t6n.be6Y
函数中包含了实际的加密算法。
锁定算法位置并且动态调试暂停之后就可以开始分析相关参数了!
1 2 3 4 5 6 | ... e6c.data = j6d.cr7k({ params: bVj1x.encText, //目标参数的数值来源 encSecKey: bVj1x.encSecKey }) ... |
可以发现着就是我吗之前要寻找的加密参数,从上面的动态调试加上堆栈回溯可以成功找到这里
在这段代码中,e6c.data
被赋值为 j6d.cr7k
函数的返回值,该函数接收两个参数:params
和 encSecKey
。这两个参数正是我们在请求中看到的加密参数。
发现加密算法的位置,然后就可以开始动态调试了,不要被这些变量名吓到,直接开动态调试分析每一个变量的值的变化:
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 | t6n.be6Y = function (X6R, e6c) { var i6c = {} //定义一个变量 , e6c = NEJ.X({}, e6c) //更新e6c的值,但是通过动态调试发现他并没有修改任何值,逆向的时候直接跳过,其实作用就是格式化字符串当其实并无影响 , mv9m = X6R.indexOf( "?" ); //动态调试发现他是一个用来定位url链接?号的功能,作用不大 //发现并无任何复制操作,只是单纯的if判断所以直接跳过,也可以去控制台看下他们的值 if (window.GEnc && /(^|\.com)\/api/.test(X6R) && !(e6c.headers && e6c.headers[eu7n.BQ2x] == eu7n.Iv5A) && !e6c.noEnc) { if (mv9m != -1) { //发现这个mv9a永远都是不成立的而且跟url相关,但是我吗的url是固定的所以这个也没什么用 i6c = j6d.gT8L(X6R.substring(mv9m + 1)); X6R = X6R.substring(0, mv9m) } if (e6c.query) { //动态调试可知,这里也比较简单只是将e6c的值赋值给i6c, i6c = NEJ.X(i6c, j6d.fT8L(e6c.query) ? j6d.gT8L(e6c.query) : e6c.query) } if (e6c.data) { //同理 i6c = NEJ.X(i6c, j6d.fT8L(e6c.data) ? j6d.gT8L(e6c.data) : e6c.data) } i6c[ "csrf_token" ] = t6n.gV8N( "__csrf" ); //这部分解析在后面,这段是获取cookie中的值 X6R = X6R.replace( "api" , "weapi" ); //url替换,也和我们没关系,我们的url是固定的 e6c.method = "post" ; //赋值api请求的方法 delete e6c.query; //后面会有解释 //第三部分算法逆向,再继续动态调试可以发现bVj1x就是我们需要的东西encText,encSecKey两个参数的来源!!!! var bVj1x = window.asrsea(JSON.stringify(i6c), bse8W([ "流泪" , "强" ]), bse8W(RR7K.md), bse8W([ "爱心" , "女孩" , "惊恐" , "大笑" ])); e6c.data = j6d.cr7k({ params: bVj1x.encText, //目标参数的数值来源 encSecKey: bVj1x.encSecKey }) } //上面就已经把参数加密出来了,也就没必要分析后面了,直接分析关键加密就可以了! var cdnHost = "y.music.163.com" ; var apiHost = "interface.music.163.com" ; if (location.host === cdnHost) { X6R = X6R.replace(cdnHost, apiHost); if (X6R.match(/^\/(we)?api/)) { X6R = "//" + apiHost + X6R } e6c.cookie = true } cxW4a(X6R, e6c) } |
查看函数源码位置:
发现这个函数是从cookie中取出一个csrf的值
这里毫无疑问是最后一步了将请求头和参数进行组装
这里将e6c的query字段删除,但是前面已经把它的值赋给了i6c
可以在看看参数可以发现e6c越看越像一个缺少参数的http请求!!
所以毫无疑问后面的代码就是将参数传入进行加密了!
我们就可以得到最原始的参数了!
这些就是原始参数了
1. i6c: 1. csrf_token: "a440a8d7bdb5da6fa2c9d17ba16a3b91" 2. encodeType: "aac" 3. ids: "[1325905146]" 4. level: "standard"
经过算法加密后就成了,我吗需要的参数encText,encSecKey
目前得到的数据有:
"/weapi/song/enhance/player/url/v1"
1 2 3 4 5 6 | data = { "csrf_token" : "a440a8d7bdb5da6fa2c9d17ba16a3b91" , "encodeType" : "aac" , "ids" : "[1325905146]" , "level" : "standard" } |
最后就差算法加密了!!!
最后分析关键加密算法了!
1 | var bVj1x = window.asrsea(JSON.stringify(i6c), bse8W([ "流泪" , "强" ]), bse8W(RR7K.md), bse8W([ "爱心" , "女孩" , "惊恐" , "大笑" ])); |
可以分析一下者该函数的参数了,这个参数可以很清晰的发现一共传入了4个很奇怪的参数其实很简单,直接拿去js控制台跑一下就可以了!
成功得到所有参数:
JSON.stringify(i6c) '{"ids":"[1325905146]","level":"standard","encodeType":"aac","csrf_token":"a440a8d7bdb5da6fa2c9d17ba16a3b91"}' bse8W(["流泪", "强"]) '010001' bse8W(RR7K.md) '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7' bse8W(["爱心", "女孩", "惊恐", "大笑"]) '0CoJUm6Qyw8W8jud'
建议遇到这种东西的时候多敲几遍在控制台,用来确定是否是常数
得到最终加密函数的输入:
1 2 3 4 5 6 | var bVj1x = window.asrsea( '{"ids":"[1325905146]","level":"standard","encodeType":"aac","csrf_token":"a440a8d7bdb5da6fa2c9d17ba16a3b91"}' , //参数1 '010001' , //参数2 '00e0b509f6259df...2741d546b8e289dc6935b3ece0462db0a22b8e7' , //参数3 '0CoJUm6Qyw8W8jud' //参数 ); |
那么剩下的就是找asrsea函数了!,东盎太调试下个断点,然后鼠标移动到函数上就可以进行代码跳转了!
可以很清晰的发现我们找到目标了,把这部分的函数都提取出来:
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 | ! function () { function a(a) { var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" , c = "" ; for (d = 0; a > d; d += 1) e = Math.random() * b.length, //这里允许随机数,也就意味着这个加密算法中允许随机数的存在,也就是任意一个数都可以,那么直接自己固定一个数就可以了 e = Math.floor(e), c += b.charAt(e); return c } function b(a, b) { //这里将数据进行了AES加密! var c = CryptoJS.enc.Utf8.parse(b) , d = CryptoJS.enc.Utf8.parse( "0102030405060708" ) , e = CryptoJS.enc.Utf8.parse(a) , f = CryptoJS.AES.encrypt(e, c, { //密钥偏移数据模式都有了,那么我就可以进行算法逆向了,CBC模式的AES加密 iv: d, mode: CryptoJS.mode.CBC }); return f.toString() } function c(a, b, c) { //进行RSA加密,但是是大厂自己实现的一套加密,逆向有些难度,但是前面的随机字符也传了进来,前面可以随机,把随机固定下来,那么这个密码自然也就固定下来了! var d, e; return setMaxDigits(131), d = new RSAKeyPair(b, "" ,c), e = encryptedString(d, a) } function d(d, e, f, g) { var h = {} , i = a(16); //直接就可以将i的值定死了! return h.encText = b(d, g), h.encText = b(h.encText, i), h.encSecKey = c(i, e, f), h } function e(a, b, d, e) { var f = {}; return f.encText = c(a + e, b, d), f } window.asrsea = d, window.ecnonasr = e }(); |
上面就是我们获取到的加密函数了!简单的给函数注释一下在来分析!开始逐个函数分析:
1 2 3 4 5 6 7 8 | function a(a) { var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" , c = "" ; for (d = 0; a > d; d += 1) e = Math.random() * b.length, //这里允许随机数,也就意味着这个加密算法中允许随机数的存在,也就是任意一个数都可以,那么直接自己固定一个数就可以了 e = Math.floor(e), c += b.charAt(e); return c } |
类似这种函数可以发现它传入一个A然后和一个随机数进行操作在返回一个C,首先验证一下这个e = Math.random() * b.length
是不是真随机,在控制台多试几次!
试完之后可以发现着其实就是一个产生随机数的盒子,直接抽象函数为:
1 2 3 4 | function a(a) { seed(a) return randnum() } |
手动测试,在浏览器的控制台试试发现a(16)可以等于很字符串,随机字符串!
function b(a, b) { //这里将数据进行了AES加密! var c = CryptoJS.enc.Utf8.parse(b) , d = CryptoJS.enc.Utf8.parse("0102030405060708") , e = CryptoJS.enc.Utf8.parse(a) , f = CryptoJS.AES.encrypt(e, c, { //密钥偏移数据模式都有了,那么我就可以进行算法逆向了,CBC模式的AES加密 iv: d, mode: CryptoJS.mode.CBC }); return f.toString() }
直接使用了标准的AES加密直接上python脚本就行了!
动态调试可以发现这个AES算法在将数据输出的时候编码成了base64加密!
function c(a, b, c) {//进行RSA加密,但是是大厂自己实现的一套加密,逆向有些难度,但是前面的随机字符也传了进来,前面可以随机,把随机固定下来,那么这个密码自然也就固定下来了! var d, e; return setMaxDigits(131), d = new RSAKeyPair(b,"",c), e = encryptedString(d, a) }
1 2 3 4 5 6 7 8 | function d(d, e, f, g) { var h = {} , i = a(16); //直接就可以将i的值定死了! return h.encText = b(d, g), h.encText = b(h.encText, i), h.encSecKey = c(i, e, f), h } |
直接进行等效转换:
function d(d, e, f, g) { var h = {} var i = a(16); //产生一个随机数i h.encText = b(d, g) //调用b函数进行AES加密 h.encText = b(h.encText, i)//继续调用AES加密,得到encText h.encSecKey = c(i, e, f)//将数据在进行RSA加密,得到encSecKey return h //将最终数据返回 }
成功梳理完成整个加密流程!!!
有前面的分析我们可以很清楚的知道d函数传入的参数:
window.asrsea函数的参数其实大部分都是常数只是被代码混淆了,直接在控制台运行一下就可以解密了!
1 2 3 4 5 6 7 8 9 | .... window.asrsea = d .... var bVj1x = window.asrsea( '{"ids":"[1325905146]","level":"standard","encodeType":"aac","csrf_token":"a440a8d7bdb5da6fa2c9d17ba16a3b91"}' , //参数1 '010001' , //参数2 '00e0b509f6259df...2741d546b8e289dc6935b3ece0462db0a22b8e7' , //参数3 '0CoJUm6Qyw8W8jud' //参数 ); |
就是:
d( d, //'{"ids":"[132590514.....8d7bdb5da6fa2c9d17ba16a3b91"}' e, //'010001' f, // '00e0b509f6259df...2741d546b8e289dc6935b3ece0462db0a22b8e7' g //'0CoJUm6Qyw8W8jud' )
成功得到加密函数的输入参数,和加密函数所使用的算法,那么最后一步写python爬虫脚本就轻而易举了!
完整的代码这里就不放了下面的连接里面都有这里只做代码讲解:
def getData(self): d={"ids":"[%s]"%self.id,"level":"standard","encodeType":"aac","csrf_token":""} d=json.dumps(d) e = '010001' f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7' g = '0CoJUm6Qyw8W8jud' i = self.a() params = self.b(d, g) params = self.b(params, i) encSecKey = self.c(i, e, f) return {'params': params, 'encSecKey': encSecKey}
这个函数主要是讲得到的参数进行加密得到params,encSecKey用来发包!
1 2 3 4 5 6 7 8 9 10 11 12 | def b( self ,data,key): """ AES 加密 """ iv = b '0102030405060708' pad = 16 - len (data) % 16 data = data + chr ( 2 ) * pad aes = AES.new(key.encode(), AES.MODE_CBC, iv) tmp = aes.encrypt(data.encode()) result = base64.b64encode(tmp).decode() return result |
正常的AES
1 2 3 4 5 6 7 | def c( self ,a,b,c): """ RSA 加密 """ a = a[:: - 1 ] result = pow ( int (hexlify(a.encode()), 16 ), int (b, 16 ), int (c, 16 )) return format (result, 'x' ).zfill( 131 ) |
正常的RSA
1 2 3 4 5 6 7 8 9 | def a( self ,a = 16 ): """ 获取16位随机字符串 """ words = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" result = '' for i in range (a): result + = words[random.randint( 0 , len (words) - 1 )] return result |
生成指定长度的随机字符串
完成加密流程后,生成的 params
和 encSecKey
将作为请求参数,提交至目标服务器,最终成功获取到目标音乐资源。以下截图展示了实际抓包分析的结果:
注:某云特别懒三四年了都没更新加密算法,而且很多地方的加密算法都是同一个,所以可以去试试!
相关资料:
更多【WEB安全-某云音乐爬取-反调试绕过,js调用栈回溯,web算法逆向】相关视频教程:www.yxfzedu.com