Scratch In-Editor Obfuscator

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

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

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

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

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.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

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