1. Home
  2. 웹/JS
  3. [JS] 난독화 풀기 - Deobfuscate

[JS] 난독화 풀기 - Deobfuscate

최근 알아낸 사실인데 아래 2개 사이트 정도면 웬만한 Javascript 난독화가 대부분 풀립니다. 당연하지만 다 풀리는건 아닌거 같구요

 

1. https://deobfuscate.relative.im/

 

JavaScript Deobfuscator

synchrony ver. 2.2.0 A simple deobfuscator for mangled or obfuscated JavaScript files view on GitHub Deobfuscate Save output if there are any errors, open developer tools > console to see them in a better view

deobfuscate.relative.im

일단 1차로 여기서 전체적으로 난독화 해제를 돌립니다.

여기서 결과물이 만족스러우면 그대로 사용해도 됩니다

 

1-1 https://deobfuscate.io/

위 1번 사이트가 잘 동작하지 않는다면 해당 사이트도 괜찮습니다. 만약에 자기가 난독화된 코드를 붙여 넣었는데 위와 같이 Obfuscator.io 로 난독화된 코드라고 인식되면 Yes를 누르고

 

https://obf-io.deobfuscate.io/

 

Obfuscator.io Deobfuscator

 

obf-io.deobfuscate.io

자동으로 이동되는 해당 사이트에서 난독화를 풀면 됩니다.

물론 붙여 넣기를 했는데 Obfuscator.io 알림이 안뜬다면 그냥 1-1 사이트에서 바로 난독화 해제 작업을 하시면 됩니다.

 

 

2. http://jsnice.org/

 

JS NICE: Statistical renaming, Type inference and Deobfuscation

// Put your JavaScript here that you want to rename, deobfuscate, // or infer types for: function chunkData(e, t) { var n = []; var r = e.length; var i = 0; for (; i < r; i += t) { if (i + t < r) { n.push(e.substring(i, i + t)); } else { n.push(e.substring

jsnice.org

위에서 1차로 나온 결과물에서 함수 이름, 변수명이 아직도 잘 보이지 않으면 여기서 돌려서 변수명, 함수명을 적당히 복구합니다. 여기까지만 해도 대부분 난독화 복구가 끝납니다.

 

주의 : 확인해보니 ES6 문법은 지원하지 않는 거 같습니다. var 이외에 let, const 등을 넣으면 이해하지 못합니다. 따라서 이런 경우엔 아래 3번 GPT를 돌리는걸 추천합니다.

 

3. ChatGPT

이후 변수명이나 함수명이 깨진건 적당히 ChatGPT 한테 코드 주고 복구하라고 시킵니다.

코드 흐름 대부분이 복구된 상태면 ChatGPT가 괜찮게 코드를 분석해서 적당한 변수명이나 함수명을 매겨줄 수 있을겁니다.

 

다만 잘못된 답을 줄수도 있으므로 GPT를 너무 신뢰는 하지 맙시다.

 

꼭 GPT뿐만이 아니라 Claude 나 Gemini 를 사용해도 비슷한 결과가 나옵니다.

 

 

직접 난독화 해제해보기 - 적절히 복구하기

