惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
N
News | PayPal Newsroom
雷峰网
雷峰网
Y
Y Combinator Blog
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog
Blog — PlanetScale
Blog — PlanetScale
F
Fortinet All Blogs
云风的 BLOG
云风的 BLOG
Microsoft Azure Blog
Microsoft Azure Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
U
Unit 42
The Cloudflare Blog
The GitHub Blog
The GitHub Blog
Recorded Future
Recorded Future
Vercel News
Vercel News
N
Netflix TechBlog - Medium
GbyAI
GbyAI
博客园 - 司徒正美
美团技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
J
Java Code Geeks
P
Proofpoint News Feed
I
InfoQ
IT之家
IT之家
F
Full Disclosure
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Google DeepMind News
Google DeepMind News
Apple Machine Learning Research
Apple Machine Learning Research
Forbes - Security
Forbes - Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cyber Attacks, Cyber Crime and Cyber Security
The Last Watchdog
The Last Watchdog
月光博客
月光博客
W
WeLiveSecurity
S
Securelist
Schneier on Security
Schneier on Security
Help Net Security
Help Net Security
T
Threat Research - Cisco Blogs
D
DataBreaches.Net
P
Privacy & Cybersecurity Law Blog
L
LINUX DO - 最新话题
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Scott Helme
Scott Helme
D
Darknet – Hacking Tools, Hacker News & Cyber Security
K
Kaspersky official blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - Franky
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org

Yesterday17's Blog

