Full Code of smallfawn/decode_action for AI

main 20668835fb10 cached
44 files
213.5 KB
79.7k tokens
145 symbols
1 requests
Download .txt
Showing preview only (231K chars total). Download the full file or copy to clipboard to get everything.
Repository: smallfawn/decode_action
Branch: main
Commit: 20668835fb10
Files: 44
Total size: 213.5 KB

Directory structure:
gitextract_x1jt3_r8/

├── .github/
│   └── workflows/
│       └── decode.yml
├── README.md
├── input.js
├── input.py
├── output.js
├── output.py
├── package.json
└── src/
    ├── decode.py
    ├── main.js
    ├── plugin/
    │   ├── awsc.js
    │   ├── common.js
    │   ├── eval.js
    │   ├── jjencode.js
    │   ├── jsconfuser.js
    │   ├── obfuscator.js
    │   ├── sojson.js
    │   └── sojsonv7.js
    ├── utility/
    │   ├── check-func.js
    │   └── safe-func.js
    └── visitor/
        ├── calculate-constant-exp.js
        ├── calculate-rstring.js
        ├── delete-extra.js
        ├── delete-illegal-return.js
        ├── delete-nested-blocks.js
        ├── delete-unreachable-code.js
        ├── delete-unused-var.js
        ├── jsconfuser/
        │   ├── anti-tooling.js
        │   ├── control-flow.js
        │   ├── duplicate-literal.js
        │   ├── global-concealing.js
        │   ├── global.js
        │   ├── minify.js
        │   ├── opaque-predicates.js
        │   ├── stack.js
        │   ├── string-compression.js
        │   └── string-concealing.js
        ├── lint-if-statement.js
        ├── merge-object.js
        ├── parse-control-flow-storage.js
        ├── prune-if-branch.js
        ├── split-assignment.js
        ├── split-member-object.js
        ├── split-sequence.js
        └── split-variable-declaration.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/decode.yml
================================================
name: Decode JavaScript File

on:
  push:
    branches:
      - main
jobs:
  decode:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    - name: Execute Python script for decoding
      run: |
        python src/decode.py

    - name: Install dependencies and run decode
      run: |
        npm install
        npm run decode -- [-i input.js] [-o output.js]
    - name: Configure Git author
      run: |
        git config --local user.email "action@github.com"
        git config --local user.name "action"
    - name: Save decoded output to repository
      run: |
        git status
        ls
        git add output.js
        git add output.py
        git commit -m "Add decoded output file"
        git push
        


================================================
FILE: README.md
================================================
# QLScriptpublic


fork仓库后 把待解密的脚本放入到input.js里面 等待60s左右即可在output.js看到解密脚本

python脚本同理 放入到input.py里面 等待60s左右即可在output.py看到解密脚本

目前支持zlib bz2 lzma

刚使用action的人手动点一下仓库上方action 初始化

脚本自适应检测加密方式

支持sojson [源jsjiami.v6]

支持sojsonv7 [源jsjiami.v7]

支持obfuscator [市面上通用加密]

支持awsc [阿里云混淆]

支持jjencode [源jjencode]

支持common [common]


