本帖最后由 rodgersnow 于 2023-12-02 15:47 编辑
前言此次修炼一举突破到了练气三层初期!终于可以和日思夜想的三阶灵兽论道了(ヾ(≧▽≦*)o)。 注:所谓灵兽,便是纳天地日月精华修行(搜集各种资源),初通人意(无泛滥广告),后识文墨、知礼节(可以评论区求资源),修为渐深亦可化形(有 app),倘若臻至天阶更有一丝机缘激活血脉得远古神兽的传承(唉~远古神兽自然就是倒闭的网站了,再修炼下去,就要被不知名的神秘组织从修仙界抹除)。
此次论道的网站是 AGE 动漫:(base64 encode) aHR0cHM6Ly93d3cuYWdlZG0ub3Jn 。 郑重声明因为是与 灵兽 论道,为了保护灵兽,此次仅仅重点记录如何绕过最核心的 WebAssembly 加密与解密部分,其它部分将省略(其它的参数利用普通方法就能解决,只要花点耐心呐)。 当然,如何定位到加密的位置我依然会详细讲解的。
阅读本文时,如果有以下知识将更游刃有余: 如果大家对文中提到的一些概念都已经理解,但是文章看不懂,请一定要记住:这一定是我的问题,是我没有讲解清楚。毕竟每个人的经历、感悟不同,我在描述时难免不达意。
芳踪难觅先启动浏览器无痕模式,打开某一集动漫的播放页 (base64 encode) aHR0cHM6Ly93d3cuYWdlZG0ub3JnL3BsYXkvMjAyMzAyMTYvMS84 ,等待视频加载出来。 按照我的习惯,先利用审查元素看看视频所在的网页标签,发现视频在一个 <iframe> 标签中。关于此标签的作用、含义具体见文档 MDN - \<iframe\> HTML 标签,简单来说是网页之中嵌套了另一个网页。 这个最初的视频播放页链接我取名为:anime_paly_url ,即看动漫时所在的网页,以便后文讨论。 所以我直接将上图中的 <iframe> 标签的 src 属性值复制出来。这里我又要取名了,将这个链接称之为 anime_iframe_src ,即动漫播放页中的 <iframe> 标签的 src 值。 在无痕浏览器的新标签页中打开这个 anime_iframe_src !同样,等待视频加载出来,优先利用审查元素查看视频所在的标签。 这里我将上图中的动漫下载地址取名为 anime_video_src 。 现在需要确定上图中 <video> 的 src 属性值,也就是 anime_video_src 是否就存在于静态的 HTML 中,所以需要查看网页源代码……可是,这个网页已经被视频占据了,所以无法通过点击右键来选择 “看网页源代码” 的功能。 这里手动添加前缀: view-source:这里是 anime_iframe_src 的值 # 还记得 anime_iframe_src 这个名字吗
如下,可以看到网页的源代码了,并且确定视频的 <video> 标签、链接是动态生成的! 问:为什么不通过浏览器抓包界面来查看网页的源代码呢?? 答:具体见后文的说明,这一步其实和后续的操作有关。
注意!现在这种情况是: - 浏览器在某个时刻得到了视频的链接,它并没有用 JS 去请求。
- 是创建了
<video> 标签,然后把视频的链接写到了其 src 属性上! - 所以抓包分析、查看该视频的请求时,其
Initiator 是看不到结果的。
此地无银三百两按照我的习惯,现在需要快速浏览 HTML 源代码来查看可疑的内容。
在此之前我要说明一下为什么我要使用浏览器中的 "查看网页源代码" 功能,而不是在抓包工具中查看(之前的文章并没有提到这一点)。也算是我斗法的经验。 以西瓜视频为例,在抓包界面查看网页代码其实不利于快速定位数据。如下图所示: 但是在 “查看网页源代码” 中我可以大概、快速定位关键数据。 如下,像这种一大块白色字体挤在一起,中间的空白很少,并且由看不懂的数字、字母组成,那么极大概率就是关键的数据了。这就是 “此地无银三百两”,因为关键的数据经过处理之后通常都很长,需要多行显示,结果却挤在一起了。 当鼠标滚轮快速滚动或者利用鼠标中键快速浏览器网页源代码时,下图这种一大块白色的区域非常显眼,这样就可以快速定位到可能的关键位置啦。 当然,找到可疑的位置后就可以在抓包页面查看缩进、美化之后的网页源代码来进行分析了。如果此种快速浏览的方法不行那就需要别的方法了。 好了,介绍了这个斗法经验,现在需要回到原来的事情上。
最终在 anime_iframe_src 的网页源代码中找到了可疑的信息:一个名为 Vurl 字符串(请记住这个名字,后文我将会多次提及到它)。 露出小尾巴:Vurl虽然 Vurl 最可疑,但还需要确定是不是它。如下是我的确认方式。 首先,在视频正常播放的情况下,确认了 Vurl 的值从出现开始就没有变化,只好继续分析。 因为遇到过一些情况: - 加密的数据放在全局变量或属性
x 中 - 网站经过一段时间的运行,
x 的值变成了解密后的数据
然后,手动修改 Vurl 的值看看结果。 最终,出现了下图!到了这里我可以大胆确定:哪怕动漫的链接不是从 Vurl 解密获取,那也和它脱不了干系,跟着它就能找到核心位置! 故,根据前面的分析,一定是把 Vurl 的值传到了某个地方。因为其名字很具有 “个性”,所以尝试全局搜索,最终定位到了关键位置。 观察其四周的代码,有大概的过程。 迅猛一些??如果一开始无法找到 Vurl ,其实也可以尝试全局搜索 encrypt、decrypt 等和加密、解密相关的关键词,或者监控 DOM 树的变化(也就是监控 <video> 标签的插入)等。 更有甚者,可以根据网站自己的控制台输出快速、迅猛地定位到核心位置。 不过我会尽可能让思路连贯起来,除非走投无路才使用 特殊关键词搜索 ,我知道的一些捷径会提,但不会在文中使用,大家在实际情况时可以更加迅猛一些。
整理思路上文定位到核心的部分了,不过为了保护灵兽,我将整个过程简化如下,并且只会着重讨论如何避免分析 hxm_encrypt、hxm_decrypt 两个加密、解密的函数的逻辑。 上文中出现了很多我自定义的名字,这都是为了下面整理出思路,否则论述起来大家也摸不着头脑,不清楚我说的是哪些东西。 键盘前论道:hxm_encrypt如下图,在 hxm_encrypt 函数处下断点、不断跟进,最后来到了 wasm 代码。 并且这两个 hxm 函数都位于 window 对象中。 三阶灵兽的威压第一步:直接下载该 wasm 文件到本地,将来用 nodejs 执行它。这对 nodejs 有版本要求,我用的是 node v18.14.2 。 第二步:找到 JS 加载 wasm 的相关代码。 因为这个网站的 wasm 是利用 go 语言编写、生成(因为在之前的图中可以见到 Go program 的字符串啦),所以会用到很多外部的函数,需要导入它们。 如下在 wasm 文本文件的顶部就指出了需要导入哪些东西。 这些导入函数就在下图中,因为内容很多,此处就不展示全部代码了,我将这个函数称之为 go_js_wasm_exec ,请一定要记住这个名字,后面将多次提及到它。 第三步:找到实际加载 wasm 的 JS 代码。 上面的 go_js_wasm_exec 函数只是准备好了东西,还没有实际加载、运行 wasm ,就像是创建了类但没有实例化对象一样。 如下,在获取 wasm 文件的请求中找到了调用者。 此处 loadWasm 的分析将省略,后文将展示其相关的代码。 第四步:在 NodeJS 中执行该 wasm 文件。 请记住这里的 const go = new Go(); 以及 go.importObject ,这在后文非常重要,有个印象也好。 const fs = require('fs');
// 这个就是第二步中提到的 go_js_wasm_exec 啦 require('./go_js_wasm_exec')
// 涉及到 JS WebAssembly API async function readAndInstantiate(file, importObject) { // 读取 Wasm 二进制文件 const buffer = fs.readFileSync(file); // 创建 WebAssembly 模块 const wasmModule = new WebAssembly.Module(buffer); // 创建 WebAssembly 实例 return WebAssembly.instantiate(wasmModule, importObject); }
// 这里就是第三步中 loadWasm 的逻辑 const go = new Go(); go["importObject"]["env"]["syscall/js.finalizeRef"] = () => { }
// main.wasm 是第一步中下载到本地的 wasm 文件 readAndInstantiate("main.wasm", go.importObject) .then(instance => { go.run(instance); // 这里开始运行 wasm
// 这个 hxm_encrypt 函数是在 wasm 内部复制给全局对象的,所以可以访问 console.log(hxm_encrypt("123456")); })
不出意外地,它报错了。 练气三层的实力上述在本地运行 wasm 为什么会报错呢?? 目前(至少必应搜索到的资料)wasm 还无法和浏览器(DON、DOM 等浏览器 API)直接交互,需要经过 js 导入相关内容到 wasm 中才行。 简单而言,比如 js 可以直接通过 window.location.href 访问当前标签页的链接。 但是 wasm 中并没有相关的指令可以做到这一点,需要用 js 将它导入到 wasm 中,这样 wasm 才能访问。
从宏观上来看,如下图 所以就目前而言 wasm 是可以在本地用 nodejs 运行的,在本地运行会报错是因为缺少了导入的东西(通常都是 window 对象的一些属性等),这个导入的入口处在 JS API 的 WebAssembly.instantiate() 方法的第二个参数。 所以这里的关键其实在于 go_js_wasm_exec 函数,因为是它起到了连接 wasm 和浏览器的作用。 如下可以确认是将 window 对象导入到了 wasm 中,所以在 wasm 中才能将两个 hxm 函数添加到 window 对象上。 那么在 wasm 中到底访问了 window 的哪些属性、方法呢??只要知道 wasm 中和浏览器做了怎样的交互,那么在 nodejs 中就可以还原出来。这里需要用到 Proxy 对象。 现在我们知道 wasm 中会访问 global.window ,也就是会访问全局对象 window ,那么继续利用 Proxy 对象来看一看究竟在 wasm 中访问了 window 的哪些东西! 注意!此时不能使用 global.window = xx 的形式来修改 global.window 属性。 因为 global 自身就是 window ,哪有自己修改自己的??就像 window.window = 1 是没有效果的。
// 这是一个对 window 进行代{过}{滤}理的 Proxy let _window = new Proxy(window, { get(target, property) { console.log(`window Getting ${property}: ${target[property]}`); return target[property]; }, set(target, property, value) { console.log(`window Setting ${property}=${value}`); return target[property] = value; }, apply: (target, thisArg, args) => { console.log(`window call ${target}(${args})`); return target.apply(thisArg, args); } });
let _global = global; // 创建 global 的 Proxy 对象,看看访问了哪些属性、方法 global = new Proxy(_global, { get(target, property) { // 当访问 global.window 时,返回一个对 window 的 Proxy 对象!!! if (property === 'window') { return _window; } console.log(`global Getting ${property}: ${target[property]}`); return target[property]; } });
最终确定了关键的东西 window.Domain ! 至此!可以在 nodejs 中模拟并调用 hxm_encrypt、hxm_decrypt 这两个函数了,只需要在 go_js_wasm_exec 文件中添加如下代码!也就是补全 wasm 的运行环境。 尾声虽然网站使用了 wasm 对数据进行加密与解密,但是该 wasm 运行的期间与浏览器、JS 的交互太浅(在本文中仅仅只是访问了 window.Domain 这一个属性),从而可以比较简单地在本地补全 wasm 的运行环境,姑且算是 “绕过了它的加密与解密部分”,我也不清楚 wasm 文件中到底做了什么。 不过该网站还有一个的陷阱!如果还采取 缺什么补什么 的思想,则可能陷入僵局。 另外,此次发文之后又会修炼一段时间……根骨不行,只能多修炼了。
番外篇:来自灵兽的彩蛋事实上,我确实无法从 wasm 中找出 aes 加密、解密的 key ,多出来的时间多看会动漫吧。
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|