2026 新年解密红包 / Melody Flag | Yesterday17's Blog 谈谈 Iori 的设计思路(二):如何实现一个 Showroom 录制工具? | Yesterday17's Blog 谈谈 Iori 的设计思路(一):从 Nico Timeshift 说起 | Yesterday17's Blog Iori Minyami 0.1.0 发布 | Yesterday17's Blog 2025 新年解密红包 / Melody Flag | Yesterday17's Blog 使用 Cloudflare Warp 解决罗森票务的海外登录问题 | Yesterday17's Blog How To Blog 04: The Astro v5 Era | Yesterday17's Blog 谈谈 tokio::select! 的公平性 | Yesterday17's Blog Learning Pingora 05 - Connect with TLS | Yesterday17's Blog Leaving Bytedance | Yesterday17's Blog 大橋彩香 AsiaTour「Reflection」上海公演 个人向记录 & Repo | Yesterday17's Blog Recoving from burnout - What happened? | Yesterday17 Yubikey 重建手册 | Yesterday17's Blog How To Blog 03: Heimus | Yesterday17's Blog 🪧 Blog Migration Accouncement | Yesterday17's Blog Learn Your IDE - VSCode 是如何仅重启插件的? | Yesterday17's Blog How To Blog 02: Astro❤️Password | Yesterday17's Blog How To Blog 01: Why, How, and the Future | Yesterday17's Blog Learning Pingora 04 - Establish L4 Connection | Yesterday17's Blog Learning Pingora 03 - Upstreams and Peers | Yesterday17's Blog Learning Pingora 02 - A Simple HTTP Server | Yesterday17's Blog Learning Pingora 01 - Getting Started | Yesterday17's Blog 2024 新年解密红包 / Melody Flag | Yesterday17's Blog 向新的一年飞驰——记录 2023 | Yesterday17's Blog 「サクラノ刻」对话选摘(2) | Yesterday17's Blog PGP Key Revocation 注销声明 | Yesterday17's Blog 「サクラノ刻」对话选摘(1) | Yesterday17's Blog 2023 新年解密红包 / Melody Flag | Yesterday17's Blog 『蒼の彼方のフォーリズム』通关感想 | Yesterday17's Blog 单显卡直通教程 | Yesterday17's Blog 对博客与笔记的思考 | Yesterday17's Blog Project Anni 之旅(3)自动化 Flutter 应用 CI/CD 上架流程 | Yesterday17's Blog AsobiStage 直接播放链接 | Yesterday17 如何在后分P时代进行投稿——sswa使用详解 | Yesterday17's Blog JSON RPC 与 LSP 协议基础 | Yesterday17's Blog Grajapa Shueisha / BookEnd 加密方式调查 | Yesterday17's Blog 【2022篇+WriteUp】如何再收一个新年红包? | Yesterday17's Blog 如何将良心云的良心功能清理干净 | Yesterday17's Blog 【油猴脚本】bilibili 投稿页面返回旧版+旧版页面强制允许分P上传 | Yesterday17's Blog Cloudr1v1 授权方式分析 | Yesterday17 Typora 1.0.2 逆向实录 | Yesterday17's Blog Project Anni 之旅(2)ValueAfterTable——toml-rs的实现与限制 | Yesterday17's Blog IPv4透明代理+IPv6 Passthrough——树莓派单臂软路由折腾记 | Yesterday17's Blog Chaos; Child 汉化补丁 神秘编码探索 | Yesterday17's Blog 镣铐与舞蹈——个性与共性之迷思 | Yesterday17's Blog Go 学习笔记 02 - 找准 io 之道 | Yesterday17's Blog NAT Slipstreaming v1 原理浅析 | Yesterday17's Blog 绕过「9-nine-」的 CDKEY 验证——KrkrPlugin 正(?)向实录 | Yesterday17's Blog 静流的青春纪念册——「サクラノ刻 -櫻の森の下を歩む-」体验版感言 | Yesterday17's Blog Project Anni 之旅 01 - 从 clap-builder 到 derive | Yesterday17's Blog [Google CTF 2021] CPP WriteUp | Yesterday17's Blog 获取 アソビステージ 的实际播放链接 | Yesterday17's Blog 90 行 Rust 代码实现 AsyncTeeReader | Yesterday17's Blog 或许还算有价值一读的文章列表 | Yesterday17's Blog 从零开始的 Seedbox 之旅 | Yesterday17's Blog [随笔]技术型博客行文迷思(1) | Yesterday17's Blog 浅谈 git fetch 的工作方式 | Yesterday17's Blog 『ソーサレス*アライヴ! ~the World's End Fallen Star~』通关感想" | Yesterday17's Blog Rust std::fmt 格式语法简述 | Yesterday17's Blog 日亚修改居住国的解决方案 | Yesterday17's Blog [Windows/Linux] GC553 的 Switch 完美采集之路 | Yesterday17's Blog 【翻译】Subtyping and Variance / 子类型与变型 | Yesterday17's Blog Berd's Red Envelope 2021 WriteUp | Yesterday17's Blog 【中英对照】ALSA 音频 API 使用教程/A Tutorial on Using the ALSA Audio API | Yesterday17's Blog 从 cue_scanner.l 看 CUE Sheet 的词法单元 | Yesterday17's Blog Postman 历史记录导出的解决方案 | Yesterday17's Blog 《恋爱绮谭 不存在的夏天》通关感想 | Yesterday17's Blog [微机实验/TD-PITE] 微机接口综合实验 | Yesterday17's Blog [微机实验/TD-PITE] 键盘扫描及数码管显示实验 | Yesterday17's Blog [微机实验/TD-PITE] 数码管显示实验 | Yesterday17's Blog Airsonic Advanced+Google Drive+Caddy 部署纪实 | Yesterday17's Blog X-NUCA 2020 - hellowasm 题解 | Yesterday17's Blog [微机实验/TD-PITE] 8251 串行接口实验 | Yesterday17's Blog Node.js child_process.fork 与 env 污染 RCE | Yesterday17's Blog EP.01 「夜の向日葵」 | Yesterday17's Blog [微机实验/TD-PITE] 8254 定时/计数器实验+选做实验 | Yesterday17's Blog [JLU CTF/2020] babywasm WriteUp | Yesterday17's Blog PHP 反序列化与经典利用 | Yesterday17's Blog WebAssembly 逆向简述 | Yesterday17's Blog 『彼女、お借りします』一期完结点评 | Yesterday17's Blog [微机实验/TD-PITE] D/A 转换实验+选做实验 | Yesterday17's Blog [微机实验/TD-PITE] A/D 转换实验+选做实验 | Yesterday17's Blog 开源项目申请 JetBrains Open Source License 简单流程 | Yesterday17's Blog 微软拼音与 JetBrains 搜索快捷键冲突的解决方案 | Yesterday17's Blog [微机实验/TD-PITE] 8259 中断优先级实验+选做实验 | Yesterday17's Blog IFTTT 测试(续) | Yesterday17 IFTTT 测试 | Yesterday17's Blog [微机实验/TD-PITE] 存储器扩展实验+选做实验 | Yesterday17's Blog 新版 GCC 针对 -fdump-translation-unit 的替代方案 | Yesterday17's Blog 一次 HSTS 策略配置的排错之旅 | Yesterday17's Blog YukiNative 踩坑记——Windows 的消息队列 | Yesterday17's Blog 我是我自己——论获取 HTTPS 证书时的验证步骤 | Yesterday17's Blog 【设计文档】对 PUG 的大规模设计修订(1.1) | Yesterday17's Blog GS65 折腾记(2)加装固态,分区,Grub2 引导 Manjaro LiveCD | Yesterday17's Blog 「さくら、もゆ。」的空白字体列表——一次逆向问题定位过程实录 | Yesterday17's Blog GSuite 探索篇(1)使用 Service Account 向 Google Drive 传输文件 | Yesterday17's Blog 『サクラノ詩 -櫻の森の上を舞う-』通关感想 | Yesterday17's Blog 《ATRI -My Dear Moments-》通关感想 | Yesterday17's Blog [工具][VSCode 扩展] AegiKit——方便 Aegisub 使用的工具箱 | Yesterday17's Blog 贝塞尔曲线、字体矢量化与曲线运算 | Yesterday17's Blog
Shinym@s 初探 00 - 开始 | Yesterday17's Blog
Yesterday17 · 2020-04-04 · via Yesterday17's Blog