================================================
FILE: input.js
================================================
function _0x4d77(){const _0x2f79e5=['\u67e5\u8be2\u521d\u59cb\u79ef\u5206\u51fa\u9519\uff1a','\x70\x61\x74\x68','\x5b\u274c\x20\x45\x52\x52\x4f\x52\x5d\x20\u6ce8\u518c\u94fe\u63a5\u88ab\u4fee\u6539\uff01','\x5b\u274c\x20\x45\x52\x52\x4f\x52\x5d\x20\u73af\u5883\u53d8\u91cf','\x34\x76\x4c\x52\x6b\x61\x4a','\x50\x4f\x53\x54','\uff08\x63\x6f\x64\x65\uff1a','\x6d\x73\x67','\u79ef\u5206\u53d8\u52a8\uff1a','\u7684\x43\x4b\u5df2\u4fdd\u5b58\u5230\u672c\u5730\x5d','\x65\x78\x69\x73\x74\x73\x53\x79\x6e\x63','\x72\x65\x73\x6f\x6c\x76\x65','\x34\x77\x50\x42\x6a\x69\x45','\x68\x74\x74\x70\x3a\x2f\x2f\x68\x35\x2e\x79\x69\x64\x69\x6e\x67\x79\x75\x65\x63\x68\x65\x6e\x67\x2e\x63\x6f\x6d\x2f\x61\x70\x69\x2f\x6d\x69\x73\x73\x69\x6f\x6e\x2f\x73\x69\x67\x6e','\x6e\x61\x6d\x65','\x5b\u274c\x20\x45\x52\x52\x4f\x52\x5d\x20\u5f53\u524d\u94fe\u63a5\uff1a','\x6a\x73\x6f\x6e','\x0a\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d','\x31\x34\x6c\x43\x4c\x42\x4c\x6d','\x5b\u26a0\ufe0f\x20\u8d26\u53f7\u683c\u5f0f\u9519\u8bef\uff1a','\x6d\x6b\x64\x69\x72\x53\x79\x6e\x63','\x74\x6f\x46\x69\x78\x65\x64','\x61\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x2f\x6a\x73\x6f\x6e','\x5b\u274c\x20\x45\x52\x52\x4f\x52\x5d\x20\u811a\u672c\u6821\u9a8c\u5931\u8d25\uff1a','\x72\x65\x61\x64\x46\x69\x6c\x65\x53\x79\x6e\x63','\u7b7e\u5230\u540e\u79ef\u5206\uff1a','\x5b\u274c\x20\u8d26\u53f7','\x31\x30\x57\x78\x74\x63\x6c\x65','\x3d\x3d\x3d\x3d\x3d\x20\u516c\u544a\u4fe1\u606f\x20\x3d\x3d\x3d\x3d\x3d','\u7684\x43\x4b\u6587\u4ef6\uff0c\u9a8c\u8bc1\u6709\u6548\u6027\x2e\x2e\x2e\x5d','\x65\x78\x69\x74','\u8131\u654f\u59d3\u540d\uff1a','\u7684\x43\x4b\u6587\u4ef6\uff0c\u6267\u884c\u767b\u5f55\x2e\x2e\x2e\x5d','\x73\x70\x6c\x69\x74','\x73\x74\x72\x69\x6e\x67\x69\x66\x79','\x75\x74\x66\x2d\x38','\x5b\u2139\ufe0f\x20\u672a\u627e\u5230\u8d26\u53f7','\x66\x69\x6c\x74\x65\x72','\x73\x75\x63\x63\x65\x73\x73','\x36\x34\x31\x38\x31\x38\x34\x5a\x4a\x5a\x50\x48\x43','\x64\x61\x74\x61','\x2e\x74\x78\x74','\u914d\u7f6e\u4e3a\u7a7a\uff0c\u8bf7\u68c0\u67e5','\x0a\ud83d\udcbb\x20\u9752\u9f99\u811a\u672c\uff1a\x68\x74\x74\x70\x73\x3a\x2f\x2f\x70\x61\x6e\x2e\x71\x75\x61\x72\x6b\x2e\x63\x6e\x2f\x73\x2f\x61\x34\x30\x64\x66\x33\x35\x38\x36\x38\x65\x33\x0a\ud83d\udcac\x20\u4f01\u9e45\u7fa4\u804a\uff1a\x68\x74\x74\x70\x73\x3a\x2f\x2f\x71\x6d\x2e\x71\x71\x2e\x63\x6f\x6d\x2f\x71\x2f\x75\x74\x37\x59\x4d\x6d\x6f\x4b\x59\x77\x0a\ud83d\udcf1\x20\u4f01\u9e45\u9891\u9053\uff1a\x68\x74\x74\x70\x73\x3a\x2f\x2f\x70\x64\x2e\x71\x71\x2e\x63\x6f\x6d\x2f\x73\x2f\x39\x79\x6d\x63\x71\x6b\x73\x31\x33\x0a\x20\x20\x20\x20','\u8bfb\u53d6\x2f\u6821\u9a8c\x43\x4b\u5931\u8d25\uff1a','\x6d\x61\x74\x63\x68','\x5b\u2705\x20\x53\x55\x43\x43\x45\x53\x53\x5d\x20\u6ce8\u518c\u94fe\u63a5\u6821\u9a8c\u901a\u8fc7','\uff0c\u8bf7\u6dfb\u52a0\u8d26\u53f7\u5bc6\u7801\uff08\u683c\u5f0f\uff1a\u624b\u673a\u53f7\x7c\u5bc6\u7801\x20\u56de\u8f66\u5206\u9694\uff09','\x34\x30\x30\x30\x35\x38\x35\x54\x6b\x6d\x56\x56\x57','\x65\x6e\x76','\x36\x38\x30\x36\x36\x39\x34\x67\x74\x72\x52\x64\x4c','\x31\x38\x31\x36\x32\x30\x33\x4f\x4b\x57\x4b\x5a\x63','\x68\x74\x74\x70\x3a\x2f\x2f\x68\x35\x2e\x79\x69\x64\x69\x6e\x67\x79\x75\x65\x63\x68\x65\x6e\x67\x2e\x63\x6f\x6d\x2f\x23\x2f\x70\x61\x67\x65\x73\x2f\x72\x65\x67\x69\x73\x74\x65\x72\x2f\x69\x6e\x64\x65\x78\x3f\x70\x72\x6f\x6d\x6f\x43\x6f\x64\x65\x3d\x50\x4f\x43\x31\x33\x30\x31\x35\x39','\u672a\u627e\u5230\u300c\x2f\x2a\x20\u6ce8\u518c\u94fe\u63a5\x3a\x20\x78\x78\x78\x20\x2a\x2f\u300d\u683c\u5f0f\u7684\u6ce8\u91ca','\x63\x6f\x64\x65','\u7b7e\u5230\u524d\u79ef\u5206\uff1a','\x31\x36\x35\x37\x32\x33\x38\x34\x47\x51\x68\x52\x55\x78','\x0a\x5b\u2705\x20\u6240\u6709\u8d26\u53f7\u5904\u7406\u5b8c\u6210\x5d','\x6c\x6f\x67','\x5b\u2139\ufe0f\x20\u5f00\u59cb\u5904\u7406\u8d26\u53f7\uff1a','\x68\x74\x74\x70\x3a\x2f\x2f\x68\x35\x2e\x79\x69\x64\x69\x6e\x67\x79\x75\x65\x63\x68\x65\x6e\x67\x2e\x63\x6f\x6d\x2f','\x68\x74\x74\x70\x3a\x2f\x2f\x68\x35\x2e\x79\x69\x64\x69\x6e\x67\x79\x75\x65\x63\x68\x65\x6e\x67\x2e\x63\x6f\x6d\x2f\x61\x70\x69\x2f\x75\x73\x65\x72\x2f\x69\x6e\x66\x6f','\x0a\x5b\u26a0\ufe0f\x20\u8d26\u53f7','\x5b\u274c\x20\x45\x52\x52\x4f\x52\x5d\x20\u5408\u6cd5\u94fe\u63a5\uff1a','\x6d\x65\x73\x73\x61\x67\x65','\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x4c\x69\x6e\x75\x78\x3b\x20\x41\x6e\x64\x72\x6f\x69\x64\x20\x31\x30\x3b\x20\x4b\x29\x20\x41\x70\x70\x6c\x65\x57\x65\x62\x4b\x69\x74\x2f\x35\x33\x37\x2e\x33\x36\x20\x28\x4b\x48\x54\x4d\x4c\x2c\x20\x6c\x69\x6b\x65\x20\x47\x65\x63\x6b\x6f\x29\x20\x43\x68\x72\x6f\x6d\x65\x2f\x31\x33\x33\x2e\x30\x2e\x30\x2e\x30\x20\x4d\x6f\x62\x69\x6c\x65\x20\x53\x61\x66\x61\x72\x69\x2f\x35\x33\x37\x2e\x33\x36\x20\x45\x64\x67\x41\x2f\x31\x33\x33\x2e\x30\x2e\x30\x2e\x30','\x68\x74\x74\x70\x3a\x2f\x2f\x68\x35\x2e\x79\x69\x64\x69\x6e\x67\x79\x75\x65\x63\x68\x65\x6e\x67\x2e\x63\x6f\x6d\x2f\x61\x70\x69\x2f\x75\x73\x65\x72\x2f\x6c\x6f\x67\x69\x6e','\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d','\x6c\x65\x6e\x67\x74\x68','\u7684\x43\x4b\u8fc7\u671f\x2f\u65e0\u6548\uff0c\u51c6\u5907\u91cd\u65b0\u767b\u5f55\x2e\x2e\x2e\x5d','\x5b\u274c\x20\x45\x52\x52\x4f\x52\x5d\x20\u672a\u914d\u7f6e\u73af\u5883\u53d8\u91cf','\x79\x64\x79\x63\x5f\x7a\x6d','\u67e5\u8be2\u6700\u7ec8\u79ef\u5206\u51fa\u9519\uff1a','\x70\x6f\x69\x6e\x74','\x6d\x61\x70','\x34\x34\x30\x37\x39\x38\x59\x56\x6d\x61\x4b\x43','\u767b\u5f55\u51fa\u9519\uff1a','\x74\x72\x69\x6d','\u5f00\u59cb\u767b\u5f55\x2e\x2e\x2e\x5d','\x31\x34\x36\x38\x36\x30\x37\x38\x46\x6f\x4d\x69\x6f\x68','\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d\x3d','\x6d\x61\x72\x6b\x2e\x76\x69\x61\x2e\x67\x70','\x68\x74\x74\x70\x3a\x2f\x2f\x68\x35\x2e\x79\x69\x64\x69\x6e\x67\x79\x75\x65\x63\x68\x65\x6e\x67\x2e\x63\x6f\x6d','\x0a\x5b\u2705\x20\u8d26\u53f7','\uff0c\u91cd\u65b0\u767b\u5f55\x2e\x2e\x2e\x5d','\x5b\u2705\x20\u8d26\u53f7','\x67\x7a\x69\x70\x2c\x20\x64\x65\x66\x6c\x61\x74\x65'];_0x4d77=function(){return _0x2f79e5;};return _0x4d77();}const _0x532065=_0x2ddd;(function(_0x41e400,_0x4daf27){const _0x325cf9=_0x2ddd,_0xbde78c=_0x41e400();while(!![]){try{const _0x153720=-parseInt(_0x325cf9(0x190))/0x1*(parseInt(_0x325cf9(0x178))/0x2)+parseInt(_0x325cf9(0x160))/0x3*(-parseInt(_0x325cf9(0x188))/0x4)+-parseInt(_0x325cf9(0x15d))/0x5+parseInt(_0x325cf9(0x15f))/0x6+-parseInt(_0x325cf9(0x196))/0x7*(-parseInt(_0x325cf9(0x154))/0x8)+parseInt(_0x325cf9(0x165))/0x9+-parseInt(_0x325cf9(0x19f))/0xa*(parseInt(_0x325cf9(0x17c))/0xb);if(_0x153720===_0x4daf27)break;else _0xbde78c['push'](_0xbde78c['shift']());}catch(_0x8a31e6){_0xbde78c['push'](_0xbde78c['shift']());}}}(_0x4d77,0xe9ecf));function printNotice(){const _0x2655bb=_0x2ddd;console[_0x2655bb(0x167)](_0x2655bb(0x1a0)),console[_0x2655bb(0x167)](_0x2655bb(0x158)[_0x2655bb(0x17a)]()),console['\x6c\x6f\x67'](_0x2655bb(0x170));}printNotice();const fs=require('\x66\x73'),path=require(_0x532065(0x185)),REQUIRED_REG_LINK=_0x532065(0x161),CK_DIR=path[_0x532065(0x18f)](__dirname,'\x79\x64\x79\x63\x5f\x63\x6b'),ENV_NAME=_0x532065(0x174);function desensitizeMobile(_0x3e5dea){const _0x36b811=_0x532065;if(!_0x3e5dea||_0x3e5dea[_0x36b811(0x171)]!==0xb)return _0x3e5dea;return _0x3e5dea['\x72\x65\x70\x6c\x61\x63\x65'](/(\d{3})\d{4}(\d{4})/,'\x24\x31\x2a\x2a\x2a\x2a\x24\x32');}function checkRegLink(){const _0x142061=_0x532065;try{const _0x208b14=/\/\*\s*注册链接:\s*(.+?)\s*\*\//,_0x3fe438=__filename,_0x36b24f=fs['\x72\x65\x61\x64\x46\x69\x6c\x65\x53\x79\x6e\x63'](_0x3fe438,'\x75\x74\x66\x2d\x38'),_0x1f4952=_0x36b24f['\x73\x70\x6c\x69\x74'](/\r?\n/);let _0x48d03f='';for(let _0x4e4b88 of _0x1f4952){const _0x505f8e=_0x4e4b88[_0x142061(0x17a)]()[_0x142061(0x15a)](_0x208b14);if(_0x505f8e&&_0x505f8e[0x1]){_0x48d03f=_0x505f8e[0x1][_0x142061(0x17a)]();break;}}if(!_0x48d03f)throw new Error(_0x142061(0x162));if(_0x48d03f!==REQUIRED_REG_LINK){console['\x6c\x6f\x67'](_0x142061(0x186)),console[_0x142061(0x167)](_0x142061(0x16c)+REQUIRED_REG_LINK),console[_0x142061(0x167)](_0x142061(0x193)+_0x48d03f);throw new Error('\u6ce8\u518c\u94fe\u63a5\u6821\u9a8c\u5931\u8d25\x0a\u8bf7\u5230\u4f5c\u8005\u7f51\u76d8\u91cc\u4e0b\u8f7d\u6b63\u7248');}console[_0x142061(0x167)](_0x142061(0x15b));}catch(_0xca380e){console[_0x142061(0x167)](_0x142061(0x19b)+_0xca380e['\x6d\x65\x73\x73\x61\x67\x65']),process[_0x142061(0x1a2)](0x1);}}function _0x2ddd(_0x2a5c8e,_0x1d1cb5){const _0x4d7723=_0x4d77();return _0x2ddd=function(_0x2ddddc,_0x5bb5b1){_0x2ddddc=_0x2ddddc-0x151;let _0x24c9ee=_0x4d7723[_0x2ddddc];return _0x24c9ee;},_0x2ddd(_0x2a5c8e,_0x1d1cb5);}function commonHeaders(_0x39001f){const _0x4205b5=_0x532065;return{'\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74':_0x4205b5(0x16e),'\x41\x63\x63\x65\x70\x74\x2d\x45\x6e\x63\x6f\x64\x69\x6e\x67':_0x4205b5(0x183),'\x43\x6f\x6e\x74\x65\x6e\x74\x2d\x54\x79\x70\x65':_0x4205b5(0x19a),'\x73\x6f\x75\x72\x63\x65':'\x68\x35','\x74\x6f\x6b\x65\x6e':_0x39001f,'\x4f\x72\x69\x67\x69\x6e':_0x4205b5(0x17f),'\x58\x2d\x52\x65\x71\x75\x65\x73\x74\x65\x64\x2d\x57\x69\x74\x68':_0x4205b5(0x17e),'\x52\x65\x66\x65\x72\x65\x72':_0x4205b5(0x169),'\x41\x63\x63\x65\x70\x74\x2d\x4c\x61\x6e\x67\x75\x61\x67\x65':'\x7a\x68\x2d\x43\x4e\x2c\x7a\x68\x3b\x71\x3d\x30\x2e\x39\x2c\x65\x6e\x2d\x55\x53\x3b\x71\x3d\x30\x2e\x38\x2c\x65\x6e\x3b\x71\x3d\x30\x2e\x37'};}async function handleAccount(_0x30df42,_0x4c6efc){const _0x286330=_0x532065,_0x233eed=desensitizeMobile(_0x30df42);console[_0x286330(0x167)](_0x286330(0x195)),console[_0x286330(0x167)](_0x286330(0x168)+_0x233eed+'\x5d'),console['\x6c\x6f\x67'](_0x286330(0x17d));!fs['\x65\x78\x69\x73\x74\x73\x53\x79\x6e\x63'](CK_DIR)&&fs[_0x286330(0x198)](CK_DIR,{'\x72\x65\x63\x75\x72\x73\x69\x76\x65':!![]});const _0xc31206=path[_0x286330(0x18f)](CK_DIR,_0x30df42+_0x286330(0x156));let _0xb1c840='';try{if(fs[_0x286330(0x18e)](_0xc31206)){_0xb1c840=fs[_0x286330(0x19c)](_0xc31206,_0x286330(0x1a7))[_0x286330(0x17a)](),console['\x6c\x6f\x67']('\x5b\u2139\ufe0f\x20\u8bfb\u53d6\u5230\u8d26\u53f7'+_0x233eed+_0x286330(0x1a1));const _0x39c434=await fetch(_0x286330(0x16a),{'\x6d\x65\x74\x68\x6f\x64':'\x50\x4f\x53\x54','\x68\x65\x61\x64\x65\x72\x73':commonHeaders(_0xb1c840),'\x62\x6f\x64\x79':JSON[_0x286330(0x1a6)]({})}),_0x3fb30d=await _0x39c434[_0x286330(0x194)]();_0x3fb30d[_0x286330(0x153)]&&_0x3fb30d[_0x286330(0x163)]===0x0?console[_0x286330(0x167)](_0x286330(0x182)+_0x233eed+'\u7684\x43\x4b\u6709\u6548\uff0c\u76f4\u63a5\u4f7f\u7528\x5d'):(console[_0x286330(0x167)]('\x5b\u26a0\ufe0f\x20\u8d26\u53f7'+_0x233eed+_0x286330(0x172)),_0xb1c840='');}else console[_0x286330(0x167)](_0x286330(0x151)+_0x233eed+_0x286330(0x1a4));}catch(_0x57444d){console[_0x286330(0x167)]('\x5b\u26a0\ufe0f\x20\u8d26\u53f7'+_0x233eed+_0x286330(0x159)+_0x57444d[_0x286330(0x16d)]+_0x286330(0x181)),_0xb1c840='';}if(!_0xb1c840)try{console[_0x286330(0x167)]('\x5b\u2139\ufe0f\x20\u8d26\u53f7'+_0x233eed+_0x286330(0x17b));const _0x2de728={'\x6d\x6f\x62\x69\x6c\x65':_0x30df42,'\x70\x61\x73\x73\x77\x6f\x72\x64':_0x4c6efc},_0x1b47e=await fetch(_0x286330(0x16f),{'\x6d\x65\x74\x68\x6f\x64':'\x50\x4f\x53\x54','\x68\x65\x61\x64\x65\x72\x73':commonHeaders(''),'\x62\x6f\x64\x79':JSON[_0x286330(0x1a6)](_0x2de728)}),_0x2cce7a=await _0x1b47e[_0x286330(0x194)]();if(_0x2cce7a['\x73\x75\x63\x63\x65\x73\x73']&&_0x2cce7a[_0x286330(0x163)]===0x0)_0xb1c840=_0x2cce7a[_0x286330(0x155)],console['\x6c\x6f\x67'](_0x286330(0x182)+_0x233eed+'\u767b\u5f55\u6210\u529f\x5d'),fs['\x77\x72\x69\x74\x65\x46\x69\x6c\x65\x53\x79\x6e\x63'](_0xc31206,_0xb1c840,_0x286330(0x1a7)),console[_0x286330(0x167)](_0x286330(0x182)+_0x233eed+_0x286330(0x18d));else throw new Error('\u767b\u5f55\u5931\u8d25\uff1a'+_0x2cce7a[_0x286330(0x18b)]+_0x286330(0x18a)+_0x2cce7a[_0x286330(0x163)]+'\uff09');}catch(_0x2332c6){console[_0x286330(0x167)]('\x5b\u274c\x20\u8d26\u53f7'+_0x233eed+_0x286330(0x179)+_0x2332c6[_0x286330(0x16d)]+'\x5d');return;}let _0x93744d=0x0,_0xd5d7f7='';try{const _0x2f6efd=await fetch(_0x286330(0x16a),{'\x6d\x65\x74\x68\x6f\x64':_0x286330(0x189),'\x68\x65\x61\x64\x65\x72\x73':commonHeaders(_0xb1c840),'\x62\x6f\x64\x79':JSON[_0x286330(0x1a6)]({})}),_0x57d838=await _0x2f6efd[_0x286330(0x194)]();if(_0x57d838['\x73\x75\x63\x63\x65\x73\x73']&&_0x57d838[_0x286330(0x163)]===0x0){const _0x5c3dff=_0x57d838[_0x286330(0x155)];_0xd5d7f7=_0x5c3dff[_0x286330(0x192)][_0x286330(0x171)]>0x1?_0x5c3dff[_0x286330(0x192)][0x0]+'\x2a'['\x72\x65\x70\x65\x61\x74'](_0x5c3dff[_0x286330(0x192)][_0x286330(0x171)]-0x1):_0x5c3dff[_0x286330(0x192)],_0x93744d=Number(_0x5c3dff[_0x286330(0x176)])[_0x286330(0x199)](0x2),console['\x6c\x6f\x67'](_0x286330(0x180)+_0x233eed+'\u521d\u59cb\u4fe1\u606f\x5d'),console['\x6c\x6f\x67'](_0x286330(0x1a3)+_0xd5d7f7),console[_0x286330(0x167)](_0x286330(0x164)+_0x93744d+'\x20\u5206');}else throw new Error('\u67e5\u8be2\u521d\u59cb\u4fe1\u606f\u5931\u8d25\uff1a'+_0x57d838[_0x286330(0x18b)]);}catch(_0x32f31b){console['\x6c\x6f\x67'](_0x286330(0x19e)+_0x233eed+_0x286330(0x184)+_0x32f31b[_0x286330(0x16d)]+'\x5d');return;}try{const _0x11f4e6=await fetch(_0x286330(0x191),{'\x6d\x65\x74\x68\x6f\x64':'\x50\x4f\x53\x54','\x68\x65\x61\x64\x65\x72\x73':commonHeaders(_0xb1c840),'\x62\x6f\x64\x79':JSON[_0x286330(0x1a6)]({})}),_0x172df5=await _0x11f4e6[_0x286330(0x194)]();_0x172df5[_0x286330(0x153)]||_0x172df5[_0x286330(0x163)]===0x0?console['\x6c\x6f\x67']('\x0a\x5b\u2705\x20\u8d26\u53f7'+_0x233eed+'\u7b7e\u5230\u7ed3\u679c\x5d\x20'+_0x172df5[_0x286330(0x18b)]):console[_0x286330(0x167)](_0x286330(0x16b)+_0x233eed+'\u7b7e\u5230\u63d0\u793a\x5d\x20'+_0x172df5[_0x286330(0x18b)]);}catch(_0xc6a3ce){console[_0x286330(0x167)](_0x286330(0x19e)+_0x233eed+'\u7b7e\u5230\u51fa\u9519\uff1a'+_0xc6a3ce['\x6d\x65\x73\x73\x61\x67\x65']+'\x5d');}try{const _0x2bd980=await fetch('\x68\x74\x74\x70\x3a\x2f\x2f\x68\x35\x2e\x79\x69\x64\x69\x6e\x67\x79\x75\x65\x63\x68\x65\x6e\x67\x2e\x63\x6f\x6d\x2f\x61\x70\x69\x2f\x75\x73\x65\x72\x2f\x69\x6e\x66\x6f',{'\x6d\x65\x74\x68\x6f\x64':'\x50\x4f\x53\x54','\x68\x65\x61\x64\x65\x72\x73':commonHeaders(_0xb1c840),'\x62\x6f\x64\x79':JSON[_0x286330(0x1a6)]({})}),_0x2d7910=await _0x2bd980['\x6a\x73\x6f\x6e']();if(_0x2d7910[_0x286330(0x153)]&&_0x2d7910[_0x286330(0x163)]===0x0){const _0x3611cb=Number(_0x2d7910[_0x286330(0x155)][_0x286330(0x176)])[_0x286330(0x199)](0x2),_0x19cf59=(_0x3611cb-_0x93744d)[_0x286330(0x199)](0x2);console[_0x286330(0x167)](_0x286330(0x180)+_0x233eed+'\u6700\u7ec8\u4fe1\u606f\u6c47\u603b\x5d'),console['\x6c\x6f\x67'](_0x286330(0x1a3)+_0xd5d7f7),console['\x6c\x6f\x67'](_0x286330(0x164)+_0x93744d+'\x20\u5206'),console[_0x286330(0x167)](_0x286330(0x19d)+_0x3611cb+'\x20\u5206'),console['\x6c\x6f\x67'](_0x286330(0x18c)+(_0x19cf59>0x0?'\x2b':'')+_0x19cf59+'\x20\u5206');}else throw new Error('\u67e5\u8be2\u6700\u7ec8\u4fe1\u606f\u5931\u8d25\uff1a'+_0x2d7910[_0x286330(0x18b)]);}catch(_0x10d25c){console[_0x286330(0x167)](_0x286330(0x19e)+_0x233eed+_0x286330(0x175)+_0x10d25c[_0x286330(0x16d)]+'\x5d');}}async function main(){const _0x10c2b1=_0x532065;checkRegLink();const _0x55c639=process[_0x10c2b1(0x15e)][ENV_NAME]||'';!_0x55c639&&(console[_0x10c2b1(0x167)](_0x10c2b1(0x173)+ENV_NAME+_0x10c2b1(0x15c)),process[_0x10c2b1(0x1a2)](0x1));const _0x254a32=_0x55c639[_0x10c2b1(0x1a5)](/\r?\n/)[_0x10c2b1(0x152)](_0x138461=>_0x138461[_0x10c2b1(0x17a)]());_0x254a32[_0x10c2b1(0x171)]===0x0&&(console[_0x10c2b1(0x167)](_0x10c2b1(0x187)+ENV_NAME+_0x10c2b1(0x157)),process[_0x10c2b1(0x1a2)](0x1));console[_0x10c2b1(0x167)]('\x5b\u2139\ufe0f\x20\u5171\u68c0\u6d4b\u5230\x20'+_0x254a32[_0x10c2b1(0x171)]+'\x20\u4e2a\u8d26\u53f7\uff0c\u5f00\u59cb\u6279\u91cf\u5904\u7406\x2e\x2e\x2e\x5d');for(const _0x5c13e9 of _0x254a32){const [_0x291b78,_0x5094c5]=_0x5c13e9[_0x10c2b1(0x1a5)]('\x7c')[_0x10c2b1(0x177)](_0x570265=>_0x570265[_0x10c2b1(0x17a)]());if(!_0x291b78||!_0x5094c5){console[_0x10c2b1(0x167)](_0x10c2b1(0x197)+_0x5c13e9+'\uff0c\u8bf7\u6309\u300c\u624b\u673a\u53f7\x7c\u5bc6\u7801\u300d\u683c\u5f0f\u914d\u7f6e\x5d');continue;}await handleAccount(_0x291b78,_0x5094c5);}console[_0x10c2b1(0x167)](_0x10c2b1(0x166));}main();


================================================
FILE: input.py
================================================
# -*- coding: utf-8 -*-

'''
Powered By Beidu
Create at [2024-11-28 21:12]
ENV: ck变量:cj_ck, 卡密变量: CN_Card
CRON: 11 1,3,5,7,9,11,13,15,17,19,22 * * *

'''

import base64, zlib, lzma, bz2, gzip
exec((lambda Gkd: compile(Gkd, '<string>', 'exec'))(zlib.decompress(lzma.decompress(bz2.decompress(gzip.decompress(base64.b64decode('H4sIADJsSGcC/wArQNS/QlpoOTFBWSZTWahC+igAMP7/////////////////////////////////////////////4D+/e9d07u73tqW77z3u+93bvG+83l9977W2++m7ur7r27b19s+951fdHvuvee8+97x996+vt5e9971487bPkPb1tfe+e973t9dfOvO7tne++Kr23vmrube975b57e9vV9pW+3fe7u895vc32n3nb7vvahvN3eee++333Nnd3174dq23053j177Y93329ffeu93OO9973q+729fdva597W11Tu33d7pd9nO7e6vfd7bx77vufI671877t58vny717u3vvPPt77xPL25m+699vvve++7dzm2fd9yuSe7d9fb77yFT2AaATANNRphoDQ1NpGBPQTBkm1TyMTEyMaTTaCYAJowExoJgmI0wRhMTBMTCNNGEwEzQEzEE2pmmINEwBNUOqp/sgE9AaCYAmAAAT0BGJpgmTJ6RgEzSZMTE0wTAJgATE0xMCMNGmjIGgYhoNBpo1GCbU1PTDRogp7QaankxNSiFU/2pgGppip/oJgMENCYTJgAJsqfqYJphMBMaTTAmCPRoBk0DJpoNDTQ0aEDAmTTBDRpoxGExomE0NPQmTJo0wjJkaVEqn/oAGkxo1PJTwTEwmaABMmTMo0ybJpowmQNEwmyaDTIAAU2aBMATE0m2gJgjTSn4E9TaEYmhgJgjCmyanlPFNoyaNNGqiFU/wAAEwBqYaYjAmjAmmho1PJpT2gI9NCZTxTyZGATKnsI0wTCTw0IYNIZJ4BNqPVPAieAKeTKenqnqemJonppNkZMTAExTylDqqf+mCaYBGhkyaaNBMAJ6CaYJiYTamjJiYJ6UeTTVP0YTAmmmAmNI00yZMACaaYDQE9TCepiYjAAIZMaU8ARpo1PUZk1MqIAEAigXQD+IDhBAPfgINCH38AyDpH500cXHcvR03wZHD0NLqdkEPnvd77TPePaD8l+856WxfQZ9LiRSWWrtkB1eZvMce7f7ifIpPeBSk4Ih2wjWQX3/2BvvboFfSBmGHyMGOLOtp2VFEIO46H6K7IaPOPoBDCBSzjA4I39nOFZwaR0nxaJ0xHcs3B6btUMMadg03n4/xv8VybWvmP99pNpzVMqF7ft4Xq36AOSrxGyGrSTukhU/vJ/FBK1/KH90i0Cf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'))))).decode()))


================================================
FILE: output.js
================================================
//Wed Apr 01 2026 00:52:28 GMT+0000 (Coordinated Universal Time)
//Base:<url id="cv1cref6o68qmpt26ol0" type="url" status="parsed" title="GitHub - echo094/decode-js: JS混淆代码的AST分析工具 AST analysis tool for obfuscated JS code" wc="2165">https://github.com/echo094/decode-js</url>
//Modify:<url id="cv1cref6o68qmpt26olg" type="url" status="parsed" title="GitHub - smallfawn/decode_action: 世界上本来不存在加密,加密的人多了,也便成就了解密" wc="741">https://github.com/smallfawn/decode_action</url>
function printNotice() {
  console.log("===== 公告信息 =====");
  console.log("\n💻 青龙脚本:https://pan.quark.cn/s/a40df35868e3\n💬 企鹅群聊:https://qm.qq.com/q/ut7YMmoKYw\n📱 企鹅频道:https://pd.qq.com/s/9ymcqks13\n    ".trim());
  console.log("====================");
}
printNotice();
const fs = require("fs");
const path = require("path");
const REQUIRED_REG_LINK = "http://h5.yidingyuecheng.com/#/pages/register/index?promoCode=POC130159";
const CK_DIR = path.resolve(__dirname, "ydyc_ck");
const ENV_NAME = "ydyc_zm";
function desensitizeMobile(_0x3e5dea) {
  if (!_0x3e5dea || _0x3e5dea.length !== 11) {
    return _0x3e5dea;
  }
  return _0x3e5dea.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
}
function checkRegLink() {
  try {
    const _0x208b14 = /\/\*\s*注册链接:\s*(.+?)\s*\*\//;
    const _0x3fe438 = __filename;
    const _0x36b24f = fs.readFileSync(_0x3fe438, "utf-8");
    const _0x1f4952 = _0x36b24f.split(/\r?\n/);
    let _0x48d03f = "";
    for (let _0x4e4b88 of _0x1f4952) {
      const _0x505f8e = _0x4e4b88.trim().match(_0x208b14);
      if (_0x505f8e && _0x505f8e[1]) {
        _0x48d03f = _0x505f8e[1].trim();
        break;
      }
    }
    if (!_0x48d03f) {
      throw new Error("未找到「/* 注册链接: xxx */」格式的注释");
    }
    if (_0x48d03f !== REQUIRED_REG_LINK) {
      console.log("[❌ ERROR] 注册链接被修改!");
      console.log("[❌ ERROR] 合法链接:" + REQUIRED_REG_LINK);
      console.log("[❌ ERROR] 当前链接:" + _0x48d03f);
      throw new Error("注册链接校验失败\n请到作者网盘里下载正版");
    }
    console.log("[✅ SUCCESS] 注册链接校验通过");
  } catch (_0xca380e) {
    console.log("[❌ ERROR] 脚本校验失败:" + _0xca380e.message);
    process.exit(1);
  }
}
function commonHeaders(_0x39001f) {
  return {
    "User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Mobile Safari/537.36 EdgA/133.0.0.0",
    "Accept-Encoding": "gzip, deflate",
    "Content-Type": "application/json",
    source: "h5",
    token: _0x39001f,
    Origin: "http://h5.yidingyuecheng.com",
    "X-Requested-With": "mark.via.gp",
    Referer: "http://h5.yidingyuecheng.com/",
    "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7"
  };
}
async function handleAccount(_0x30df42, _0x4c6efc) {
  const _0x233eed = desensitizeMobile(_0x30df42);
  console.log("\n====================================");
  console.log("[ℹ️ 开始处理账号:" + _0x233eed + "]");
  console.log("====================================");
  !fs.existsSync(CK_DIR) && fs.mkdirSync(CK_DIR, {
    recursive: true
  });
  const _0xc31206 = path.resolve(CK_DIR, _0x30df42 + ".txt");
  let _0xb1c840 = "";
  try {
    if (fs.existsSync(_0xc31206)) {
      _0xb1c840 = fs.readFileSync(_0xc31206, "utf-8").trim();
      console.log("[ℹ️ 读取到账号" + _0x233eed + "的CK文件,验证有效性...]");
      const _0x39c434 = await fetch("http://h5.yidingyuecheng.com/api/user/info", {
        method: "POST",
        headers: commonHeaders(_0xb1c840),
        body: JSON.stringify({})
      });
      const _0x3fb30d = await _0x39c434.json();
      _0x3fb30d.success && _0x3fb30d.code === 0 ? console.log("[✅ 账号" + _0x233eed + "的CK有效,直接使用]") : (console.log("[⚠️ 账号" + _0x233eed + "的CK过期/无效,准备重新登录...]"), _0xb1c840 = "");
    } else {
      console.log("[ℹ️ 未找到账号" + _0x233eed + "的CK文件,执行登录...]");
    }
  } catch (_0x57444d) {
    console.log("[⚠️ 账号" + _0x233eed + "读取/校验CK失败:" + _0x57444d.message + ",重新登录...]");
    _0xb1c840 = "";
  }
  if (!_0xb1c840) {
    try {
      console.log("[ℹ️ 账号" + _0x233eed + "开始登录...]");
      const _0x2de728 = {
        mobile: _0x30df42,
        password: _0x4c6efc
      };
      const _0x1b47e = await fetch("http://h5.yidingyuecheng.com/api/user/login", {
        method: "POST",
        headers: commonHeaders(""),
        body: JSON.stringify(_0x2de728)
      });
      const _0x2cce7a = await _0x1b47e.json();
      if (_0x2cce7a.success && _0x2cce7a.code === 0) {
        _0xb1c840 = _0x2cce7a.data;
        console.log("[✅ 账号" + _0x233eed + "登录成功]");
        fs.writeFileSync(_0xc31206, _0xb1c840, "utf-8");
        console.log("[✅ 账号" + _0x233eed + "的CK已保存到本地]");
      } else {
        throw new Error("登录失败:" + _0x2cce7a.msg + "(code:" + _0x2cce7a.code + ")");
      }
    } catch (_0x2332c6) {
      console.log("[❌ 账号" + _0x233eed + "登录出错:" + _0x2332c6.message + "]");
      return;
    }
  }
  let _0x93744d = 0;
  let _0xd5d7f7 = "";
  try {
    const _0x2f6efd = await fetch("http://h5.yidingyuecheng.com/api/user/info", {
      method: "POST",
      headers: commonHeaders(_0xb1c840),
      body: JSON.stringify({})
    });
    const _0x57d838 = await _0x2f6efd.json();
    if (_0x57d838.success && _0x57d838.code === 0) {
      const _0x5c3dff = _0x57d838.data;
      _0xd5d7f7 = _0x5c3dff.name.length > 1 ? _0x5c3dff.name[0] + "*".repeat(_0x5c3dff.name.length - 1) : _0x5c3dff.name;
      _0x93744d = Number(_0x5c3dff.point).toFixed(2);
      console.log("\n[✅ 账号" + _0x233eed + "初始信息]");
      console.log("脱敏姓名:" + _0xd5d7f7);
      console.log("签到前积分:" + _0x93744d + " 分");
    } else {
      throw new Error("查询初始信息失败:" + _0x57d838.msg);
    }
  } catch (_0x32f31b) {
    console.log("[❌ 账号" + _0x233eed + "查询初始积分出错:" + _0x32f31b.message + "]");
    return;
  }
  try {
    const _0x11f4e6 = await fetch("http://h5.yidingyuecheng.com/api/mission/sign", {
      method: "POST",
      headers: commonHeaders(_0xb1c840),
      body: JSON.stringify({})
    });
    const _0x172df5 = await _0x11f4e6.json();
    _0x172df5.success || _0x172df5.code === 0 ? console.log("\n[✅ 账号" + _0x233eed + "签到结果] " + _0x172df5.msg) : console.log("\n[⚠️ 账号" + _0x233eed + "签到提示] " + _0x172df5.msg);
  } catch (_0xc6a3ce) {
    console.log("[❌ 账号" + _0x233eed + "签到出错:" + _0xc6a3ce.message + "]");
  }
  try {
    const _0x2bd980 = await fetch("http://h5.yidingyuecheng.com/api/user/info", {
      method: "POST",
      headers: commonHeaders(_0xb1c840),
      body: JSON.stringify({})
    });
    const _0x2d7910 = await _0x2bd980.json();
    if (_0x2d7910.success && _0x2d7910.code === 0) {
      const _0x3611cb = Number(_0x2d7910.data.point).toFixed(2);
      const _0x19cf59 = (_0x3611cb - _0x93744d).toFixed(2);
      console.log("\n[✅ 账号" + _0x233eed + "最终信息汇总]");
      console.log("脱敏姓名:" + _0xd5d7f7);
      console.log("签到前积分:" + _0x93744d + " 分");
      console.log("签到后积分:" + _0x3611cb + " 分");
      console.log("积分变动:" + (_0x19cf59 > 0 ? "+" : "") + _0x19cf59 + " 分");
    } else {
      throw new Error("查询最终信息失败:" + _0x2d7910.msg);
    }
  } catch (_0x10d25c) {
    console.log("[❌ 账号" + _0x233eed + "查询最终积分出错:" + _0x10d25c.message + "]");
  }
}
async function main() {
  checkRegLink();
  const _0x55c639 = process.env[ENV_NAME] || "";
  !_0x55c639 && (console.log("[❌ ERROR] 未配置环境变量" + ENV_NAME + ",请添加账号密码(格式:手机号|密码 回车分隔)"), process.exit(1));
  const _0x254a32 = _0x55c639.split(/\r?\n/).filter(_0x138461 => _0x138461.trim());
  _0x254a32.length === 0 && (console.log("[❌ ERROR] 环境变量" + ENV_NAME + "配置为空,请检查"), process.exit(1));
  console.log("[ℹ️ 共检测到 " + _0x254a32.length + " 个账号,开始批量处理...]");
  for (const _0x5c13e9 of _0x254a32) {
    const [_0x291b78, _0x5094c5] = _0x5c13e9.split("|").map(_0x570265 => _0x570265.trim());
    if (!_0x291b78 || !_0x5094c5) {
      console.log("[⚠️ 账号格式错误:" + _0x5c13e9 + ",请按「手机号|密码」格式配置]");
      continue;
    }
    await handleAccount(_0x291b78, _0x5094c5);
  }
  console.log("\n[✅ 所有账号处理完成]");
}
main();

================================================
FILE: package.json
================================================
{
  "name": "decode-js",
  "type": "module",
  "scripts": {
    "decode": "node src/main.js",
    "deob": "node src/main.js -t obfuscator",
    "deso": "node src/main.js -t sojson",
    "desov7": "node src/main.js -t sojsonv7",
    "lint": "eslint --ext .js --fix src"
  },
  "dependencies": {
    "@babel/generator": "^7.17.10",
    "@babel/parser": "^7.17.10",
    "@babel/traverse": "^7.17.10",
    "@babel/types": "^7.17.10",
    "base64url": "^3.0.1",
    "crypto-js": "^4.2.0",
    "eslint": "^8.23.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.2.1",
    "isolated-vm": "^4.7.2",
    "prettier": "^2.7.1",
    "vm2": "^3.9.11"
  }
}


================================================
FILE: src/decode.py
================================================
import base64
import bz2
import zlib
import lzma
import gzip
from datetime import datetime
#from Crypto.Cipher import AES
#from cryptography.fernet import Fernet
#from Crypto.Cipher import ChaCha20
# 获取当前日期和时间
now = datetime.now()

# 将日期和时间格式化为字符串
formatted_date = now.strftime("%Y-%m-%d %H:%M:%S")


def try_decompress(data):
    try:
        decompressed_data = gzip.decompress(data)
        return decompressed_data
    except Exception as e:
        pass
    # 尝试使用 bz2 解压缩
    try:
        decompressed_data = bz2.decompress(data)
        # print("使用 bz2 解压缩成功")
        return decompressed_data
    except Exception as e:
        pass
    # 尝试使用 zlib 解压缩
    try:
        decompressed_data = zlib.decompress(data)
        # print("使用 zlib 解压缩成功")
        return decompressed_data
    except Exception as e:
        pass
    # 尝试使用 lzma 解压缩
    try:
        decompressed_data = lzma.decompress(data)
        # print("使用 lzma 解压缩成功")
        return decompressed_data
    except Exception as e:
        pass
    # 如果无法解压缩,则返回原始数据
    return data


def try_decode_base64(data):
    try:
        decoded_data = base64.b64decode(data)
        # print("使用 base64 解码成功")
        return decoded_data
    except Exception as e:
        pass
    # 如果无法解码,则返回原始数据
    return data


def extract_base64_encoded(data):
    # 查找 base64.b64decode( 的起始位置
    start_idx = data.find("base64.b64decode(")
    if start_idx == -1:
        return None  # 如果未找到目标字符串,返回 None
    # 查找 ' 的位置,从 base64.b64decode( 后面开始找
    quote_idx = data.index("'", start_idx + len("base64.b64decode("))
    # 提取 'XXXX' 中的 XXXX 部分
    encoded_string = data[quote_idx + 1:data.index("'", quote_idx + 1)]
    return encoded_string


def Encoded_script_decode(data):

    return


def decrypt_nested(data):
    while True:
        new_data = try_decode_base64(data)
        # print("解密前的数据:", data)
        new_data = try_decompress(new_data)
        # print("解密后的数据:", new_data)
        if "exec(" in str(new_data):
            # 更新 decrypted_data 以便下一次循环使用
            if "Encoded script" in str(new_data):
                new_data = "该加密未适配 敬请期待"
                print("该加密未适配 敬请期待")
                break
            elif "exec(" in str(new_data):
                data = extract_base64_encoded(str(new_data))
            else:
                print("未知 加密 无法进一步解密")
                new_data = "未知 加密 无法进一步解密"
                break  # 如果 new_data 中不再包含 "exec",跳出循环
            # print(data)
        else:
            print("无法进一步解密,退出循环")
            break  # 如果 new_data 中不再包含 "exec",跳出循环

    return new_data  # 返回最终解密后的数据


with open('./input.py', 'r', encoding='utf-8') as file:
    # 读取文件内容
    content = file.read().strip()
    # 打印内容
    encoded_data = extract_base64_encoded(content)
    # print(encoded_data)
# 解密嵌套加密数据
final_decrypted_data = decrypt_nested(encoded_data)
# 输出最终解密结果
# print("最终解密结果:")
def process_data(data):
    if isinstance(data, str):
        # 如果是字符串,则编码为字节对象
        byte_data = data.encode('utf-8')
    elif isinstance(data, bytes):
        # 如果已经是字节对象,则直接使用
        byte_data = data
    else:
        # 如果不是字符串也不是字节对象,抛出异常或做其他处理
        raise TypeError("Expected string or bytes-like object")
    return byte_data

print(final_decrypted_data)
with open("./output.py", 'wb') as f:
    f.write(process_data("#")+process_data(formatted_date)+process_data("\n")+process_data(final_decrypted_data))


================================================
FILE: src/main.js
================================================
import fs from 'fs';
import { fileURLToPath } from 'url';
import * as path from 'path';
import process from 'process';

// Dynamically import ESM modules
const commonModule = await import('./plugin/common.js');
const jjencodeModule = await import('./plugin/jjencode.js');
const sojsonModule = await import('./plugin/sojson.js');
const sojsonv7Module = await import('./plugin/sojsonv7.js');
const obfuscatorModule = await import('./plugin/obfuscator.js');
const awscModule = await import('./plugin/awsc.js');
const jsconfuserModule = await import('./plugin/jsconfuser.js');

// Provide default exports if necessary
const PluginCommon = commonModule.default || commonModule;
const PluginJjencode = jjencodeModule.default || jjencodeModule;
const PluginSojson = sojsonModule.default || sojsonModule;
const PluginSojsonV7 = sojsonv7Module.default || sojsonv7Module;
const PluginObfuscator = obfuscatorModule.default || obfuscatorModule;
const PluginAwsc = awscModule.default || awscModule;
const PluginJsconfuser = jsconfuserModule.default || jsconfuserModule;
// Read command-line arguments
let encodeFile = 'input.js';
let decodeFile = 'output.js';

for (let i = 2; i < process.argv.length; i += 2) {
  if (process.argv[i] === '-i') {
    encodeFile = process.argv[i + 1];
  } else if (process.argv[i] === '-o') {
    decodeFile = process.argv[i + 1];
  }
}

console.log(`输入: ${encodeFile}`);
console.log(`输出: ${decodeFile}`);

// Read source code
const sourceCode = fs.readFileSync(encodeFile, { encoding: 'utf-8' });

let processedCode = sourceCode;
let pluginUsed = '';
let time;

// Try plugins in sequence until the processed code differs from the original
const plugins = [
  { name: 'obfuscator', plugin: PluginObfuscator },
  { name: 'sojsonv7', plugin: PluginSojsonV7 },
    { name: 'sojson', plugin: PluginSojson },

  { name: 'jsconfuser', plugin: PluginJsconfuser },
  { name: 'awsc', plugin: PluginAwsc },
  { name: 'jjencode', plugin: PluginJjencode },
  { name: 'common', plugin: PluginCommon }, // Use common plugin last
];

for (const plugin of plugins) {
  // Check for specific string in sourceCode to break early
  if (sourceCode.indexOf('smEcV') !== -1) {
    break;
  }

  try {
    const code = plugin.plugin(sourceCode);
    if (code && code !== processedCode) {
      processedCode = code;
      pluginUsed = plugin.name;
      break;
    }
  } catch (error) {
    console.error(`插件 ${plugin.name} 处理时发生错误: ${error.message}`);
  }
}

// Check if processed code differs from source code
if (processedCode !== sourceCode) {
  time = new Date();
  const header = [
    `//${time}`,
    "//Base:<url id=\"cv1cref6o68qmpt26ol0\" type=\"url\" status=\"parsed\" title=\"GitHub - echo094/decode-js: JS混淆代码的AST分析工具 AST analysis tool for obfuscated JS code\" wc=\"2165\">https://github.com/echo094/decode-js</url>",
    "//Modify:<url id=\"cv1cref6o68qmpt26olg\" type=\"url\" status=\"parsed\" title=\"GitHub - smallfawn/decode_action: 世界上本来不存在加密,加密的人多了,也便成就了解密\" wc=\"741\">https://github.com/smallfawn/decode_action</url>"
  ].join('\n');

  // Combine header and processed code
  const outputCode = header + '\n' + processedCode;

  // Write to file
  fs.writeFile(decodeFile, outputCode, (err) => {
    if (err) {
      throw err;
    } else {
      console.log(`使用插件 ${pluginUsed} 成功处理并写入文件 ${decodeFile}`);
    }
  });
} else {
  console.log(`所有插件处理后的代码与原代码一致,未写入文件。`);
}


================================================
FILE: src/plugin/awsc.js
================================================
/**
 * Reference:
 * * [某宝登录bx-ua参数逆向思路(fireyejs 225算法)](https://zhuanlan.zhihu.com/p/626187669)
 */
import { parse } from '@babel/parser'
import _generate from '@babel/generator'
const generator = _generate.default
import _traverse from '@babel/traverse'
const traverse = _traverse.default
import * as t from '@babel/types'

function RemoveVoid(path) {
  if (path.node.operator === 'void') {
    path.replaceWith(path.node.argument)
  }
}

function LintConditionalAssign(path) {
  if (!t.isAssignmentExpression(path?.parent)) {
    return
  }
  let { test, consequent, alternate } = path.node
  let { operator, left } = path.parent
  consequent = t.assignmentExpression(operator, left, consequent)
  alternate = t.assignmentExpression(operator, left, alternate)
  path.parentPath.replaceWith(
    t.conditionalExpression(test, consequent, alternate)
  )
}

function LintConditionalIf(ast) {
  function conditional(path) {
    let { test, consequent, alternate } = path.node
    // console.log(generator(test, { minified: true }).code)
    if (t.isSequenceExpression(path.parent)) {
      if (!sequence(path.parentPath)) {
        path.stop()
      }
      return
    }
    if (t.isLogicalExpression(path.parent)) {
      if (!logical(path.parentPath)) {
        path.stop()
      }
      return
    }
    if (!t.isExpressionStatement(path.parent)) {
      console.error(`Unexpected parent type: ${path.parent.type}`)
      path.stop()
      return
    }
    consequent = t.expressionStatement(consequent)
    alternate = t.expressionStatement(alternate)
    let statement = t.ifStatement(test, consequent, alternate)
    path.replaceWithMultiple(statement)
  }

  function sequence(path) {
    if (t.isLogicalExpression(path.parent)) {
      return logical(path.parentPath)
    }
    let body = []
    for (const item of path.node.expressions) {
      body.push(t.expressionStatement(item))
    }
    let node = t.blockStatement(body, [])
    let replace_path = path
    if (t.isExpressionStatement(path.parent)) {
      replace_path = path.parentPath
    } else if (!t.isBlockStatement(path.parent)) {
      console.error(`Unexpected parent type: ${path.parent.type}`)
      return false
    }
    replace_path.replaceWith(node)
    return true
  }

  function logical(path) {
    let { operator, left, right } = path.node
    if (operator !== '&&') {
      console.error(`Unexpected logical operator: ${operator}`)
      return false
    }
    if (!t.isExpressionStatement(path.parent)) {
      console.error(`Unexpected parent type: ${path.parent.type}`)
      return false
    }
    let node = t.ifStatement(left, t.expressionStatement(right))
    path.parentPath.replaceWith(node)
    return true
  }

  traverse(ast, {
    ConditionalExpression: { enter: conditional },
  })
}

function LintLogicalIf(path) {
  let { operator, left, right } = path.node
  if (operator !== '&&') {
    // console.warn(`Unexpected logical operator: ${operator}`)
    return
  }
  if (!t.isExpressionStatement(path.parent)) {
    console.warn(`Unexpected parent type: ${path.parent.type}`)
    return
  }
  let node = t.ifStatement(left, t.expressionStatement(right))
  path.parentPath.replaceWith(node)
  return
}

function LintIfStatement(path) {
  let { test, consequent, alternate } = path.node
  let changed = false
  if (!t.isBlockStatement(consequent)) {
    consequent = t.blockStatement([consequent])
    changed = true
  }
  if (alternate && !t.isBlockStatement(alternate)) {
    alternate = t.blockStatement([alternate])
    changed = true
  }
  if (!changed) {
    return
  }
  path.replaceWith(t.ifStatement(test, consequent, alternate))
}

function LintIfTest(path) {
  let { test, consequent, alternate } = path.node
  if (!t.isSequenceExpression(test)) {
    return
  }
  if (!t.isBlockStatement(path.parent)) {
    return
  }
  let body = test.expressions
  let last = body.pop()
  let before = t.expressionStatement(t.sequenceExpression(body))
  path.insertBefore(before)
  path.replaceWith(t.ifStatement(last, consequent, alternate))
}

function LintSwitchCase(path) {
  let { test, consequent } = path.node
  if (consequent.length == 1 && t.isBlockStatement(consequent[0])) {
    return
  }
  let block = t.blockStatement(consequent)
  path.replaceWith(t.switchCase(test, [block]))
}

function LintReturn(path) {
  let { argument } = path.node
  if (!t.isSequenceExpression(argument)) {
    return
  }
  if (!t.isBlockStatement(path.parent)) {
    return
  }
  let body = argument.expressions
  let last = body.pop()
  let before = t.expressionStatement(t.sequenceExpression(body))
  path.insertBefore(before)
  path.replaceWith(t.returnStatement(last))
}

function LintSequence(path) {
  let body = []
  for (const item of path.node.expressions) {
    body.push(t.expressionStatement(item))
  }
  let node = t.blockStatement(body, [])
  let replace_path = path
  if (t.isExpressionStatement(path.parent)) {
    replace_path = path.parentPath
  } else if (!t.isBlockStatement(path.parent)) {
    console.warn(`Unexpected parent type: ${path.parent.type}`)
    return
  }
  replace_path.replaceWith(node)
  return
}

function LintBlock(path) {
  let { body } = path.node
  if (!body.length) {
    return
  }
  let changed = false
  let arr = []
  for (const item of body) {
    if (!t.isBlockStatement(item)) {
      arr.push(item)
      continue
    }
    changed = true
    for (const sub of item.body) {
      arr.push(sub)
    }
  }
  if (!changed) {
    return
  }
  path.replaceWith(t.blockStatement(arr))
}

export default function (code) {
  let ast = parse(code)
  // Lint
  traverse(ast, {
    UnaryExpression: RemoveVoid,
  })
  traverse(ast, {
    ConditionalExpression: { exit: LintConditionalAssign },
  })
  LintConditionalIf(ast)
  traverse(ast, {
    LogicalExpression: { exit: LintLogicalIf },
  })
  traverse(ast, {
    IfStatement: { exit: LintIfStatement },
  })
  traverse(ast, {
    IfStatement: { enter: LintIfTest },
  })
  traverse(ast, {
    SwitchCase: { enter: LintSwitchCase },
  })
  traverse(ast, {
    ReturnStatement: { enter: LintReturn },
  })
  traverse(ast, {
    SequenceExpression: { exit: LintSequence },
  })
  traverse(ast, {
    BlockStatement: { exit: LintBlock },
  })

  code = generator(ast, {
    comments: false,
    jsescOption: { minimal: true },
  }).code
  return code
}


================================================
FILE: src/plugin/common.js
================================================
import { parse } from '@babel/parser'
import _generate from '@babel/generator'
const generator = _generate.default
import _traverse from '@babel/traverse'
const traverse = _traverse.default
import deleteUnreachableCode from '../visitor/delete-unreachable-code.js'
import deleteNestedBlocks from '../visitor/delete-nested-blocks.js'
import calculateConstantExp from '../visitor/calculate-constant-exp.js'
import calculateRString from '../visitor/calculate-rstring.js'

export default function (code) {
  let ast
  try {
    ast = parse(code, { errorRecovery: true })
  } catch (e) {
    console.error(`Cannot parse code: ${e.reasonCode}`)
    return null
  }
  traverse(ast, deleteUnreachableCode)
  traverse(ast, deleteNestedBlocks)
  traverse(ast, calculateConstantExp)
  traverse(ast, calculateRString)
  code = generator(ast).code
  return code
}


================================================
FILE: src/plugin/eval.js
================================================
import { parse } from '@babel/parser'
import _generate from '@babel/generator'
const generator = _generate.default
import _traverse from '@babel/traverse'
const traverse = _traverse.default
import * as t from '@babel/types'

function unpack(code) {
  let ast = parse(code, { errorRecovery: true })
  let lines = ast.program.body
  let data = null
  for (let line of lines) {
    if (t.isEmptyStatement(line)) {
      continue
    }
    if (data) {
      return null
    }
    if (
      t.isCallExpression(line?.expression) &&
      line.expression.callee?.name === 'eval' &&
      line.expression.arguments.length === 1 &&
      t.isCallExpression(line.expression.arguments[0])
    ) {
      data = t.expressionStatement(line.expression.arguments[0])
      continue
    }
    return null
  }
  if (!data) {
    return null
  }
  code = generator(data, { minified: true }).code
  return eval(code)
}

function pack(code) {
  let ast1 = parse('(function(){}())')
  let ast2 = parse(code)
  traverse(ast1, {
    FunctionExpression(path) {
      let body = t.blockStatement(ast2.program.body)
      path.replaceWith(t.functionExpression(null, [], body))
      path.stop()
    },
  })
  code = generator(ast1, { minified: false }).code
  return code
}

export default {
  unpack,
  pack,
}


================================================
FILE: src/plugin/jjencode.js
================================================
/**
 * Check the format and decode if possible
 *
 * @param {string} code the encoded code
 * @returns null or string
 */
function getCode(code) {
  // split the code by semicolon
  let blocks = []
  for (let line of code.split(';')) {
    if (line.length && line !== '\n') {
      blocks.push(line)
    }
  }
  if (blocks.length !== 6) {
    console.error('The number of code blocks is incorrect!')
    return null
  }
  // try to get the global variable name
  const line1 = blocks[0].split('=')
  if (line1.length !== 2 || line1[1].indexOf('~[]') === -1) {
    console.error('Cannot find variable name!')
    return null
  }
  // extract the target code
  const target = blocks[5]
  const variable = line1[0]
  const left = `${variable}.$(${variable}.$(${variable}.$$+"\\""+`
  let i = 0
  let s = 0
  while (i < left.length && s < target.length) {
    if (left[i] === target[s]) {
      ++i
    }
    ++s
  }
  const right = '"\\"")())()'
  let j = right.length - 1
  let e = target.length - 1
  while (j >= 0 && e >= 0) {
    if (right[j] === target[e]) {
      --j
    }
    --e
  }
  if (s >= e) {
    console.error('Cannot find the target code!')
    return null
  }
  const selected = target.substring(s, e)
  blocks[5] = `${variable}.$(${variable}.$$+"\\""+${selected}+"\\"")()`
  const result = eval(blocks.join(';'))
  return result
}

/**
 * This encoding method originates from http://utf-8.jp/public/jjencode.html,
 * and it does not change the original code (encoder, not obfuscation).
 */
export default function (code) {
  code = getCode(code)
  if (!code) {
    return null
  }
  return code
}


================================================
FILE: src/plugin/jsconfuser.js
================================================
import { parse } from '@babel/parser'
import _generate from '@babel/generator'
const generator = _generate.default
import _traverse from '@babel/traverse'
const traverse = _traverse.default

import calculateConstantExp from '../visitor/calculate-constant-exp.js'
import pruneIfBranch from '../visitor/prune-if-branch.js'
import jcAntiTooling from '../visitor/jsconfuser/anti-tooling.js'
import jcControlFlow from '../visitor/jsconfuser/control-flow.js'
import jcDuplicateLiteral from '../visitor/jsconfuser/duplicate-literal.js'
import jcGlobalConcealing from '../visitor/jsconfuser/global-concealing.js'
import jcMinifyInit from '../visitor/jsconfuser/minify.js'
import jcOpaquePredicates from '../visitor/jsconfuser/opaque-predicates.js'
import jcStackInit from '../visitor/jsconfuser/stack.js'
import jcStringCompression from '../visitor/jsconfuser/string-compression.js'
import jcStringConceal from '../visitor/jsconfuser/string-concealing.js'

export default function (code) {
  let ast
  try {
    ast = parse(code, { errorRecovery: true })
  } catch (e) {
    console.error(`Cannot parse code: ${e.reasonCode}`)
    return null
  }
  // AntiTooling
  traverse(ast, jcAntiTooling)
  // Minify
  const jcMinify = jcMinifyInit()
  traverse(ast, jcMinify.deMinifyArrow)
  // DuplicateLiteralsRemoval
  traverse(ast, jcDuplicateLiteral)
  // Stack
  const jcStack = jcStackInit(jcMinify.arrowFunc)
  traverse(ast, jcStack.deStackFuncLen)
  traverse(ast, jcStack.deStackFuncOther)
  // StringCompression
  traverse(ast, jcStringCompression)
  // StringConcealing
  traverse(ast, jcStringConceal.deStringConcealing)
  traverse(ast, jcStringConceal.deStringConcealingPlace)
  // StringSplitting
  traverse(ast, calculateConstantExp)
  // Stack (run again)
  traverse(ast, jcStack.deStackFuncOther)
  // OpaquePredicates
  traverse(ast, jcOpaquePredicates)
  traverse(ast, calculateConstantExp)
  traverse(ast, pruneIfBranch)
  // GlobalConcealing
  traverse(ast, jcGlobalConcealing)
  // ControlFlowFlattening
  traverse(ast, jcControlFlow.deControlFlowFlatteningStateless)
  traverse(ast, calculateConstantExp)
  // ExpressionObfuscation
  code = generator(ast, {
    comments: false,
    jsescOption: { minimal: true },
  }).code
  return code
}


================================================
FILE: src/plugin/obfuscator.js
================================================
/**
 * 整合自下面两个项目:
 * * cilame/v_jstools
 * * Cqxstevexw/decodeObfuscator
 */
import { parse } from '@babel/parser'
import _generate from '@babel/generator'
const generator = _generate.default
import _traverse from '@babel/traverse'
const traverse = _traverse.default
import * as t from '@babel/types'
import ivm from 'isolated-vm'
import PluginEval from './eval.js'
import calculateConstantExp from '../visitor/calculate-constant-exp.js'
import deleteIllegalReturn from '../visitor/delete-illegal-return.js'
import deleteUnusedVar from '../visitor/delete-unused-var.js'
import lintIfStatement from '../visitor/lint-if-statement.js'
import mergeObject from '../visitor/merge-object.js'
import parseControlFlowStorage from '../visitor/parse-control-flow-storage.js'
import pruneIfBranch from '../visitor/prune-if-branch.js'
import splitAssignment from '../visitor/split-assignment.js'
import splitSequence from '../visitor/split-sequence.js'
import splitVarDeclaration from '../visitor/split-variable-declaration.js'

const isolate = new ivm.Isolate()
const globalContext = isolate.createContextSync()
function virtualGlobalEval(jsStr) {
  return globalContext.evalSync(String(jsStr))
}

const optGenMin = {
  comments: false,
  minified: true,
  jsescOption: { minimal: true },
}

/**
 * Extract the literal value of an object, and remove an object if all
 * references to the object are replaced.
 */
function decodeObject(ast) {
  function collectObject(path) {
    const id = path.node.id
    const init = path.node.init
    if (!t.isIdentifier(id) || !t.isObjectExpression(init)) {
      return
    }
    const obj_name = id.name
    const bind = path.scope.getBinding(obj_name)
    let valid = true
    let count = 0
    let obj = {}
    for (const item of init.properties) {
      if (!t.isObjectProperty(item) || !t.isLiteral(item.value)) {
        valid = false
        break
      }
      if (!t.isIdentifier(item.key)) {
        valid = false
        break
      }
      ++count
      obj[item.key.name] = item.value
    }
    if (!valid || !count) {
      return
    }
    let safe = true
    for (let ref of bind.referencePaths) {
      const parent = ref.parentPath
      if (ref.key !== 'object' || !parent.isMemberExpression()) {
        safe = false
        continue
      }
      const key = parent.node.property
      if (!t.isIdentifier(key) || parent.node.computed) {
        safe = false
        continue
      }
      if (Object.prototype.hasOwnProperty.call(obj, key.name)) {
        parent.replaceWith(obj[key.name])
      } else {
        safe = false
      }
    }
    bind.scope.crawl()
    if (safe) {
      path.remove()
      console.log(`删除对象: ${obj_name}`)
    }
  }
  traverse(ast, {
    VariableDeclarator: collectObject,
  })
  return ast
}

/**
 * If the StringArrayRotateFunction does not exist, we can only verify a
 * string-array by checking the StringArrayCallsWrapper.
 *
 * @param {t.File} ast The ast file
 * @returns Object
 */
function stringArrayV0(ast) {
  console.info('Try v0 mode...')
  function check_wrapper(ref, array_name) {
    ref = ref.parentPath
    const index = ref.node.property?.name
    if (ref.key !== 'init') {
      return null
    }
    ref = ref.parentPath
    const value = ref.node.id?.name
    ref = ref.getFunctionParent()
    if (!index || ref.node.params?.[0]?.name !== index) {
      return null
    }
    const container = ref.node.body.body
    const ret_node = container[container.length - 1]
    if (!t.isReturnStatement(ret_node) || ret_node.argument?.name !== value) {
      return null
    }
    if (ref.key !== 'init') {
      return null
    }
    const rm_path = ref.parentPath
    if (array_name == rm_path.node.id.name) {
      return null
    }
    return rm_path
  }
  // check if all the binding references are wrapper
  function check_other_function(path, array_name) {
    const binding = path.scope.getBinding(array_name)
    if (!binding.referencePaths) {
      return
    }
    const ob_func_str = []
    const ob_dec_call = []
    for (const ref of binding.referencePaths) {
      if (ref.findParent((path) => path.removed)) {
        continue
      }
      if (ref.parentPath.isMemberExpression() && ref.key === 'object') {
        const rm_path = check_wrapper(ref, array_name)
        if (!rm_path) {
          console.error('Unexpected reference')
          return null
        }
        const code = generator(rm_path.node, optGenMin).code
        rm_path.get('body').replaceWith(t.blockStatement([]))
        ob_func_str.push(code)
        ob_dec_call.push({ name: rm_path.node.id.name, path: rm_path })
      } else {
        console.error('Unexpected reference')
        return null
      }
    }
    if (!ob_func_str.length) {
      return null
    }
    ob_func_str.push(generator(path.node, optGenMin).code)
    path.remove()
    return {
      version: 0,
      stringArrayName: array_name,
      stringArrayCodes: ob_func_str,
      stringArrayCalls: ob_dec_call,
    }
  }
  let ret_obj = {
    version: 0,
    stringArrayName: null,
    stringArrayCodes: [],
    stringArrayCalls: [],
  }
  function check_string_array(path) {
    if (path.getFunctionParent()) {
      return
    }
    const init = path.get('init')
    if (!init.isArrayExpression()) {
      return
    }
    if (!init.node.elements.length) {
      return
    }
    const array_name = path.node.id.name
    const obj = check_other_function(path, array_name)
    if (obj) {
      ret_obj = obj
      path.stop()
    }
  }
  traverse(ast, {
    VariableDeclarator: check_string_array,
  })
  return ret_obj
}

/**
 * Before version 2.19.0, the string-array is a single array.
 * Hence, we have to find StringArrayRotateFunction instead.
 *
 * @param {t.File} ast The ast file
 * @returns Object
 */
function stringArrayV2(ast) {
  console.info('Try v2 mode...')
  let obj = {
    version: 2,
    stringArrayName: null,
    stringArrayCodes: [],
    stringArrayCalls: [],
  }
  // Function to rotate string list ("func2")
  function find_rotate_function(path) {
    const callee = path.get('callee')
    const args = path.node.arguments
    if (
      !callee.isFunctionExpression() ||
      callee.node.params.length !== 2 ||
      args.length == 0 ||
      args.length > 2 ||
      !t.isIdentifier(args[0])
    ) {
      return
    }
    const arr = callee.node.params[0].name
    const cmpV = callee.node.params[1].name
    // >= 2.10.0
    const fp1 = `(){try{if()break${arr}push(${arr}shift())}catch(){${arr}push(${arr}shift())}}`
    // < 2.10.0
    const fp2 = `=function(){while(--){${arr}push(${arr}shift)}}${cmpV}`
    const code = '' + callee.get('body')
    if (!checkPattern(code, fp1) && !checkPattern(code, fp2)) {
      return
    }
    obj.stringArrayName = args[0].name
    // The string array can be found by its binding
    const bind = path.scope.getBinding(obj.stringArrayName)
    const def = t.variableDeclaration('var', [bind.path.node])
    obj.stringArrayCodes.push(generator(def, optGenMin).code)
    // The calls can be found by its references
    for (let ref of bind.referencePaths) {
      if (ref?.listKey === 'arguments') {
        // This is the rotate function
        continue
      }
      if (ref.findParent((path) => path.removed)) {
        continue
      }
      // the key is 'object'
      let up1 = ref.getFunctionParent()
      if (up1.node.id) {
        // 2.12.0 <= v < 2.15.4
        // The `stringArrayCallsWrapperName` is included in the definition
        obj.stringArrayCodes.push(generator(up1.node, optGenMin).code)
        up1.node.body = t.blockStatement([])
        obj.stringArrayCalls.push({ name: up1.node.id.name, path: up1 })
        continue
      }
      if (up1.key === 'init') {
        // v < 2.12.0
        // The `stringArrayCallsWrapperName` is defined by VariableDeclarator
        up1 = up1.parentPath
        const node = t.variableDeclaration('var', [up1.node])
        obj.stringArrayCodes.push(generator(node, optGenMin).code)
        up1.node.init = null
        obj.stringArrayCalls.push({ name: up1.node.id.name, path: up1 })
        continue
      }
      // 2.15.4 <= v < 2.19.0
      // The function includes another function with the same name
      up1 = up1.parentPath
      const wrapper = up1.node.left.name
      let up2 = up1.getFunctionParent()
      if (!up2 || up2.node?.id?.name !== wrapper) {
        console.warn('Unexpected reference!')
        continue
      }
      obj.stringArrayCodes.push(generator(up2.node, optGenMin).code)
      up1.remove()
      up2.node.body = t.blockStatement([])
      obj.stringArrayCalls.push({ name: wrapper, path: up2 })
    }
    // Remove the string array
    bind.path.remove()
    // Add the rotate function
    const node = t.expressionStatement(path.node)
    obj.stringArrayCodes.push(generator(node, optGenMin).code)
    path.stop()
    if (path.parentPath.isUnaryExpression()) {
      path.parentPath.remove()
    } else {
      path.remove()
    }
  }
  traverse(ast, { CallExpression: find_rotate_function })
  if (obj.stringArrayCodes.length < 3 || !obj.stringArrayCalls.length) {
    console.error('Essential code missing!')
    obj.stringArrayName = null
  }
  return obj
}

/**
 * Find the string-array codes by matching string-array function
 * (valid version >= 2.19.0)
 *
 * @param {t.File} ast The ast file
 * @returns Object
 */
function stringArrayV3(ast) {
  console.info('Try v3 mode...')
  let ob_func_str = []
  let ob_dec_name = []
  let ob_string_func_name = null
  // Normally, the string array func ("func1") follows the template below:
  // function aaa() {
  //   const bbb = [...]
  //   aaa = function () {
  //     return bbb;
  //   };
  //   return aaa();
  // }
  // In some cases (lint), the assignment is merged into the ReturnStatement
  // After finding the possible func1, this method will check all the binding
  // references and put the child encode function into list.
  function find_string_array_function(path) {
    if (path.getFunctionParent()) {
      return
    }
    if (
      !t.isIdentifier(path.node.id) ||
      path.node.params.length ||
      !t.isBlockStatement(path.node.body)
    ) {
      return
    }
    const body = path.node.body.body
    if (body.length < 2 || body.length > 3) {
      return
    }
    const name_func = path.node.id.name
    let string_var = -1
    try {
      if (
        body[0].declarations.length != 1 ||
        !(string_var = body[0].declarations[0].id.name) ||
        !t.isArrayExpression(body[0].declarations[0].init)
      ) {
        return
      }
      const nodes = [...body]
      nodes.shift()
      const code = generator(t.BlockStatement(nodes), optGenMin).code
      const fp = `${name_func}=function(){return${string_var}}${name_func}()`
      if (!checkPattern(code, fp)) {
        return
      }
    } catch {
      return
    }
    const binding = path.scope.getBinding(name_func)
    if (!binding.referencePaths) {
      return
    }
    let paths = binding.referencePaths
    let nodes = []
    // The sorting function maybe missing in some config
    function find2(refer_path) {
      if (
        refer_path.parentPath.isCallExpression() &&
        refer_path.listKey === 'arguments' &&
        refer_path.key === 0
      ) {
        let rm_path = refer_path.parentPath
        if (rm_path.parentPath.isExpressionStatement()) {
          rm_path = rm_path.parentPath
        }
        nodes.push([rm_path.node, 'func2'])
        rm_path.remove()
      }
    }
    paths.map(find2)
    function find3(refer_path) {
      if (refer_path.findParent((path) => path.removed)) {
        return
      }
      if (
        refer_path.parentPath.isCallExpression() &&
        refer_path.key === 'callee'
      ) {
        let rm_path = refer_path.parentPath.getFunctionParent()
        if (name_func == rm_path.node.id.name) {
          return
        }
        const code = generator(rm_path.node, optGenMin).code
        rm_path.node.body = t.blockStatement([])
        nodes.push([code, 'func3', rm_path])
      } else {
        console.error('Unexpected reference')
      }
    }
    paths.map(find3)
    if (!name_func) {
      return
    }
    ob_string_func_name = name_func
    ob_func_str.push(generator(path.node, optGenMin).code)
    nodes.map(function (item) {
      if (item[1] == 'func3') {
        ob_func_str.push(item[0])
        ob_dec_name.push({ name: item[2].node.id.name, path: item[2] })
        return
      }
      let node = item[0]
      if (t.isCallExpression(node)) {
        node = t.expressionStatement(node)
      }
      ob_func_str.push(generator(node, optGenMin).code)
    })
    path.stop()
    path.remove()
  }
  traverse(ast, { FunctionDeclaration: find_string_array_function })
  return {
    version: 3,
    stringArrayName: ob_string_func_name,
    stringArrayCodes: ob_func_str,
    stringArrayCalls: ob_dec_name,
  }
}

function decodeGlobal(ast) {
  let obj = stringArrayV3(ast)
  if (!obj.stringArrayName) {
    obj = stringArrayV2(ast)
  }
  if (!obj.stringArrayName) {
    obj = stringArrayV0(ast)
  }
  if (!obj.stringArrayName) {
    console.error('Cannot find string list!')
    return false
  }
  console.log(`String List Name: ${obj.stringArrayName}`)
  let ob_func_str = obj.stringArrayCodes
  let ob_dec_call = obj.stringArrayCalls
  try {
    virtualGlobalEval(ob_func_str.join(';'))
  } catch (e) {
    // issue #31
    if (e.name === 'ReferenceError') {
      let lost = e.message.split(' ')[0]
      traverse(ast, {
        Program(path) {
          let loc = path.scope.getBinding(lost).path
          let obj = t.variableDeclaration(loc.parent.kind, [loc.node])
          ob_func_str.unshift(generator(obj, optGenMin).code)
          loc.node.init = null
          ob_dec_call.push({ name: lost, path: loc })
          path.stop()
        },
      })
      virtualGlobalEval(ob_func_str.join(';'))
    }
  }

  // 递归删除混淆函数
  function getChild(father) {
    if (father.key !== 'argument' || !father.parentPath.isReturnStatement()) {
      console.error(`Unexpected chained call: ${father}`)
      return null
    }
    const func = father.getFunctionParent()
    let name = func.node.id?.name
    let root
    let code
    if (name) {
      // FunctionDeclaration
      // function A (...) { return function B (...) }
      root = func
      code = generator(root.node, optGenMin).code
    } else {
      // FunctionExpression
      // var A = function (...) { return function B (...) }
      root = func.parentPath
      code = generator(t.variableDeclaration('var', [root])).code
      name = root.node.id.name
    }
    return {
      name: name,
      path: root,
      code: code,
    }
  }
  function dfs(stk, item) {
    stk.push(item)
    const cur_val = item.name
    console.log(`Enter sub ${stk.length}:${cur_val}`)
    let pfx = ''
    for (let parent of stk) {
      pfx += parent.code + ';'
    }
    virtualGlobalEval(pfx)
    let scope = item.path.scope
    if (item.path.isFunctionDeclaration()) {
      scope = item.path.parentPath.scope
    }
    const binding = scope.getBinding(cur_val)
    binding.scope.crawl()
    const refs = binding.scope.bindings[cur_val].referencePaths
    const refs_next = []
    // 有4种链式调用情况:
    // - VariableDeclarator和FunctionDeclaration为原版
    // - AssignmentExpression 出现于 #50
    // - FunctionExpression 出现于 #94
    for (let ref of refs) {
      const parent = ref.parentPath
      if (ref.key === 'callee') {
        // CallExpression
        let old_call = parent + ''
        try {
          // 运行成功则说明函数为直接调用并返回字符串
          let new_str = virtualGlobalEval(old_call)
          console.log(`map: ${old_call} -> ${new_str}`)
          parent.replaceWith(t.StringLiteral(new_str))
        } catch (e) {
          // 运行失败则说明函数为其它混淆函数的子函数
          console.log(`sub: ${old_call}`)
          const ret = getChild(parent)
          if (ret) {
            refs_next.push(ret)
          }
        }
      } else if (ref.key === 'init') {
        // VariableDeclarator
        refs_next.push({
          name: ref.parent.id.name,
          path: ref.parentPath,
          code: 'var ' + ref.parentPath,
        })
      } else if (ref.key === 'right') {
        // AssignmentExpression
        // 这种情况尚不完善 可能会产生额外替换
        refs_next.push({
          name: ref.parent.left.name,
          path: ref.parentPath,
          code: 'var ' + ref.parentPath,
        })
      }
    }
    for (let ref of refs_next) {
      dfs(stk, ref)
    }
    binding.scope.crawl()
    console.log(`Exit sub ${stk.length}:${cur_val}`)
    stk.pop()
    if (!item.path.parentPath.isCallExpression()) {
      item.path.remove()
      binding.scope.crawl()
      return
    }
    // 只会出现在AssignmentExpression情况下 需要再次运行
    item.path.replaceWith(t.identifier(cur_val))
    item.path = binding.path
    binding.scope.crawl()
    dfs(stk, item)
  }
  for (let item of ob_dec_call) {
    item.code = ''
    dfs([], item)
  }
  return true
}

function stringArrayLite(ast) {
  const visitor = {
    VariableDeclarator(path) {
      const name = path.node.id.name
      if (!path.get('init').isArrayExpression()) {
        return
      }
      const elements = path.node.init.elements
      for (const element of elements) {
        if (!t.isLiteral(element)) {
          return
        }
      }
      const bind = path.scope.getBinding(name)
      if (!bind.constant) {
        return
      }
      for (const ref of bind.referencePaths) {
        if (
          !ref.parentPath.isMemberExpression() ||
          ref.key !== 'object' ||
          ref.parentPath.key == 'left' ||
          !t.isNumericLiteral(ref.parent.property)
        ) {
          return
        }
      }
      console.log(`Extract string array: ${name}`)
      for (const ref of bind.referencePaths) {
        const i = ref.parent.property.value
        ref.parentPath.replaceWith(elements[i])
      }
      bind.scope.crawl()
      path.remove()
    },
  }
  traverse(ast, visitor)
}

function decodeCodeBlock(ast) {
  // 合并字面量
  traverse(ast, calculateConstantExp)
  // 先合并分离的Object定义
  traverse(ast, mergeObject)
  // 在变量定义完成后判断是否为代码块加密内容
  traverse(ast, parseControlFlowStorage)
  // 合并字面量(在解除区域混淆后会出现新的可合并分割)
  traverse(ast, calculateConstantExp)
  return ast
}

function cleanSwitchCode(path) {
  // 扁平控制:
  // 会使用一个恒为true的while语句包裹一个switch语句
  // switch语句的执行顺序又while语句上方的字符串决定
  // 首先碰断是否符合这种情况
  const node = path.node
  let valid = false
  if (t.isBooleanLiteral(node.test) && node.test.value) {
    valid = true
  }
  if (t.isArrayExpression(node.test) && node.test.elements.length === 0) {
    valid = true
  }
  if (!valid) {
    return
  }
  if (!t.isBlockStatement(node.body)) {
    return
  }
  const body = node.body.body
  if (
    !t.isSwitchStatement(body[0]) ||
    !t.isMemberExpression(body[0].discriminant) ||
    !t.isBreakStatement(body[1])
  ) {
    return
  }
  // switch语句的两个变量
  const swithStm = body[0]
  const arrName = swithStm.discriminant.object.name
  const argName = swithStm.discriminant.property.argument.name
  // 在while上面的节点寻找这两个变量
  let arr = []
  let rm = []
  path.getAllPrevSiblings().forEach((pre_path) => {
    if (!pre_path.isVariableDeclaration()) {
      return
    }
    for (let i = 0; i < pre_path.node.declarations.length; ++i) {
      const declaration = pre_path.get(`declarations.${i}`)
      let { id, init } = declaration.node
      if (arrName == id.name) {
        if (t.isStringLiteral(init?.callee?.object)) {
          arr = init.callee.object.value.split('|')
          rm.push(declaration)
        }
      }
      if (argName == id.name) {
        if (t.isLiteral(init)) {
          rm.push(declaration)
        }
      }
    }
  })
  if (rm.length !== 2) {
    return
  }
  rm.forEach((pre_path) => {
    pre_path.remove()
  })
  console.log(`扁平化还原: ${arrName}[${argName}]`)
  // 重建代码块
  const caseList = swithStm.cases
  let resultBody = []
  arr.map((targetIdx) => {
    // 从当前序号开始直到遇到continue
    let valid = true
    targetIdx = parseInt(targetIdx)
    while (valid && targetIdx < caseList.length) {
      const targetBody = caseList[targetIdx].consequent
      const test = caseList[targetIdx].test
      if (!t.isStringLiteral(test) || parseInt(test.value) !== targetIdx) {
        console.log(`switch中出现乱序的序号: ${test.value}:${targetIdx}`)
      }
      for (let i = 0; i < targetBody.length; ++i) {
        const s = targetBody[i]
        if (t.isContinueStatement(s)) {
          valid = false
          break
        }
        if (t.isReturnStatement(s)) {
          valid = false
          resultBody.push(s)
          break
        }
        if (t.isBreakStatement(s)) {
          console.log(`switch中出现意外的break: ${arrName}[${argName}]`)
        } else {
          resultBody.push(s)
        }
      }
      targetIdx++
    }
  })
  // 替换整个while语句
  path.replaceInline(resultBody)
}

function cleanDeadCode(ast) {
  traverse(ast, calculateConstantExp)
  traverse(ast, pruneIfBranch)
  traverse(ast, { WhileStatement: { exit: cleanSwitchCode } })
  return ast
}

function standardIfStatement(path) {
  const consequent = path.get('consequent')
  const alternate = path.get('alternate')
  const test = path.get('test')
  const evaluateTest = test.evaluateTruthy()

  if (!consequent.isBlockStatement()) {
    consequent.replaceWith(t.BlockStatement([consequent.node]))
  }
  if (alternate.node !== null && !alternate.isBlockStatement()) {
    alternate.replaceWith(t.BlockStatement([alternate.node]))
  }

  if (consequent.node.body.length == 0) {
    if (alternate.node == null) {
      path.replaceWith(test.node)
    } else {
      consequent.replaceWith(alternate.node)
      alternate.remove()
      path.node.alternate = null
      test.replaceWith(t.unaryExpression('!', test.node, true))
    }
  }

  if (alternate.isBlockStatement() && alternate.node.body.length == 0) {
    alternate.remove()
    path.node.alternate = null
  }

  if (evaluateTest === true) {
    path.replaceWithMultiple(consequent.node.body)
  } else if (evaluateTest === false) {
    alternate.node === null
      ? path.remove()
      : path.replaceWithMultiple(alternate.node.body)
  }
}

function standardLoop(path) {
  const node = path.node
  if (!t.isBlockStatement(node.body)) {
    node.body = t.BlockStatement([node.body])
  }
}

function purifyCode(ast) {
  // 标准化if语句
  traverse(ast, { IfStatement: standardIfStatement })
  // 标准化for语句
  traverse(ast, { ForStatement: standardLoop })
  // 标准化while语句
  traverse(ast, { WhileStatement: standardLoop })
  // 删除空语句
  traverse(ast, {
    EmptyStatement: (path) => {
      path.remove()
    },
  })
  traverse(ast, splitAssignment)
  // 删除未使用的变量
  traverse(ast, deleteUnusedVar)
  // 替换索引器
  function FormatMember(path) {
    let curNode = path.node
    if (!t.isStringLiteral(curNode.property)) {
      return
    }
    if (curNode.computed === undefined || !curNode.computed === true) {
      return
    }
    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(curNode.property.value)) {
      return
    }
    curNode.property = t.identifier(curNode.property.value)
    curNode.computed = false
  }
  traverse(ast, { MemberExpression: FormatMember })

  // 替换类和对象的计算方法和计算属性
  // ["method"](){} -> "method"(){}
  function FormatComputed(path) {
    let curNode = path.node
    if (!t.isStringLiteral(curNode.key)) {
      return
    }
    curNode.computed = false
  }
  // "method"(){} -> method(){}
  function stringLiteralToIdentifier(path) {
    let curNode = path.node
    if (!t.isStringLiteral(curNode.key) || curNode.computed === true) {
      return
    }
    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(curNode.key.value)) {
      return
    }
    curNode.key = t.identifier(curNode.key.value)
  }
  traverse(ast, {
    'Method|Property': (path) => {
      FormatComputed(path)
      stringLiteralToIdentifier(path)
    },
  })

  // 拆分语句
  traverse(ast, splitSequence)
  return ast
}

