Scratch In-Editor Obfuscator

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

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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