最近对那个 SC 的汉化脚本很感兴趣,简单了解了一下,发现是通过 prismJsp 实现的注入。正好个人对 SC 的代码也很感兴趣,于是就有了这系列(前提不咕)的文章。

在这里提前注明,这个系列中所有的脚本、测试的环境均为 Chrome 隐身模式,测试链接为 https://shinycolors.enza.fun/tutorial 。(期待后文吃书)

ToC

  • 简介
  • 工具
  • 预处理:拦截 Raven
  • 源码:主页
    • <link rel="preload">
    • window.console 覆盖
    • 结构分析
  • enza-platform
  • enza-game
  • 结语

简介

现在 SC 和之前相比安全性更高了,全局变量也没了,都藏起来了,之前写的防止自动音频暂停的脚本[1]自然也用不了了。之前的脚本是这样的:

(function () {

const mute = setInterval(() => {

if (aoba && aoba.soundManager) {

aoba.soundManager.mute = () => {};

clearInterval(mute);

}

}, 1000);

})();

可以看到,在之前的那个版本,aoba 是全局变量,随便改,随便操作,但现在就不一样了。

工具

在整个逆向过程中,我试图整理出一套适合前端逆向使用的工具(特别是 SC),于是 ShinyHelper 就诞生了。

目前它的功能还很简单,只有下面所说的这几个。但随着源码阅读的深入,我也会逐渐完善的。

目前源码托管在 GitHub 的私有仓库,原仓库可能不再更新。下面是对应 Organization一个公开仓库,想要看源码的可以在这个系列的任意一篇文章下面留言,根据认识情况加人(

预处理:拦截 Raven

还记得上一篇文章里提到的 拦截 Raven 吗?这就是为了这个项目而生的。我们需要简化调用栈层数,因为 SC 的层数已经爆炸多了。在读到后面你会发现,SC 是通过 eval 执行代码的,因此断点很多时候都没法打。能简化层数的话我们是一定需要尝试简化的。这里直接用到了上文对应的代码,并使用了 chrome.webRequest.onBeforeRequestListener 进行脚本加载的拦截。

源码:主页

这里我们记录下主页的全部源码,一是方便下文观看,二是之后如果有更新方便对比。记录的时间为:2020年 04月 04日 星期六 17:27:28 CST

<!doctype html>

<html lang="ja">

<head>

<!-- Google Tag Manager -->

<script>

(function (w, d, s, l, i) {

w[l] = w[l] || [];

w[l].push({

"gtm.start": new Date().getTime(),

event: "gtm.js",

});

var f = d.getElementsByTagName(s)[0],

j = d.createElement(s),

dl = l != "dataLayer" ? "&l=" + l : "";

j.async = true;

j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;

f.parentNode.insertBefore(j, f);

})(window, document, "script", "dataLayer", "GTM-WGSNLMS");

</script>

<!-- End Google Tag Manager -->

<meta charset="utf-8" />

<!--<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, user-scalable=no"/>-->

<meta

name="viewport"

content="width=device-width, initial-scale=1.0, user-scalable=no"

/>

<link

rel="preload"

href="//sdk.enza.fun/enza-platform-v1.26.js?v=20191213"

as="script"

/>

<title>アイドルマスター シャイニーカラーズ</title>

<style>

body#prim-body {

margin: 0;

text-align: center;

background-color: #feffff;

}