function checkPattern(code, pattern) {
  let i = 0
  let j = 0
  while (i < code.length && j < pattern.length) {
    if (code[i] == pattern[j]) {
      ++j
    }
    ++i
  }
  return j == pattern.length
}

const deleteSelfDefendingCode = {
  VariableDeclarator(path) {
    const { id, init } = path.node
    const selfName = id.name
    if (!t.isCallExpression(init)) {
      return
    }
    if (!t.isIdentifier(init.callee)) {
      return
    }
    const callName = init.callee.name
    const args = init.arguments
    if (
      args.length != 2 ||
      !t.isThisExpression(args[0]) ||
      !t.isFunctionExpression(args[1])
    ) {
      return
    }
    const block = generator(args[1]).code
    const patterns = [
      // @7920538
      `return${selfName}.toString().search().toString().constructor(${selfName}).search()`,
      // @7135b09
      `const=function(){const=.constructor()return.test(${selfName})}return()`,
      // #94
      `var=function(){var=.constructor()return.test(${selfName})}return()`,
    ]
    let valid = false
    for (let pattern of patterns) {
      valid |= checkPattern(block, pattern)
    }
    if (!valid) {
      return
    }
    const refs = path.scope.bindings[selfName].referencePaths
    for (let ref of refs) {
      if (ref.key == 'callee') {
        ref.parentPath.remove()
        break
      }
    }
    path.remove()
    console.info(`Remove SelfDefendingFunc: ${selfName}`)
    const scope = path.scope.getBinding(callName).scope
    scope.crawl()
    const bind = scope.bindings[callName]
    if (bind.referenced) {
      console.error(`Call func ${callName} unexpected ref!`)
    }
    bind.path.remove()
    console.info(`Remove CallFunc: ${callName}`)
  },
}

