Arca base64 autodecoder

Arca.live Base64 auto decoder

目前為 2023-12-18 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Arca base64 autodecoder
  3. // @name:ko 아카라이브 Base64 자동 디코더
  4. // @version 1.17
  5. // @author Laria
  6. // @match https://arca.live/b/*/*
  7. // @description Arca.live Base64 auto decoder
  8. // @description:ko 아카라이브 Base64 자동 복호화 스크립트
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=arca.live
  10. // @license MIT
  11. // @encoding utf-8
  12. // @run-at document-end
  13. // @supportURL https://greasyfork.org/ko/scripts/482577-arca-base64-autodecoder
  14. // @namespace https://greasyfork.org/users/1235854
  15. // ==/UserScript==
  16.  
  17. /*
  18. * == Change log ==
  19. * 1.0 - Release
  20. * 1.1 - Invalid character update (replace -> replaceAll)
  21. * 1.11 - Improved show multiple links
  22. * 1.12 - Show Single links Bugfix
  23. * 1.13 - Bugfix 1.12
  24. * 1.14 - Base64 add padding func
  25. * 1.15 - Add annotation, display improvements
  26. * 1.16 - Display improved - CSS applied
  27. * 1.17 - var safe, max_iter defined (~7, def:3)
  28. */
  29.  
  30. const version = '1.16R';
  31.  
  32. //max attempt decode depth, defalut depth : 3
  33. //Caution! browser performance impact..
  34. var max_iter = 3
  35.  
  36.  
  37. //base64 encoded(http:/*, https:/*) string prefix
  38. const regArr = [
  39. /(aHR0cDovL|aHR0cHM6Ly)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 1 time
  40. /(YUhSMGNEb3ZM|YUhSMGNITTZMe)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 2 time
  41. /(WVVoU01HTkViM1pN|WVVoU01HTklUVFpNZ)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 3 time
  42. /(V1ZWb1UwMUhUa1ZpTTFwT|V1ZWb1UwMUhUa2xVVkZwTl)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 4 time
  43. /(VjFaV2IxVXdNVWhVYTFacFRURndU|VjFaV2IxVXdNVWhVYTJ4VlZrWndUb)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 5 time
  44. /(VmpGYVYySXhWWGROVldoVllURmFjRlJVUm5kV|VmpGYVYySXhWWGROVldoVllUSjRWbFpyV25kVW)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 6 time
  45. /(Vm1wR1lWWXlTWGhXV0dST1ZsZG9WbGxVUm1GalJsSlZVbTVrV|Vm1wR1lWWXlTWGhXV0dST1ZsZG9WbGxVU2pSV2JGcHlWMjVrVl)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 7 time
  46. ];
  47. //auto optimization
  48. max_iter = max_iter > regArr.length ? regArr.length : max_iter;
  49. //regex prefix - drag
  50. const regInvalid = /[^\w\+\/=]/;
  51.  
  52. //auto add padding - add '=' padding in base64 encoded string
  53. function base64AddPadding(str) {
  54. return str + Array((4 - str.length % 4) % 4 + 1).join('=');
  55. }
  56.  
  57. //base64 decode
  58. var Base64 = {
  59. _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
  60. decode : function (input) {
  61. var output = "";
  62. var chr1, chr2, chr3;
  63. var enc1, enc2, enc3, enc4;
  64. var i = 0;
  65.  
  66. input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  67.  
  68. while (i < input.length) {
  69. enc1 = this._keyStr.indexOf(input.charAt(i++));
  70. enc2 = this._keyStr.indexOf(input.charAt(i++));
  71. enc3 = this._keyStr.indexOf(input.charAt(i++));
  72. enc4 = this._keyStr.indexOf(input.charAt(i++));
  73.  
  74. chr1 = (enc1 << 2) | (enc2 >> 4);
  75. chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
  76. chr3 = ((enc3 & 3) << 6) | enc4;
  77.  
  78. //last bits
  79. output = output + String.fromCharCode(chr1);
  80. if (enc3 != 64) { //=
  81. output = output + String.fromCharCode(chr2);
  82. }
  83. if (enc4 != 64) { //==
  84. output = output + String.fromCharCode(chr3);
  85. }
  86. }
  87.  
  88. output = Base64._utf8_decode(output);
  89. return output;
  90. },
  91. // private method for UTF-8 decoding
  92. _utf8_decode : function (utftext) {
  93. var string = "";
  94. var i = 0;
  95. var c = 0;
  96. var c1 = 0;
  97. var c2 = 0;
  98. var c3 = 0;
  99.  
  100. while ( i < utftext.length ) {
  101. c = utftext.charCodeAt(i);
  102. if (c < 128) {
  103. string += String.fromCharCode(c);
  104. i++;
  105. }
  106. else if((c > 191) && (c < 224)) {
  107. c2 = utftext.charCodeAt(i+1);
  108. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  109. i += 2;
  110. }
  111. else {
  112. c2 = utftext.charCodeAt(i+1);
  113. c3 = utftext.charCodeAt(i+2);
  114. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  115. i += 3;
  116. }
  117. }
  118. return string;
  119. }
  120. };
  121.  
  122. var hindex = 0; //total decode count
  123.  
  124. //drag function comparison
  125. var lastSelected = document;
  126. var lastSelectedTime = Date.now();
  127.  
  128. //create link each components
  129. function createLink(src, index, url, depth) {
  130. //n번째 링크 (base64 깊이: 0) [ ABCDEF= ]
  131. return '<a href="' + url + '" title="' + url + ' (새 창으로 열기)" target="_blank" rel="external nofollow noopener noreferrer">' + index.toString() + '번째 링크 (base64 깊이: ' + depth.toString() + ')</a> [ <span style="font-size: 87.5%;color: rgb(71 188 115);">' + src.toString() + '</span> ]';
  132. }
  133.  
  134. //decode & generate
  135. function replacerGen(numIter) {
  136. return function(source) {
  137. try {
  138. rstring = ""; //return msg
  139. console.log('\n' + (hindex+1).toString() + '번째 인코딩된 링크: ' + source.toString()); //source
  140.  
  141. //decode
  142. var converted = Base64.decode(base64AddPadding(source));
  143. //attempt to decode nested base64 encoded string
  144. for(var i=0; i<numIter; i++) {
  145. converted = Base64.decode(base64AddPadding(converted));
  146. }
  147. hindex++;
  148.  
  149. //remove invalid string - �
  150. converted = decodeURI(encodeURI(converted).replaceAll('%00', ''));
  151. console.log(hindex.toString() + '번째 디코딩 완료: ' + converted.toString()); //converted
  152.  
  153. //split by new line
  154. converted = converted.split(/\r?\n/);
  155. //single component
  156. if (converted.length == 2 && converted[converted.length-1] == '') {
  157. rstring += createLink(source, hindex, converted[0], numIter+1);
  158. //multiple component
  159. } else if (converted.length > 1) {
  160. rstring += '[ <span style="font-size: 87.5%;color: rgb(71 188 115);">' + source.toString() + '</span> ]';
  161. nindex = 1;
  162. converted.forEach(function(j) {
  163. if (j != '') {
  164. rstring += '<br>' + createLink('링크 자동 분할 : ' + nindex.toString() + '번째', hindex, j, numIter+1);
  165. hindex++;
  166. nindex++;
  167. }
  168. });
  169. //apply last components
  170. hindex--;
  171. nindex--;
  172.  
  173. rstring = '<span style="color: rgb(232 62 140);"><b><i>분할된 링크 총 ' + nindex.toString() + '개</i></b></span> ' + rstring;
  174. } else rstring += createLink(source, hindex, converted, numIter+1);
  175. return rstring;
  176. } catch(e) {
  177. console.warn('\n에러 발생: ' + e);
  178. console.warn('base64 변환 실패: ' + source.toString());
  179. }
  180. return '<span style="color: rgb(255 0 0);">[ base64 변환 실패: ' + source.toString() + '</span> ]';
  181. };
  182. }
  183.  
  184. //user drag event
  185. //function disabled
  186. function selClicked(event) {
  187. var sel = document.getSelection().toString();
  188. if (!sel.match(regInvalid) && sel.length >= 10 && lastSelectedTime + 200 < Date.now()) {
  189. try {
  190. var converted = decodeURI(encodeURI(Base64.decode(base64AddPadding(sel))).replaceAll('%00', ''));
  191. this.innerHTML = this.innerHTML.replace(sel, converted);
  192. this.removeEventListener('click', selClicked);
  193. } catch (e) {
  194. return;
  195. } finally {
  196. }
  197. }
  198. }
  199.  
  200. //main
  201. (function() {
  202. 'use strict';
  203.  
  204. console.log('Arca.live Base64 오토 디코더 V' + version + '\n디코딩 준비');
  205. //article
  206. var article = document.getElementsByClassName("article-content")[0];
  207. for(var i=0; i<max_iter; i++) {
  208. article.innerHTML = article.innerHTML.replaceAll(regArr[i], replacerGen(i));
  209. }
  210.  
  211. //comment
  212. var comments = document.getElementsByClassName("list-area");
  213. if(comments.length != 0) {
  214. for(var j=0; j<max_iter; j++) {
  215. comments[0].innerHTML = comments[0].innerHTML.replaceAll(regArr[j], replacerGen(j));
  216. }
  217. }
  218.  
  219. /*
  220. //user drag
  221. document.addEventListener('selectionchange', function() {
  222. var sel = document.getSelection().anchorNode;
  223. if(sel) {
  224. sel = sel.parentElement;
  225. if(sel != lastSelected) {
  226. lastSelected.removeEventListener('click', selClicked);
  227. sel.addEventListener('click', selClicked);
  228. lastSelected = sel;
  229. lastSelectedTime = Date.now();
  230. }
  231. }
  232. })
  233. */
  234.  
  235. console.log('총 ' + hindex.toString() + '개 디코딩 작업 완료');
  236. })();