body#prim-body::before {

background: url("/background.jpg?v=16d1afd1ce064e56b7683669c99ac0f559a74307")

no-repeat center center;

background-size: cover;

-webkit-background-size: cover;

display: block;

position: fixed;

top: 0;

left: 0;

width: 100%;

height: 100vh;

content: "";

z-index: -1;

}

#ezpf-root .ezpf-drawer-whole-container {

width: calc(100vh * 1.77778);

margin: 0 auto;

left: 0;

right: 0;

}

@media screen and (max-aspect-ratio: 16/9) {

#ezpf-root .ezpf-drawer-whole-container {

width: 100%;

}

}

div.scrollable::-webkit-scrollbar {

display: none;

}

</style>

<link

rel="icon"

type="image/png"

href="/favicon_16.73f3ef3a310d483e315f10410e758ae2.png"

sizes="16x16"

/>

<link

rel="icon"

type="image/png"

href="/favicon_32.3e898b9094141d728f13a21c3170866c.png"

sizes="32x32"

/>

<meta name="apple-mobile-web-app-title" content="シャニマス" />

<meta name="apple-mobile-web-app-status-bar-style" content="default" />

<meta name="theme-color" content="#8adfff" />

<link

rel="apple-touch-icon"

sizes="512x512"

href="/icon_512x512.81bceb463844edc063d7bfbfc13d5cfa.png"

/>

<link

rel="apple-touch-icon"

sizes="192x192"

href="/icon_192x192.a3842ac040d3600657c1d8aa98b3db4d.png"

/>

<link rel="manifest" href="" />

</head>

<body id="prim-body">

<script src="//sdk.enza.fun/enza-platform-v1.26.js?v=20191213"></script>

<script

src="https://cdn.ravenjs.com/3.24.0/raven.min.js"

crossorigin="anonymous"

></script>

<script>

window.console.log = function () {};

window.console.warn = function () {};

window.console.group = function () {};

window.console.groupCollapsed = function () {};

window.console.groupEnd = function () {};

</script>

<script

type="text/javascript"

src="/enza-game.min.js?v=16d1afd1ce064e56b7683669c99ac0f559a74307"

integrity="sha256-EWB86GCqFjjckbQ9jk1U4NJepe5nL7mo6CRFZ/WsWOc="

crossorigin="anonymous"

></script>

<script

type="text/javascript"

src="/pixi-particles.min.js?v=16d1afd1ce064e56b7683669c99ac0f559a74307"

integrity="sha256-ymWZ7CMdFIoQaSG8dGrN43LKRMuR1UZtFWNUz9+NFX8="

crossorigin="anonymous"

></script>

<script

type="text/javascript"

src="/pixi-ae.min.js?v=16d1afd1ce064e56b7683669c99ac0f559a74307"

integrity="sha256-6kTowyDbHgB5y0Lu5mRS2Ge4f2SXSXmnbjCWJUcgsNI="

crossorigin="anonymous"

></script>

<script

type="text/javascript"

src="/env.js?v=b244f34c59273c8f992c7f1098e13369"

></script>

<script

type="text/javascript"

src="/commons.chunk-f60f4558ccbd2af54df8.js"

integrity="sha256-qFBtZT+9qBOxncri0yeMQ3psS9lTQpnvXuRvqid/+u8="

crossorigin="anonymous"

