Scratch In-Editor Obfuscator

Scratchのエディタ上で直接、変数・リスト・定義名をランダム化する

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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);
})();