백문이 불여일견이죠? 해당 글의 내용이 좀 빈약하기도 하고 생각보다 인기도 좀 있어서 실제로 JS 난독화를 풀어보는 실습예제를 한 번 가져와봤습니다. 

 

    const _0x2a2e7b = _0x1f8d;
    function _0x1f8d(_0x372aca, _0x77bbd0) {
      const _0x524fff = _0x524f();
      return (
        (_0x1f8d = function (_0x1f8d75, _0x295236) {
          _0x1f8d75 = _0x1f8d75 - 0x12c;
          let _0x1b7269 = _0x524fff[_0x1f8d75];
          return _0x1b7269;
        }),
        _0x1f8d(_0x372aca, _0x77bbd0)
      );
    }
    (function (_0x3c1336, _0x55751d) {
      const _0x26309b = _0x1f8d,
        _0xcbf495 = _0x3c1336();
      while (!![]) {
        try {
          const _0x1f6587 = -parseInt(_0x26309b(0x150)) / 0x1 + parseInt(_0x26309b(0x15a)) / 0x2 + (-parseInt(_0x26309b(0x154)) / 0x3) * (-parseInt(_0x26309b(0x149)) / 0x4) + -parseInt(_0x26309b(0x12f)) / 0x5 + -parseInt(_0x26309b(0x13c)) / 0x6 + (parseInt(_0x26309b(0x158)) / 0x7) * (parseInt(_0x26309b(0x148)) / 0x8) + parseInt(_0x26309b(0x155)) / 0x9;
          if (_0x1f6587 === _0x55751d) break;
          else _0xcbf495["push"](_0xcbf495["shift"]());
        } catch (_0x10aaad) {
          _0xcbf495["push"](_0xcbf495["shift"]());
        }
      }
    })(_0x524f, 0x6435e);
    const scriptWork = new Object(),
      swimhi = new MutationObserver(function (_0x5d14fa) {
        const _0x2ef7b5 = _0x1f8d;
        for (var _0x5bd6fa = 0x0, _0x24bcc8 = _0x5d14fa[_0x2ef7b5(0x143)]; _0x5bd6fa < _0x24bcc8; _0x5bd6fa++) {
          const _0x166160 = _0x5d14fa[_0x5bd6fa]["addedNodes"][0x0];
          if (_0x166160 && _0x166160[_0x2ef7b5(0x141)] === 0x1) {
            if (_0x166160[_0x2ef7b5(0x142)] === _0x2ef7b5(0x14a)) {
              const _0x117aff = _0x166160[_0x2ef7b5(0x130)];
              if (_0x117aff) {
                if (_0x117aff["indexOf"](_0x2ef7b5(0x13f)) != -0x1 || _0x117aff[_0x2ef7b5(0x144)](_0x2ef7b5(0x14e)) != -0x1) (_0x166160[_0x2ef7b5(0x12e)] = _0x2ef7b5(0x138)), _0x166160[_0x2ef7b5(0x145)](), (scriptWork[_0x2ef7b5(0x157)] += _0x117aff + "★");
                else {
                  if (_0x117aff[_0x2ef7b5(0x144)](_0x2ef7b5(0x139)) != -0x1 || _0x117aff[_0x2ef7b5(0x144)](_0x2ef7b5(0x135)) != -0x1 || _0x117aff[_0x2ef7b5(0x144)](_0x2ef7b5(0x140)) != -0x1) (_0x166160[_0x2ef7b5(0x12e)] = _0x2ef7b5(0x138)), _0x166160["remove"]();
                  else {
                    if (_0x117aff[_0x2ef7b5(0x144)](_0x2ef7b5(0x134)) != -0x1 && _0x117aff["indexOf"](_0x2ef7b5(0x14c)) === -0x1) {
                      const _0x19d18e = _0x117aff[_0x2ef7b5(0x12c)]("\x22required\x20name=search_term_string\x22", _0x2ef7b5(0x131));
                      _0x166160[_0x2ef7b5(0x130)] = _0x19d18e;
                    }
                  }
                }
                continue;
              }
              const _0x4e2fd2 = _0x166160["src"];
              if (_0x4e2fd2 && _0x4e2fd2[_0x2ef7b5(0x144)]("tistory_admin") != -0x1) {
                if (_0x4e2fd2["indexOf"](_0x2ef7b5(0x136)) != -0x1) {
                  const _0x551269 = _0x166160[_0x2ef7b5(0x159)];
                  (_0x166160[_0x2ef7b5(0x12e)] = _0x2ef7b5(0x138)), _0x166160[_0x2ef7b5(0x145)](), (scriptWork["intergrity"] = _0x551269), (scriptWork[_0x2ef7b5(0x136)] = _0x4e2fd2);
                } else {
                  if (_0x4e2fd2[_0x2ef7b5(0x144)](_0x2ef7b5(0x151)) != -0x1) (_0x166160["type"] = _0x2ef7b5(0x138)), _0x166160["remove"](), (scriptWork["reaction"] = _0x4e2fd2);
                  else {
                    if (_0x4e2fd2[_0x2ef7b5(0x144)](_0x2ef7b5(0x14d)) != -0x1) (_0x166160[_0x2ef7b5(0x12e)] = "javascript/blocked"), _0x166160[_0x2ef7b5(0x145)](), (scriptWork[_0x2ef7b5(0x13d)] = _0x4e2fd2);
                    else {
                      if (_0x4e2fd2[_0x2ef7b5(0x144)]("comment.js") != -0x1) (_0x166160["type"] = _0x2ef7b5(0x138)), _0x166160[_0x2ef7b5(0x145)](), (scriptWork["comment"] = _0x4e2fd2);
                      else _0x4e2fd2[_0x2ef7b5(0x144)](_0x2ef7b5(0x132)) != -0x1 ? ((_0x166160[_0x2ef7b5(0x153)] = _0x2ef7b5(0x146)), _0x4e2fd2[_0x2ef7b5(0x144)]("index-legacy") && swimhi[_0x2ef7b5(0x14f)]()) : ((_0x166160[_0x2ef7b5(0x12e)] = _0x2ef7b5(0x138)), _0x166160[_0x2ef7b5(0x145)]());
                    }
                  }
                }
              } else _0x4e2fd2 && _0x4e2fd2[_0x2ef7b5(0x144)](_0x2ef7b5(0x137)) != -0x1 && ((_0x166160[_0x2ef7b5(0x12e)] = _0x2ef7b5(0x138)), _0x166160[_0x2ef7b5(0x145)](), (scriptWork[_0x2ef7b5(0x13a)] = _0x4e2fd2));
              const _0x4ec522 = function (_0x19bde9) {
                const _0x22e437 = _0x2ef7b5;
                if (_0x166160[_0x22e437(0x12d)](_0x22e437(0x12e)) === _0x22e437(0x138)) _0x19bde9[_0x22e437(0x133)]();
                _0x166160[_0x22e437(0x152)](_0x22e437(0x13b), _0x4ec522);
              };
              _0x166160[_0x2ef7b5(0x147)](_0x2ef7b5(0x13b), _0x4ec522);
            } else _0x166160[_0x2ef7b5(0x12e)] === _0x2ef7b5(0x13e) && ((_0x166160[_0x2ef7b5(0x12e)] = _0x2ef7b5(0x156)), _0x166160[_0x2ef7b5(0x145)]());
          }
        }
      });
    swimhi[_0x2a2e7b(0x15b)](document[_0x2a2e7b(0x14b)], {
      childList: !![],
      subtree: !![],
    });
    function _0x524f() {
      const _0x5bff19 = ["6MDpvOO", "8868771FbbnGY", "text/blocked", "word", "462EZTKxY", "integrity", "947272EEUJXU", "observe", "replace", "getAttribute", "type", "3525840vaRqXb", "textContent", "\x22required\x20name=search_term_string\x22,\x22query-input\x22:\x22required\x20name=search_term_string\x22", "tiara", "preventDefault", "required\x20name=search_term_string", "switchFold", "jquery", "kakao.min.js", "javascript/blocked", "lightbox.options", "kakao", "beforescriptexecute", "4413150dMwgyC", "base", "text/css", "window.tjQuery", "loadedComments", "nodeType", "tagName", "length", "indexOf", "remove", "true", "addEventListener", "55816oERETz", "1377916MTdDTP", "SCRIPT", "documentElement", "\x22query-input\x22", "base.js", "tistoryFootnote.add", "disconnect", "757340nJqJqQ", "reaction", "removeEventListener", "async"];
      _0x524f = function () {
        return _0x5bff19;
      };
      return _0x524f();
    }

우선 제 블로그 스킨의 html 파일 최상단에 위치한 script 태그에 있는 JS 코드입니다. 제가 스킨을 유료로 구입했는데 스킨 개발자님이 JS를 난독화 해두셨습니다. 스킨을 샀으니 아무렴 제가 원하는 대로 스킨 수정이 필요한데, 티스토리 스킨을 수정하려면 스킨이 어떤 구조로 제작되어있는지 알아야 합니다. 당연히 위 코드도 어떤 의미인지 알아둬야 하겠죠.

 

아마 이런 상황에선 스킨 개발자님한테 원본 코드를 요청하는게 맞겠지만 이 글을 찾아오신 분들이 그렇듯, 대부분이 난독화된 코드에 대한 원본 코드 및 작동 원리를 얻어내기 어려운 상황에 쳐해있을 것 입니다.

 

그럴땐 이 글에서 소개한 대로 난독화를 풀어내고 난독화를 푼 다음엔 마구잡이 16진수로 매겨진 변수명이나 함수명을 적절히 복구해야 합니다.

 

일단 난독화를 풀기 전에 코드를 좀 살펴봅시다.

 

const _0x5bff19 = ["6MDpvOO", "8868771FbbnGY", "text/blocked", "word", "462EZTKxY", "integrity", "947272EEUJXU", "observe", "replace", "getAttribute", "type", "3525840vaRqXb", "textContent", "\x22required\x20name=search_term_string\x22,\x22query-input\x22:\x22required\x20name=search_term_string\x22", "tiara", "preventDefault", "required\x20name=search_term_string", "switchFold", "jquery", "kakao.min.js", "javascript/blocked", "lightbox.options", "kakao", "beforescriptexecute", "4413150dMwgyC", "base", "text/css", "window.tjQuery", "loadedComments", "nodeType", "tagName", "length", "indexOf", "remove", "true", "addEventListener", "55816oERETz", "1377916MTdDTP", "SCRIPT", "documentElement", "\x22query-input\x22", "base.js", "tistoryFootnote.add", "disconnect", "757340nJqJqQ", "reaction", "removeEventListener", "async"];

보면 _0x5bff19 라는 변수에 해당 코드에서 사용할 여러 키워드들을 문자열 배열 형태로 저장해놓은 것을 알 수 있습니다.

 

function _0x524f() {
  const _0x5bff19 = [
  생략
  ];
  _0x524f = function () {
    return _0x5bff19;
  };
  return _0x524f();
}