></script>

<script

integrity="sha256-FZRsC6ZKInAh3e1v2UVGAjruWcFZQmqDJMxo2xCjuu8="

type="text/javascript"

src="/app-10fa5e5e15ff0052b150.js"

></script>

<script id="tagjs" type="text/javascript">

(function () {

var tagjs = document.createElement("script");

var s = document.getElementsByTagName("script")[0];

tagjs.async = true;

tagjs.src = "//s.yjtag.jp/tag.js#site=CvxwH8J";

s.parentNode.insertBefore(tagjs, s);

})();

</script>

<noscript>

<iframe

src="//b.yjtag.jp/iframe?c=CvxwH8J"

width="1"

height="1"

frameborder="0"

scrolling="no"

marginheight="0"

marginwidth="0"

></iframe>

</noscript>

</body>

</html>

不得不吐槽,这个用两个 HTML 块实现的 <details> 真的很草

这里我们拆开来看:

我们会发现,关键的主脚本代码出现了两次,这是由于 preload 的加载策略导致的。具体可以参考 MDN 的这篇文章

window.console 覆盖

这里我们发现,为了防止信息泄露,SC 将 console 的实现覆盖了。这里我们当然不能让它覆盖了(笑),于是同样是使用 Proxy。(已加入工具)

window.console = new Proxy(window.console, {

get: function (target, key) {

if (typeof target[key] === "function") {

return target[key].bind(target);

}

return target[key];

},

set() {

return true;

},

});

结构分析

这里我们来对主页加载的脚本进行简单的分析。其实上一步禁止 console 覆盖之后就已经暴露出很多东西了,比如:

框架锁定(笑)
框架锁定(笑)

抛去没有作用的脚本,剩下的存在真实效用的脚本还有这些:

  • https://sdk.enza.fun/enza-platform-v1.26.js?v=20191213
  • https://shinycolors.enza.fun/enza-game.min.js?v=16d1afd1ce064e56b7683669c99ac0f559a74307
  • https://shinycolors.enza.fun/pixi-particles.min.js?v=16d1afd1ce064e56b7683669c99ac0f559a74307
  • https://shinycolors.enza.fun/pixi-ae.min.js?v=16d1afd1ce064e56b7683669c99ac0f559a74307
  • https://shinycolors.enza.fun/env.js?v=b244f34c59273c8f992c7f1098e13369
  • https://shinycolors.enza.fun/commons.chunk-f60f4558ccbd2af54df8.js
  • https://shinycolors.enza.fun/app-10fa5e5e15ff0052b150.js

可以看出,和 enza 有关的有 enza-platformenza-game,和游戏本体有关的有 commons.chunkappenv,和渲染有关的有 pixi-particlespixi-ae(这二者都是扩展,不知道为什么没有打包进去)。

在这里先提一句,后文我们需要习惯 ezxx 的缩写,比如 ezpf 就是 enza-platformezg 就是 enza-game 的缩写。这是他们源码里自己的写法(笑)

⚠️ 破坏性更新提示

在后续的版本中,enza 移除了 common.chunk。

enza-platform

顾名思义,这里都是 enza 这个平台对应的相关函数。比如:

弹个框这种的
弹个框这种的

实际代码逻辑中应该只有支付、登录方面会涉及到和平台交互的地方(我猜)。

enza-game

在知道了 PIXI 之后,可以发现 enza-game 中大部分的模块其实都是 PIXI 的内容,掺杂了其他诸如 sceneManager 啊,soundManager 之类的内容,都属于复用型代码,这里不细讲,留到后续篇目慢慢分析。初步看来是 TypeScript + Webpack + PIXI,也不知道具体是不是这样(

结语

这篇文章可以说是什么都没说,只简单地开了个头。摸鱼摸了一天,这文章写的我都有点忘记目的了(不是)。总之出发点我现在也不知道是什么了,什么都不多说了,我去P卡了(

(小插曲

From enza-game
From enza-game
PIXI v4.7.0,2018 年 1月 18 日发布
PIXI v4.7.0,2018 年 1月 18 日发布
2 月 7 日就事前登录了
2 月 7 日就事前登录了

《爆肝一个多月》