const deleteDebugProtectionCode = {
  FunctionDeclaration(path) {
    const { id, params, body } = path.node
    if (
      !t.isIdentifier(id) ||
      params.length !== 1 ||
      !t.isIdentifier(params[0]) ||
      !t.isBlockStatement(body) ||
      body.body.length !== 2 ||
      !t.isFunctionDeclaration(body.body[0]) ||
      !t.isTryStatement(body.body[1])
    ) {
      return
    }
    const debugName = id.name
    const ret = params[0].name
    const subNode = body.body[0]
    if (
      !t.isIdentifier(subNode.id) ||
      subNode.params.length !== 1 ||
      !t.isIdentifier(subNode.params[0])
    ) {
      return
    }
    const subName = subNode.id.name
    const counter = subNode.params[0].name
    const code = generator(body).code
    const pattern = `function${subName}(${counter}){${counter}debugger${subName}(++${counter})}try{if(${ret})return${subName}${subName}(0)}catch(){}`
    if (!checkPattern(code, pattern)) {
      return
    }
    const scope1 = path.parentPath.scope
    const refs = scope1.bindings[debugName].referencePaths
    for (let ref of refs) {
      if (ref.findParent((path) => path.removed)) {
        continue
      }
      if (ref.key == 0) {
        // DebugProtectionFunctionInterval @e8e92c6
        const rm = ref.getFunctionParent().parentPath
        rm.remove()
        continue
      }
      // ref.key == 'callee'
      const up1 = ref.getFunctionParent()
      const callName = up1.parent.callee.name
      if (callName === 'setInterval') {
        // DebugProtectionFunctionInterval @51523c0
        const rm = up1.parentPath
        rm.remove()
        continue
      }
      const up2 = up1.getFunctionParent()?.parentPath
      if (up2) {
        // DebugProtectionFunctionCall
        const scope2 = up2.scope.getBinding(callName).scope
        up2.remove()
        scope1.crawl()
        scope2.crawl()
        const bind = scope2.bindings[callName]
        bind.path.remove()
        console.info(`Remove CallFunc: ${callName}`)
        continue
      }
      // exceptions #95
      const rm = ref.parentPath
      rm.remove()
    }
    path.remove()
    console.info(`Remove DebugProtectionFunc: ${debugName}`)
  },
}

const deleteConsoleOutputCode = {
  VariableDeclarator(path) {
    const { id, init } = path.node
    const selfName = id.name
    if (!t.isCallExpression(init)) {
      return
    }
    if (!t.isIdentifier(init.callee)) {
      return
    }
    const callName = init.callee.name
    const args = init.arguments
    if (
      args.length != 2 ||
      !t.isThisExpression(args[0]) ||
      !t.isFunctionExpression(args[1])
    ) {
      return
    }
    const block = generator(args[1]).code
    const pattern = `console=console=log,warn,info,error,for(){${callName}constructor.prototype.bind${callName}${callName}bind${callName}}`
    if (!checkPattern(block, pattern)) {
      return
    }
    const refs = path.scope.bindings[selfName].referencePaths
    for (let ref of refs) {
      if (ref.key == 'callee') {
        ref.parentPath.remove()
        break
      }
    }
    path.remove()
    console.info(`Remove ConsoleOutputFunc: ${selfName}`)
    const scope = path.scope.getBinding(callName).scope
    scope.crawl()
    const bind = scope.bindings[callName]
    if (bind.referenced) {
      console.error(`Call func ${callName} unexpected ref!`)
    }
    bind.path.remove()
    console.info(`Remove CallFunc: ${callName}`)
  },
}

function unlockEnv(ast) {
  //可能会误删一些代码,可屏蔽
  traverse(ast, deleteSelfDefendingCode)
  traverse(ast, deleteDebugProtectionCode)
  traverse(ast, deleteConsoleOutputCode)
  return ast
}

export default function (code) {
  let ret = PluginEval.unpack(code)
  let global_eval = false
  if (ret) {
    global_eval = true
    code = ret
  }
  let ast
  try {
    ast = parse(code, { errorRecovery: true })
  } catch (e) {
    console.error(`Cannot parse code: ${e.reasonCode}`)
    return null
  }
  // IllegalReturn
  traverse(ast, deleteIllegalReturn)
  // Lint before split statements
  traverse(ast, lintIfStatement)
  // Split declarations to avoid bugs
  traverse(ast, splitVarDeclaration)
  // 清理二进制显示内容
  traverse(ast, {
    StringLiteral: ({ node }) => {
      delete node.extra
    },
    NumericLiteral: ({ node }) => {
      delete node.extra
    },
  })
  console.log('还原数值...')
  if (!decodeObject(ast)) {
    return null
  }
  console.log('处理全局加密...')
  if (!decodeGlobal(ast)) {
    return null
  }
  console.log('提高代码可读性...')
  ast = purifyCode(ast)
  console.log('处理代码块加密...')
  stringArrayLite(ast)
  ast = decodeCodeBlock(ast)
  console.log('清理死代码...')
  ast = cleanDeadCode(ast)
  // 刷新代码
  ast = parse(generator(ast, optGenMin).code, { errorRecovery: true })
  console.log('提高代码可读性...')
  ast = purifyCode(ast)
  console.log('解除环境限制...')
  ast = unlockEnv(ast)
  console.log('净化完成')
  code = generator(ast, { jsescOption: { minimal: true } }).code
  if (global_eval) {
    code = PluginEval.pack(code)
  }
  return code
}


================================================
FILE: src/plugin/sojson.js
================================================
/**
 * 在 babel_asttool.js 的基础上修改而来
 */
import { parse } from '@babel/parser'
import _generate from '@babel/generator'
const generator = _generate.default
import _traverse from '@babel/traverse'
const traverse = _traverse.default
import * as t from '@babel/types'
import ivm from 'isolated-vm'
import PluginEval from './eval.js'
import calculateConstantExp from '../visitor/calculate-constant-exp.js'
import deleteUnusedVar from '../visitor/delete-unused-var.js'
import parseControlFlowStorage from '../visitor/parse-control-flow-storage.js'
import pruneIfBranch from '../visitor/prune-if-branch.js'
import splitSequence from '../visitor/split-sequence.js'

const isolate = new ivm.Isolate()
const globalContext = isolate.createContextSync()
function virtualGlobalEval(jsStr) {
  return globalContext.evalSync(String(jsStr))
}

function decodeGlobal(ast) {
  // 清理空语句
  let i = 0
  while (i < ast.program.body.length) {
    if (t.isEmptyStatement(ast.program.body[i])) {
      ast.program.body.splice(i, 1)
    } else {
      ++i
    }
  }
  // 前3句非空语句分别为签名信息、预处理函数、解密函数。
  if (i < 3) {
    console.log('Error: code too short')
    return false
  }
  // 分离解密语句与内容语句
  let decrypt_code = ast.program.body.slice(0, 3)
  if (!t.isVariableDeclaration(decrypt_code[0])) {
    console.log('Error: line 1 is not variable declaration')
    return false
  }
  let decrypt_fun = decrypt_code[2]
  if (t.isExpressionStatement(decrypt_fun)) {
    decrypt_fun = decrypt_code[1]
  }
  let decrypt_val
  if (t.isVariableDeclaration(decrypt_fun)) {
    decrypt_val = decrypt_fun.declarations[0].id.name
  } else if (t.isFunctionDeclaration(decrypt_fun)) {
    decrypt_val = decrypt_fun.id.name
  } else {
    console.log('Error: cannot find decrypt variable')
    return false
  }
  console.log(`主加密变量: ${decrypt_val}`)
  let content_code = ast.program.body.slice(3)
  // 运行解密语句
  ast.program.body = decrypt_code
  let { code } = generator(ast, {
    compact: true,
  })
  virtualGlobalEval(code)
  // 遍历内容语句
  function funToStr(path) {
    let node = path.node
    if (!t.isIdentifier(node.callee, { name: decrypt_val })) {
      return
    }
    let tmp = path.toString()
    let value = virtualGlobalEval(tmp)
    // console.log(`还原前:${tmp} 还原后:${value}`)
    path.replaceWith(t.valueToNode(value))
  }
  function memToStr(path) {
    let node = path.node
    if (!t.isIdentifier(node.object, { name: decrypt_val })) {
      return
    }
    let tmp = path.toString()
    let value = virtualGlobalEval(tmp)
    // console.log(`还原前:${tmp} 还原后:${value}`)
    path.replaceWith(t.valueToNode(value))
  }
  ast.program.body = content_code
  traverse(ast, {
    CallExpression: funToStr,
    MemberExpression: memToStr,
  })
  return ast
}

function cleanSwitchCode(path) {
  // 扁平控制:
  // 会使用一个恒为true的while语句包裹一个switch语句
  // switch语句的执行顺序又while语句上方的字符串决定
  // 首先碰断是否符合这种情况
  const node = path.node
  if (!(t.isBooleanLiteral(node.test) || t.isUnaryExpression(node.test))) {
    return
  }
  if (!(node.test.prefix || node.test.value)) {
    return
  }
  if (!t.isBlockStatement(node.body)) {
    return
  }
  const body = node.body.body
  if (
    !t.isSwitchStatement(body[0]) ||
    !t.isMemberExpression(body[0].discriminant) ||
    !t.isBreakStatement(body[1])
  ) {
    return
  }
  // switch语句的两个变量
  const swithStm = body[0]
  const arrName = swithStm.discriminant.object.name
  const argName = swithStm.discriminant.property.argument.name
  console.log(`扁平化还原: ${arrName}[${argName}]`)
  // 在while上面的节点寻找这两个变量
  let arr = []
  path.getAllPrevSiblings().forEach((pre_path) => {
    const { declarations } = pre_path.node
    let { id, init } = declarations[0]
    if (arrName == id.name) {
      arr = init.callee.object.value.split('|')
      pre_path.remove()
    }
    if (argName == id.name) {
      pre_path.remove()
    }
  })
  // 重建代码块
  const caseList = swithStm.cases
  let resultBody = []
  arr.map((targetIdx) => {
    // 从当前序号开始直到遇到continue
    let valid = true
    targetIdx = parseInt(targetIdx)
    while (valid && targetIdx < caseList.length) {
      const targetBody = caseList[targetIdx].consequent
      const test = caseList[targetIdx].test
      if (!t.isStringLiteral(test) || parseInt(test.value) !== targetIdx) {
        console.log(`switch中出现乱序的序号: ${test.value}:${targetIdx}`)
      }
      for (let i = 0; i < targetBody.length; ++i) {
        const s = targetBody[i]
        if (t.isContinueStatement(s)) {
          valid = false
          break
        }
        if (t.isReturnStatement(s)) {
          valid = false
          resultBody.push(s)
          break
        }
        if (t.isBreakStatement(s)) {
          console.log(`switch中出现意外的break: ${arrName}[${argName}]`)
        } else {
          resultBody.push(s)
        }
      }
      targetIdx++
    }
  })
  // 替换整个while语句
  path.replaceInline(resultBody)
}

function cleanDeadCode(ast) {
  traverse(ast, calculateConstantExp)
  traverse(ast, pruneIfBranch)
  traverse(ast, { WhileStatement: { exit: cleanSwitchCode } })
  return ast
}

function checkPattern(code, pattern) {
  let i = 0
  let j = 0
  while (i < code.length && j < pattern.length) {
    if (code[i] == pattern[j]) {
      ++j
    }
    ++i
  }
  return j == pattern.length
}

/**
 * Two RegExp tests will be conducted here:
 * * If '\n' exists (code formatted)
 * * If '\u' or '\x' does not exist (literal formatted)
 *
 * An infinite call stack will appear if either of the test fails.
 * (by replacing the 'e' with '\u0435')
 */
const deleteSelfDefendingCode = {
  VariableDeclarator(path) {
    const { id, init } = path.node
    const selfName = id.name
    if (!t.isCallExpression(init)) {
      return
    }
    if (!t.isIdentifier(init.callee)) {
      return
    }
    const callName = init.callee.name
    const args = init.arguments
    if (
      args.length != 2 ||
      !t.isThisExpression(args[0]) ||
      !t.isFunctionExpression(args[1])
    ) {
      return
    }
    const block = generator(args[1]).code
    const pattern = `RegExp()return.test(.toString())RegExp()return.test(.toString())\u0435\u0435`
    if (!checkPattern(block, pattern)) {
      return
    }
    const refs = path.scope.bindings[selfName].referencePaths
    for (let ref of refs) {
      if (ref.key == 'callee') {
        ref.parentPath.remove()
        break
      }
    }
    path.remove()
    console.info(`Remove SelfDefendingFunc: ${selfName}`)
    const scope = path.scope.getBinding(callName).scope
    scope.crawl()
    const bind = scope.bindings[callName]
    if (bind.referenced) {
      console.error(`Call func ${callName} unexpected ref!`)
    }
    bind.path.remove()
    console.info(`Remove CallFunc: ${callName}`)
  },
}

/**
 * A "debugger" will be inserted by:
 * * v5: directly.
 * * v6: calling Function constructor twice.
 */
