Scratchのエディタ上で直接、変数・リスト・定義名をランダム化する
// ==UserScript==
// @name Scratch In-Editor Obfuscator
// @namespace https://scratch.mit.edu/
// @version 1.0
// @description Scratchのエディタ上で直接、変数・リスト・定義名をランダム化する
// @author You
// @match https://scratch.mit.edu/projects/*
// @grant none
// ==/UserScript==
(() => {
'use strict';
const makeRandomName = () => '_0x' + Math.random().toString(36).substring(2, 8);
const obfuscateCurrentProject = () => {
// ScratchのVM(Virtual Machine)を取得
const vm = window.vm || (document.querySelector('canvas') && document.querySelector('canvas').__reactInternalInstance$?.return?.stateNode?.props?.vm);
if (!vm) {
alert('Scratchのエディタデータが見つかりませんでした。');
return;
}
const nameMap = new Map();
const getNewName = (oldName) => {
if (oldName.startsWith('☁')) return oldName; // クラウド変数は維持
if (!nameMap.has(oldName)) {
nameMap.set(oldName, makeRandomName());
}
return nameMap.get(oldName);
};
// 1. 変数・リスト・メッセージの書き換え
vm.runtime.targets.forEach(target => {
// 変数名
for (const id in target.variables) {
target.variables[id].name = getNewName(target.variables[id].name);
}
// リスト名
for (const id in target.lists) {
target.lists[id].name = getNewName(target.lists[id].name);
}
// メッセージ名
for (const id in target.broadcasts) {
target.broadcasts[id] = getNewName(target.broadcasts[id]);
}
});
// 2. カスタムブロック(定義)の書き換え
vm.runtime.targets.forEach(target => {
for (const blockId in target.blocks._blocks) {
const block = target.blocks._blocks[blockId];
if (block.mutation && block.mutation.proccode) {
const parts = block.mutation.proccode.split(/(%[sbnb])/);
parts[0] = getNewName(parts[0]);
block.mutation.proccode = parts.join('');
}
}
});
// 3. 画面(ワークスペース)を再描画して表示を更新
vm.emitWorkspaceUpdate();
alert('画面上の変数名や定義名をランダム化しました!');
};
// UIボタンの作成
const addButton = () => {
if (document.getElementById('direct-obfuscate-btn')) return;
const btn = document.createElement('button');
btn.id = 'direct-obfuscate-btn';
btn.innerText = '⚡ 画面上の名前を難読化';
btn.style.cssText = `
position: fixed;
top: 10px;
right: 180px;
z-index: 99999;
padding: 6px 12px;
background: #e91e63;
color: white;
border: none;
border-radius: 4px;
font-weight: bold;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0,0,0,0.3);
`;
btn.addEventListener('click', obfuscateCurrentProject);
document.body.appendChild(btn);
};
const timer = setInterval(() => {
if (document.querySelector('.gui_menu-bar_2300M') || document.querySelector('canvas')) {
addButton();
}
}, 1000);
})();