그리고 그걸 _0x524f 라는 함수로 감싸고 _0x524f라는 함수가 위 문자열 배열들을 반환하도록 합니다. 그리고 또 그 함수를 _0x524f 라는 함수로 감싼다음에 호출시 본인을 또 return 하고 있네요.

 

이름만 봐도 어지러운데 구조 역시 혼돈의 도가니입니다. 난독화 목적이 읽기 힘들라고 한 처사이니 당연하겠죠..

 

function _0x1f8d(_0x372aca, _0x77bbd0) {
  const _0x524fff = _0x524f();
  return (
    (_0x1f8d = function (_0x1f8d75, _0x295236) {
      _0x1f8d75 = _0x1f8d75 - 0x12c;
      let _0x1b7269 = _0x524fff[_0x1f8d75];
      return _0x1b7269;
    }),
    _0x1f8d(_0x372aca, _0x77bbd0)
  );
}

그리고 또 보면 방금 본 함수 _0x524f 를 _0x524fff 라는 변수에 또 대입해서 활용합니다.

사실 난독화를 해제해주는 도구를 위에서 설명했기에 코드 흐름을 딱히 살필 필요는 없습니다.

 

const _0x5bff19 = ["6MDpvOO", "8868771FbbnGY", "text/blocked", "word", "462EZTKxY", "integrity", "947272EEUJXU", "observe", "replace", "getAttribute", "type", "3525840vaRqXb", "textContent", "\x22required\x20name=search_term_string\x22,\x22query-input\x22:\x22required\x20name=search_term_string\x22", "tiara", "preventDefault", "required\x20name=search_term_string", "switchFold", "jquery", "kakao.min.js", "javascript/blocked", "lightbox.options", "kakao", "beforescriptexecute", "4413150dMwgyC", "base", "text/css", "window.tjQuery", "loadedComments", "nodeType", "tagName", "length", "indexOf", "remove", "true", "addEventListener", "55816oERETz", "1377916MTdDTP", "SCRIPT", "documentElement", "\x22query-input\x22", "base.js", "tistoryFootnote.add", "disconnect", "757340nJqJqQ", "reaction", "removeEventListener", "async"];

다만 위와 같이 코드에서 사용할 키워드들을 문자열 배열 형태로 넣어서 활용하는건 난독화시 보이는 전형적인 패턴이니 알아둘 필요가 있습니다.[각주*밑에서 이에 대해 잠깐 다룹니다.*] 난독화를 풀었을 때 이 배열로 된 것이 사라지고 풀려야 난독화 해제가 좀 제대로 진행됐다고 파악할 수 있겠습니다. (물론 난독화 도구마다 이런 패턴이 없을수도 있고 양상이 다 다를수 있습니다.)

 

 while (!![])

또한 이런 웃긴 코드도 볼 수 있는데 이건 Javascript의 난해한 특성을 반영해서 만든 난독화로 javascript로 !![] 을 불리언값(Boolean) 으로 변경해보면 true 가 됩니다. 한마디로 저건 while(true) 로 무한 반복을 나타낸 코드죠 ㅋㅋㅋ.

 

* 웹 브라우저에서 콘솔 창에 들어가서 Boolean(!![]) 을 통해서 !![] 를 Boolean 으로 강제 변환해보면 true를 반환하는걸 확인해볼 수 있습니다. 대단하고 쓰레기같은 JS의 세계

 

 

어쨌던 최상단에 있는 어지러운 난독화된 코드를 위에서 소개한 두 링크에 넣어서 난독화를 풀어보고 비교해보겠습니다.

 

deobfuscate.relative.im

const scriptWork = new Object(),
  swimhi = new MutationObserver(function (_0x5d14fa) {
    for (
      var _0x5bd6fa = 0, _0x24bcc8 = _0x5d14fa.length;
      _0x5bd6fa < _0x24bcc8;
      _0x5bd6fa++
    ) {
      const _0x166160 = _0x5d14fa[_0x5bd6fa].addedNodes[0]
      if (_0x166160 && _0x166160.nodeType === 1) {
        if (_0x166160.tagName === 'SCRIPT') {
          const _0x117aff = _0x166160.textContent
          if (_0x117aff) {
            if (
              _0x117aff.indexOf('window.tjQuery') != -1 ||
              _0x117aff.indexOf('tistoryFootnote.add') != -1
            ) {
              _0x166160.type = 'javascript/blocked'
              _0x166160.remove()
              scriptWork.word += _0x117aff + '\u2605'
            } else {
              if (
                _0x117aff.indexOf('lightbox.options') != -1 ||
                _0x117aff.indexOf('switchFold') != -1 ||
                _0x117aff.indexOf('loadedComments') != -1
              ) {
                _0x166160.type = 'javascript/blocked'
                _0x166160.remove()
              } else {
                if (
                  _0x117aff.indexOf('required name=search_term_string') != -1 &&
                  _0x117aff.indexOf('"query-input"') === -1
                ) {
                  const _0x19d18e = _0x117aff.replace(
                    '"required name=search_term_string"',
                    '"required name=search_term_string","query-input":"required name=search_term_string"'
                  )
                  _0x166160.textContent = _0x19d18e
                }
              }
            }
            continue
          }
          const _0x4e2fd2 = _0x166160.src
          if (_0x4e2fd2 && _0x4e2fd2.indexOf('tistory_admin') != -1) {
            if (_0x4e2fd2.indexOf('jquery') != -1) {
              const _0x551269 = _0x166160.integrity
              _0x166160.type = 'javascript/blocked'
              _0x166160.remove()
              scriptWork.intergrity = _0x551269
              scriptWork.jquery = _0x4e2fd2
            } else {
              if (_0x4e2fd2.indexOf('reaction') != -1) {
                _0x166160.type = 'javascript/blocked'
                _0x166160.remove()
                scriptWork.reaction = _0x4e2fd2
              } else {
                if (_0x4e2fd2.indexOf('base.js') != -1) {
                  _0x166160.type = 'javascript/blocked'
                  _0x166160.remove()
                  scriptWork.base = _0x4e2fd2
                } else {
                  if (_0x4e2fd2.indexOf('comment.js') != -1) {
                    _0x166160.type = 'javascript/blocked'
                    _0x166160.remove()
                    scriptWork.comment = _0x4e2fd2
                  } else {
                    _0x4e2fd2.indexOf('tiara') != -1
                      ? ((_0x166160.async = 'true'),
                        _0x4e2fd2.indexOf('index-legacy') &&
                          swimhi.disconnect())
                      : ((_0x166160.type = 'javascript/blocked'),
                        _0x166160.remove())
                  }
                }
              }
            }
          } else {
            _0x4e2fd2 &&
              _0x4e2fd2.indexOf('kakao.min.js') != -1 &&
              ((_0x166160.type = 'javascript/blocked'),
              _0x166160.remove(),
              (scriptWork.kakao = _0x4e2fd2))
          }
          const _0x4ec522 = function (_0x19bde9) {
            if (_0x166160.getAttribute('type') === 'javascript/blocked') {
              _0x19bde9.preventDefault()
            }
            _0x166160.removeEventListener('beforescriptexecute', _0x4ec522)
          }
          _0x166160.addEventListener('beforescriptexecute', _0x4ec522)
        } else {
          _0x166160.type === 'text/css' &&
            ((_0x166160.type = 'text/blocked'), _0x166160.remove())
        }
      }
    }
  })