const deleteDebugProtectionCode = {
  FunctionDeclaration(path) {
    const { id, params, body } = path.node
    if (
      !t.isIdentifier(id) ||
      params.length !== 1 ||
      !t.isIdentifier(params[0]) ||
      !t.isBlockStatement(body) ||
      body.body.length !== 2 ||
      !t.isFunctionDeclaration(body.body[0]) ||
      !t.isTryStatement(body.body[1])
    ) {
      return
    }
    const debugName = id.name
    const ret = params[0].name
    const subNode = body.body[0]
    if (
      !t.isIdentifier(subNode.id) ||
      subNode.params.length !== 1 ||
      !t.isIdentifier(subNode.params[0])
    ) {
      return
    }
    const subName = subNode.id.name
    const counter = subNode.params[0].name
    const code = generator(body).code
    const pattern = `function${subName}(${counter}){${counter}debug${subName}(++${counter})}try{if(${ret})return${subName}${subName}(0)}catch(){}`
    if (!checkPattern(code, pattern)) {
      return
    }
    const scope1 = path.parentPath.scope
    const refs = scope1.bindings[debugName].referencePaths
    for (let ref of refs) {
      if (ref.findParent((path) => path.removed)) {
        continue
      }
      let parent = ref.getFunctionParent()
      if (parent.key == 0) {
        // DebugProtectionFunctionInterval
        // window.setInterval(Function(), ...)
        const rm = parent.parentPath
        rm.remove()
        continue
      }
      // DebugProtectionFunctionCall
      const callName = parent.parent.callee.name
      const up2 = parent.getFunctionParent().parentPath
      const scope2 = up2.scope.getBinding(callName).scope
      up2.remove()
      scope1.crawl()
      scope2.crawl()
      const bind = scope2.bindings[callName]
      bind.path.remove()
      console.info(`Remove CallFunc: ${callName}`)
    }
    path.remove()
    console.info(`Remove DebugProtectionFunc: ${debugName}`)
  },
}

const deleteConsoleOutputCode = {
  VariableDeclarator(path) {
    const { id, init } = path.node
    const selfName = id.name
    if (!t.isCallExpression(init)) {
      return
    }
    if (!t.isIdentifier(init.callee)) {
      return
    }
    const callName = init.callee.name
    const args = init.arguments
    if (
      args.length != 2 ||
      !t.isThisExpression(args[0]) ||
      !t.isFunctionExpression(args[1])
    ) {
      return
    }
    const body = args[1].body.body
    if (body.length !== 3) {
      return
    }
    if (
      !t.isVariableDeclaration(body[0]) ||
      !t.isVariableDeclaration(body[1]) ||
      !t.isIfStatement(body[2])
    ) {
      return
    }
    const feature = [
      [],
      ['window', 'process', 'require', 'global'],
      [
        'console',
        'log',
        'warn',
        'debug',
        'info',
        'error',
        'exception',
        'trace',
      ],
    ]
    let valid = true
    for (let i = 1; i < 3; ++i) {
      const { code } = generator(body[i])
      feature[i].map((key) => {
        if (code.indexOf(key) == -1) {
          valid = false
        }
      })
    }
    if (!valid) {
      return
    }
    const refs = path.scope.bindings[selfName].referencePaths
    for (let ref of refs) {
      if (ref.key == 'callee') {
        ref.parentPath.remove()
        break
      }
    }
    path.remove()
    console.info(`Remove ConsoleOutputFunc: ${selfName}`)
    const scope = path.scope.getBinding(callName).scope
    scope.crawl()
    const bind = scope.bindings[callName]
    if (bind.referenced) {
      console.error(`Call func ${callName} unexpected ref!`)
    }
    bind.path.remove()
    console.info(`Remove CallFunc: ${callName}`)
  },
}

const deleteVersionCheck = {
  StringLiteral(path) {
    const msg = '删除版本号,js会定期弹窗,还请支持我们的工作'
    if (path.node.value !== msg) {
      return
    }
    let fnPath = path.getFunctionParent().parentPath
    if (!fnPath.isCallExpression()) {
      return
    }
    fnPath.remove()
    console.log('Remove VersionCheck')
  },
}

function unlockEnv(ast) {
  // 查找并删除`自卫模式`函数
  traverse(ast, deleteSelfDefendingCode)
  // 查找并删除`禁止控制台调试`函数
  traverse(ast, deleteDebugProtectionCode)
  // 清空`禁止控制台输出`函数
  traverse(ast, deleteConsoleOutputCode)
  // 删除版本号检测
  traverse(ast, deleteVersionCheck)
  return ast
}

function purifyFunction(path) {
  const node = path.node
  if (!t.isIdentifier(node.left) || !t.isFunctionExpression(node.right)) {
    return
  }
  const name = node.left.name
  if (node.right.body.body.length !== 1) {
    return
  }
  let retStmt = node.right.body.body[0]
  if (!t.isReturnStatement(retStmt)) {
    return
  }
  if (!t.isBinaryExpression(retStmt.argument, { operator: '+' })) {
    return
  }
  try {
    const fnPath = path.getFunctionParent() || path.scope.path
    fnPath.traverse({
      CallExpression: function (_path) {
        const _node = _path.node.callee
        if (!t.isIdentifier(_node, { name: name })) {
          return
        }
        let args = _path.node.arguments
        _path.replaceWith(t.binaryExpression('+', args[0], args[1]))
      },
    })
    path.remove()
    console.log(`拼接类函数: ${name}`)
  } catch {
    let code = generator(path.node, { minified: true }).code
    console.warn('Purify function failed: ' + code)
  }
}

function purifyCode(ast) {
  // 净化拼接字符串的函数
  traverse(ast, { AssignmentExpression: purifyFunction })
  // 计算常量表达式
  traverse(ast, calculateConstantExp)
  // 替换索引器
  function FormatMember(path) {
    // _0x19882c['removeCookie']['toString']()
    //  |
    //  |
    //  |
    //  v
    // _0x19882c.removeCookie.toString()
    let curNode = path.node
    if (!t.isStringLiteral(curNode.property)) {
      return
    }
    if (curNode.computed === undefined || !curNode.computed === true) {
      return
    }
    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(curNode.property.value)) {
      return
    }
    curNode.property = t.identifier(curNode.property.value)
    curNode.computed = false
  }
  traverse(ast, { MemberExpression: FormatMember })
  // 分割表达式
  traverse(ast, splitSequence)
  // 删除空语句
  traverse(ast, {
    EmptyStatement: (path) => {
      path.remove()
    },
  })
  // 删除未使用的变量
  traverse(ast, deleteUnusedVar)
  return ast
}

export default function (code) {
  let ret = PluginEval.unpack(code)
  let global_eval = false
  if (ret) {
    global_eval = true
    code = ret
  }
  let ast = parse(code)
  // 清理二进制显示内容
  traverse(ast, {
    StringLiteral: ({ node }) => {
      delete node.extra
    },
  })
  traverse(ast, {
    NumericLiteral: ({ node }) => {
      delete node.extra
    },
  })
  console.log('处理全局加密...')
  ast = decodeGlobal(ast)
  if (!ast) {
    return null
  }
  console.log('处理代码块加密...')
  traverse(ast, parseControlFlowStorage)
  console.log('清理死代码...')
  ast = cleanDeadCode(ast)
  // 刷新代码
  ast = parse(
    generator(ast, {
      comments: false,
      jsescOption: { minimal: true },
    }).code
  )
  console.log('提高代码可读性...')
  ast = purifyCode(ast)
  console.log('解除环境限制...')
  ast = unlockEnv(ast)
  console.log('净化完成')
  code = generator(ast, {
    comments: false,
    jsescOption: { minimal: true },
  }).code
  if (global_eval) {
    code = PluginEval.pack(code)
  }
  return code
}


================================================
FILE: src/plugin/sojsonv7.js
================================================
/**
 * For jsjiami.com.v7
 */
import { parse } from '@babel/parser'
import _generate from '@babel/generator'
const generator = _generate.default
import _traverse from '@babel/traverse'
const traverse = _traverse.default
import * as t from '@babel/types'
import ivm from 'isolated-vm'
import PluginEval from './eval.js'
import calculateConstantExp from '../visitor/calculate-constant-exp.js'
import deleteIllegalReturn from '../visitor/delete-illegal-return.js'
import deleteUnusedVar from '../visitor/delete-unused-var.js'
import parseControlFlowStorage from '../visitor/parse-control-flow-storage.js'
import pruneIfBranch from '../visitor/prune-if-branch.js'
import splitSequence from '../visitor/split-sequence.js'

const isolate = new ivm.Isolate()
const globalContext = isolate.createContextSync()
function virtualGlobalEval(jsStr) {
  return globalContext.evalSync(String(jsStr))
}
function evalOneTime(str) {
  const vm = new ivm.Isolate()
  const ret = vm.createContextSync().evalSync(String(str))
  vm.dispose()
  return ret
}

function decodeGlobal(ast) {
  // 清理空语句
  let i = 0
  while (i < ast.program.body.length) {
    if (t.isEmptyStatement(ast.program.body[i])) {
      ast.program.body.splice(i, 1)
    } else {
      ++i
    }
  }
  // line 1: version and string table
  // line x: preprocessing function of string table
  // line y: main encode function containing the var of string table
  if (i < 3) {
    console.log('Error: code too short')
    return false
  }
  // split the first line
  traverse(ast, {
    Program(path) {
      path.stop()
      const l1 = path.get('body.0')
      if (!l1.isVariableDeclaration()) {
        return
      }
      const defs = l1.node.declarations
      const kind = l1.node.kind
      for (let i = defs.length - 1; i; --i) {
        l1.insertAfter(t.VariableDeclaration(kind, [defs[i]]))
        l1.get(`declarations.${i}`).remove()
      }
      l1.scope.crawl()
    },
  })
  // find the main encode function
  // [version, string-array, call, ...]
  let decrypt_code = []
  for (let i = 0; i < 3; ++i) {
    decrypt_code.push(t.EmptyStatement())
  }
  const first_line = ast.program.body[0]
  let var_version
  if (t.isVariableDeclaration(first_line)) {
    if (first_line.declarations.length) {
      var_version = first_line.declarations[0].id.name
    }
  } else if (t.isCallExpression(first_line?.expression)) {
    let call_func = first_line.expression.callee?.name
    let i = ast.program.body.length
    let find = false
    while (--i) {
      let part = ast.program.body[i]
      if (!t.isFunctionDeclaration(part) || part?.id?.name !== call_func) {
        continue
      }
      if (find) {
        // remove duplicate definition
        ast.program.body[i] = t.emptyStatement()
        continue
      }
      find = true
      let obj = part.body.body[0]?.expression?.left
      if (!obj || !t.isMemberExpression(obj) || obj.object?.name !== 'global') {
        break
      }
      var_version = obj.property?.value
      decrypt_code.push(part)
      ast.program.body[i] = t.emptyStatement()
      continue
    }
  }
  if (!var_version) {
    console.error('Line 1 is not version variable!')
    return false
  }
  console.info(`Version var: ${var_version}`)
  decrypt_code[0] = first_line
  ast.program.body.shift()

  // iterate and classify all refs of var_version
  const refs = {
    string_var: null,
    string_path: null,
    def: [],
  }
  traverse(ast, {
    Identifier: (path) => {
      const name = path.node.name
      if (name !== var_version) {
        return
      }
      const up1 = path.parentPath
      if (up1.isVariableDeclarator()) {
        refs.def.push(path)
      } else if (up1.isArrayExpression()) {
        let node_table = path.getFunctionParent()
        while (node_table.getFunctionParent()) {
          node_table = node_table.getFunctionParent()
        }
        let var_string_table = null
        if (node_table.node.id) {
          var_string_table = node_table.node.id.name
        } else {
          while (!node_table.isVariableDeclarator()) {
            node_table = node_table.parentPath
          }
          var_string_table = node_table.node.id.name
        }
        let valid = true
        up1.traverse({
          MemberExpression(path) {
            valid = false
            path.stop()
          },
        })
        if (valid) {
          refs.string_var = var_string_table
          refs.string_path = node_table
        } else {
          console.info(`Drop string table: ${var_string_table}`)
        }
      } else if (up1.isAssignmentExpression() && path.key === 'left') {
        // We don't need to execute this reference
        // Instead, we can delete it directly
        const up2 = up1.parentPath
        up2.replaceWith(up2.node.left)
      } else {
        console.warn(`Unexpected ref var_version: ${up1}`)
      }
    },
  })
  // check if contains string table
  let var_string_table = refs.string_var
  if (!var_string_table) {
    console.error('Cannot find string table')
    return false
  }
  //  check if contains rotate function and decrypt variable
  let decrypt_val
  let decrypt_path
  let binds = refs.string_path.scope.getBinding(var_string_table)
  function parse_main_call(path) {
    decrypt_path = path
    const node = path.node
    const copy = t.functionDeclaration(node.id, node.params, node.body)
    node.body = t.blockStatement([])
    return copy
  }
  // remove path of string table
  if (refs.string_path.isVariableDeclarator()) {
    decrypt_code[1] = t.variableDeclaration('var', [refs.string_path.node])
  } else {
    decrypt_code[1] = refs.string_path.node
  }
  refs.string_path.remove()
  // iterate refs
  let cache = undefined
  for (let bind of binds.referencePaths) {
    if (bind.findParent((path) => path.removed)) {
      continue
    }
    const parent = bind.parentPath
    if (parent.isCallExpression() && bind.listKey === 'arguments') {
      // This is the rotate function.
      // If it's in a sequence expression, it can be handled together.
      // Or, we should handle it after this iteration.
      cache = parent
      continue
    }
    if (parent.isSequenceExpression()) {
      // rotate function
      decrypt_code.push(t.expressionStatement(parent.node))
      const up2 = parent.parentPath
      if (up2.isIfStatement()) {
        // In the new version, rotate function will be enclosed by an
        // empty IfStatement
        up2.remove()
      } else {
        parent.remove()
      }
      continue
    }
    if (parent.isVariableDeclarator()) {
      // main decrypt val
      let top = parent.getFunctionParent()
      while (top.getFunctionParent()) {
        top = top.getFunctionParent()
      }
      decrypt_code[2] = parse_main_call(top)
      decrypt_val = top.node.id.name
      continue
    }
    if (parent.isCallExpression() && !parent.node.arguments.length) {
      // main decrypt val
      if (!t.isVariableDeclarator(parent.parentPath.node)) {
        continue
      }
      let top = parent.getFunctionParent()
      while (top.getFunctionParent()) {
        top = top.getFunctionParent()
      }
      decrypt_code[2] = parse_main_call(top)
      decrypt_val = top.node.id.name
      continue
    }
    if (parent.isExpressionStatement()) {
      parent.remove()
      continue
    }
    console.warn(`Unexpected ref var_string_table: ${parent}`)
  }
  // If rotateFunction is detected but not handled, we should handle it now.
  if (decrypt_code.length === 3 && cache) {
    if (cache.parentPath.isExpressionStatement()) {
      decrypt_code.push(cache.parent)
      cache = cache.parentPath
    } else {
      decrypt_code.push(t.expressionStatement(cache.node))
    }
    cache.remove()
  }
  decrypt_path.parentPath.scope.crawl()
  if (!decrypt_val) {
    console.error('Cannot find decrypt variable')
    return
  }
  console.log(`Main call wrapper name: ${decrypt_val}`)

  // 运行解密语句
  let content_code = ast.program.body
  ast.program.body = decrypt_code
  let { code } = generator(ast, {
    compact: true,
  })
  virtualGlobalEval(code)
  // 遍历内容语句
  ast.program.body = content_code
  function funToStr(path) {
    let tmp = path.toString()
    let value = virtualGlobalEval(tmp)
    // console.log(`还原前:${tmp} 还原后:${value}`)
    path.replaceWith(t.valueToNode(value))
  }
  function memToStr(path) {
    let tmp = path.toString()
    let value = virtualGlobalEval(tmp)
    // console.log(`还原前:${tmp} 还原后:${value}`)
    path.replaceWith(t.valueToNode(value))
  }
  function dfs(stk, item) {
    stk.push(item)
    const cur_val = item.name
    console.log(`Enter sub ${stk.length}:${cur_val}`)
    let pfx = ''
    for (let parent of stk) {
      pfx += parent.code + ';'
    }
    virtualGlobalEval(pfx)
    let scope = item.path.scope
    if (item.path.isFunctionDeclaration()) {
      scope = item.path.parentPath.scope
    }
    // var is function scoped and let is block scoped
    // Hence, var may not be in the current scope, e.g., in a for-loop
    const binding = scope.getBinding(cur_val)
    scope = binding.scope
    const refs = binding.referencePaths
    const refs_next = []
    for (let ref of refs) {
      const parent = ref.parentPath
      if (ref.key === 'init') {
        // VariableDeclarator
        refs_next.push({
          name: parent.node.id.name,
          path: parent,
          code: 'var ' + parent,
        })
      } else if (ref.key === 'right') {
        // AssignmentExpression
        refs_next.push({
          name: parent.node.left.name,
          path: parent,
          code: 'var ' + parent,
        })
      } else if (ref.key === 'object') {
        // MemberExpression
        memToStr(parent)
      } else if (ref.key === 'callee') {
        // CallExpression
        funToStr(parent)
      } else {
        console.error('Unexpected reference')
      }
    }
    for (let ref of refs_next) {
      dfs(stk, ref)
    }
    scope.crawl()
    item.path.remove()
    scope.crawl()
    console.log(`Exit sub ${stk.length}:${cur_val}`)
    stk.pop()
  }
  const root = {
    name: decrypt_val,
    path: decrypt_path,
    code: '',
  }
  dfs([], root)
  return ast
}

function cleanSwitchCode1(path) {
  // 扁平控制:
  // 会使用一个恒为true的while语句包裹一个switch语句
  // switch语句的执行顺序又while语句上方的字符串决定
  // 首先碰断是否符合这种情况
  const node = path.node
  if (!(t.isBooleanLiteral(node.test) || t.isUnaryExpression(node.test))) {
    return
  }
  if (!(node.test.prefix || node.test.value)) {
    return
  }
  if (!t.isBlockStatement(node.body)) {
    return
  }
  const body = node.body.body
  if (
    !t.isSwitchStatement(body[0]) ||
    !t.isMemberExpression(body[0].discriminant) ||
    !t.isBreakStatement(body[1])
  ) {
    return
  }
  // switch语句的两个变量
  const swithStm = body[0]
  const arrName = swithStm.discriminant.object.name
  const argName = swithStm.discriminant.property.argument.name
  console.log(`扁平化还原: ${arrName}[${argName}]`)
  // 在while上面的节点寻找这两个变量
  let arr = []
  path.getAllPrevSiblings().forEach((pre_path) => {
    const { declarations } = pre_path.node
    let { id, init } = declarations[0]
    if (arrName == id.name) {
      arr = init.callee.object.value.split('|')
      pre_path.remove()
    }
    if (argName == id.name) {
      pre_path.remove()
    }
  })
  // 重建代码块
  const caseList = swithStm.cases
  let resultBody = []
  arr.map((targetIdx) => {
    // 从当前序号开始直到遇到continue
    let valid = true
    targetIdx = parseInt(targetIdx)
    while (valid && targetIdx < caseList.length) {
      const targetBody = caseList[targetIdx].consequent
      const test = caseList[targetIdx].test
      if (!t.isStringLiteral(test) || parseInt(test.value) !== targetIdx) {
        console.log(`switch中出现乱序的序号: ${test.value}:${targetIdx}`)
      }
      for (let i = 0; i < targetBody.length; ++i) {
        const s = targetBody[i]
        if (t.isContinueStatement(s)) {
          valid = false
          break
        }
        if (t.isReturnStatement(s)) {
          valid = false
          resultBody.push(s)
          break
        }
        if (t.isBreakStatement(s)) {
          console.log(`switch中出现意外的break: ${arrName}[${argName}]`)
        } else {
          resultBody.push(s)
        }
      }
      targetIdx++
    }
  })
  // 替换整个while语句
  path.replaceInline(resultBody)
}

function cleanSwitchCode2(path) {
  // 扁平控制:
  // 会使用一个空的for语句包裹一个switch语句
  // switch语句的执行顺序由for语句上方的字符串决定
  // 首先判断是否符合这种情况
  const node = path.node
  if (node.init || node.test || node.update) {
    return
  }
  if (!t.isBlockStatement(node.body)) {
    return
  }
  const body = node.body.body
  if (
    !t.isSwitchStatement(body[0]) ||
    !t.isMemberExpression(body[0].discriminant) ||
    !t.isBreakStatement(body[1])
  ) {
    return
  }
  // switch语句的两个变量
  const swithStm = body[0]
  const arrName = swithStm.discriminant.object.name
  const argName = swithStm.discriminant.property.argument.name
  // 在while上面的节点寻找这两个变量
  let arr = null
  for (let pre_path of path.getAllPrevSiblings()) {
    if (!pre_path.isVariableDeclaration()) {
      continue
    }
    let test = '' + pre_path
    try {
      arr = evalOneTime(test + `;${arrName}.join('|')`)
      arr = arr.split('|')
    } catch {
      //
    }
  }
  if (!Array.isArray(arr)) {
    return
  }
  console.log(`扁平化还原: ${arrName}[${argName}]`)
  // 重建代码块
  const caseMap = {}
  for (let item of swithStm.cases) {
    caseMap[item.test.value] = item.consequent
  }
  let resultBody = []
  arr.map((targetIdx) => {
    // 从当前序号开始直到遇到continue
    let valid = true
    while (valid && targetIdx < arr.length) {
      const targetBody = caseMap[targetIdx]
      for (let i = 0; i < targetBody.length; ++i) {
        const s = targetBody[i]
        if (t.isContinueStatement(s)) {
          valid = false
          break
        }
        if (t.isReturnStatement(s)) {
          valid = false
          resultBody.push(s)
          break
        }
        if (t.isBreakStatement(s)) {
          console.log(`switch中出现意外的break: ${arrName}[${argName}]`)
        } else {
          resultBody.push(s)
        }
      }
      targetIdx++
    }
  })
  // 替换整个while语句
  path.replaceInline(resultBody)
}

function cleanDeadCode(ast) {
  traverse(ast, calculateConstantExp)
  traverse(ast, pruneIfBranch)
  traverse(ast, { WhileStatement: { exit: cleanSwitchCode1 } })
  traverse(ast, { ForStatement: { exit: cleanSwitchCode2 } })
  return ast
}

function removeUniqueCall(path) {
  let up1 = path.parentPath
  let decorator = up1.node.callee.name
  console.info(`Remove decorator: ${decorator}`)
  let bind1 = up1.scope.getBinding(decorator)
  bind1.path.remove()
  if (up1.key === 'callee') {
    up1.parentPath.remove()
  } else if (up1.key === 'init') {
    let up2 = up1.parentPath
    let call = up2.node.id.name
    console.info(`Remove call: ${call}`)
    let bind2 = up2.scope.getBinding(call)
    up2.remove()
    for (let ref of bind2.referencePaths) {
      if (ref.findParent((path) => path.removed)) {
        continue
      }
      if (ref.key === 'callee') {
        let rm = ref.parentPath
        if (rm.key === 'expression') {
          rm = rm.parentPath
        }
        rm.remove()
      } else {
        console.warn(`Unexpected ref key: ${ref.key}`)
      }
    }
  }
}

function unlockDebugger(path) {
  const decl_path = path.getFunctionParent()?.getFunctionParent()
  if (!decl_path) {
    return
  }
  // Check if contains inf-loop
  let valid = false
  path.getFunctionParent().traverse({
    WhileStatement(path) {
      if (t.isBooleanLiteral(path.node.test) && path.node.test) {
        valid = true
      }
    },
  })
  if (!valid) {
    return
  }
  const name = decl_path.node.id.name
  const bind = decl_path.scope.getBinding(name)
  console.info(`Debug test and inf-loop: ${name}`)
  for (let ref of bind.referencePaths) {
    if (ref.findParent((path) => path.removed)) {
      continue
    }
    if (ref.listKey === 'arguments') {
      // setInterval
      let rm = ref.getFunctionParent().parentPath
      if (rm.key === 'expression') {
        rm = rm.parentPath
      }
      rm.remove()
    } else if (ref.key === 'callee') {
      // lint test for this method
      let rm = ref.getFunctionParent()
      removeUniqueCall(rm)
    } else {
      console.warn(`Unexpected ref key: ${ref.key}`)
    }
  }
  decl_path.remove()
  path.stop()
}

function unlockConsole(path) {
  if (!t.isArrayExpression(path.node.init)) {
    return
  }
  let pattern = 'log|warn|debug|info|error|exception|table|trace'
  let count = 0
  for (let ele of path.node.init.elements) {
    if (~pattern.indexOf(ele.value)) {
      ++count
      continue
    }
    return
  }
  if (count < 5) {
    return
  }
  let left1 = path.getSibling(0)
  const code = generator(left1.node, { minified: true }).code
  pattern = ['window', 'process', 'require', 'global']
  pattern.map((key) => {
    if (code.indexOf(key) == -1) {
      return
    }
  })
  let rm = path.getFunctionParent()
  removeUniqueCall(rm)
}

function unlockLint(path) {
  if (path.findParent((up) => up.removed)) {
    return
  }
  if (path.node.value !== '(((.+)+)+)+$') {
    return
  }
  let rm = path.getFunctionParent()
  removeUniqueCall(rm)
}

function unlockDomainLock(path) {
  const array_list = [
    '[7,116,5,101,3,117,0,100]',
    '[5,110,0,100]',
    '[7,110,0,108]',
    '[7,101,0,104]',
  ]
  const checkArray = (node) => {
    const trim = node.split(' ').join('')
    for (let i = 0; i < 4; ++i) {
      if (array_list[i] == trim) {
        return i + 1
      }
    }
    return 0
  }
  if (path.findParent((up) => up.removed)) {
    return
  }
  let mask = 1 << checkArray('' + path)
  if (mask == 1) {
    return
  }
  let rm = path.getFunctionParent()
  rm.traverse({
    ArrayExpression: function (item) {
      mask = mask | (1 << checkArray('' + item))
    },
  })
  if (mask & 0b11110) {
    console.info('Find domain lock')
    removeUniqueCall(rm)
  }
}

function unlockEnv(ast) {
  // 删除`禁止控制台调试`函数
  traverse(ast, { DebuggerStatement: unlockDebugger })
  // 删除`禁止控制台输出`函数
  traverse(ast, { VariableDeclarator: unlockConsole })
  // 删除`禁止换行`函数
  traverse(ast, { StringLiteral: unlockLint })
  // 删除`安全域名`函数
  traverse(ast, { ArrayExpression: unlockDomainLock })
}

