Greasy Fork

Arca base64 autodecoder

아카라이브 Base64 자동 복호화

目前为 2023-12-18 提交的版本。查看 最新版本

// ==UserScript==
// @name         Arca base64 autodecoder
// @version      1.14
// @author       Laria
// @match        https://arca.live/b/*/*
// @description  아카라이브 Base64 자동 복호화
// @icon         https://www.google.com/s2/favicons?sz=64&domain=arca.live
// @run-at       document-end
// @namespace https://greasyfork.org/users/1235854
// ==/UserScript==

/*
 * 1.0 - Release
 * 1.1 - Invalid character update (replace -> replaceAll)
 * 1.11 - Improved show multiple links
 * 1.12 - Show Single links Bugfixlinks
 * 1.13 - Show Single links Bugfix
 * 1.14 - base64 add padding func
*/

const regArr = [
    /(aHR0cDovL|aHR0cHM6Ly)(\w|=|\+|\/)*(?=[^\+=\w\/])/g,
    /(YUhSMGNEb3ZM|YUhSMGNITTZMe)(\w|=|\+|\/)*(?=[^\+=\w\/])/g,
    /(WVVoU01HTkViM1pN|WVVoU01HTklUVFpNZ)(\w|=|\+|\/)*(?=[^\+=\w\/])/g,
]
const regInvalid = /[^\w\+\/=]/;

function base64AddPadding(str) {
    return str + Array((4 - str.length % 4) % 4 + 1).join('=');
}

var Base64 = {
    _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
    decode : function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {
            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }
        }

        output = Base64._utf8_decode(output);
        return output;
    },
    // private method for UTF-8 decoding
    _utf8_decode : function (utftext) {
        var string = "";
        var i = 0;
        var c = 0;
        var c1 = 0;
        var c2 = 0;
        var c3 = 0;

        while ( i < utftext.length ) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i+1);
                c3 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }
}



var hindex = 0; //total index
const max_iter = 3; //max try decode iteration attamp, def:3

var lastSelected = document;
var lastSelectedTime = Date.now();

function createLink(orig, index, url, depth) {
    return '<a href="' + url
           + '" target="_blank" rel="noreferrer">' + index.toString()
           + '번째 링크 (base64 깊이: ' + depth.toString()
           + ')</a>'
           + '<br>[ <a>' + orig.toString() + '</a> ]';
}

function replacerGen(numIter) {
    return function(match) {
        try {
            return_f = ""; //return msg
            console.log(match)
            var converted = Base64.decode(base64AddPadding(match));
            for(var i=0; i<numIter; i++) {
                converted = Base64.decode(base64AddPadding(converted));
            }
            hindex++;

            converted = decodeURI(encodeURI(converted).replaceAll('%00', ''));  //remove invalid string - �

            converted = converted.split(/\r?\n/);
            if ((converted.length == 2 && converted[converted.length-1] == '') || converted.length <= 1) {
              return_f+=createLink(match, hindex, converted[0], numIter+1);
            } else if (converted.length > 1) {
              return_f+='[ ' + match.toString() + ' ]';
              nindex = 1;
              converted.forEach(function(k) {
                if (k != '') {
                  return_f+='<br>' + createLink('링크 자동 분할 : ' + nindex.toString() + '번째', hindex, k, numIter+1);
                  hindex++;
                  nindex++;
                }
              });
              hindex--; //last components
              nindex--;
              return_f = '분할된 링크 총 ' + nindex + '개 ' + return_f;
            } else return_f+=createLink(match, hindex, converted, numIter+1);

            return return_f
        } catch(e) {
            console.log(e);
            console.log('base64 변환 실패 : ' + match);
        }
        return '[base64 변환 실패 : ' + match + ']';
    }
}

//function disabled
function selClicked(event) {
    var sel = document.getSelection().toString();
    if (!sel.match(regInvalid)
        && sel.length >= 10
        && lastSelectedTime + 200 < Date.now()) {
        try {
            var converted = Base64.decode(sel);
        } catch (e) {
            return;
        } finally {
            this.innerHTML = this.innerHTML.replace(sel, converted);
            this.removeEventListener('click', selClicked);
        }
    }
}

(function() {
    'use strict';

    var article = document.getElementsByClassName("article-content")[0];
    for(var i=0; i<max_iter; i++) {
        article.innerHTML = article.innerHTML.replaceAll(regArr[i], replacerGen(i));
    }

    var comments = document.getElementsByClassName("list-area");
    if(comments.length != 0) {
        for(var i=0; i<max_iter; i++) {
            comments[0].innerHTML = comments[0].innerHTML.replaceAll(regArr[i], replacerGen(i));
        }
    }
    /*
    document.addEventListener('selectionchange', function() {
        var sel = document.getSelection().anchorNode;
        if(sel) {
            sel = sel.parentElement;
            if(sel != lastSelected) {
                lastSelected.removeEventListener('click', selClicked);
                sel.addEventListener('click', selClicked);
                lastSelected = sel;
                lastSelectedTime = Date.now();
            }
        }
    })*/
})();