swimhi.observe(document.documentElement, {
  childList: true,
  subtree: true,
})

 

obf-io.deobfuscate.io

const scriptWork = new Object();
const swimhi = new MutationObserver(function (_0x5d14fa) {
  var _0x5bd6fa = 0x0;
  for (var _0x24bcc8 = _0x5d14fa.length; _0x5bd6fa < _0x24bcc8; _0x5bd6fa++) {
    const _0x166160 = _0x5d14fa[_0x5bd6fa].addedNodes[0x0];
    if (_0x166160 && _0x166160.nodeType === 0x1) {
      if (_0x166160.tagName === "SCRIPT") {
        const _0x117aff = _0x166160.textContent;
        if (_0x117aff) {
          if (_0x117aff.indexOf("window.tjQuery") != -0x1 || _0x117aff.indexOf("tistoryFootnote.add") != -0x1) {
            _0x166160.type = "javascript/blocked";
            _0x166160.remove();
            scriptWork.word += _0x117aff + "★";
          } else {
            if (_0x117aff.indexOf("lightbox.options") != -0x1 || _0x117aff.indexOf("switchFold") != -0x1 || _0x117aff.indexOf("loadedComments") != -0x1) {
              _0x166160.type = "javascript/blocked";
              _0x166160.remove();
            } else {
              if (_0x117aff.indexOf("required name=search_term_string") != -0x1 && _0x117aff.indexOf("\"query-input\"") === -0x1) {
                const _0x19d18e = _0x117aff.replace("\"required name=search_term_string\"", "\"required name=search_term_string\",\"query-input\":\"required name=search_term_string\"");
                _0x166160.textContent = _0x19d18e;
              }
            }
          }
          continue;
        }
        const _0x4e2fd2 = _0x166160.src;
        if (_0x4e2fd2 && _0x4e2fd2.indexOf("tistory_admin") != -0x1) {
          if (_0x4e2fd2.indexOf("jquery") != -0x1) {
            const _0x551269 = _0x166160.integrity;
            _0x166160.type = "javascript/blocked";
            _0x166160.remove();
            scriptWork.intergrity = _0x551269;
            scriptWork.jquery = _0x4e2fd2;
          } else {
            if (_0x4e2fd2.indexOf("reaction") != -0x1) {
              _0x166160.type = "javascript/blocked";
              _0x166160.remove();
              scriptWork.reaction = _0x4e2fd2;
            } else {
              if (_0x4e2fd2.indexOf("base.js") != -0x1) {
                _0x166160.type = "javascript/blocked";
                _0x166160.remove();
                scriptWork.base = _0x4e2fd2;
              } else {
                if (_0x4e2fd2.indexOf("comment.js") != -0x1) {
                  _0x166160.type = "javascript/blocked";
                  _0x166160.remove();
                  scriptWork.comment = _0x4e2fd2;
                } else if (_0x4e2fd2.indexOf("tiara") != -0x1) {
                  _0x166160.async = "true";
                  if (_0x4e2fd2.indexOf("index-legacy")) {
                    swimhi.disconnect();
                  }
                } else {
                  _0x166160.type = "javascript/blocked";
                  _0x166160.remove();
                }
              }
            }
          }
        } else if (_0x4e2fd2 && _0x4e2fd2.indexOf("kakao.min.js") != -0x1) {
          _0x166160.type = "javascript/blocked";
          _0x166160.remove();
          scriptWork.kakao = _0x4e2fd2;
        }
        const _0x4ec522 = function (_0x19bde9) {
          if (_0x166160.getAttribute("type") === "javascript/blocked") {
            _0x19bde9.preventDefault();
          }
          _0x166160.removeEventListener("beforescriptexecute", _0x4ec522);
        };
        _0x166160.addEventListener("beforescriptexecute", _0x4ec522);
      } else if (_0x166160.type === "text/css") {
        _0x166160.type = "text/blocked";
        _0x166160.remove();
      }
    }
  }
});
swimhi.observe(document.documentElement, {
  childList: true,
  subtree: true
});

 

생각보다 놀랍게도 꽤나 괜찮게 난독화가 해제되었습니다! 변수명이 창나있는건 비슷한데 어쨌던 아까 해당 코드에서 사용하는 문자열들이 배열에 묶이고, 또 함수로 2중 포장되어서 읽기가 거의 불가능했는데 그런 패턴이 다 사라졌네요.

 

 

일단 2개의 사이트에서 작업했고 WinMerge[각주*WinMerge라는 프로그램은 2개나, 3개의 텍스트에서 서로의 차이를 비교해줘서 보여주는 프로그램입니다.*]라는 프로그램으로 차이를 비교해보면 대충 위와 같은데요.

 

대충 봐서는 obf-io.deobfuscate.io 쪽이 JS의 16진수 표현인 0x0 등을 그대로 사용해서 deobfuscate.relative.im 사이트에서 제공하는 난독화 해제 코드보다 조금 더 지저분한 코드가 나왔습니다.

그리고 코드들을 엄밀히 비교해보면 if문 비교 순서가 다르다던가 하는 그런 차이도 있습니다.

 

우선 난독화를 해제하기 전 코드와, 난독화를 해제하기 후 코드가 완전히 동일한지는 코드의 길이가 방대할 수록 보장하기가 어렵습니다. 코드를 하나 하나 분석하는건 어렵고 블랙박스 테스트[각주*블랙박스 테스트란 소프트웨어의 내부 구조나 작동 원리를 모르는 상태에서 소프트웨어의 동작을 검사하는 방법으로 프로그램을 테스트 하는 사람은 소스코드나 구조를 일일히 분석하지 않고 외부 사용자의 입장에서 시스템에 접근하여, 입력값에 대한 출력값을 분석하는 방법을 뜻합니다. 여기서는 난독화전 코드와 난독화 후 코드의 작동이 동일한지 확인하는게 블랙박스 테스트가 됩니다.*]를 해보는게 전부 일 뿐입니다.

 

그럼에도 제가 난독화를 풀고 JS를 돌려봤는데 별다른 문제 없이 동작함을 여러번 확인했으니 난독화 후 코드가 난독화 전 코드와 기능이 달라지는것에 대해선 크게 염려하지 않아도 될 듯 싶습니다. (물론 검증은 필요합니다.)

 

어쨌던 두 사이트 중에 어떤 코드를 쓰냐 하면 저는 보기 좋은 전자쪽(deobfuscate.relative.im) 을 고르겠습니다. 여러분은 두 사이트 다 난독화 해제를 돌려보시고 보기 좋은 코드를 골라주세요.

 