/**
 * If a function acts as follows:
 * A = function (p1, p2) { return p1 + p2 }
 *
 * Convert its call to a binary expression:
 * A(a, b) => a + b
 */
function purifyFunction(path) {
  const left = path.get('left')
  const right = path.get('right')
  if (!left.isIdentifier() || !right.isFunctionExpression()) {
    return
  }
  const name = left.node.name
  const params = right.node.params
  if (params.length !== 2) {
    return
  }
  const name1 = params[0].name
  const name2 = params[1].name
  if (right.node.body.body.length !== 1) {
    return
  }
  let retStmt = right.node.body.body[0]
  if (!t.isReturnStatement(retStmt)) {
    return
  }
  if (!t.isBinaryExpression(retStmt.argument, { operator: '+' })) {
    return
  }
  if (
    retStmt.argument.left?.name !== name1 ||
    retStmt.argument.right?.name !== name2
  ) {
    return
  }
  const fnPath = path.getFunctionParent() || path.scope.path
  fnPath.traverse({
    CallExpression: function (_path) {
      const _node = _path.node.callee
      if (!t.isIdentifier(_node, { name: name })) {
        return
      }
      let args = _path.node.arguments
      _path.replaceWith(t.binaryExpression('+', args[0], args[1]))
    },
  })
  path.remove()
  console.log(`拼接类函数: ${name}`)
}

function purifyCode(ast) {
  // 净化拼接字符串的函数
  traverse(ast, { AssignmentExpression: purifyFunction })
  // 计算常量表达式
  traverse(ast, calculateConstantExp)
  // 替换索引器
  function FormatMember(path) {
    // _0x19882c['removeCookie']['toString']()
    //  |
    //  |
    //  |
    //  v
    // _0x19882c.removeCookie.toString()
    let curNode = path.node
    if (!t.isStringLiteral(curNode.property)) {
      return
    }
    if (curNode.computed === undefined || !curNode.computed === true) {
      return
    }
    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(curNode.property.value)) {
      return
    }
    curNode.property = t.identifier(curNode.property.value)
    curNode.computed = false
  }
  traverse(ast, { MemberExpression: FormatMember })
  // 分割表达式
  traverse(ast, splitSequence)
  // 删除空语句
  traverse(ast, {
    EmptyStatement: (path) => {
      path.remove()
    },
  })
  // 删除未使用的变量
  traverse(ast, deleteUnusedVar)
}

export default function (code) {
  let ret = PluginEval.unpack(code)
  let global_eval = false
  if (ret) {
    global_eval = true
    code = ret
  }
  let ast
  try {
    ast = parse(code, { errorRecovery: true })
  } catch (e) {
    console.error(`Cannot parse code: ${e.reasonCode}`)
    return null
  }
  // IllegalReturn
  traverse(ast, deleteIllegalReturn)
  // 清理二进制显示内容
  traverse(ast, {
    StringLiteral: ({ node }) => {
      delete node.extra
    },
  })
  traverse(ast, {
    NumericLiteral: ({ node }) => {
      delete node.extra
    },
  })
  console.log('处理全局加密...')
  ast = decodeGlobal(ast)
  if (!ast) {
    return null
  }
  console.log('处理代码块加密...')
  traverse(ast, parseControlFlowStorage)
  console.log('清理死代码...')
  ast = cleanDeadCode(ast)
  // 刷新代码
  ast = parse(
    generator(ast, {
      comments: false,
      jsescOption: { minimal: true },
    }).code
  )
  console.log('提高代码可读性...')
  purifyCode(ast)
  ast = parse(generator(ast, { comments: false }).code)
  console.log('解除环境限制...')
  unlockEnv(ast)
  console.log('净化完成')
  code = generator(ast, {
    comments: false,
    jsescOption: { minimal: true },
  }).code
  if (global_eval) {
    code = PluginEval.pack(code)
  }
  return code
}


================================================
FILE: src/utility/check-func.js
================================================
function checkPattern(code, pattern) {
  let i = 0
  let j = 0
  while (i < code.length && j < pattern.length) {
    if (code[i] == pattern[j]) {
      ++j
    }
    ++i
  }
  return j == pattern.length
}

export default {
  checkPattern,
}


================================================
FILE: src/utility/safe-func.js
================================================
import * as t from '@babel/types'

function safeDeleteNode(name, path) {
  let binding
  if (path.isFunctionDeclaration()) {
    binding = path.parentPath.scope.getBinding(name)
  } else {
    binding = path.scope.getBinding(name)
  }
  if (!binding) {
    return false
  }
  binding.scope.crawl()
  binding = binding.scope.getBinding(name)
  if (binding.references) {
    return false
  }
  for (const item of binding.constantViolations) {
    item.remove()
  }
  const decl = binding.path
  if (decl.removed) {
    return true
  }
  if (!decl.isVariableDeclarator() && !decl.isFunctionDeclaration()) {
    return true
  }
  binding.path.remove()
  return true
}

function safeGetLiteral(path) {
  if (path.isUnaryExpression()) {
    if (path.node.operator === '-' && path.get('argument').isNumericLiteral()) {
      return -1 * path.get('argument').node.value
    }
    return null
  }
  if (path.isLiteral()) {
    return path.node.value
  }
  return null
}

function safeGetName(path) {
  if (path.isIdentifier()) {
    return path.node.name
  }
  if (path.isLiteral()) {
    return path.node.value
  }
  return null
}

function safeReplace(path, value) {
  if (typeof value === 'string') {
    path.replaceWith(t.stringLiteral(value))
    return
  }
  if (typeof value === 'number') {
    path.replaceWith(t.numericLiteral(value))
    return
  }
  path.replaceWithSourceString(value)
}

export default {
  safeDeleteNode,
  safeGetLiteral,
  safeGetName,
  safeReplace,
}


================================================
FILE: src/visitor/calculate-constant-exp.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default
import * as t from '@babel/types'

function checkLiteral(node) {
  if (t.isNumericLiteral(node)) {
    return 'positive'
  }
  if (t.isLiteral(node)) {
    return 'literal'
  }
  if (!t.isUnaryExpression(node)) {
    return false
  }
  if (!t.isNumericLiteral(node.argument)) {
    return false
  }
  if (node.operator === '-') {
    return 'negative'
  }
  return false
}

/**
 * Calculate BinaryExpression if left and right are both literals.
 * Otherwise, the expression can't be simplified.
 * Note that negative numbers are UnaryExpressions.
 */
function calculateBinaryExpression(path) {
  const { left, right } = path.node
  if (!checkLiteral(left) || !checkLiteral(right)) {
    return
  }
  const code = generator(path.node).code
  try {
    const ret = eval(code)
    // The strings cannot use replaceWithSourceString
    // For example, string "ab" will be parsed as identifier ab
    if (typeof ret === 'string') {
      path.replaceWith(t.stringLiteral(ret))
    } else {
      path.replaceWithSourceString(ret)
    }
  } catch {
    //
  }
}

/**
 * Calculate UnaryExpression:
 * - the operator is `!` and the argument is ArrayExpression or Literal.
 * - the operator is `-` and the argument is a negative number
 * - the operator is `+`, or `~`, and the argument is a number
 * - the operator is 'void' and the argument is Literal.
 * - the operator is 'typeof' and the argument is Literal.
 *
 * Otherwise, the expression can't be simplified.
 * For example, `typeof window` can be calculated but it's not constant.
 */
function calculateUnaryExpression(path) {
  const node0 = path.node
  const node1 = node0.argument
  const isLiteral = checkLiteral(node1)
  if (node0.operator === '!') {
    if (t.isArrayExpression(node1)) {
      if (node1.elements.length === 0) {
        path.replaceWith(t.booleanLiteral(false))
      }
    }
    if (isLiteral) {
      const code = generator(node0).code
      path.replaceWith(t.booleanLiteral(eval(code)))
    }
    return
  }
  if (node0.operator === '-') {
    if (isLiteral === 'negative') {
      const code = generator(node0).code
      path.replaceWithSourceString(eval(code))
    }
    return
  }
  if (node0.operator === '+' || node0.operator === '~') {
    if (isLiteral === 'negative' || isLiteral === 'positive') {
      const code = generator(node0).code
      path.replaceWithSourceString(eval(code))
    }
    return
  }
  if (node0.operator === 'void') {
    if (isLiteral) {
      path.replaceWith(t.identifier('undefined'))
    }
    return
  }
  if (node0.operator === 'typeof') {
    if (isLiteral) {
      const code = generator(node0).code
      path.replaceWith(t.stringLiteral(eval(code)))
    }
    return
  }
}

export default {
  BinaryExpression: { exit: calculateBinaryExpression },
  UnaryExpression: { exit: calculateUnaryExpression },
}


================================================
FILE: src/visitor/calculate-rstring.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default
import * as t from '@babel/types'

/**
 * "sh".split("").reverse().join("") -> "hs"
 */
export default {
  StringLiteral(path) {
    if (path.key !== 'object') {
      return
    }
    let root = path
    let count = 6
    while (root.parentPath && count) {
      if (
        root.parentPath.isMemberExpression() ||
        root.parentPath.isCallExpression()
      ) {
        root = root.parentPath
        --count
      } else {
        break
      }
    }
    if (count) {
      return
    }
    const code = generator(root.node).code
    try {
      const ret = eval(code)
      root.replaceWith(t.stringLiteral(ret))
    } catch {
      //
    }
  },
}


================================================
FILE: src/visitor/delete-extra.js
================================================
/**
 * 0x10 -> 16, "\u0058" -> "X"
 * not ASCII-safe (disable jsescOption:minimal to keep ASCII-safe)
 */
export default {
  StringLiteral: ({ node }) => {
    delete node.extra
  },
  NumericLiteral: ({ node }) => {
    delete node.extra
  },
}


================================================
FILE: src/visitor/delete-illegal-return.js
================================================
/**
 * delete ReturnStatement in Program scope
 */
export default {
  ReturnStatement(path) {
    if (!path.getFunctionParent()) {
      path.remove()
    }
  },
}


================================================
FILE: src/visitor/delete-nested-blocks.js
================================================
const isIntersect = (path, bindings) => {
  path.scope.crawl()
  for (const key of Object.keys(bindings)) {
    if (path.scope.hasBinding(key)) {
      return true
    }
  }
  return false
}

/**
 * Avoid nested blocks.
 *
 * This is slightly different from the @putout/plugin-remove-nested-blocks :
 * https://github.com/coderaiser/putout/issues/224#issuecomment-2614051528
 */
export default {
  BlockStatement: (path) => {
    const { parentPath } = path
    if (!parentPath.isBlockStatement() && !parentPath.isProgram()) {
      return
    }
    let valid = path.container.length === 1
    if (!isIntersect(parentPath, path.scope.bindings)) {
      valid = true
    }
    if (!valid) {
      return
    }
    path.replaceWithMultiple(path.node.body)
    path.scope.crawl()
  },
}


================================================
FILE: src/visitor/delete-unreachable-code.js
================================================
import * as t from '@babel/types'

/**
 * DFS the BlockStatement to find and return the location of the first
 * ReturnStatement, which is not inside a Conditional Block.
 */
const checkReturnLocation = (body) => {
  for (let i = 0; i < body.length; ++i) {
    if (t.isReturnStatement(body[i])) {
      return i
    }
    if (t.isBlockStatement(body[i])) {
      const ret = checkReturnLocation(body[i].body)
      if (~ret) {
        return i
      }
    }
  }
  return -1
}

/**
 * Remove the codes after the first ReturnStatement, which is not inside a
 * Conditional Block. The FunctionDeclaration will be preserved.
 *
 * This is slightly different from the @putout/plugin-remove-unreachable-code :
 * https://github.com/coderaiser/putout/issues/224#issuecomment-2614051528
 */
export default {
  BlockStatement: (path) => {
    const body = path.node.body
    const loc = checkReturnLocation(body)
    if (loc == -1) {
      return
    }
    for (let i = body.length - 1; i > loc; --i) {
      if (t.isFunctionDeclaration(body[i])) {
        continue
      }
      body.splice(i, 1)
    }
    if (loc === 0 && t.isBlockStatement(body[0])) {
      const inner = body.shift()
      while (inner.body.length) {
        body.unshift(inner.body.pop())
      }
    }
    path.scope.crawl()
  },
}


================================================
FILE: src/visitor/delete-unused-var.js
================================================
import * as t from '@babel/types'

/**
 * Delete unused variables with the following exceptions:
 *
 * - ForOfStatement
 * - ForInStatement
 *
 */
export default {
  VariableDeclarator: (path) => {
    const { node, scope } = path
    const name = node.id.name
    const binding = scope.getBinding(name)
    if (!binding || binding.referenced || !binding.constant) {
      return
    }
    if (node.init && !t.isLiteral(node.init)) {
      return
    }
    const up1 = path.parentPath
    const up2 = up1?.parentPath
    if (t.isForOfStatement(up2)) {
      return
    }
    if (t.isForInStatement(up2)) {
      return
    }
    console.log(`Unused variable: ${name}`)
    if (up1.node.declarations.length === 1) {
      up1.remove()
      up1.scope.crawl()
    } else {
      path.remove()
      scope.crawl()
    }
  },
}


================================================
FILE: src/visitor/jsconfuser/anti-tooling.js
================================================
import * as t from '@babel/types'

function deAntiToolingCheckFunc(path) {
  if (path.node.params.length) {
    return false
  }
  const body = path.node.body
  if (!t.isBlockStatement(body)) {
    return false
  }
  if (body.body.length) {
    return false
  }
  return true
}

function deAntiToolingExtract(path, func_name) {
  let binding = path.scope.getBinding(func_name)
  for (let ref of binding.referencePaths) {
    if (!ref.parentPath.isCallExpression() || !ref.key === 'callee') {
      continue
    }
    const call = ref.parentPath
    if (!call.listKey === 'body') {
      continue
    }
    for (let node of call.node.arguments) {
      call.insertBefore(node)
    }
    call.remove()
  }
  binding.scope.crawl()
  binding = path.scope.getBinding(func_name)
  if (binding.references === 0) {
    path.remove()
  }
}

const deAntiTooling = {
  FunctionDeclaration(path) {
    const func_name = path.node.id?.name
    if (!func_name) {
      return
    }
    if (!deAntiToolingCheckFunc(path)) {
      return
    }
    console.log(`AntiTooling Func Name: ${func_name}`)
    deAntiToolingExtract(path, func_name)
  },
}

export default deAntiTooling


================================================
FILE: src/visitor/jsconfuser/control-flow.js
================================================
import safeFunc from '../../utility/safe-func.js'
const safeGetLiteral = safeFunc.safeGetLiteral
const safeGetName = safeFunc.safeGetName
const safeReplace = safeFunc.safeReplace

function checkControlVar(path) {
  const parent = path.parentPath
  if (path.key !== 'right' || !parent.isAssignmentExpression()) {
    return false
  }
  const var_path = parent.get('left')
  const var_name = var_path.node?.name
  if (!var_name) {
    return false
  }
  let root_path = parent.parentPath
  if (root_path.isExpressionStatement) {
    root_path = root_path.parentPath
  }
  const binding = parent.scope.getBinding(var_name)
  for (const ref of binding.referencePaths) {
    if (ref === var_path) {
      continue
    }
    let cur = ref
    let valid = false
    while (cur && cur !== root_path) {
      if (cur.isSwitchCase() || cur === path) {
        valid = true
        break
      }
      cur = cur.parentPath
    }
    if (!valid) {
      return false
    }
    if (ref.key === 'object') {
      const prop = ref.parentPath.get('property')
      if (!prop.isLiteral() && !prop.isIdentifier()) {
        return false
      }
      continue
    }
    if (ref.key === 'right') {
      const left = ref.parentPath.get('left')
      if (!left.isMemberExpression()) {
        return false
      }
      const obj = safeGetName(left.get('object'))
      if (obj !== var_name) {
        return false
      }
      continue
    }
  }
  return true
}

/**
 * Process the constant properties in the controlVar
 *
 * Template:
 * ```javascript
 * controlVar = {
 *   // strings
 *   key_string: 'StringLiteral',
 *   // numbers
 *   key_number: 'NumericLiteral',
 * }
 * ```
 *
 * Some kinds of deadCode may in inserted to the fake chunks:
 *
 * ```javascript
 * controlVar = false
 * controlVar = undefined
 * controlVar[randomControlKey] = undefined
 * delete controlVar[randomControlKey]
 * ```
 */
const deControlFlowFlatteningStateless = {
  ObjectExpression(path) {
    if (!checkControlVar(path)) {
      return
    }
    const parent = path.parentPath
    const var_name = parent.get('left').node?.name
    console.log(`[ControlFlowFlattening] parse stateless in obj: ${var_name}`)
    const props = {}
    const prop_num = path.node.properties.length
    for (let i = 0; i < prop_num; ++i) {
      const prop = path.get(`properties.${i}`)
      const key = safeGetName(prop.get('key'))
      const value = safeGetLiteral(prop.get('value'))
      if (!key || !value) {
        continue
      }
      props[key] = value
    }
    const binding = parent.scope.getBinding(var_name)
    for (const ref of binding.referencePaths) {
      if (ref.key !== 'object') {
        continue
      }
      const prop = safeGetName(ref.parentPath.get('property'))
      if (!prop) {
        continue
      }
      if (!Object.prototype.hasOwnProperty.call(props, prop)) {
        continue
      }
      const upper = ref.parentPath
      if (upper.key === 'left' && upper.parentPath.isAssignmentExpression()) {
        // this is in the fake chunk
        ref.parentPath.parentPath.remove()
        continue
      }
      safeReplace(ref.parentPath, props[prop])
    }
    binding.scope.crawl()
  },
}

/**
 *
 * Template:
 * ```javascript
 * flaggedLabels = {
 *   currentLabel: { flagKey: 'xxx', flagValue : 'true or false' }
 * }
 * labelToStates[chunk[i].label] = stateValues: [] => caseStates[i]
 * initStateValues = labelToStates[startLabel]
 * endState
 * chunks = [
 *   {
 *     body: [
 *       {
 *         type: "GotoStatement",
 *         label: "END_LABEL",
 *       }
 *     ],
 *   }
 *   {
 *     label: "END_LABEL",
 *     body: [],
 *   }
 * ]
 * while (stateVars) {
 *   switch (stateVars) {
 *     // fake assignment expression
 *     case fake_assignment: {
 *       stateVar = 'rand'
 *       // 'GotoStatement label'
 *     }
 *     // clone chunks
 *     case fake_clone: {
 *       // contain a real chunk
 *     }
 *     // fake jumps
 *     case real_1: {
 *       if (false) {
 *         // 'GotoStatement label'
 *       }
 *       // follow with real statements
 *     }
 *   }
 * }
 * The key may exist in its parent's map
 * ```
 */
const deControlFlowFlatteningState = {
  ObjectExpression(path) {
    if (!checkControlVar(path)) {
      return
    }
  },
}

export default {
  deControlFlowFlatteningStateless,
  deControlFlowFlatteningState,
}


================================================
FILE: src/visitor/jsconfuser/duplicate-literal.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default
import * as t from '@babel/types'

import ivm from 'isolated-vm'
const isolate = new ivm.Isolate()

import safeFunc from '../../utility/safe-func.js'
const safeReplace = safeFunc.safeReplace

function checkArrayName(path) {
  if (path.key !== 'argument') {
    return null
  }
  const ret_path = path.parentPath
  if (!ret_path.isReturnStatement() || ret_path.key !== 0) {
    return null
  }
  const array_fn_path = ret_path.getFunctionParent()
  const array_fn_name = array_fn_path.node.id?.name
  if (!array_fn_name) {
    return null
  }
  const binding = array_fn_path.parentPath.scope.bindings[array_fn_name]
  if (binding.references !== 1) {
    return null
  }
  let ref = binding.referencePaths[0]
  while (ref && !ref.isAssignmentExpression()) {
    ref = ref.parentPath
  }
  if (!ref) {
    return null
  }
  const array_name = ref.node.left?.name
  if (!array_name) {
    return null
  }
  return {
    func_name: array_fn_name,
    func_path: array_fn_path,
    array_name: array_name,
    array_path: ref,
  }
}

function parseArrayWarp(vm, path) {
  let func = path.getFunctionParent(path)
  let name = null
  let binding = null
  if (func.isArrowFunctionExpression()) {
    func = func.parentPath
    name = func.node.id.name
    binding = func.scope.getBinding(name)
  } else {
    name = func.node.id.name
    binding = func.parentPath.scope.getBinding(name)
  }
  console.log(`Process array warp function: ${name}`)
  vm.evalSync(generator(func.node).code)
  for (const ref of binding.referencePaths) {
    const call = ref.parentPath
    if (ref.key !== 'callee') {
      console.warn(`Unexpected ref of array warp function: ${call}`)
      continue
    }
    const value = vm.evalSync(generator(call.node).code)
    safeReplace(call, value)
  }
  binding.scope.crawl()
  binding = binding.scope.getBinding(name)
  if (!binding.references) {
    func.remove()
  }
}

/**
 * Template:
 * ```javascript
 * var arrayName = getArrayFn()
 * function getArrayFn (){
 *   return [...arrayExpression]
 * }
 * ```
 */
const deDuplicateLiteral = {
  ArrayExpression(path) {
    let obj = checkArrayName(path)
    if (!obj) {
      return
    }
    console.log(`Find arrayName: ${obj.array_name}`)
    let decl_node = t.variableDeclarator(
      obj.array_path.node.left,
      obj.array_path.node.right
    )
    decl_node = t.variableDeclaration('var', [decl_node])
    const code = [generator(obj.func_path.node).code, generator(decl_node).code]
    let binding = obj.array_path.scope.getBinding(obj.array_name)
    for (const ref of binding.referencePaths) {
      const vm = isolate.createContextSync()
      vm.evalSync(code[0])
      vm.evalSync(code[1])
      parseArrayWarp(vm, ref)
    }
    binding.scope.crawl()
    binding = binding.scope.bindings[obj.array_name]
    if (!binding.references) {
      obj.array_path.remove()
      binding.path.remove()
    }
    binding = obj.func_path.parentPath.scope.getBinding(obj.func_name)
    binding.scope.crawl()
    binding = binding.scope.getBinding(obj.func_name)
    if (!binding.references) {
      obj.func_path.remove()
    }
  },
}

export default deDuplicateLiteral


================================================
FILE: src/visitor/jsconfuser/global-concealing.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default
import * as t from '@babel/types'

import findGlobalFn from './global.js'
import safeFunc from '../../utility/safe-func.js'
const safeDeleteNode = safeFunc.safeDeleteNode
import checkFunc from '../../utility/check-func.js'
const checkPattern = checkFunc.checkPattern

function findGlobalVar(glo_name, glo_path) {
  let tmp_path = glo_path.parentPath.getFunctionParent()
  if (
    !tmp_path ||
    !tmp_path.parentPath.isMemberExpression() ||
    !tmp_path.parentPath.parentPath.isCallExpression()
  ) {
    return null
  }
  const tmp_body = tmp_path.node.body.body
  tmp_path = tmp_path.parentPath.parentPath
  const ret_node = tmp_body[tmp_body.length - 1]
  if (
    !t.isReturnStatement(ret_node) ||
    !t.isAssignmentExpression(ret_node.argument)
  ) {
    return null
  }
  const code = generator(ret_node.argument.right).code
  const template = `${glo_name}call(this)`
  if (!checkPattern(code, template)) {
    return null
  }
  const glo_var = ret_node.argument.left.name
  const binding = glo_path.scope.getBinding(glo_var)
  for (const ref of binding.referencePaths) {
    if (
      !ref.parentPath.isMemberExpression() ||
      !ref.parentPath.parentPath.isReturnStatement()
    ) {
      continue
    }
    const func_path = ref.getFunctionParent()
    const func_name = func_path.node.id.name
    return {
      glo_var: glo_var,
      tmp_path: tmp_path,
      glo_fn_name: func_name,
      glo_fn_path: func_path,
    }
  }
  return null
}

function getGlobalConcealingNames(glo_fn_path) {
  const obj = {}
  glo_fn_path.traverse({
    SwitchCase(path) {
      const code = generator(path.node.test).code
      const key = parseInt(code)
      if (Number.isNaN(key)) {
        console.error(`[GlobalConcealing] concealed key: ${code}`)
        obj['invalid'] = true
        return
      }
      let consequent = path.node.consequent[0]
      if (t.isReturnStatement(consequent)) {
        obj[key] = consequent.argument.property.value
      } else {
        if (t.isExpressionStatement(consequent)) {
          consequent = consequent.expression
        }
        obj[key] = consequent.right.left.value
      }
    },
  })
  return obj
}

/**
 * Hide the global vars found by module GlobalAnalysis
 *
 * Template:
 * ```javascript
 * // Add to head:
 * var globalVar, tempVar = function () {
 *   getGlobalVariableFnName = createGetGlobalTemplate()
 *   return globalVar = getGlobalVariableFnName.call(this)
 * }["call"]()
 * // Add to foot:
 * function globalFn (indexParamName) {
 *   var returnName
 *   switch (indexParamName) {
 *     case state_x: {
 *       return globalVar[name]
 *     }
 *     case state_y: {
 *       returnName = name || globalVar[name]
 *       break
 *     }
 *   }
 *   return globalVar[returnName]
 * }
 * // References:
 * // name -> globalFn(state)
 * ```
 */
const deGlobalConcealing = {
  FunctionDeclaration(path) {
    const glo_obj = findGlobalFn(path)
    if (!glo_obj) {
      return null
    }
    const obj = findGlobalVar(glo_obj.glo_fn_name, glo_obj.glo_fn_path)
    if (!obj) {
      return null
    }
    console.log(`[GlobalConcealing] globalVar: ${obj.glo_var}`)
    const glo_vars = getGlobalConcealingNames(obj.glo_fn_path)
    console.log(`[GlobalConcealing] globalFn: ${obj.glo_fn_name}`)
    let binding = obj.glo_fn_path.parentPath.scope.getBinding(obj.glo_fn_name)
    let remain = false
    for (const ref of binding.referencePaths) {
      const repl_path = ref.parentPath
      if (ref.key !== 'callee' || !repl_path.isCallExpression()) {
        continue
      }
      const key = parseInt(generator(repl_path.node.arguments[0]).code)
      if (glo_vars[key]) {
        repl_path.replaceWith(t.identifier(glo_vars[key]))
      } else {
        remain = true
      }
    }
    if (!remain && safeDeleteNode(obj.glo_fn_name, obj.glo_fn_path)) {
      obj.tmp_path.remove()
    }
  },
}

