您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
base64코드 자동복호화
当前为
// ==UserScript== // @name kone base64 자동복호화 // @namespace http://tampermonkey.net/ // @version 1.2.1 // @description base64코드 자동복호화 // @author SYJ // @match https://arca.live/* // @match https://kone.gg/* // @icon https://www.google.com/s2/favicons?sz=64&domain=arca.live // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_addValueChangeListener // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @license MIT // ==/UserScript== // 자주 바뀜. 취약한 셀렉터 const SHADOW_ROOT_SELECTOR = "body main div.prose-container"; window.addEventListener('load', ()=>setTimeout(main, 1000)); async function main(){ observeUrlChange(renderUI); const isAutoMode = await GM_getValue('toggleVal', true); if (isAutoMode) { observeUrlChange(autoApply); } else { setTimeout(manuallyApply, 1000); } } function doDecode(text) { ///'use strict'; let result = text; //result = dec(/aHR0c[0-9A-Za-z+]{20,}[=]{0,2}/, result); //aHR0c:1회인코딩된것. //result = dec(/YUhSMGN[0-9A-Za-z]{20,}[=]{0,2}/, result, true); //YUhSMGN:2회인코딩된것. //result = dec(/WVVoU01HTklUVFp[0-9A-Za-z+]{20,}[=]{0,2}/, result, true); // 3회인코딩된것. //result = dec(/V1ZWb1UwMUhUa2[0-9A-Za-z]{20,}[=]{0,2}/, result, true); // 4회인코딩된것. result = dec(/[0-9A-Za-z]{6,}[=]{0,2}/, result); //문자열 5회 + '=' 1~2회 //result = dec(/[0-9A-Za-z]{50,}[=]{0,2}/, result); //문자열 200회 + '=' 0~2회 return result; } function dec(reg, text) { let result = text; const originals = Array.from(reg.exec(result) ?? []); for (const original of originals){ const decoded = decodeNtime(original); result = result.replace(original, decoded); } return result; } const MAX_DECODE_COUNT = 10+1; function decodeNtime(str) { let decoded = str; let old = str; for (let i=0; i<MAX_DECODE_COUNT; i++){ old = decoded; decoded = decodeOneTime(decoded); console.log(decoded, old) if (decoded === old) return decoded; } function decodeOneTime(str) { try{ return base64DecodeUnicode(str);} catch{ return str;} } function base64DecodeUnicode(str) { // 1) atob으로 디코딩 → 바이너리(한 글자당 1바이트) 문자열 // 2) 각 문자 코드를 16진수 %xx 형태로 변환 // 3) decodeURIComponent로 UTF-8 해석 const percentEncodedStr = Array .from(atob(str)) .map(char => '%' + char.charCodeAt(0).toString(16).padStart(2, '0')) .join(''); return decodeURIComponent(percentEncodedStr); } } function manuallyApply() { document.body.addEventListener('dblclick', function(e) { console.log('더블클릭 감지! 🎉',e.target,event.composedPath()[0]); const el = e.composedPath()[0]; const nodes = Array.from(el.childNodes).filter(node=>node.nodeType ===Node.TEXT_NODE) console.log(nodes) for (const node of nodes){ const original = node.textContent; const decodedLink = doDecode(original); // console.log(node, original, decodedLink); if (original === decodedLink) continue; linkifyTextNode(node, decodedLink); } }) } function autoApply() { const contents = Array.from(document.body.querySelectorAll(`main ${textTagNames}`)); const mainContents = Array.from(document.querySelector(SHADOW_ROOT_SELECTOR)?.shadowRoot?.querySelectorAll(textTagNames) ?? []); contents.push(...mainContents); for (const tag of contents) { const nodes = Array.from(tag.childNodes).filter(node=>node.nodeType ===Node.TEXT_NODE) for (const node of nodes){ const original = node.textContent; const decodedLink = doDecode(original); if (original === decodedLink) continue; console.log('[DECODE] ',original, decodedLink); linkifyTextNode(node, decodedLink); } } } const textTagNames = 'p, span, div, a, li,' + // 일반 컨테이너 'h1, h2, h3, h4, h5, h6,' + // 제목 요소 'em, strong, u, b, i, small, mark, ' + // 인라인 포맷팅 요소 'label, button, option, textarea' // 폼/인터페이스 요소 function linkifyTextNode(Node, text) { // 텍스트노드 중 url을 찾아 a태그로 변환. (액션 포함) // URL 매칭 (https:// 로 시작해서 공백 전까지) const urlRegex = /(https?:\/\/[^\s]+)/; const match = urlRegex.exec(text); if (!match) { // URL 없으면 텍스트 덮어씌우고 종료 Node.textContent = text; return; } const url = match[0]; const start = match.index; const urlLen = url.length; // "텍스트1 URL 텍스트2" 꼴의 텍스트노드를 세 개로 분리 // 1) URL 앞부분과 뒤를 분리 const textNode = document.createTextNode(text); const afterUrlStart = textNode.splitText(start); const afterUrlEnd = afterUrlStart.splitText(urlLen); const beforeUrlStart = textNode; // 3) <a> 요소 생성 후 URL 텍스트 노드 대신 교체 const a = makeATag(url) Node.parentNode.replaceChild(a, Node); a.before(beforeUrlStart); a.after(afterUrlEnd); function makeATag(link){ const aTag = document.createElement('a'); aTag.href = link; aTag.textContent = link; aTag.target = '_blank'; aTag.rel = 'noreferrer'; return aTag; } } // UI async function renderUI() { // 1) 값 로드 let val = await GM_getValue('toggleVal', false); let menuId; // 2) 배지 생성 /* const badge = document.createElement('div'); Object.assign(badge.style, { position: 'fixed', top: '10px', right: '10px', padding: '4px 8px', background: 'rgba(0,0,0,0.7)', color: '#fff', fontSize: '14px', borderRadius: '4px', zIndex: '9999', }); document.body.append(badge); */ // 3) 렌더 함수 function render() { // 메뉴 해제 후 다시 등록 if (menuId) GM_unregisterMenuCommand(menuId); menuId = GM_registerMenuCommand( `자동모드 토글 (현재: ${val?'ON':'OFF'})`, toggleValue ); // 배지 업데이트 //badge.textContent = `현재 값: ${val}`; } // 4) 토글 함수 (즉시 UI 업데이트 포함) async function toggleValue() { const newVal = !val; await GM_setValue('toggleVal', newVal); val = newVal; // 변수 갱신 render(); // 메뉴·배지 즉시 갱신 } // 초기 렌더 render(); } const observeUrlChange = (func) => { func(); let oldHref = document.location.href; const body = document.querySelector('body'); const observer = new MutationObserver(mutations => { if (oldHref !== document.location.href) { oldHref = document.location.href; setTimeout(func, 1000); } }); observer.observe(body, { childList: true, subtree: true }); };