const scriptWork = new Object(),
  swimhi = new MutationObserver(function (_0x5d14fa) {
    for (
      var _0x5bd6fa = 0, _0x24bcc8 = _0x5d14fa.length;
      _0x5bd6fa < _0x24bcc8;
      _0x5bd6fa++
    ) {
      const _0x166160 = _0x5d14fa[_0x5bd6fa].addedNodes[0]
      if (_0x166160 && _0x166160.nodeType === 1) {
        if (_0x166160.tagName === 'SCRIPT') {
... 생략

swimhi.observe(document.documentElement, {
  childList: true,
  subtree: true,
})

어쨌던 전 전자쪽의 코드를 가져와 봤는데 일부 변수만 변수명이 살아있을 뿐 읽기 어려운 이름으로 변수들이 마구 난독화 되어있는걸 알 수 있습니다. + JS의 최신 문법이라고 할 수 있는 ES6 에서 권장하는 변수 선언법인 let, const 가 아닌 var이 사용된 것들도 있음을 알 수 있습니다. 그럼에도 이 상황에서 코드를 읽어보면 일단은 변수명이나 함수명만 깨져있을뿐 코드를 자세히 읽으면 충분히 적당한 변수명이나 함수명을 매길 수 있습니다.

 

하지만 코드 길이가 길어질수록 그것을 사람이 다 하는건 너무 가혹하니깐 ChatGPT나 Claude3 같은 LLM 모델에 기반한 AI 챗봇을 활용해서 변수명, 함수명 복구를 요청하면 됩니다.

 

참고 : 단순히 난독화된 코드를 가지고 인공지능 챗봇한테 이것을 풀어봐라 하면 아직까지는 잘 못풉니다. [*간단한 문제 한정으로만 풀 수 있음] 앞서 한것처럼 난독화를 해제하는 사이트나 프로그램을 활용해서 전처리를 하고 변수명 매기기 정도만 인공지능한테 시켜야 합니다.

 

저는 아래와 같이 두괄식으로 GPT에게 변수명 복구를 요청해보겠습니다.

 

 

프롬프트

아래 코드의 변수명, 함수명이 난잡해서 읽기가 어렵다. 해당 원칙을 지켜서 작업하라.

1. 아래의 코드에서 읽기 어려운 변수명, 함수명에 적당히 이름을 붙여라.

2. 변수명/ 함수명의 경우 snake case [or camel case] 형태로 일관성 있게 작성할 것

3. 코드의 logic 은 변경하지 말고 변수명과 함수명만 그대로 매길 것

+ 주석이 필요하다면 아래 4번도 추가
4. 코드의 작동에 이해가 쉽도록 상세한 한글 주석도 추가하라.

 

일종의 prompt 형태로 만들었지만 사실 어떻게 질의하던 상관없습니다. 너무 대충만 안적으시면 됩니다.

당연하지만 상세하고 구체적으로 AI 챗봇에 질의할 수록 높은 퀄리티의 답변이 나옵니다.

 

아래는 GPT 무료 모델로 얻어낸 결과입니다. 요새는 GPT만든 OpenAI 쪽에서 4o를 한도내에서 무료로 쓰게 해주고 그 한도가 끝나면 경량 모델인 4o mini 로 답변을 하는것으로 기억하는데,, 정확히는 잘 모르겠네요. 어쨌던 GPT4 를 한도내에서 무료로 쓸 수 있게 해준 듯 싶은데 최근 퀄리티가 급상승 했습니다.

 

꼭 GPT가 아니라도 클로드나 구글 잼민이같은 챗봇을 가지고 작업하셔도 됩니다. 당연하지만 유료로 결제해서 쓰는게 퀄리티가 더 좋겠죠? 

 

const script_work = new Object(),  // script 작업에 대한 정보를 저장하는 객체
  mutation_observer = new MutationObserver(function (mutations) {  // DOM 변화를 감지하는 MutationObserver
    for (let mutation_index = 0, mutation_length = mutations.length; mutation_index < mutation_length; mutation_index++) {
      const added_node = mutations[mutation_index].addedNodes[0];  // 추가된 노드 중 첫 번째 노드를 가져옴
      if (added_node && added_node.nodeType === 1) {  // 노드가 있고, 그 노드가 엘리먼트 노드인 경우
        if (added_node.tagName === 'SCRIPT') {  // 추가된 노드가 <script> 태그일 때
          const script_content = added_node.textContent;  // 스크립트의 내용을 가져옴
          if (script_content) {  // 스크립트 내용이 있을 경우
            // 특정 문자열이 스크립트 내용에 포함되어 있으면 스크립트를 차단하고 제거
            if (
              script_content.indexOf('window.tjQuery') != -1 ||
              script_content.indexOf('tistoryFootnote.add') != -1
            ) {
              added_node.type = 'javascript/blocked';  // 스크립트의 타입을 'blocked'로 변경
              added_node.remove();  // 스크립트를 DOM에서 제거
              script_work.word += script_content + '\u2605';  // 스크립트 내용을 기록
            } else {
              // 특정 다른 문자열이 포함된 스크립트 역시 차단하고 제거
              if (
                script_content.indexOf('lightbox.options') != -1 ||
                script_content.indexOf('switchFold') != -1 ||
                script_content.indexOf('loadedComments') != -1
              ) {
                added_node.type = 'javascript/blocked';  // 스크립트 타입을 차단으로 변경
                added_node.remove();  // 스크립트를 제거
... 생략
 
  
// MutationObserver가 document의 자식 노드와 서브 트리를 감시하도록 설정
mutation_observer.observe(document.documentElement, {
  childList: true,  // 자식 노드의 변화를 감지
  subtree: true,  // 하위 트리의 변화도 감지
});

와우! 기대 이상으로 깔끔한 결과를 얻어냈습니다. 이제 드디어 이 코드가 무슨 기능을 하는 지 한번에 알수 있네요. 무언가를 감시하는 mutation observer을 작동시키는데 뭘 감시하는지 하니 특정 스크립트와 css style을 감지해서 강제 제거하는 기능이네요. 제 블로그에서 lightbox 기능이 작동을 안해서 왜인지 몰랐는데 이 mutation observer 때문이였네요.

 

참고로 아주 짧은 코드가 아닌 이상 챗봇에서 얻어낸 결과를 기존 난독화된 원본에 그대로 붙여넣기 해서 사용하는건 비추합니다.

 

GPT 뿐만이 아니라 Claude 도 그렇고 여러 챗봇 AI 많이 써보신 분들은 알겠지만 실수도 많고 코드가 길어져서 입력이 너무 커지면 코드 일부분을 출력하지 않고 도려내는 문제도 있습니다.

 

따라서 기존 난독화된 코드를 보고 하나 하나씩 대조해가면서 IDE의 변수명이나 함수명 바꿔주는 단축키 (VSCODE의 경우 F2가 되겠네요) 를 사용해서 일일히 한줄씩 주석을 달아가면서 수정하는걸 추천합니다.

 

이렇게 해야하는 이유는 다시 한번 말하지만 코드의 신뢰성 때문입니다. AI가 준 결과는 부정확할 가능성이 매우 높으니깐요. 어쨌던 이렇게 수작업을 하시면 난독화된 코드를 풀어내는것, 그리고 또 변수명, 함수명 복구를 수작업 + 주석을 달면서 자연스레 코드 리뷰도 되는 장점이 있답니다.

 

요새 AI에 너무 의존하면서 코딩하는 습관이 생겼는데 저는 그 습관을 버리고자 코파일럿이나 GPT, Cluade 등 인공지능과 최대한 멀리 떨어져서 생각하면서 코딩하는 습관을 기르고자 합니다.

어쩌다가 글 마무리 멘트가 되어버렸는데요. 음.. 이걸론 아쉬우니 난독화를 더 풀어봅시다.

 

for (let i = 0; i < 10; i++){
    console.log(`loop at ${i}`)
}

 

이번엔 원본 코드를 아는 상태에서 진행합니다. 이번 예제는 이겁니다. 퀄리티가 갑자기 급감한거 같지만 기분탓입니다.

 

https://obfuscator.io/#code

 

JavaScript Obfuscator Tool

JavaScript Obfuscator Tool A free and efficient obfuscator for JavaScript (including support of ES2022). Make your code harder to copy and prevent people from stealing your work. This tool is a Web UI to the excellent (and open source) javascript-obfuscato

obfuscator.io

위 코드를 해당 사이트에서 난독화 합니다. 인터넷에 널리 배포되고 있고 github 페이지도 있어서 오프라인으로 돌려볼 수도 있네요.

const _0x52c0e3 = _0x5ef9;
(function (_0x3b4495, _0x5e9c7e) {
  const _0x3daac8 = _0x5ef9,
    _0x2ecf27 = _0x3b4495();
  while (!![]) {
    try {
      const _0x48d556 =
        -parseInt(_0x3daac8(0xc3)) / 0x1 +
        -parseInt(_0x3daac8(0xc2)) / 0x2 +
        -parseInt(_0x3daac8(0xc7)) / 0x3 +
        (-parseInt(_0x3daac8(0xc6)) / 0x4) *
          (-parseInt(_0x3daac8(0xc9)) / 0x5) +
        (parseInt(_0x3daac8(0xc0)) / 0x6) * (-parseInt(_0x3daac8(0xca)) / 0x7) +
        -parseInt(_0x3daac8(0xc4)) / 0x8 +
        parseInt(_0x3daac8(0xc1)) / 0x9;
      if (_0x48d556 === _0x5e9c7e) break;
      else _0x2ecf27["push"](_0x2ecf27["shift"]());
    } catch (_0x41951c) {
      _0x2ecf27["push"](_0x2ecf27["shift"]());
    }
  }
})(_0x1ddf, 0x48c81);
function _0x5ef9(_0x2704d7, _0x385656) {
  const _0x1ddf55 = _0x1ddf();
  return (
    (_0x5ef9 = function (_0x5ef9ee, _0x39887f) {
      _0x5ef9ee = _0x5ef9ee - 0xc0;
      let _0x2c0fa0 = _0x1ddf55[_0x5ef9ee];
      return _0x2c0fa0;
    }),
    _0x5ef9(_0x2704d7, _0x385656)
  );
}
for (let i = 0x0; i < 0xa; i++) {
  console[_0x52c0e3(0xc8)](_0x52c0e3(0xc5) + i);
}
function _0x1ddf() {
  const _0x2d38e3 = [
    "343628CKzfIK",
    "223688tPlFXb",
    "1836616vHOrWf",
    "loop\x20at\x20",
    "1460476tmlGtV",
    "262719akOvXP",
    "log",
    "5MZwTxT",
    "2217453mvUHSM",
    "6TmveTo",
    "8661825WoYHsW",
  ];
  _0x1ddf = function () {
    return _0x2d38e3;
  };
  return _0x1ddf();
}

사이트에서 상세 옵션 아무것도 안건들고 난독화를 하니깐 이렇게 바꿔줍니다.

그런데 난독화된 결과를 보면 익숙한 내용이 있죠?

 

const _0x2d38e3 = [
    "343628CKzfIK",
    "223688tPlFXb",
    "1836616vHOrWf",
    "loop\x20at\x20",
    "1460476tmlGtV",
    "262719akOvXP",
    "log",
    "5MZwTxT",
    "2217453mvUHSM",
    "6TmveTo",
    "8661825WoYHsW",
  ];

보다 싶이 프로그램에서 사용될만한 문자열이 또 배열에 저장되어 있네요.

 

난독화 사이트에서 확인해보니 이것은 String Array라는 기능이였습니다. 말 그대로 문자열 배열에 프로그램에서 쓸 문자열을 저장하는거죠. 만약에 저걸 끄고 난독화를 돌리면?

 

for (let i = 0x0; i < 0xa; i++) {
  console["log"]("loop\x20at\x20" + i);
}

이렇게 난독화를 했다고 하기 민망할 정도의 결과가 나오는 군요. 코드를 난해하게 만드는 범인이 저 옵션이였나 봅니다. 근데 신기한건 console.log 로 호출하는게 아니라 console["log"] 로 호출을 해도 JS는 실행을 허용하네요. 역시 신기한 자바스크립트의 세계.. 이외에도 숫자들이 사람이 읽기 어려운 16진수로 바뀌어 있습니다.

 

const _0x52c0e3 = _0x5ef9;
(function (_0x3b4495, _0x5e9c7e) {
  const _0x3daac8 = _0x5ef9,
    _0x2ecf27 = _0x3b4495();
  while (!![]) {
    try {
      const _0x48d556 =
        -parseInt(_0x3daac8(0xc3)) / 0x1 +
        -parseInt(_0x3daac8(0xc2)) / 0x2 +
        -parseInt(_0x3daac8(0xc7)) / 0x3 +
        (-parseInt(_0x3daac8(0xc6)) / 0x4) *
          (-parseInt(_0x3daac8(0xc9)) / 0x5) +
        (parseInt(_0x3daac8(0xc0)) / 0x6) * (-parseInt(_0x3daac8(0xca)) / 0x7) +
        -parseInt(_0x3daac8(0xc4)) / 0x8 +
        parseInt(_0x3daac8(0xc1)) / 0x9;
      if (_0x48d556 === _0x5e9c7e) break;
      else _0x2ecf27["push"](_0x2ecf27["shift"]());
    } catch (_0x41951c) {
      _0x2ecf27["push"](_0x2ecf27["shift"]());
... 생략

어쨌던 돌아와서 위 긴 코드를 https://deobfuscate.relative.im/  에 넣고 다시 돌려봅니다.

 

for (let i = 0; i < 10; i++) {
  console.log('loop at ' + i)
}

이번건 간단해서 그런가 뭘 할 필요가 없었습니다. 원래 벡틱(`) 을 이용한 문자열 formatting 을 했었는데 그게 +로 대체된 것만 빼곤 동일합니다.

 

사실 JS의 난독화는 유명한 Obfuscator(난독화 도구) 를 사용한 이상 대부분이 오픈소스로 풀려있는 도구나 사이트에 의해 풀립니다. 변수명이나 함수명이 읽기 어렵게 박살날뿐 이것도 AI로 복원하면 그만입니다.

 

function nextprev() {
  const e = document.querySelector(".wowed");
  if (!e) return !1;
  const t = e.querySelector(".totalprev"),
    n = e.querySelector(".totalnext"),
    a = document.getElementById("Parse_Area");
  if (t) a.appendChild(t);
  else {
    const e = document.createElement("div"),
      t = e.cloneNode(!1),
      n = document.createElement("span");
    (e.className = "totalprev"),
      (t.className = "thumb5"),
      (n.className = "prevv2"),
      (n.textContent = "글이 없거나, 비공개/보호글"),
      e.appendChild(t),
      e.appendChild(n),
      a.appendChild(e);
  }
  if ((console.log("work"), n)) a.appendChild(n);
  else {
    const e = document.createElement("div"),
      t = e.cloneNode(!1),
      n = document.createElement("span");
    (e.className = "totalnext"),
      (t.className = "thumb5"),
      (n.className = "nextt2"),
      (n.textContent = "글이 없거나, 비공개/보호글"),
      e.appendChild(t),
      e.appendChild(n),
      a.appendChild(e);
  }
}

마지막으로 이 예제를 볼까요? 이번건 좀 특이한데요. 변수명이 마구잡이로 만들어진 16진수가  아니라 e,t, n 같은 변수를 사용하고 있습니다. 이는 사실 엄밀히 이야기 하면 난독화 보다는 압축(Compress, Minify)에 가까운데요.

 

코드 길이를 줄이기 위해서 변수명을 e나 t, n 과 같은 간단한 문자로 바꾸는 것 입니다.

물론 e,t,n 을 반복적으로 사용해야 하니 변수명이 겹치는 것이 필연적으로 발생해 중괄호 영역으로 변수명을 보호하거나 JS의 즉시 실행 함수란 것을 활용해서 한 번 함수로 묶던가 하는 작업을 거칩니다.

 

사실 프로그래머 입장에선 의미있는 변수명 매기는것도 힘든데 왜 이런 압축을 하냐 의문을 가지시는 분들이 있으실겁니다. 아래부턴 TMI 니깐 읽으실 분들만 읽으시길 바랍니다.

 

기본적으로 네트워크 통신을 하면서 병목이 발생하는 부분은 네트워크 I/O 입니다. 더 쉽게 이야기 해서 우리 컴퓨터의 웹브라우저로 naver.com 에 접속을 하는 상황을 가정해보겠습니다.

 

우리가 브라우저에 naver.com 을 치고 접속을 하는 순간 저희는 네트워크 상으로 네이버 컴퓨터에 접속하며, 네이버 컴퓨터한테 웹통신을 위해 네이버 페이지 좀 보내줘~ 라고 요청을 하게 됩니다. 여기서 네이버 페이지 데이터가 바로 웹을 배우면 알 수 있는 HTML, CSS, JS구요. 

 

그러면 네이버 컴퓨터는 네이버 사이트의 내용을 담은 문서 파일 HTML, CSS, JS를 저희에게 전송하게 됩니다. 여기서 네이버 컴퓨터는 흔히들 서버(Server) 라고 하고 저희가 사용한 웹 브라우저를 클라이언트(Client) 라고 합니다.

 

여기서 웹브라우저를 조작하는 사람은 클라이언트라고 하지 않습니다. 클라이언트 - 서버 관계에서 사람은 논외 대상이에요 햇갈리지 마시길 바랍니다. 웹 브라우저를 조작하는 건 꼭 사람이 아니라 자동화된 SW가 될 수도 있고, 다른 기계가 될 수도 있기 때문입니다.

 

 

어쨌던 웹 브라우저, 크롬이라고 하면 크롬은 이 받아온 HTML, CSS, JS를 해석해서 저희 컴퓨터의 CPU, GPU를 활용해 웹 사이트를 적절히 그리게 됩니다.

 

그러면 네이버 서버쪽 컴퓨터에 접속해서 HTML, CSS, JS를 저희 컴퓨터쪽에 최종적으로 받아오는 속도가 빠를까요,

아니면 받아온 HTML,CSS, JS를 저희 컴퓨터 CPU가 해석해 브라우저에 그리는게 빠를까요?

 

당연하지만 후자가 압도적으로 빠릅니다. CPU는 인터넷 통신이 비할 수 없을 정도로 빠르죠. VPN으로 외국 IP 대역을 사용해서 인터넷을 써보신 분들은 아실겁니다. 아주 아주 느립니다 ㅎ. 아니면 와이파이 1,2칸 터지는 인터넷 신호가 불안정해서 속도가 안나오는 상황 또는 인터넷 속도가 느려 터져서 게임 다운로드가 1~2시간씩 한참걸리는 상황을 기억해보시면 더 쉽구요.

 

결론적으로 웹브라우저로 인터넷 탐색을 할 때 속도를 끌어내기 위해선 사용자의 컴퓨터 속도보단 네트워크의 속도 개선이 훨씬 중요하다는 셈이 됩니다.

 

네트워크 속도 개선을 위해 하는 여러 행위 중 하나가 바로 JS를 압축하는 행위입니다. 아까도 말했듯이 웹브라우저가 웹 문서를 얻어내기 위해 HTML, CSS, JS를 상대방 서버에서 얻어오는데 JS를 압축해서 주면 더 적은양의 데이터로 서버에서 전송이 가능하고 더 적은양의 데이터는 가벼우니 더 빠르게 전송이 되어 결론적으로 웹사이트 속도를 빠르게 하는 원동력이 되겠죠!

 

추가로 코드가 짧아졌다는건 CPU 쪽에 처리도 더 빠르게 할 수 있다는 뜻이 됩니다. 네트워크 속도도 빠르게 하고 JS를 해석하는 CPU 쪽에서도 속도가 빨라지고 일석 이조인 셈 입니다.

 

물론 변수명을 e,t,n 으로 간략화 함에 따라 원래 코드를 읽기 어렵게 해서 약간의 코드 보호 및 난독화를 하는 효과도 있구요. 그럼에도 엄밀히 이야기 하자면 난독화보단 압축(Minify) 에 가깝습니다.

 

https://daram2-everyday.tistory.com/36

 

[Javascript] - min.js 등 min 파일이란?

템플릿을 다운받아보면 min파일을 자주 보게 된다. min파일은 minify (축소하다)의 줄임으로 공백과 줄 바꿈을 제거하여 용량을 줄인 파일이다. 덕분에 전송량을 줄일 수 있다. .js파일은 가독성(들

daram2-everyday.tistory.com

보통 js 파일에 min.js 같이 min이 붙어 있는게 있는데 이게 바로 js를 압축해서 공백과 줄 바꿈을 지운 파일이랍니다. + 추가로 위 코드에서 봤듯이 e,t,n 과 같은 짧은 변수 이름으로 압축을 시도하구요.

어떻게든 속도를 빠르게 끌어내기 위한 발악인 것 입니다.

 

function nextprev() {
  const e = document.querySelector(".wowed");
  if (!e) return !1;
  const t = e.querySelector(".totalprev"),
    n = e.querySelector(".totalnext"),
    a = document.getElementById("Parse_Area");
  if (t) a.appendChild(t);
  else {
    const e = document.createElement("div"),
      t = e.cloneNode(!1),
      n = document.createElement("span");
    (e.className = "totalprev"),
      (t.className = "thumb5"),
      (n.className = "prevv2"),
      (n.textContent = "글이 없거나, 비공개/보호글"),
      e.appendChild(t),
      e.appendChild(n),
      a.appendChild(e);
  }
  if ((console.log("work"), n)) a.appendChild(n);
  else {
    const e = document.createElement("div"),
      t = e.cloneNode(!1),
      n = document.createElement("span");
    (e.className = "totalnext"),
      (t.className = "thumb5"),
      (n.className = "nextt2"),
      (n.textContent = "글이 없거나, 비공개/보호글"),
      e.appendChild(t),
      e.appendChild(n),
      a.appendChild(e);
  }
}

본론으로 돌아와서 이 코드는 기능적으로 설명하면 제 블로그에서 이전 글, 다음 글을 만들어내는 코드인데요. 난독화는 아니고 압축이 된 것이지만 사실 e,t,n 으로 바뀐걸 읽는 저희 입장에선 난독화나 다름 없습니다. 이걸 보기 좋게 바꾸는건 역시 그냥 AI를 사용하면 됩니다.

 

위에서 언급한 프롬프트를 기반으로 AI에게 요청해 변수명이랑 함수명만 다시 매겨주세요. [여기선 함수명이 없긴 하지만]

 

// 다음 글과 이전 글을 표시하는 함수
function handle_next_prev() {
  // .wowed 클래스를 가진 요소를 선택
  const current_element = document.querySelector(".wowed");
  
  // .wowed 요소가 없으면 함수 종료
  if (!current_element) return false;

  // 현재 요소에서 .totalprev와 .totalnext 요소를 찾음
  const prev_element = current_element.querySelector(".totalprev");
  const next_element = current_element.querySelector(".totalnext");

  // #Parse_Area라는 ID를 가진 요소를 선택
  const parse_area = document.getElementById("Parse_Area");

  // 이전 요소가 있으면 #Parse_Area에 추가
  if (prev_element) {
    parse_area.appendChild(prev_element);
  } else {
    // 이전 요소가 없으면 새로운 div와 span을 생성하여 #Parse_Area에 추가
    const new_prev_div = document.createElement("div");
    const new_prev_thumb = new_prev_div.cloneNode(false);  // 새로운 썸네일 div
    const new_prev_text = document.createElement("span");

    // 생성된 요소에 클래스 이름과 텍스트를 할당
    new_prev_div.className = "totalprev";
    new_prev_thumb.className = "thumb5";
    new_prev_text.className = "prevv2";
    new_prev_text.textContent = "글이 없거나, 비공개/보호글";

    // 새로운 div에 썸네일과 텍스트를 추가하고, 이를 #Parse_Area에 추가
    new_prev_div.appendChild(new_prev_thumb);
    new_prev_div.appendChild(new_prev_text);
    parse_area.appendChild(new_prev_div);
  }

  // 다음 요소가 있으면 #Parse_Area에 추가
  if (next_element) {
    parse_area.appendChild(next_element);
  } else {
    // 다음 요소가 없으면 새로운 div와 span을 생성하여 #Parse_Area에 추가
    const new_next_div = document.createElement("div");
    const new_next_thumb = new_next_div.cloneNode(false);  // 새로운 썸네일 div
    const new_next_text = document.createElement("span");

    // 생성된 요소에 클래스 이름과 텍스트를 할당
    new_next_div.className = "totalnext";
    new_next_thumb.className = "thumb5";
    new_next_text.className = "nextt2";
    new_next_text.textContent = "글이 없거나, 비공개/보호글";

    // 새로운 div에 썸네일과 텍스트를 추가하고, 이를 #Parse_Area에 추가
    new_next_div.appendChild(new_next_thumb);
    new_next_div.appendChild(new_next_text);
    parse_area.appendChild(new_next_div);
  }

  // 작업 완료 로그 출력
  console.log("work");
}

GPT로 얻어낸 결과입니다. 역시 깔끔하네요 ㅎㅎ. 참고로 return !1; 같은 코드가 있었는데 1의 not, 반대는 0이므로 사실 return false 랑 같은것 입니다. 이런것도 보기 안좋았는데 GPT가 알아서 처리를 해주었네요.

 

하지만 다시 언급하지만 GPT의 결과를 무지성으로 적용시키지 말고 원본 코드랑 비교해가면서 이름만 매기는걸 권장합니다. 또 복구한 코드가 잘 작동한다면 이제 웹 사이트에 업로드 할때는 Minify나 난독화 과정을 거쳐서 다시 올려주는게 코드 보호나 속도 측면에서 좋겠죠?

 

난독화 해제 도구는 무적이 아니다

사실 이 글의 예제까지 읽어보면 뭔가 난독화는 항상 풀리고 난독화 해제 도구는 무적이다! 라고 생각할 수 있겠지만 전혀 그렇지 않습니다.

 

메이저한 난독화 도구야 그 작동 원리가 잘 알려져 있어서 그 난독화 과정을 역으로 진행해서 풀 수 있는거고 이름 없는 무명의 복잡한 난독화 도구를 사용한다면 시중에 있는 도구로 풀 수 없는 경우가 거의 99.9% 입니다. 

 

이럴때에는 그 무명의 난독화 도구를 얻어낸 다음에 그 난독화 도구가 어떤 입력을 넣으면 어떻게 출력을 뱉는지 연관성을 조사한다던가, 가능하다면 난독화 도구의 소스코드를 분석해 작동 원리를 알아낸다던가.

그 난독화 도구의 소스코드를 얻어낼 수 없다면 리버싱해서 꾸역 꾸역 원리를 찾아야 한다던가,

아니면 더 최악의 상황으로 그 이름 모를 난독화 도구가 인터넷으로 구할 수 없다던가

 

정말 정말 많은 경우의 수가 있습니다. 물론 네트워크로 전송해야하는 웹 통신 특성상 html,css,js는 공개된 형태로 전송되어 보안에 약점이 존재할 수 밖에 없겠다만 js의 경우 난독화가 항상 풀리는게 아님에 염두해 둡시다.

 

 이는 꼭 js 뿐만이 아닌 모든 프로그래밍 언어에 적용됩니다.

 

 

 

출처

https://bipark.tistory.com/147

 

블랙박스 테스트

블랙박스 테스트블랙박스 테스트는 애플리케이션의 내부 동작 방식을 알지 못하고 기능성을 검사하는 소프트웨어 테스트 방법이다. 테스터는 코드나 설계에 대한 지식 없이 외부 

bipark.tistory.com

https://catsbi.oopy.io/7c084479-c9d0-44a1-acb9-f6b43a19e332

 

블랙박스 테스트, 화이트박스 테스트

블랙박스 테스트(Black Box Test)

catsbi.oopy.io

 

SNS 공유하기
네이버밴드
카카오톡
페이스북
X(트위터)

최근글
인기글
이모티콘창 닫기
울음
안녕
감사
당황
피폐