export default deGlobalConcealing


================================================
FILE: src/visitor/jsconfuser/global.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default
import * as t from '@babel/types'

import safeFunc from '../../utility/safe-func.js'
const safeGetName = safeFunc.safeGetName
import checkFunc from '../../utility/check-func.js'
const checkPattern = checkFunc.checkPattern

/**
 * GlobalTemplate 1 (currently not support):
 * ```javascript
 * function {getGlobalFnName}(){
 *   var localVar = false;
 *   eval(${transform.jsConfuserVar("localVar")} + " = true")
 *   if (!localVar) {
 *     {countermeasures}
 *   }
 *   const root = eval("this");
 *   return root;
 * }
 * ```
 * GlobalTemplate 2:
 * ```javascript
 * function {getGlobalFnName}(array = [a, b, c, d]){
 *   var bestMatch
 *   var itemsToSearch = []
 *   try {
 *     bestMatch = Object
 *     itemsToSearch["push"](("")["__proto__"]["constructor"]["name"])
 *   } catch(e) {
 *   }
 *   // ...
 *   return bestMatch || this;
 * }
 * ```
 */
function findGlobalFn(path) {
  const glo_fn_name = path.node.id?.name
  if (!glo_fn_name) {
    return null
  }
  let node = path.node.params?.[0]
  if (
    !node ||
    !t.isAssignmentPattern(node) ||
    !t.isArrayExpression(node.right) ||
    node.right.elements.length !== 4
  ) {
    return null
  }
  const array_name = node.left.name
  const code = generator(path.node.body).code
  const template =
    'try{=Objectpush(__proto__constructorname)}catch{}' +
    `:for(;<${array_name}length;)try{=${array_name}[]()` +
    'for()if(typeof)continue}catch{}return||this'
  if (!checkPattern(code, template)) {
    return
  }
  const deps = []
  const array = path.get('params.0.right')
  for (let i = 0; i < 4; ++i) {
    const ele_name = safeGetName(array.get(`elements.${i}`))
    const binding = path.scope.getBinding(ele_name)
    deps.push({
      name: ele_name,
      path: binding.path,
      pos: binding.path.node.start,
    })
  }
  deps.push({
    name: glo_fn_name,
    path: path,
    pos: path.node.start,
  })
  return {
    glo_fn_name: glo_fn_name,
    glo_fn_path: path,
    deps: deps,
  }
}

export default findGlobalFn


================================================
FILE: src/visitor/jsconfuser/minify.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default

function checkArrowWrap(path) {
  if (path.node?.name !== 'arguments') {
    return null
  }
  if (!path.parentPath.isSpreadElement()) {
    return null
  }
  const call = path.parentPath.parentPath
  if (path.parentPath.listKey !== 'arguments' || !call.isCallExpression()) {
    return null
  }
  if (call.key !== 'argument' || !call.parentPath.isReturnStatement()) {
    return null
  }
  const func_name = call.node.callee?.name
  if (!func_name) {
    return null
  }
  let wrap = call.getFunctionParent()
  if (wrap.key !== 'init') {
    return null
  }
  wrap = wrap.parentPath
  const wrap_name = wrap.node.id?.name
  wrap = wrap.parentPath
  if (
    wrap.listKey !== 'body' ||
    wrap.key !== 0 ||
    wrap.container.length !== 2
  ) {
    return null
  }
  const str = generator(wrap.container[1]).code
  if (str.indexOf(wrap_name) === -1) {
    return null
  }
  wrap = wrap.getFunctionParent()
  const arrow_name = wrap.node?.id?.name
  if (!arrow_name || wrap.node.params?.[0]?.name !== func_name) {
    return null
  }
  return {
    name: arrow_name,
    path: wrap,
  }
}

/**
 * Template:
 * ```javascript
 * function arrowFunctionName (arrowFn, functionLength = 0){
 *   var functionObject = function(){ return arrowFn(...arguments) };
 *   return Object.defineProperty(functionObject, "length", {
 *     "value": functionLength,
 *     "configurable": true
 *   });
 * }
 * ```
 */
export default function () {
  let arrowFunc = null
  const deMinifyArrow = {
    Identifier(path) {
      let obj = checkArrowWrap(path)
      if (!obj) {
        return
      }
      arrowFunc = obj.name
      console.log(`Find arrowFunctionName: ${obj.name}`)
      let binding = obj.path.parentPath.scope.bindings[obj.name]
      for (const ref of binding.referencePaths) {
        if (ref.key !== 'callee') {
          console.warn(`Unexpected ref of arrowFunctionName: ${obj.name}`)
          continue
        }
        const repl_path = ref.parentPath
        repl_path.replaceWith(repl_path.node.arguments[0])
      }
      binding.scope.crawl()
      binding = obj.path.parentPath.scope.bindings[obj.name]
      if (!binding.references) {
        obj.path.remove()
      }
    },
  }
  return {
    arrowFunc,
    deMinifyArrow,
  }
}


================================================
FILE: src/visitor/jsconfuser/opaque-predicates.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default
import * as t from '@babel/types'

import ivm from 'isolated-vm'
const isolate = new ivm.Isolate()

import safeFunc from '../../utility/safe-func.js'
const safeDeleteNode = safeFunc.safeDeleteNode
const safeGetName = safeFunc.safeGetName
const safeReplace = safeFunc.safeReplace
import checkFunc from '../../utility/check-func.js'
const checkPattern = checkFunc.checkPattern

function checkOpaqueObject(path) {
  const parent = path.parentPath
  if (!parent.isAssignmentExpression()) {
    return null
  }
  const tmp_name = safeGetName(parent.get('left'))
  const func_path = parent.getFunctionParent()
  if (
    !func_path ||
    func_path.key !== 'callee' ||
    !func_path.parentPath.isCallExpression()
  ) {
    return null
  }
  const func_body = func_path.node.body?.body
  if (!func_body || func_body.length < 2) {
    return null
  }
  const last_node = func_body[func_body.length - 1]
  if (
    !t.isReturnStatement(last_node) ||
    last_node.argument?.name !== tmp_name
  ) {
    return null
  }
  const root_path = func_path.parentPath.parentPath
  if (!root_path.isAssignmentExpression()) {
    return null
  }
  const pred_name = safeGetName(root_path.get('left'))
  const obj = {
    pred_name: pred_name,
    pred_path: root_path,
    props: {},
  }
  for (const prop of path.node.properties) {
    const key = prop.key.name
    const value = prop.value
    if (t.isNumericLiteral(value)) {
      obj.props[key] = {
        type: 'number',
      }
      continue
    }
    if (t.isStringLiteral(value)) {
      obj.props[key] = {
        type: 'string',
      }
      continue
    }
    if (t.isArrayExpression(value)) {
      if (value.elements.length === 0) {
        obj.props[key] = {
          type: 'array_dep',
        }
      }
      continue
    }
    if (t.isArrowFunctionExpression(value) || t.isFunctionExpression(value)) {
      const param = value.params?.[0]?.left?.name
      if (!param) {
        continue
      }
      const code = generator(value).code
      const template =
        `(${param}=){if(${pred_name}[0])${pred_name}push()` +
        `return${pred_name}${param}}`
      if (checkPattern(code, template)) {
        obj.props[key] = {
          type: 'array',
        }
      }
      continue
    }
  }
  return obj
}

/**
 * Template:
 * ```javascript
 * // This is defined in the global space
 * var predicateName = (function () {
 *   var tempName = {
 *     prop_array_1: [],
 *     prop_array: function (paramName = 'length') {
 *       if (!predicateName[prop_array_1][0]) {
 *          predicateName[prop_array_1][0].push(rand1)
 *       }
 *       return predicateName[prop_array_1][paramName]
 *     },
 *     prop_number: rand2,
 *     prop_string: rand_str,
 *   }
 *   return tempName
 * })()
 * // Below will appear multiple times
 * predicateName[prop_array]() ? test : fake
 * predicateName[prop_number] > rand3 ? test : fake
 * predicateName[prop_string].charAt(index) == real_char ? test : fake
 * predicateName[prop_string].charCodeAt(index) == real_char ? test : fake
 * ```
 */
const deOpaquePredicates = {
  ObjectExpression(path) {
    const obj = checkOpaqueObject(path)
    if (!obj) {
      return
    }
    console.log(`[OpaquePredicates] predicateName : ${obj.pred_name}`)
    const vm = isolate.createContextSync()
    const code = generator(obj.pred_path.node).code
    vm.evalSync('var ' + code)
    obj.pred_path.get('right').replaceWith(t.numericLiteral(0))
    let binding = obj.pred_path.scope.getBinding(obj.pred_name)
    binding.scope.crawl()
    binding = binding.scope.getBinding(obj.pred_name)
    for (const ref of binding.referencePaths) {
      if (ref.key !== 'object') {
        continue
      }
      const member = ref.parentPath
      const prop = member.get('property')
      if (!prop || !Object.prototype.hasOwnProperty.call(obj.props, prop)) {
        continue
      }
      let expr = member
      while (
        expr.parentPath.isCallExpression() ||
        expr.parentPath.isMemberExpression()
      ) {
        expr = expr.parentPath
      }
      const test = generator(expr.node).code
      const res = vm.evalSync(test)
      safeReplace(expr, res)
    }
    safeDeleteNode(obj.pred_name, obj.pred_path)
  },
}

export default deOpaquePredicates


================================================
FILE: src/visitor/jsconfuser/stack.js
================================================
import { parse } from '@babel/parser'
import _generate from '@babel/generator'
const generator = _generate.default
import * as t from '@babel/types'

import ivm from 'isolated-vm'
const isolate = new ivm.Isolate()

import calculateConstantExp from '../calculate-constant-exp.js'

import safeFunc from '../../utility/safe-func.js'
const safeGetName = safeFunc.safeGetName
const safeReplace = safeFunc.safeReplace

let arrowFunc = null

function checkFuncLen(path) {
  if (path.node?.name !== 'configurable' || path.key !== 'key') {
    return null
  }
  const prop = path.parentPath
  if (!prop.isObjectProperty() || prop.key !== 1) {
    return null
  }
  const obj = prop.parentPath
  if (obj.node.properties.length !== 2) {
    return null
  }
  if (obj.node.properties[0]?.key?.name !== 'value') {
    return null
  }
  if (obj.listKey !== 'arguments') {
    return null
  }
  const arg_num = obj.container.length
  if (obj.key !== arg_num - 1) {
    return null
  }
  const func_name = obj.container[arg_num - 3]?.name
  const warp = obj.getFunctionParent()
  if (warp.node.params?.[0]?.name !== func_name) {
    return null
  }
  const func_len_name = warp.node?.id?.name
  if (!func_len_name || func_len_name === arrowFunc) {
    return null
  }
  return {
    name: func_len_name,
    path: warp,
  }
}

/**
 * type: param, value, ref, invalid
 */
function initStackCache(len) {
  const cache = {}
  for (let i = 0; i < len; ++i) {
    cache[i] = {
      type: 'param',
    }
  }
  return cache
}

function processAssignLeft(vm, cache, path, prop_name, stk_name) {
  const father = path.parentPath
  const right = father.get('right')
  if (right.isBinaryExpression()) {
    cache[prop_name] = {
      type: 'invalid',
    }
    return
  }
  if (right.isLiteral()) {
    vm.evalSync(generator(father.node).code)
    cache[prop_name] = {
      type: 'value',
      value: right.node,
    }
    return
  }
  if (right.isArrayExpression()) {
    const elements = right.node.elements
    if (elements.length === 1 && elements[0]?.value === 'charCodeAt') {
      cache[prop_name] = {
        type: 'value',
        value: right.node,
      }
      return
    }
  }
  if (right.isUnaryExpression() && right.node.operator === '-') {
    vm.evalSync(generator(father.node).code)
    cache[prop_name] = {
      type: 'value',
      value: right.node,
    }
    return
  }
  if (right.isMemberExpression() && right.node.object?.name === stk_name) {
    const right_prop = right.get('property')
    if (right_prop.isBinaryExpression()) {
      return
    }
    let ref = safeGetName(right_prop)
    if (!Object.prototype.hasOwnProperty.call(cache, ref)) {
      cache[prop_name] = {
        type: 'invalid',
      }
      return
    }
    while (cache[ref].type === 'ref') {
      ref = cache[ref].value
    }
    if (cache[ref].type === 'value') {
      right.replaceWith(cache[ref].value)
      vm.evalSync(generator(father.node).code)
      cache[prop_name] = {
        type: 'value',
        value: cache[ref].value,
      }
    } else {
      cache[prop_name] = {
        type: 'ref',
        value: ref,
      }
    }
    return
  }
  cache[prop_name] = {
    type: 'invalid',
  }
}

function processAssignInvalid(cache, path, prop_name) {
  cache[prop_name] = {
    type: 'invalid',
  }
}

function processReplace(cache, path, prop_name) {
  const value = cache[prop_name].value
  const type = cache[prop_name].type
  if (type === 'ref') {
    path.node.computed = true
    safeReplace(path.get('property'), value)
    return true
  }
  if (type === 'value') {
    path.replaceWith(value)
    return true
  }
  return false
}

function checkStackInvalid(path, invalid) {
  const stk_name = path.node.params[0].argument.name
  const body_path = path.get('body')
  body_path.traverse({
    MemberExpression: {
      exit(path) {
        if (path.node.object.name !== stk_name) {
          return
        }
        const father = path.parentPath
        const prop = path.get('property')
        const prop_name = safeGetName(prop)
        if (father.isUpdateExpression()) {
          invalid[prop_name] = 1
          return
        }
        if (body_path.scope == father.scope) {
          return
        }
        if (!father.isAssignmentExpression() || path.key !== 'left') {
          return
        }
        invalid[prop_name] = 1
      },
    },
  })
  return invalid
}

function checkChangeValid(invalid, used) {
  let valid = true
  Object.keys(used).forEach(function (key) {
    if (Object.prototype.hasOwnProperty.call(invalid, key)) {
      valid = false
    }
  })
  return valid
}

function tryStackReplace(path, len, invalid, used) {
  const stk_name = path.node.params[0].argument.name
  const body_path = path.get('body')
  const cache = initStackCache(len)
  const vm = isolate.createContextSync()
  vm.evalSync(`var ${stk_name} = []`)
  let changed = false
  body_path.traverse({
    MemberExpression: {
      exit(path) {
        if (path.node.object.name !== stk_name) {
          return
        }
        const prop = path.get('property')
        if (prop.isBinaryExpression()) {
          return
        }
        const prop_name = safeGetName(prop)
        if (!prop_name) {
          return
        }
        if (Object.prototype.hasOwnProperty.call(invalid, prop_name)) {
          processAssignInvalid(cache, path, prop_name)
          return
        }
        const exist = Object.prototype.hasOwnProperty.call(cache, prop_name)
        if (exist && cache[prop_name].type === 'param') {
          return
        }
        const father = path.parentPath
        if (father.isAssignmentExpression() && path.key === 'left') {
          processAssignLeft(vm, cache, path, prop_name, stk_name)
        } else if (exist) {
          used[prop_name] = 1
          changed |= processReplace(cache, path, prop_name)
        }
      },
    },
  })
  const binding = body_path.scope.getBinding(stk_name)
  binding.scope.crawl()
  return changed
}

function getStackParamLen(path) {
  const stk_name = path.node.params?.[0]?.argument?.name
  if (!stk_name) {
    return 'unknown'
  }
  const body_path = path.get('body')
  let len = 'unknown'
  body_path.traverse({
    MemberExpression: {
      exit(path) {
        if (path.node.object.name !== stk_name) {
          return
        }
        const prop = path.get('property')
        if (prop.isBinaryExpression()) {
          return
        }
        const prop_name = safeGetName(prop)
        if (!prop_name || prop_name !== 'length') {
          return
        }
        const father = path.parentPath
        if (!father.isAssignmentExpression() || path.key !== 'left') {
          return
        }
        const right = father.get('right')
        if (right.isBinaryExpression()) {
          return
        }
        if (!right.isLiteral()) {
          return
        }
        len = right.node.value
        path.stop()
      },
    },
  })
  return len
}

function processStackParam(path, len) {
  if (path.isArrowFunctionExpression()) {
    console.log(`[Stack] Process arrowFunctionExpression, len: ${len}`)
  } else if (path.isFunctionExpression()) {
    console.log(`[Stack] Process functionExpression, len: ${len}`)
  } else {
    console.log(`[Stack] Process Function ${path.node.id.name}, len: ${len}`)
  }
  const orig_code = generator(path.node).code
  let changed = true
  const invalid = {}
  let used = {}
  while (changed) {
    checkStackInvalid(path, invalid)
    if (!checkChangeValid(invalid, used)) {
      path.replaceWith(parse(orig_code).program.body[0])
      used = {}
    }
    changed = tryStackReplace(path, len, invalid, used)
    path.traverse(calculateConstantExp)
  }
}

const deStackFuncLen = {
  Identifier(path) {
    let obj = checkFuncLen(path)
    if (!obj) {
      return
    }
    console.log(`[Stack] Find functionLengthName: ${obj.name}`)
    let binding = obj.path.parentPath.scope.bindings[obj.name]
    for (const ref of binding.referencePaths) {
      if (ref.key !== 'callee') {
        console.warn(
          `[Stack] Unexpected ref of functionLengthName: ${obj.name}`
        )
        continue
      }
      const repl_path = ref.parentPath
      const arg = repl_path.node.arguments[0]
      const len = repl_path.node.arguments[1].value
      if (t.isIdentifier(arg)) {
        const func_name = arg.name
        const func_decl = repl_path.scope.getBinding(func_name).path
        processStackParam(func_decl, len)
        repl_path.remove()
      } else {
        repl_path.replaceWith(arg)
        processStackParam(repl_path, len)
      }
    }
    binding.scope.crawl()
    binding = obj.path.parentPath.scope.bindings[obj.name]
    if (!binding.references) {
      obj.path.remove()
    }
  },
}

const deStackFuncOther = {
  RestElement(path) {
    if (path.listKey !== 'params') {
      return
    }
    const func = path.getFunctionParent()
    const len = getStackParamLen(func)
    if (len === 'unknown') {
      return
    }
    processStackParam(func, len)
  },
}

export default function (func) {
  arrowFunc = func
  return {
    deStackFuncLen,
    deStackFuncOther,
  }
}


================================================
FILE: src/visitor/jsconfuser/string-compression.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default

import ivm from 'isolated-vm'
const isolate = new ivm.Isolate()

import safeFunc from '../../utility/safe-func.js'
const safeReplace = safeFunc.safeReplace
import checkFunc from '../../utility/check-func.js'
const checkPattern = checkFunc.checkPattern

function findStringDecoder(path) {
  if (path.node?.name !== 'charCodeAt' || path.key !== 'property') {
    return null
  }
  let loop = path
  while (loop && !loop.isForStatement()) {
    loop = loop.parentPath
  }
  const i = loop?.node?.update?.argument?.name
  if (!i) {
    return null
  }
  const func = loop.getFunctionParent()
  const param = func.node.params?.[0]?.name
  if (!param) {
    return null
  }
  const code = generator(func.node).code
  const template =
    `function(${param}){var=${param}.split()for(${i}=1;${i}<.length;${i}++)` +
    `[${i}].charCodeAt(0)[${i}].push().charAt(0)return.join().split()}`
  if (!checkPattern(code, template)) {
    return null
  }
  return {
    name: func.node.id.name,
    path: func,
  }
}

function findStringGet(path) {
  const decoder_name = path.node.id.name
  let binding = path.parentPath.scope.getBinding(decoder_name)
  if (!binding || binding.references !== 1) {
    return null
  }
  const ref = binding.referencePaths[0]
  if (ref.key !== 1 || ref.listKey !== 'arguments') {
    return null
  }
  const get_ref_path = ref.parentPath.get('arguments.0')
  const get_name = get_ref_path.node?.name
  if (!get_name) {
    return null
  }
  binding = get_ref_path.scope.getBinding(get_name)
  return {
    name: get_name,
    path: binding.path,
    ref: get_ref_path,
  }
}

function findStringSplit(path) {
  while (path && !path.isAssignmentExpression()) {
    path = path.parentPath
  }
  const split_name = path?.node?.left?.name
  if (!split_name) {
    return null
  }
  const binding = path.scope.getBinding(split_name)
  return {
    name: split_name,
    path: path,
    def: binding.path,
  }
}

function findStringFn(path, name) {
  const binding = path.scope.getBinding(name)
  const ref = binding.referencePaths?.[0]
  if (!ref) {
    return null
  }
  const fn_path = ref.getFunctionParent(name)
  const fn_name = fn_path.node.id.name
  return {
    name: fn_name,
    path: fn_path,
  }
}

/**
 * Template:
 * ```javascript
 * var split = (function (getStringParamName, decoderParamName) {
 *   return decoderParamName(getStringParamName())
 * })(getStringName, decoder)
 * function getStringName () {
 *   var str = splits[0]
 *   var objectToTest = {}
 *   if ('testingFor' in objectToTest) {
 *     str += splits[1]
 *   }
 *   return str
 * }
 * function decoder (b) {
 *   // DecodeTemplate
 * }
 * function fnName (index) {
 *   return split[index]
 * }
 * ```
 */
const deStringCompression = {
  Identifier(path) {
    const decoder_obj = findStringDecoder(path)
    if (!decoder_obj) {
      return
    }
    const get_obj = findStringGet(decoder_obj.path)
    if (!get_obj) {
      return
    }
    const split_obj = findStringSplit(get_obj.ref)
    if (!get_obj) {
      return
    }
    const fn_obj = findStringFn(split_obj.path, split_obj.name)
    if (!get_obj) {
      return
    }
    console.log(`Find stringCompression Fn: ${fn_obj.name}`)
    const vm = isolate.createContextSync()
    vm.evalSync(generator(decoder_obj.path.node).code)
    vm.evalSync(generator(get_obj.path.node).code)
    vm.evalSync('var ' + generator(split_obj.path.node).code)
    vm.evalSync(generator(fn_obj.path.node).code)
    let binding = fn_obj.path.parentPath.scope.getBinding(fn_obj.name)
    for (const ref of binding.referencePaths) {
      if (ref.key !== 'callee') {
        console.warn(
          `Unexpected ref of stringCompression Fn: ${ref.parentPath}`
        )
        continue
      }
      const repl_path = ref.parentPath
      try {
        const value = vm.evalSync(generator(repl_path.node).code)
        safeReplace(repl_path, value)
      } catch (e) {
        console.warn(
          `Unexpected ref of stringCompression Fn: ${ref.parentPath}`
        )
      }
    }
    binding.scope.crawl()
    binding = binding.scope.bindings[fn_obj.name]
    if (!binding.references) {
      fn_obj.path.remove()
    }
    binding.scope.crawl()
    binding = split_obj.path.scope.getBinding(split_obj.name)
    if (!binding.references) {
      split_obj.path.remove()
      split_obj.def.remove()
    }
    binding.scope.crawl()
    binding = get_obj.path.scope.getBinding(get_obj.name)
    if (!binding.references) {
      get_obj.path.remove()
    }
    binding.scope.crawl()
    binding = decoder_obj.path.scope.getBinding(decoder_obj.name)
    if (!binding.references) {
      decoder_obj.path.remove()
    }
  },
}

export default deStringCompression


================================================
FILE: src/visitor/jsconfuser/string-concealing.js
================================================
import _generate from '@babel/generator'
const generator = _generate.default
import * as t from '@babel/types'

import ivm from 'isolated-vm'
const isolate = new ivm.Isolate()

import findGlobalFn from './global.js'
import safeFunc from '../../utility/safe-func.js'
const safeDeleteNode = safeFunc.safeDeleteNode
const safeGetName = safeFunc.safeGetName
const safeReplace = safeFunc.safeReplace

function insertDepItemVar(deps, name, path) {
  const binding = path.scope.getBinding(name)
  if (binding.path === path) {
    deps.push({
      name: name,
      path: binding.path,
      node: t.variableDeclaration('var', [binding.path.node]),
      pos: binding.path.node.start,
    })
    return
  }
  deps.push({
    name: name,
    path: path,
    pos: path.node.start,
  })
  deps.push({
    name: name,
    path: binding.path,
    node: t.variableDeclaration('var', [binding.path.node]),
    pos: binding.path.node.start,
  })
}

/**
 * Template:
 * ```javascript
 * var __globalObject = {getGlobalFnName}() || {};
 * var __TextDecoder = __globalObject["TextDecoder"];
 * var __Uint8Array = __globalObject["Uint8Array"];
 * var __Buffer = __globalObject["Buffer"];
 * var __String = __globalObject["String"] || String;
 * var __Array = __globalObject["Array"] || Array;
 * ```
 */
function findGlobalFnRef(obj) {
  const path = obj.glo_fn_path
  const glo_fn_name = obj.glo_fn_name
  let binding = path.parentPath.scope.getBinding(glo_fn_name)
  let glo_fn_ref = binding.referencePaths[0]
  while (!glo_fn_ref.isAssignmentExpression()) {
    glo_fn_ref = glo_fn_ref.parentPath
  }
  const glo_obj_name = glo_fn_ref.node.left.name
  obj.glo_obj_name = glo_obj_name
  obj.glo_obj_path = glo_fn_ref
  obj.glo_obj_ref = {}
  insertDepItemVar(obj.deps, glo_obj_name, glo_fn_ref)
  binding = glo_fn_ref.scope.getBinding(glo_obj_name)
  for (const ref of binding.referencePaths) {
    const prop = safeGetName(ref.parentPath.get('property'))
    if (!prop) {
      continue
    }
    let root = ref
    while (!root.isAssignmentExpression()) {
      root = root.parentPath
    }
    const ref_name = safeGetName(root.get('left'))
    obj.glo_obj_ref[prop] = ref_name
    insertDepItemVar(obj.deps, ref_name, root)
  }
  return
}

/**
 * Template:
 * ```javascript
 * var utf8ArrayToStr = (function () {
 *   // ...
 * })();
 * function bufferToStringName () {
 *   if(typeof __TextDecoder !== "undefined" && __TextDecoder) {
 *     return new __TextDecoder()["decode"](new __Uint8Array(buffer));
 *   } else if(typeof __Buffer !== "undefined" && __Buffer) {
 *     return __Buffer["from"](buffer)["toString"]("utf-8");
 *   } else {
 *     return utf8ArrayToStr(buffer);
 *   }
 * }
 * ```
 */
function findBufferToString(obj) {
  const path = obj.glo_obj_path
  const ref_array = obj.glo_obj_ref['Array']
  let binding = path.scope.getBinding(ref_array)
  for (const ref of binding.referencePaths) {
    if (ref.key !== 'callee') {
      continue
    }
    let a2s_path = ref.getFunctionParent()
    while (!a2s_path.isAssignmentExpression()) {
      a2s_path = a2s_path.parentPath
    }
    obj.a2s_name = safeGetName(a2s_path.get('left'))
    obj.a2s_path = a2s_path
    insertDepItemVar(obj.deps, obj.a2s_name, obj.a2s_path)
    break
  }
  if (!obj.a2s_name) {
    return false
  }
  binding = obj.a2s_path.scope.getBinding(obj.a2s_name)
  const b2s_path = binding.referencePaths[0].getFunctionParent()
  obj.b2s_name = safeGetName(b2s_path.get('id'))
  obj.b2s_path = b2s_path
  obj.deps.push({
    name: obj.b2s_name,
    path: b2s_path,
    pos: b2s_path.node.start,
  })
  binding = b2s_path.parentPath.scope.getBinding(obj.b2s_name)
  const child = []
  for (const ref of binding.referencePaths) {
    const decode_fn = ref.getFunctionParent()
    let valid = false
    decode_fn.traverse({
      StringLiteral(path) {
        if (path.node.value.length === 91) {
          valid = true
          path.stop()
        }
      },
    })
    if (!valid) {
      return false
    }
    child.push({
      name: decode_fn.node.id.name,
      decoder: decode_fn,
    })
  }
  obj.child = child
  return true
}

function generatorStringConcealingDepCode(obj) {
  obj.deps.sort((a, b) => {
    return a.pos - b.pos
  })
  const dep_node = t.program([])
  for (const item of obj.deps) {
    if (item.node) {
      dep_node.body.push(item.node)
    } else {
      dep_node.body.push(item.path.node)
    }
  }
  obj.dep_code = generator(dep_node).code
}

function renameProperty(member) {
  const obj_name = safeGetName(member.get('object'))
  const prop_name = safeGetName(member.get('property'))
  const new_name = member.scope.generateUidIdentifier(`_tmp_local_`)
  const binding = member.scope.getBinding(obj_name)
  let first = true
  for (const ref of binding.referencePaths) {
    const item = ref.parentPath
    const prop = safeGetName(item.get('property'))
    if (prop !== prop_name) {
      continue
    }
    if (first) {
      let body = item
      while (body.listKey !== 'body') {
        body = body.parentPath
      }
      body.container.unshift(
        t.variableDeclaration('var', [t.variableDeclarator(new_name)])
      )
      body.scope.crawl()
      first = false
    }
    item.replaceWith(new_name)
  }
  member.scope.crawl()
}

/**
 * Template:
 * ```javascript
 * var cacheName = [], arrayName = []
 * // Below will appear multiple times
 * var getterFnName = (x, y, z, a, b)=>{
 *   if ( x !== y ) {
 *   
Download .txt
gitextract_x1jt3_r8/

├── .github/
│   └── workflows/
│       └── decode.yml
├── README.md
├── input.js
├── input.py
├── output.js
├── output.py
├── package.json
└── src/
    ├── decode.py
    ├── main.js
    ├── plugin/
    │   ├── awsc.js
    │   ├── common.js
    │   ├── eval.js
    │   ├── jjencode.js
    │   ├── jsconfuser.js
    │   ├── obfuscator.js
    │   ├── sojson.js
    │   └── sojsonv7.js
    ├── utility/
    │   ├── check-func.js
    │   └── safe-func.js
    └── visitor/
        ├── calculate-constant-exp.js
        ├── calculate-rstring.js
        ├── delete-extra.js
        ├── delete-illegal-return.js
        ├── delete-nested-blocks.js
        ├── delete-unreachable-code.js
        ├── delete-unused-var.js
        ├── jsconfuser/
        │   ├── anti-tooling.js
        │   ├── control-flow.js
        │   ├── duplicate-literal.js
        │   ├── global-concealing.js
        │   ├── global.js
        │   ├── minify.js
        │   ├── opaque-predicates.js
        │   ├── stack.js
        │   ├── string-compression.js
        │   └── string-concealing.js
        ├── lint-if-statement.js
        ├── merge-object.js
        ├── parse-control-flow-storage.js
        ├── prune-if-branch.js
        ├── split-assignment.js
        ├── split-member-object.js
        ├── split-sequence.js
        └── split-variable-declaration.js
Download .txt
SYMBOL INDEX (145 symbols across 32 files)

FILE: input.js
  function _0x4d77 (line 1) | function _0x4d77(){const _0x2f79e5=['\u67e5\u8be2\u521d\u59cb\u79ef\u520...
  function printNotice (line 1) | function printNotice(){const _0x2655bb=_0x2ddd;console[_0x2655bb(0x167)]...
  function desensitizeMobile (line 1) | function desensitizeMobile(_0x3e5dea){const _0x36b811=_0x532065;if(!_0x3...
  function checkRegLink (line 1) | function checkRegLink(){const _0x142061=_0x532065;try{const _0x208b14=/\...
  function _0x2ddd (line 1) | function _0x2ddd(_0x2a5c8e,_0x1d1cb5){const _0x4d7723=_0x4d77();return _...
  function commonHeaders (line 1) | function commonHeaders(_0x39001f){const _0x4205b5=_0x532065;return{'\x55...
  function handleAccount (line 1) | async function handleAccount(_0x30df42,_0x4c6efc){const _0x286330=_0x532...
  function main (line 1) | async function main(){const _0x10c2b1=_0x532065;checkRegLink();const _0x...

FILE: output.js
  function printNotice (line 4) | function printNotice() {
  constant REQUIRED_REG_LINK (line 12) | const REQUIRED_REG_LINK = "http://h5.yidingyuecheng.com/#/pages/register...
  constant CK_DIR (line 13) | const CK_DIR = path.resolve(__dirname, "ydyc_ck");
  constant ENV_NAME (line 14) | const ENV_NAME = "ydyc_zm";
  function desensitizeMobile (line 15) | function desensitizeMobile(_0x3e5dea) {
  function checkRegLink (line 21) | function checkRegLink() {
  function commonHeaders (line 50) | function commonHeaders(_0x39001f) {
  function handleAccount (line 63) | async function handleAccount(_0x30df42, _0x4c6efc) {
  function main (line 173) | async function main() {

FILE: src/decode.py
  function try_decompress (line 17) | def try_decompress(data):
  function try_decode_base64 (line 48) | def try_decode_base64(data):
  function extract_base64_encoded (line 59) | def extract_base64_encoded(data):
  function Encoded_script_decode (line 71) | def Encoded_script_decode(data):
  function decrypt_nested (line 76) | def decrypt_nested(data):
  function process_data (line 112) | def process_data(data):

FILE: src/plugin/awsc.js
  function RemoveVoid (line 12) | function RemoveVoid(path) {
  function LintConditionalAssign (line 18) | function LintConditionalAssign(path) {
  function LintConditionalIf (line 31) | function LintConditionalIf(ast) {
  function LintLogicalIf (line 98) | function LintLogicalIf(path) {
  function LintIfStatement (line 113) | function LintIfStatement(path) {
  function LintIfTest (line 130) | function LintIfTest(path) {
  function LintSwitchCase (line 145) | function LintSwitchCase(path) {
  function LintReturn (line 154) | function LintReturn(path) {
  function LintSequence (line 169) | function LintSequence(path) {
  function LintBlock (line 186) | function LintBlock(path) {

FILE: src/plugin/eval.js
  function unpack (line 8) | function unpack(code) {
  function pack (line 37) | function pack(code) {

FILE: src/plugin/jjencode.js
  function getCode (line 7) | function getCode(code) {

FILE: src/plugin/obfuscator.js
  function virtualGlobalEval (line 27) | function virtualGlobalEval(jsStr) {
  function decodeObject (line 41) | function decodeObject(ast) {
  function stringArrayV0 (line 105) | function stringArrayV0(ast) {
  function stringArrayV2 (line 209) | function stringArrayV2(ast) {
  function stringArrayV3 (line 315) | function stringArrayV3(ast) {
  function decodeGlobal (line 437) | function decodeGlobal(ast) {
  function stringArrayLite (line 580) | function stringArrayLite(ast) {
  function decodeCodeBlock (line 619) | function decodeCodeBlock(ast) {
  function cleanSwitchCode (line 631) | function cleanSwitchCode(path) {
  function cleanDeadCode (line 729) | function cleanDeadCode(ast) {
  function standardIfStatement (line 736) | function standardIfStatement(path) {
  function standardLoop (line 774) | function standardLoop(path) {
  function purifyCode (line 781) | function purifyCode(ast) {
  function checkPattern (line 846) | function checkPattern(code, pattern) {
  method VariableDeclarator (line 859) | VariableDeclarator(path) {
  method FunctionDeclaration (line 914) | FunctionDeclaration(path) {
  method VariableDeclarator (line 987) | VariableDeclarator(path) {
  function unlockEnv (line 1030) | function unlockEnv(ast) {

FILE: src/plugin/sojson.js
  function virtualGlobalEval (line 20) | function virtualGlobalEval(jsStr) {
  function decodeGlobal (line 24) | function decodeGlobal(ast) {
  function cleanSwitchCode (line 95) | function cleanSwitchCode(path) {
  function cleanDeadCode (line 173) | function cleanDeadCode(ast) {
  function checkPattern (line 180) | function checkPattern(code, pattern) {
  method VariableDeclarator (line 201) | VariableDeclarator(path) {
  method FunctionDeclaration (line 250) | FunctionDeclaration(path) {
  method VariableDeclarator (line 311) | VariableDeclarator(path) {
  method StringLiteral (line 387) | StringLiteral(path) {
  function unlockEnv (line 401) | function unlockEnv(ast) {
  function purifyFunction (line 413) | function purifyFunction(path) {
  function purifyCode (line 449) | function purifyCode(ast) {

FILE: src/plugin/sojsonv7.js
  function virtualGlobalEval (line 21) | function virtualGlobalEval(jsStr) {
  function evalOneTime (line 24) | function evalOneTime(str) {
  function decodeGlobal (line 31) | function decodeGlobal(ast) {
  function cleanSwitchCode1 (line 343) | function cleanSwitchCode1(path) {
  function cleanSwitchCode2 (line 421) | function cleanSwitchCode2(path) {
  function cleanDeadCode (line 498) | function cleanDeadCode(ast) {
  function removeUniqueCall (line 506) | function removeUniqueCall(path) {
  function unlockDebugger (line 537) | function unlockDebugger(path) {
  function unlockConsole (line 580) | function unlockConsole(path) {
  function unlockLint (line 608) | function unlockLint(path) {
  function unlockDomainLock (line 619) | function unlockDomainLock(path) {
  function unlockEnv (line 654) | function unlockEnv(ast) {
  function purifyFunction (line 672) | function purifyFunction(path) {
  function purifyCode (line 716) | function purifyCode(ast) {

FILE: src/utility/check-func.js
  function checkPattern (line 1) | function checkPattern(code, pattern) {

FILE: src/utility/safe-func.js
  function safeDeleteNode (line 3) | function safeDeleteNode(name, path) {
  function safeGetLiteral (line 32) | function safeGetLiteral(path) {
  function safeGetName (line 45) | function safeGetName(path) {
  function safeReplace (line 55) | function safeReplace(path, value) {

FILE: src/visitor/calculate-constant-exp.js
  function checkLiteral (line 5) | function checkLiteral(node) {
  function calculateBinaryExpression (line 29) | function calculateBinaryExpression(path) {
  function calculateUnaryExpression (line 60) | function calculateUnaryExpression(path) {

FILE: src/visitor/calculate-rstring.js
  method StringLiteral (line 9) | StringLiteral(path) {

FILE: src/visitor/delete-illegal-return.js
  method ReturnStatement (line 5) | ReturnStatement(path) {

FILE: src/visitor/jsconfuser/anti-tooling.js
  function deAntiToolingCheckFunc (line 3) | function deAntiToolingCheckFunc(path) {
  function deAntiToolingExtract (line 17) | function deAntiToolingExtract(path, func_name) {
  method FunctionDeclaration (line 40) | FunctionDeclaration(path) {

FILE: src/visitor/jsconfuser/control-flow.js
  function checkControlVar (line 6) | function checkControlVar(path) {
  method ObjectExpression (line 82) | ObjectExpression(path) {
  method ObjectExpression (line 172) | ObjectExpression(path) {

FILE: src/visitor/jsconfuser/duplicate-literal.js
  function checkArrayName (line 11) | function checkArrayName(path) {
  function parseArrayWarp (line 47) | function parseArrayWarp(vm, path) {
  method ArrayExpression (line 87) | ArrayExpression(path) {

FILE: src/visitor/jsconfuser/global-concealing.js
  function findGlobalVar (line 11) | function findGlobalVar(glo_name, glo_path) {
  function getGlobalConcealingNames (line 55) | function getGlobalConcealingNames(glo_fn_path) {
  method FunctionDeclaration (line 109) | FunctionDeclaration(path) {

FILE: src/visitor/jsconfuser/global.js
  function findGlobalFn (line 38) | function findGlobalFn(path) {

FILE: src/visitor/jsconfuser/minify.js
  function checkArrowWrap (line 4) | function checkArrowWrap(path) {
  method Identifier (line 66) | Identifier(path) {

FILE: src/visitor/jsconfuser/opaque-predicates.js
  function checkOpaqueObject (line 15) | function checkOpaqueObject(path) {
  method ObjectExpression (line 119) | ObjectExpression(path) {

FILE: src/visitor/jsconfuser/stack.js
  function checkFuncLen (line 17) | function checkFuncLen(path) {
  function initStackCache (line 57) | function initStackCache(len) {
  function processAssignLeft (line 67) | function processAssignLeft(vm, cache, path, prop_name, stk_name) {
  function processAssignInvalid (line 137) | function processAssignInvalid(cache, path, prop_name) {
  function processReplace (line 143) | function processReplace(cache, path, prop_name) {
  function checkStackInvalid (line 158) | function checkStackInvalid(path, invalid) {
  function checkChangeValid (line 187) | function checkChangeValid(invalid, used) {
  function tryStackReplace (line 197) | function tryStackReplace(path, len, invalid, used) {
  function getStackParamLen (line 241) | function getStackParamLen(path) {
  function processStackParam (line 281) | function processStackParam(path, len) {
  method Identifier (line 305) | Identifier(path) {
  method RestElement (line 341) | RestElement(path) {

FILE: src/visitor/jsconfuser/string-compression.js
  function findStringDecoder (line 12) | function findStringDecoder(path) {
  function findStringGet (line 42) | function findStringGet(path) {
  function findStringSplit (line 65) | function findStringSplit(path) {
  function findStringFn (line 81) | function findStringFn(path, name) {
  method Identifier (line 118) | Identifier(path) {

FILE: src/visitor/jsconfuser/string-concealing.js
  function insertDepItemVar (line 14) | function insertDepItemVar(deps, name, path) {
  function findGlobalFnRef (line 49) | function findGlobalFnRef(obj) {
  function findBufferToString (line 96) | function findBufferToString(obj) {
  function generatorStringConcealingDepCode (line 150) | function generatorStringConcealingDepCode(obj) {
  function renameProperty (line 165) | function renameProperty(member) {
  function processSingleGetter (line 243) | function processSingleGetter(obj, decoder_name, decoder_path) {
  method FunctionDeclaration (line 340) | FunctionDeclaration(path) {
  function tryStringConcealingPlace (line 366) | function tryStringConcealingPlace(path) {
  method StringLiteral (line 396) | StringLiteral(path) {
  method ArrayExpression (line 416) | ArrayExpression(path) {
  method ObjectExpression (line 432) | ObjectExpression(path) {

FILE: src/visitor/lint-if-statement.js
  function LintIfStatement (line 3) | function LintIfStatement(path) {

FILE: src/visitor/merge-object.js
  function mergeObject (line 3) | function mergeObject(path) {

FILE: src/visitor/parse-control-flow-storage.js
  function parseObject (line 5) | function parseObject(path) {

FILE: src/visitor/prune-if-branch.js
  function pruneIfBranch (line 1) | function pruneIfBranch(path) {

FILE: src/visitor/split-assignment.js
  method IfStatement (line 10) | IfStatement(path) {
  method VariableDeclaration (line 35) | VariableDeclaration(path) {

FILE: src/visitor/split-member-object.js
  function splitMemberObject (line 1) | function splitMemberObject(path) {

FILE: src/visitor/split-sequence.js
  function doSplit (line 3) | function doSplit(insertPath, path) {
  function splitSequence (line 13) | function splitSequence(path) {

FILE: src/visitor/split-variable-declaration.js
  function splitVariableDeclaration (line 3) | function splitVariableDeclaration(path) {
Condensed preview — 44 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (235K chars).
[
  {
    "path": ".github/workflows/decode.yml",
    "chars": 818,
    "preview": "name: Decode JavaScript File\n\non:\n  push:\n    branches:\n      - main\njobs:\n  decode:\n    runs-on: ubuntu-latest\n    perm"
  },
  {
    "path": "README.md",
    "chars": 323,
    "preview": "# QLScriptpublic\n\n\nfork仓库后 把待解密的脚本放入到input.js里面 等待60s左右即可在output.js看到解密脚本\n\npython脚本同理 放入到input.py里面 等待60s左右即可在output.py看"
  },
  {
    "path": "input.js",
    "chars": 16144,
    "preview": "function _0x4d77(){const _0x2f79e5=['\\u67e5\\u8be2\\u521d\\u59cb\\u79ef\\u5206\\u51fa\\u9519\\uff1a','\\x70\\x61\\x74\\x68','\\x5b\\u2"
  },
  {
    "path": "input.py",
    "chars": 34923,
    "preview": "# -*- coding: utf-8 -*-\n\n'''\nPowered By Beidu\nCreate at [2024-11-28 21:12]\nENV: ck变量:cj_ck, 卡密变量: CN_Card\nCRON: 11 1,3,5"
  },
  {
    "path": "output.js",
    "chars": 7796,
    "preview": "//Wed Apr 01 2026 00:52:28 GMT+0000 (Coordinated Universal Time)\n//Base:<url id=\"cv1cref6o68qmpt26ol0\" type=\"url\" status"
  },
  {
    "path": "package.json",
    "chars": 671,
    "preview": "{\n  \"name\": \"decode-js\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"decode\": \"node src/main.js\",\n    \"deob\": \"node src/main"
  },
  {
    "path": "src/decode.py",
    "chars": 3391,
    "preview": "import base64\nimport bz2\nimport zlib\nimport lzma\nimport gzip\nfrom datetime import datetime\n#from Crypto.Cipher import AE"
  },
  {
    "path": "src/main.js",
    "chars": 3391,
    "preview": "import fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport * as path from 'path';\nimport process from 'process';\n\n"
  },
  {
    "path": "src/plugin/awsc.js",
    "chars": 6331,
    "preview": "/**\n * Reference:\n * * [某宝登录bx-ua参数逆向思路(fireyejs 225算法)](https://zhuanlan.zhihu.com/p/626187669)\n */\nimport { parse } fr"
  },
  {
    "path": "src/plugin/common.js",
    "chars": 850,
    "preview": "import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimpor"
  },
  {
    "path": "src/plugin/eval.js",
    "chars": 1286,
    "preview": "import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimpor"
  },
  {
    "path": "src/plugin/jjencode.js",
    "chars": 1613,
    "preview": "/**\n * Check the format and decode if possible\n *\n * @param {string} code the encoded code\n * @returns null or string\n *"
  },
  {
    "path": "src/plugin/jsconfuser.js",
    "chars": 2247,
    "preview": "import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimpor"
  },
  {
    "path": "src/plugin/obfuscator.js",
    "chars": 30545,
    "preview": "/**\n * 整合自下面两个项目:\n * * cilame/v_jstools\n * * Cqxstevexw/decodeObfuscator\n */\nimport { parse } from '@babel/parser'\nimpor"
  },
  {
    "path": "src/plugin/sojson.js",
    "chars": 13919,
    "preview": "/**\n * 在 babel_asttool.js 的基础上修改而来\n */\nimport { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nco"
  },
  {
    "path": "src/plugin/sojsonv7.js",
    "chars": 21618,
    "preview": "/**\n * For jsjiami.com.v7\n */\nimport { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst gener"
  },
  {
    "path": "src/utility/check-func.js",
    "chars": 241,
    "preview": "function checkPattern(code, pattern) {\n  let i = 0\n  let j = 0\n  while (i < code.length && j < pattern.length) {\n    if "
  },
  {
    "path": "src/utility/safe-func.js",
    "chars": 1477,
    "preview": "import * as t from '@babel/types'\n\nfunction safeDeleteNode(name, path) {\n  let binding\n  if (path.isFunctionDeclaration("
  },
  {
    "path": "src/visitor/calculate-constant-exp.js",
    "chars": 2904,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nfunction"
  },
  {
    "path": "src/visitor/calculate-rstring.js",
    "chars": 736,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\n/**\n * \""
  },
  {
    "path": "src/visitor/delete-extra.js",
    "chars": 246,
    "preview": "/**\n * 0x10 -> 16, \"\\u0058\" -> \"X\"\n * not ASCII-safe (disable jsescOption:minimal to keep ASCII-safe)\n */\nexport default"
  },
  {
    "path": "src/visitor/delete-illegal-return.js",
    "chars": 164,
    "preview": "/**\n * delete ReturnStatement in Program scope\n */\nexport default {\n  ReturnStatement(path) {\n    if (!path.getFunctionP"
  },
  {
    "path": "src/visitor/delete-nested-blocks.js",
    "chars": 784,
    "preview": "const isIntersect = (path, bindings) => {\n  path.scope.crawl()\n  for (const key of Object.keys(bindings)) {\n    if (path"
  },
  {
    "path": "src/visitor/delete-unreachable-code.js",
    "chars": 1297,
    "preview": "import * as t from '@babel/types'\n\n/**\n * DFS the BlockStatement to find and return the location of the first\n * ReturnS"
  },
  {
    "path": "src/visitor/delete-unused-var.js",
    "chars": 824,
    "preview": "import * as t from '@babel/types'\n\n/**\n * Delete unused variables with the following exceptions:\n *\n * - ForOfStatement\n"
  },
  {
    "path": "src/visitor/jsconfuser/anti-tooling.js",
    "chars": 1162,
    "preview": "import * as t from '@babel/types'\n\nfunction deAntiToolingCheckFunc(path) {\n  if (path.node.params.length) {\n    return f"
  },
  {
    "path": "src/visitor/jsconfuser/control-flow.js",
    "chars": 4364,
    "preview": "import safeFunc from '../../utility/safe-func.js'\nconst safeGetLiteral = safeFunc.safeGetLiteral\nconst safeGetName = saf"
  },
  {
    "path": "src/visitor/jsconfuser/duplicate-literal.js",
    "chars": 3218,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport i"
  },
  {
    "path": "src/visitor/jsconfuser/global-concealing.js",
    "chars": 3976,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport f"
  },
  {
    "path": "src/visitor/jsconfuser/global.js",
    "chars": 2079,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport s"
  },
  {
    "path": "src/visitor/jsconfuser/minify.js",
    "chars": 2324,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\n\nfunction checkArrowWrap(path) {\n  if (path"
  },
  {
    "path": "src/visitor/jsconfuser/opaque-predicates.js",
    "chars": 4331,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport i"
  },
  {
    "path": "src/visitor/jsconfuser/stack.js",
    "chars": 9116,
    "preview": "import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimpor"
  },
  {
    "path": "src/visitor/jsconfuser/string-compression.js",
    "chars": 4777,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\n\nimport ivm from 'isolated-vm'\nconst isolat"
  },
  {
    "path": "src/visitor/jsconfuser/string-concealing.js",
    "chars": 12529,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport i"
  },
  {
    "path": "src/visitor/lint-if-statement.js",
    "chars": 541,
    "preview": "import * as t from '@babel/types'\n\nfunction LintIfStatement(path) {\n  let { test, consequent, alternate } = path.node\n  "
  },
  {
    "path": "src/visitor/merge-object.js",
    "chars": 5138,
    "preview": "import * as t from '@babel/types'\n\nfunction mergeObject(path) {\n  const { id, init } = path.node\n  if (!t.isObjectExpres"
  },
  {
    "path": "src/visitor/parse-control-flow-storage.js",
    "chars": 5268,
    "preview": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nfunction"
  },
  {
    "path": "src/visitor/prune-if-branch.js",
    "chars": 1018,
    "preview": "function pruneIfBranch(path) {\n  function clear(path, toggle) {\n    // 判定成立\n    if (toggle) {\n      path.replaceWith(pat"
  },
  {
    "path": "src/visitor/split-assignment.js",
    "chars": 1490,
    "preview": "import * as t from '@babel/types'\n\n/**\n * Split the AssignmentExpressions. For example:\n *\n * - In the test of IfStateme"
  },
  {
    "path": "src/visitor/split-member-object.js",
    "chars": 774,
    "preview": "function splitMemberObject(path) {\n  const object = path.get('object')\n  if (!object.isAssignmentExpression()) {\n    ret"
  },
  {
    "path": "src/visitor/split-sequence.js",
    "chars": 1240,
    "preview": "import * as t from '@babel/types'\n\nfunction doSplit(insertPath, path) {\n  const expressions = path.node.expressions\n  co"
  },
  {
    "path": "src/visitor/split-variable-declaration.js",
    "chars": 711,
    "preview": "import * as t from '@babel/types'\n\nfunction splitVariableDeclaration(path) {\n  // The scope of a for statement is its bo"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the smallfawn/decode_action GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 44 files (213.5 KB), approximately 79.7k tokens, and a symbol index with 145 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!