[
  {
    "path": ".github/workflows/decode.yml",
    "content": "name: Decode JavaScript File\n\non:\n  push:\n    branches:\n      - main\njobs:\n  decode:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n    - name: Checkout repository\n      uses: actions/checkout@v2\n\n    - name: Execute Python script for decoding\n      run: |\n        python src/decode.py\n\n    - name: Install dependencies and run decode\n      run: |\n        npm install\n        npm run decode -- [-i input.js] [-o output.js]\n    - name: Configure Git author\n      run: |\n        git config --local user.email \"action@github.com\"\n        git config --local user.name \"action\"\n    - name: Save decoded output to repository\n      run: |\n        git status\n        ls\n        git add output.js\n        git add output.py\n        git commit -m \"Add decoded output file\"\n        git push\n        \n"
  },
  {
    "path": "README.md",
    "content": "# QLScriptpublic\n\n\nfork仓库后 把待解密的脚本放入到input.js里面 等待60s左右即可在output.js看到解密脚本\n\npython脚本同理 放入到input.py里面 等待60s左右即可在output.py看到解密脚本\n\n目前支持zlib bz2 lzma\n\n刚使用action的人手动点一下仓库上方action 初始化\n\n脚本自适应检测加密方式\n\n支持sojson [源jsjiami.v6]\n\n支持sojsonv7 [源jsjiami.v7]\n\n支持obfuscator [市面上通用加密]\n\n支持awsc [阿里云混淆]\n\n支持jjencode [源jjencode]\n\n支持common [common]\n"
  },
  {
    "path": "input.js",
    "content": "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();\r\n"
  },
  {
    "path": "input.py",
    "content": "# -*- 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,7,9,11,13,15,17,19,22 * * *\n\n'''\n\nimport base64, zlib, lzma, bz2, gzip\nexec((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/KH90i0Cf2rM7MU/AxrFft/oNE4FA6Apxojqy9LmhOoPQGmqbCHNcFdE+J2Ht4r6E+cUvQfuVVLaT41VCMIWNbb0uYqNal5/5gO03+drM0FkmA0gqObPZ7RnGw3wEUBi4g/lK+RIOd++/agKfxwpO/qCFlfBRuv1cAz36S57uOn0rLWwaLj4I21RrI09FyG2n8+Fx7xwv2sJ8LlAd3zPVFdaXfkFSLZMyf+vwh66ProjfVTx72cHi646ZH2D6gZ52nhtx7hn41tjYV4In9n+1G75kkoF6aFWMDZdeYYsLeJeCO76w/zZwHfPRUUjUS19gpLT4MuOgYuNjDvJZbQvCueEm/20fpBE7yVfYy0WWgLirgPd243TwzoJRU47lF5f48K3R+9J3o1JwYnnEVqv5YFCoA453odHafzCKvgnmQ92BDYvVTaE4SV7k+QWPJG2jidvXtWyWlmu2yufxN/Z3+lDrXoXtV4sfKD0+Al5LvSSwzymokPf3XyPVhL3tC4+nb2FZ44AcyERNeampZtHevYZ0qOx8PTCI6jL5ioYEUlKirnAOT6qcEo7apuA37cnzBW5pI6PdgEZWze3aKAhwLsVOFOylhRfH6xi9ZwDU6pBOpX/A91VrPO4d/8t4xyUAVZibiw1CQ4dRHYOuj29Op+bV/kQEach/NarzV05rJaDY65rej1FMvB6ZMXa/p/fTgjhPpwK1x3uQRRMpkG8DQnO5Iqe7ortNVwkDBx4bHT4dXMbg2PfzzNl2Q1GYohCdklgCRB/qVxH2a7A1ir+H5nMpG4v4IOcSl9biJHefGZDSOJ1xFJLEJrcZZzF5n+Ed2QEax93kWydyISMjnWHJOhSkUZtFtojZVTj+gUCIeMQ7g2h4VefMNa7bzzAj0B/AD/XxirNBpLVj5/60oGmEct7K2rqvrM45H5rvQEK+sr0UQhVXJD8zfEF9Z6ktirHb4J3STx3u2U/3Kg3CrWwUV737d/KH4Gan4rUGue381XAiu3M0xFfqs0GhNku3X7pU5dbDCDLZOzm3aoHf64WqspDEhJbGmfUGG/P3yjrLdRJFaibyiI9mCzxHk7/htjuj+V0B1NPGu2/6jsADcwAqeVuMsiGFC0eL548oKT9sh65WaOh9mTHy6uyx2djf75hbr/8fs6ZA2/+7BZ7dLzg9sfz9No9g2KxNHDCC4cSX9k04Bn2RTd7n2CEs52zoIxrdt3XPiowWzdboqdF9F2MjVfpBVEUsB/nucRNsCIeRuVtAvMIPoSTjGITT5cuqZZ0rpnfKYj6s/sJ8pveKBLCkT77ibKTQmuDp1a840fYiWxDDNql0lwA/OoyVv7ujgoRjaS5VMIgK1bTg/S7iuoBtG/gbyCIfPl5xQccSV3qOj/YLa7n7+bTX8JxFHpYGtF+f57rSyavW+S+m5RfoUBLpgXtWcO4Z4GPq5Ni2nzRhPXdMt8qPf/ez4KhlWABLqVlip3IEDAxhnwM9r8+bPR90xTubp9vNQl94pltgtsPIALSBjZUKKQ7lVHZmCAQnNSwtvnTsi4mB+h3xqvLqlQbT8CyP5xIdkVeVT8WtB1O6QDXXTE3nElOncFIF0arTEN+69rEfKO+ppCcS35wRqWpESdrS7R9TN1tQYXDpVq4Osf6vZH/iHloUlfVOukCelUq1Ey1w8ITr9S/yH2cyz/wY7fhT01LDJo17+Bp+E9lQTpTx9AGWQqnGiU4Cqy/cZVujsVWBNd2UoZiyUD5rebJ1otoy8Y9OpvKXSrnpsdB4lORN6Uffl/HlBeN6FRFvXEIOhIq92JdZVmXmiX9hw6suusSLwTovMPnvJX2KxyItL9gTaydZBBSnq/LxSxy006aE2gdsxrSnoVNqL9BlL8x+ewN5dNqRuF1hVuAnyHs1wwvD1kqEhP4evbQr4dSICD+iQsxS7keSRTW6k9oOsD3W4G20hfQteg2t2aO25YKoDG62cc50xQHU9ariog6UeC6jPhKpr/780/3ILd0fPfX3CLtQ32IcW9Zsp+5pK3MA463qs8kwPb67zwGtzwG/RYxWgNbVo5tWOrFPq6H5YQUAbd40wWa6whrfgOi4Kq0F0cY7slNo3tSYTZ7qW0wZgXcM+O1fzoadHy3ADvvGR+4IKujlkL/L/ZRcreTvqs88JHOzR/pxYufTkwDaQeJ+sCV05BxOMwhfZPgEWwvn5cRPQDfJTYdAcZQmaEwWqp1bUTxxsztu9chpPOeB9HNvYbK2Jd/XkvUIzx2xiAlCSUDcBgfGCBM0uMZwGyUJYcgfxzoZmT7JslRLnupBhMBeZrt+Ek06jWlgNS2h7m+bPX3a2sAzOeVM3YfMcLnAgXqSOpHez8RAnQNezLbCtIkxzPuCh9D1q/xgnDbQpqruPoKCBC8+lw/mSzoWcXtbndiC9/b+eAyse68xT2YtIOIWYoTpp+Cr5xJthhL+L6GZM4rGNwc0Gphxz34MtVz2RCUHnbnD/w3IG3l5fKAXsAzr6++kRYVZWD0vaOUxAMFfIh6T1d1WJsZySUOz2UDLgH9d1ymlpCn0wOHhDeisV77ULqMqqFljb+NUgKKpV/iUS/8dz+SqAlMItlauv19rWchIjdEOuc7yNxI4wE6U/ArEi+Q7L1AiZNeJw/3VrE2foj0x4GgkwlQF7wDZi5EaKUS/H/Jir5TFalUQDGgVs7K5d0hKBCFPOS2RYgQ16dGUFHOgnKlDss1dcbiVcrzQiv/o9EyAChtRk/gCwlLuMJRif8BhPscCma9uPoTYL1vdBaK9W0StzsQciY/4nr5iHdBQSOrtXLUfS5fRzKZ3q6L+ohxhcStNMXTeAkWpzmCxrmH+LFgQB5wSM1PlRo05GCbb5k3C5EbbWgdpW2T2SDS/+sWRnK+O9N2KNO794hFy/GuhNDm8Pq4roae5nS5FdqSCEf0ObCO4ES+MICO3NnTDUSxf/LA7Fk9CHBNvyp7pCPhY1s5xcbvKc46imydn/6CCzmRFq65lFxUlYmRDpELcElZLLny28F1uYrA0d5I0dv0HiNuj+DbKiqcqv/X1QbVOeljdrXfsW+de3y0hh1DlGINPcxffPm1XUqNIOa1fn9hnooEQilt9zpFygt4MWEknTjB8dMkm0FfmZXqLXZ48IRSijRTZzvRDrCYg7flRPF/NcRbxUUUhVpXWgJj7N1W5ri/hJ17lQu4RH8/VO5rS27vWEMeoYWEtnU+rA4MQKQL2Yl5Y+glvLH4NqswPt00pIlQf4fsUlUO1iopINK+xaFeEfp6P20gvchm7hg0awcgFd8IUFDgqjTAPaKgBVB1wbHsOr2zrCF6z/rQwruwIykDOqzq3pVyV6U2VOCoDyQln3bhyn6CG7QEM15aqhy3yba0G4GvZrG+SBaxTIspCZpZXjZTRt40Od1dcEgpaBLYViEwl7a7UgCtVyQ4IkIje9Wh0mfq/WTzcFTangQxDoPg/b+3pLnJnuCfjcWew14/Ke8tTgo6SpHdoenaDZZuueeEDQzO84n8nHfW83h28KMW+kvPl8oHX6pNRRxj8tq1iJK9G8thNaD7OYx5hxBN/yFxc2NN77cYgwW9fpQvpMtfcdFkEDjf0Wcv9gwPxH33A3mDnWkoE9HlZ+3XsRqXjP1zqQo6YLWN2wmGK9b7g9X2UcnfhVscJRws+e7eme7/QbrMqZxH+ClBD7thXnfuUfn/Pr9HusHT8t8Nn2UWqw4IZF14KCN5z8HFpkToGSmVQRTfrd718fkgDVMdMTuN/SKddIvW7eoiFIrLuJ3IwsOOsMWCQc5y7/3LARmq4KplW60/ejvixnoOB47Cpo3SoeZlLWnJrXGUFnfI3ECdDYoNHoQf9RWfg6z27JWvgqyVDbEKKPX/bIrkfMV/qf1EfVXYQ4NCrUpyDaovzv2GxPSUBLekVpHY9xW33JVrf0hw9QzOWaLFGlPpVkbNbvDPROovcaD6lhYq4DPLFy59I8OaJ+yP/0JkJ7QzvMRN51Yu/lSpfQv2hslFQYujF+YHH1nBhm91NVhLUxyn/iIzEGZUzz+K93k5J4qKsinyytJm1y9/8x5MLSUXCaMEgzzEl7AI2BCUaY+uBpbuVJNoyjae495m5whoMRNRjh+t9cfRt4IUY3PuUoJ6FvWfGWfnW04qOmh13udAMgdJMobVJG0qnBY6q3D+k9S+pi/b+NMXBA6ORJzIXAc3WTXML+DSNokbjmbkspr9FyNF74ilmjcg00Fom9o/QbXcsidwzumyAqQTy6nYkz5QiiTuzHzTxjarc8oDTz5Qx9lzevf3m12qAeC1dMEqEGDLYzjQDuRJy1f/CqusmqlFCNxB0kcBfqj7T8mO0o/VcyqzEHHaNj7bombbnT2crCdtNDujLPtTmorICoOewwlw8SW5hUokYI1al3P9KDjco/iejyfsqyl/efe3jM8SrTOd2Ztc7tfJdFsK/ejufAnJrTByIPxo0N/cuvDTrxhMf+xYQzR0l2u7KVUCKe7G51pWCucU5dzlfYwlD2y/w6Gt+G0rivYeSmV0UR4bSTH+yR3zpZTUt6D16UVzqsj9VdZVPGimM8XJRLqDsOR1l9MolrUer9+8pJql8/sO1bv8vaUvBF+gjV3FTE51x9KtbndxrB6yQEytw/018JsVppahsZQ7bZPKPlmo834aFFYaAkxtrxedmDEQhDB5ObKlC+QPFwHTGfHFdtNV61zVe3PLGkIOsYdTm0neOH8XGdBEraNSN5j2Q8yO4FjgYSywWlA6jMsIBkKPBzPdgk3KS/WxfMRZhxx0LR4qvgUsA9RAvotaHfj7GNAbGUNLIIo8ZmcPhH32DeqFJyKv9yLpONia2qXlTkVdm32UQiuDhbPj+cQ/U/OR6JkeQVcxuL7z3ZzzalVNcAROaydioyT8EhdxOBRlA6KIn1RtEXn4BnjX3I/t5yIi/due9Kz4WGFAKDGohgf+AxfTMBA51O6uJZTX3X9AChc1yxzT6ytHGZde0bpa5wgsdk7xPCmU8zD4Nuc4ajzCXocrAR80wvHkiD7PRGCjzdol4kpNvKGHCHqHSU9XPvRRrD+gy46Of1MyDJ4nCyTKu+MlJ5gSmeDSsHnB4dpmQC5tuwZJGeQcAkcW4GKrtFpO/filINrvDrgVRfrpXwv0FxGlvFu2ye4JJLiiJP9c+Zmnr+4RPsrFwXDWs9V159GpO4FzIHbi1SQIidJrCu2KBBSOOV2STdtnusw0Rse+VAE0bkdY8DmSQDLJ0W/ycCKmXgKJY72xBPwWByXGywApzw3i5KMn9FkzhIl4URNYjVNe4HuT/jRsDKb1mY3Tk4YAZqdcOiowxJnTTbf9eKZgQ1NaRFm0tneyR5ZD+JU2k8BVBMP0xfvC9xs1pRupOtvTCCkweP3nS1GP/2VWk+bJpIFI2DNEMrFrt/JnOEZZVbapduqDQ8HqDLM2oRrdyBgGMTVVErOS3G2rQl21s54d4nrqaftVfzjKR7126NIcJP5JYA6ATDOgGEwN5Jr5aZNQftlt3WM22XdEeddwh5tcgZPVE+vRoUCPazT3g2djdn0+tqzmlTkFtxKB+O5loiMB4yrU8JgylqYdmYe778QJ615HLCPaqYA/9uxjF0x7rET/VmKTfo2vuQrroZueXkg1OjDKHpbkUgkqryOy4fokHPJ8+TjQqM7RHG4PvxduN0lI2BuFlzFhwM+5W+j6Zf6wOPvjuih9zUeamQHDoidVBc0/XIpZuw/xRUhgZM2W6D/9duiWhOp56z3IY+otPrAQXp7z7ogQm2SicaMcOhLaLQojIDoNjpsRWml7zBEWwX31TWcdPP4+2WE2ePQ6hzWuwj/t47Co9ykWMAq6xoT9hF/x1A+xmxySSV55OSY9pG6+HXor5LBOf4iHBldNzPaAvv84gwPmOhmLNi/SS4IfoTsyxfGjAvH2ne02cWRqtYmbLANLOJt249ruAadMtOqdrEgevgpJlVWG3pyOo9ISUL2VDsHbm9nzo/bZQbUZYZ6e4WfSkgvFmVbvR+WXyA/ks/nJILZnaXNldzLPmhViMEAhAclxOuEe4Uev7VghrJ0tHNXiO5KucVaQ55mChLMqpTZkN08PnpPJD9XNM8Np6vtHv9Km1sZsuTbovVMJy11YZEQhDIaT7BeFs9ZJxKH3d4k97Megu3LqPxFbp6fVojGDS2J2Fme7OBbPbuR/qdPJWltbFtS54Yo+hLj0jx8IogCMZeh7T3rZt2kF77BXyE2WiIVHsaSY+ThlfgauOhPpFFnRDW49611whl/uEopVPWy7bAbOJQzqJwdrXsJb+bV2Qv1hVTyhgBi8F2upgrHj1OYRpCKFAciiRw5cXqOCDZs+w1Bmk51og1m5Hsb3xu+QA1CekAjK3jUDJhK3iCTos1H/x+mxPuibfZW+rpcocrA/JUgZ0o9HLB7aMxjDNRIHJsRXWJHMIJNZehfQXzHGAb7Uw+9SFjXTLJuLBbZuGdieBAU/kTMn4w5S4fZr+d+V4l/WttDwZ6Hgixqeql0BfwoRjOo6PTx52jyGFricOGgmN8eg+olp2r4+ZKx8ydSdregcoq4PqCUSseEmpEU7IFKqvf4q+VDsQ2ZCe4gsm63f/4i7hI8ULrWXuPjJVLUxbZc+IE337TnqUNHHfUvr2wTSWMAqvZ11cyLaonxX6HmR3sHerpWsIE7tySicDlZAkc92ZVpB7Qy2nyu15mldO5xGLI30Y6O/YN5iZh5LVp5Vz+GMrzGmU+tZ4MloaAcABd8MPuLZ94lH0t6HnSeLZnyr8p+4eD9BjJ0viQ1DXbkG26of66lXt/rQ4jyAKWu0h7KBNdzCig5WHy0FoArt2OvVGVfkn0uhas3SvllUWEcVKiGaBPN74e2mWUiY3B+tkVZoI7pDLQWPw/fvomQ8IEgHgbbFWXmJR33fAlebOMu7F2dvDS9Ew2gXhzgZ7IfhG8vCf83uqOWU5pU54w3qTj8TO0zVeTDwOJbXKwOrzjxCgDkfXTaQ27EvW0TTmR17BairgVRU3tQuAS1rDJUJTw4tl0dgWmkgZz3/YjpQVRAZj80sKQC8XJYg1HDNwUSMtX5393dtZxJtTYERk97Ls509mc1kpUy6/MZ8ID7RVe+QksXu3Ign7Rg5if4A0UsB3bo0MslHfC/cjMcbSkZmoZeG+faakD3hhQ0edNqbpH0vEEgmb9PV3PpGOg+YnWtZGiIgpb/c4f21I8VoNPVhIHNPEkxCyXGb9yqBFWf6OKccWR56WUL91S7SHVPaqI4+jk2TcAbJmRdqdYDcbL1TvwOaFLE85SsQpFkt05vRvMeiiKFvQ2bnQPiznz975EsHUtMKSwSh41pdbLvwh/b6qmFX7N4AVVDAvqqhNoIGj3cgnRjTHRcwFHzZOg+GOzMRDw9mihFXigD8u/HRhpZVAz1NSpe4Br9sQkPXSUEisaRP6NHk6rdOF4sHlOI/+9h3vu4V8GE+kau+b6C4Wn+FGq9UqQN/xaj4XnBhT1v4iEYmvTRHioSaFe+aKwnVhEcsY8/LlytlgJH7wZGEgq841n30MQKkSXRridUIstsX8m43dg2O/tquc43hzB+BaL9TwIUo7rOH8CLlx/RWaxJD4wto/zVr76hmbw1BJsRq5LJJ93HLv+Ggp/ci1TBw1V9qvruJ4B6+ChxT+XeDtUvTOgQtcyZ0Cr5ENL9gq+aAwr2NiOK5uD3gcteEvaJoFI/zgt9/JSj+93KcXRsDhyQlBpXdHF07VR74Q2ipP8vJ/GTNyGZ/r6BiUekMBn15hvbW0Q9asXZpvnVvQOLbg7+KUIybVv33OC+p21ofZQsqZG84sNLXrUeGtUKAwl82fimYBlux5/yHRDWapkVSoprG9R69iEnWRUTdQEM45obq5k3x6TF5zbNy7r0VxFOxYOWJRlYMlEHJPKdO/lZOYGGid92zI82zuw/kxjUmwK5HHa6dKeMdqObp+zIY3Vi1kGPsOMNzwXcVKr02mfZ9GzQroY4gbeY/45az2AcOL78y/1CA++/5ThDe5HsbWs0u6VoHB3xqZ8+Z/Kn9YTWpfAJKSJCVhStzw7xsGFJiHBBFfoI46Tn3V7pYPaW35aXrYRYwynpcmmmk9605O51zCkvHtCQBGmxRcc80F081AW2b92DpjZrHzzhAZl2HlDQG/2NuPE+Hz90ogkO/ewHHvGp0a48GWfrjCk/cwaSKcwrqtks6hZRK7qVuSysg/LzZch6ARKjOmLqzHbDtNlTOrRga7XlTePz9IfNZtSCLDEkCiBdeZVHNMaNfrWX5N9m4dYuxEEZB2i0x5etN5eVKX9l2dBKkFADOm4bzUW3wmr2oa7NjcLi0inhnTFQiDecdsIg5zXt56a4Rs7fgncotr2d3N3Thwdh1Ce7CfuZ/nxPpphh+xZKoPaXmhdqjKG9pm2NNFuwUgl1t9Uzo41INOfVD1nts90Ap/5V6JE7XPmXbAFG+IMJZSRgyantS/RSJKuvqE+uvMGx7kzNs7TwrpGqb0QtW3cKzvTDC6yMJG1+PN/LhlFjFeGVGjuiqoCigCRwt0LOJ6ZM/Y8EwsC2JAtZYShW1rYVvXUU89XYACtjAZW/TzV7NJcnVxp5fEc2SbjyprTEJt5vEw5JqIZ37Y6bszg+0Uct1F9Fd/ZKriDBGINqM7j6o8N51CWaWwY57F359vg2Gex/b0UwfrIgbGFK8f8enGI3bOSO1F2Nv3hW7V8I+tH1y5I5jNZ4UwsRQemhpsiuVIPTnsSI2wi50WBa0/wXhmh3qxdlc46qe0EHD9IpB3mYbmiQvqDKmK8Xab6TJylpZQg0Y/lQE5SO1PLOE4FT9OlI4brOSh/E3YVAVoG0lJ2CdPvdqa26x+CJTdYmCyzXPMYz+waHqYjzcRBbwrL0vnJBoSQIgQH8oQqTtum10uyRnnfPanGoMem20bErQlJK36AhBL+KYbI7tAHuZUk+q12UGdau9B2Z5nwiP5CTTO9o1K0Z3rYaM13LyNrXhItI6PtLt4QnYtSxUiCTr9xpkYG+7DSD2fL8kc4ivFRRfPARy9wWaVlMruVYyykE9/WmfkCLXb1k6CKjXTd8TZtV0YOjzEpdIM8kbxHKjz61R+cPOU9FcZqJyp6n42AlAxrxRu+Ndtge4+ak7zVYexbx+Z3bf6/019uW1cgwcik4Y+QDVLkO1GxuHcGN3m+uW8C6PS0hcTurufT9q+kn1HcS5e4biUif6uL88mTfHmRoVWwdpxlo/LTdJEtg7FCWegtaMrzbIz/Nw/KQ3BQsBg3ePyNkhr6o+nmYx4qBCpFAXf6T0lvgLEEUZ/ITecm8mu9VkZUqGJ1N7Y7KcYiNXzQFQAr5elQlz0Xs7EGQavNiLF7mkx3U1DHx+zaVKi0y5A/0NQbyGJXBlkdwv87dCJ7T33ebgJKnwvLA6x1jUV7pyWzKB46xdejK5KNm22/jz9NJRPzOcP6wRWM6nrZTrcfKZBuA7864jq6WUv5e9TaFDmqYEIfduqhj/ueLR2AGAgxMtDptgTT7Ok6t2x+vqRjOqVYV+vc+aW0TaSa+hWCujetibG3neqCz5UKw2nbBe50eWy2nPj5W1Rmjjm/pdu0geS0wGt6w6VARis8q2SwDUjSpq6B5lOxImdh3BYz+6QG8d3zEe4tYzrMawRDUHcKtpbMNUkWl8lMnQNaKED664aM9zPTfqDNj8od4RE+zAjFfUfJKT98l+IzB7EV4cPQW5sjo3CuCnj/GgMKDGqrtQjF+k6qnEpQ+id/u3zWCp/8Mq2sZ0SaTd87l5DshBWsVred6R6UBseIqKFjVTzaz5VxQmTUgL5Yiv2EOzsbcaSEkJhuADry7oyg4GxTH1dGvmVxyK98WgQ6cto0kkQDurtLt95dj2CmzVmKQWNv9jqtn+TvDJcWMv7SfhA7PtinyhwfPfeDQZq7gobJKSJSwi3RPbcDzAVETsm/Glwhzz/u+HU/bCyaYKp3VOzwvhyQtLq3uSRpS5DnbYNjfEfBM/Kfq3DGna1PFxvUn+WWmfu+Y6j5HXWF8HLKuCfBfKIATlG4C2y1INjvLJDUzX7HLbnhwtuA0rwHARwvxQuKVM+6mgGwM0VLXTUj4bx9qQO2m2jeDwxdnc+MArCBr5BqvQIQUY78kUngs9AfJzXQlhIkFgpF4APZrAB2rnB5mX1pTSlYZh3QcTdhaWZSjj8UxXn4frICLEX7sSwcnkuPXydxkcIlr7+ENgNpIcuu4rKIdawon8qDV7YPcy1KMyVuaKWFkzHl2ElC5toz2WpTizbpV7IxtqMTbywhEzGdM/zzgBSRoiaBvwzJ6AV8Mf+Vvxw8PailHDNMPxWnB01DxykxUFSfayFg9r8Mg6l8vjw2I5/l31tg2f4nKzkGHP/TtaaNH8rf0gEr/j1dh/Dvxi6jATpddYmtTM+fhV8IlVk28f1p8a99Lj8cFpTQPCaLdDcwLkdjyOuC7i0r7mWv/ptrWKSIHu3mCLzUwWKi69eXrKDQW50HBobjGyl0ivxoMic11NvdA9eR7gp2T49xLJBeagU09kFAuVNKEZuYsVZaseyzuuA0RGOG4VlurM+08UUpdvySihhOZN3UoUtrOzh74ptRJxsGVynCvVpMVeuLuCwxlyoPIo9FJXEYvagYFVs6f+lREAJ5/717lngubazDJ15rpIXgAb6qserVnTLW7XqMUL6cok89MOoEdmUsYvT4Rn17i7ROW//9dLJ+SUrDA4cBONbidAIM3e71SeOoKn8vQRWKO0hV7Vy3+w0D4i2HhFd6187I3qFd+RTGzCtmejsGLVvEkRSTQwj5Gs8189iUemXTWnbok300toYKq+7lIWyzaaJDxQwWTkjIeGKbnGrebNu30pMxpXl8YcTCOF/7KYG6IGMd5bmX941dFyHQ1/KSOWwStxqcObCR4CXM1Y6mS+uDmCVwUly3r+vmBrnnU/JsgzoFYn10u/A04HUAxkjPEZNtreAsIwXV20h6wR7yLvJOLBKUnIJpqd22iSsxv+w+Yo0ZpbAmhyv2drnFxot4r0Ahaw1RDbHhk33h249kl7e5XGfxT2wo2pWh83fXPOwpok96hkrH2G2AtP03mi572/yG3HoiWU6X5ZvqJW+maeCzInIuSe43lQt64Q8Ot8e8hRAqDftXz+YKJSEona0WbX5UDWurXvzOTJ50NIxQsqp83IZHBQuciCEaSCMsuj3UlGqBD/1PvfCd2m/RL0eah4I0U3Vngl6EabRNEu/wON1li9iQaLdsA1ikE4KqFwRFixJ3yVrYh4HUgvQMXKM/lOHWXdlR7sqUr265mbtxzfC0F3GnUwtE3ZPqfg5O6O+AkojfqrLmmYeqvPPQ/1LRY5ybnpxxJUwd+4HO037i4jU7c6b6HlILQ8/mQh4DRrLIPSlrv1ZFAYXaorAgqts9KBeceZxfx7vMRbXeSZk5w4pP9H199ih2H0KIVqI3pYQvpx17j+5f5EX21+5/9RM1ftkjHwg076w466Me/iRENHzY7Emycw8oJ9mtMq3EaXmWYHv2nlkogkaVM6vFvfXMIwtiD6hfrP3Luxn0eR7gj5esbTbyQ9q5/0w8cTbJ+4j2m2+OuutKULhVw23Q4VMazsqyhpNiEJZaQ9fxS7OkSvF0SwtleLV4aDdRSBHQNxQ+DBOqpisdhCstXv8M/ZHkig9Se9aVGtrNmlxWf7uzXlE3hgYMqbbM6+hP8rhdaloKeZ7xvAKQgk0ibA+1GgtkQX06jf/BiOuTsy0GihJ7q/M5N3lB7b3NDL0DCbql9ynSZ9ii0eptpvxKxLij/xv1kOd/P0Ij2sOv5ClAGdlIYmg9cfgvbIlu28x1IhpeU83pMfSjZ45Mvh0LudjAIhenN6X7jBt9RdJBmst1amMHsG5D0JXTikR5tueGEw4AGvZdD+Ogdb51SO7oo8W2SZYsTGq/XU4z9vazOVqHukObJXkY58M+3vcm6S+1bHFHK2Yw96wenirzJyMpPXYJQHq0XAOQob6lOcDjunqCQAX+bR7FElbefwc+kHQP5UyHn9sYn+ek6elRuFHsO5Go/1mRmco370sE0TnZSVoYqVXG8XkCn74frJ1EIeN/u1azYbWzz38vOp5TS2aTtNI4BqheNIzBH77J/bWMnmFSorc7CNwf1dOw6fMq8t5UYBtVvIR3NjT72BC9YL/GJ5smyMJ2X33kmqImunf+G5BFHEy7G1teKE4eEZETaKAUmT/q575spZZZcFwUacpk1Gb6/Zkxe2BGnQn9gsKtkPIf46gZbM/HJxAeg/RZqksPjCrn8H1CtUydzlDNYY8/tVxQAfJXP2i4gn7rG7n434TA/FfiAmQiyS41dD/GMI0RB6U/Yx43+nWNoosu9Q8huUgfi/IZ2l1IRAz0ICNGo4Qkp4Gz2RDxh17VGqPUNVu4tx44KbydzvNXt1POj7Ibgzmj9a0b/iZCgqm5l5u7A4D6AIUxvMH4uiN9G0yUtHIZw/iw9ZAimwCpvwVPJCUBM2BtsKDdbwmYunS+UiRRzovZgsWCINiWNqlDpsjb4I9I2x90V6VC35YmWfFSQZxvMpvfy/ZVoQnSGb/4ncPtQ0d/hNL+/q0/zbCuf6nDB0CSUqLSVogJipyx7Y5+qdP5K4px1qF5CLAPm2YH5ESdRxY2Jm+9amzba2tQmfkGpvqk3wmz9Ja03Mao5sIa2JTIamkYqZmguX0Yg8GalLtmXYy4wZvUu/Rg+VzDL3e1FTggAaHBqw8PSkmxfSo2xsiXwrrZkC+9NxAknClRfkiNhqt/SVXVf/d0I2HtzcYU3wfmyz0iUDcWSS2IWa1FFTt1f9QZ46jnHt8b4ldgl2u9v1wRCCqBHwfT5x9ZhTdfEE9lkgZ/SBUNDp1LgjGy39uSbBOB1RfUsUUCparUfk2DvrLXExBBDiY91rv7gNDMWezokzv0fHVb8sCmNNjGd2Rq1nc3bUbJc3AqvtuJUrrviYu07UD4qkSPBEAKUHgBr5IjxedDVOsV7WH4D4TT86ZcZasHyOXEk/dngjbH+6oMKXUGRyG/MYjP+2cYlyYymmvzLb5d5aYEr57e8xSeI/gG1emaj7rMclryofUAxEX0BQOeFnBh7r9bJuNS85mo8rb+9l4p1ruYpRE/shFxPnnTq8dXoYlN7jPf4bizgWjNlgfWGJUkykDFDr9oLfMuJoT+VypBvFgICdgC7ZND7AypJ0BCfqxI+pb7b86mQmOfKVnGhDs/8ORR+hHDWd85l71wt2Pn3Gsyzb2wNHWlu3r+SkAPOSYqzy5tyKeHCNZ7v6Cow2K9MPffDsTeztOI+iYvXMjJCVOY43yT4bpOahBas4IDNeFbaYCQNAU0jsdXiBUVPGNWjwVKUlQPjHnOpEbz2Y8PzbZwpGXML4ZdZsLKvawqxCxFJfHOOXf1wOpFcrPK0A/VV1S1zSKdk4FHKtZ9ck4qKdXPOXdlit8Q7bDUiNL4fYlxKMoupuygg/LP9GPp1gWxCrqNdyKDquqY9OcTDqsVLl+RWTU3Xq+QZM4m2u1d2aoqXS90tAOf4m0pIOk1LJnPiRnrDHqNxBSVEm/oSMLU1OG3CU/Scpq8C/9ENvnNdP6yw5S4FjEaPy84payjiu05KLGN3wIH3jF03pmoRsxTcXzVTcCmagF57wkwAZR52pZG8VdpRxKfUmihgvse6uh91DWvvhAGExhzTEoNY5UecIocXTSPlM6JIBEbIAdHPXMgnyu3C+qs2bjyRCy9CBYE17tcf2Iyk2l9RcKHapviT6RBt0C1kWP+M4AEnRxhZqUibTIa5uPcZ2zajepunNTr1LtNjJMHpoE8vlLFA0NaSCNdvjXiN36d1iuGsyblDx7ayxEsFO/3st0zDN7B6EsbImb6+3g96eA7KiSimV29Jwl6X7Ru5AJnr5FR4IgRMmKUBdDo93ofTgES2gk7XwMqvTOPnVjWOyhVp1mbOuN6yPRMF8VXcEff+Q+T6n2tDKM4HJEzfuc+Gx4HkfRNYTMJPxSC7bLUG/hNzSQXuFJQN3+NDohfWbbfvdKRCrjuI4eBpen45ZOqlT34E5ZP2lOnufGNl7yiCcRJau1ap11dckc/VuvaQzQh0Bz5EMbe972dJf1lzlTOhyAOq1bSpPBPitQSN6MsFjSMEvOu2mkZhyuWgw3Ly3I2iF4JihCUqjV1gbtRY9/yhxt5YmLUDY6ye39CZW2O4AzlGbFb7tr21+Di5Le2eb33yzQpRszx9G7c4hVi7xzYSGtFsKz8bp+g2pOC08gGr8nCwDQaE1xiGhJ+o6mfZdHHg86uqHMwLReL1EdDX57mYPM8+pMDFwVUkwO0UAI+upd8AJcWuN6j/NaI5VyF+IhN68IyrIYErwNO76RpdCsbZShFfHM9rrx2iJ8Zywo+oG4oSNXZVMf2Foe0DotGvsGlrYdwkI055ZBmTZ91FTuTVzUqHnoKP10kvspfy1/dnUJVx7rd7evvYklGmp+Ee4tZhtEcrsoX80XswsbTpLkRWf4T7mf3hjM3tC3/jUiPLRovBjD8D5eGGfa3PFWR8rYbLmbkTFCmXVRsInYvstCrUgFmhrIuwgy6jOrGM5XNhnvorOP6ZbcsDB5kiJpJuT+Pk9I6U109gaLv+6tgCHG5l9vNtgrHran2Vqxa8R39uytsk3na/8waXDYURzfJ5oSb5cwfBmyNPsuNVuvdNRzjuhtfuikTCcLXmAlenq2d2Cd9sLiOXHA/wTb3Qm79RhruG3jfLPFAafD5dpfcWy9L4zzBTwi2hJfQiu51K8MAmYC0op7lnTjKfH84P7/xNxljakblIpGRD374f/Abfllj2IqHhGe5OWVEMQRGpawE4LdKIldaDjOwWFGW9H+6HluV4P/4ztHj+bP5xcdjbmejSa8tfCqCLnHLaU5o54O3HyzbqTb6yM49VZOq8sS2Q/B5SUfUUNYUbSLEhWvwGrbd8A5rHOTN+o3dxIfvHcQQUx0QF3SsotYjHaISeLR0/jbzQU9X0BSvDBEpg4jsN+53cs5HcwFB393aZJaZlNwF6O3Uela8+nQlclUoL4gTdXaJkoBjpAqOnQukIQYPgWCd/WKt0gvA7L3XsZ7wxxSHP9B1hbajFxLcgWncikuGCSp6bxXFKaE4e5/88KkA4SsXNAEC7wH/n+6IbA7D3qCBEJiONKJc3MhjLmKnH1w5bBkH+WkWRkfAZyCnAan0exA9j4DBUX7yndaLOP47pSx5LWL3y4caZErYFMvgtKx+DDSXFXJUSNOMfd7BbVtPNK+x1EXaYgvWI7VbPcNCS56FKCP0ZbIutL687yR3IxdwZ2Kh2Ny3VnVuY8msL7eHP2DGOD09z2TFVPcg5bPXDj/5LqD3yo0Ry7arIm9JEXuAsQ26mHkPbUz/AYFJZgp4/Z9ClTqjqoBwDkxObz6dKJyqBz71u/1xGiiSwjsFvZfqiprzCQ8+dRiC34HKOpdKrLQ/CkKaCkEsBtRuhcnrlGV/L4tR3D64SWttpW1vrtRDZbN1cUKVm6cAdoJTlXgSti6v+hDo5cFYKJd7jjJdnPKAuhFywy1xn1oDzurRR7G1xde4L00d0jxkNaQIfJzKH1XEznwxVhnEtTZTcU8neOw6HD2a7+EAKYuedeHWRgyskPYEFc0ZWwCcMmGPAV4ADjd55fBiNhB/8BgJtBO8R6iW9iUCvsKkNluJ8Xn3mLm/yLqG/N1X0IMq1oHHUetFfNJU6gzQ+YeMuWq0OueifhYvLlfAWJRGoLHPewtKl8BbQbmuTDDQHXaTzX5uYASAC4pBXZOqNBqAVmtrdJk8h2xCIchgUrhVOCuMOzfUxcdrkIlS3KVot8S5ZpjzWmVwEZlqSupJmoI1jD0qBv8ZHYfYQg8e/uV/WqKe7Xh0HT1KY9ptfq558b2UnQZENV88ZFYGdhdjB6vOhNSTN/QLstOkDGjKD5FwZmzYACBllPDP8L9H7gph4JrL0bjSoFPsptW3SQkH3F6vB65DOJZPmhZkHk93wjkmJR3d/XvESvYwma92C9oGP7sLoRjH5sryFBclOdGFitc39IUAqI5RzqzmOl3Rpy85CuiT6PITRPJp9Tr8CeeG4dZJwr8JY2SQlu7W6kWesDh1jSAOEmaYiBIGyqy/gXcMmOJb2o6mFpvtRAcd79/9d7ejyb+rGE019hX6n85k/l5IaQwHKN2/fl17FS75Bfh2EDLucpwIvxN7rPWaTZGol2j0BuUZAFWcwLvpwyyDryBRGypcq3kkuuUU/h1yusXwPFefRXWLJ5c7OAx9B3Zsz+XrwkqGPU3nu/xwfePIvYfed6lAyXfZyV8lf9BuYdyBMfulyvIBhpHEWqsk54b720L43f8jNPf6tSwkZaSDhrDRDc3+D3U19Se2d+ahAvaVDwp7yKL17MipF7oClu7/IP7Gxrdnyj3W034FWzUs/d7+1dYPYFFSsUeaq+g8eKpIP1G5JiIdFNs5tvqwlLqff/2On97L1udErVc1W3uxx47/SzoObO9xUglbXU30h4D3HS9/gPtiVYvBGQhFPnsQXad30wUNUTtJMp1osF6zp7TDh1I86nZwRE9Rl/QvrLlNKhs/XfuW5nFyx/SB5MY8bBnsB0SskghZXCgg1SVa7ey36Xe/MeJFGCdxSpfsdls7XPRTKfNjaRTvEMzvY86V2HZAKdJBCSi5bTM+mf+akY91jqQxFMAQksuGxo9Vjhq9maVu/jq7IkmmalQ/r6IIrih0+PlTuZcabYoWitV9gLK2PRHp94get/0gIB7rR6d1wSWxveF+gdljtYx1TX9UosrPpyJOUqw+tak4uadZnssDiZ2czTixOT871dXCGwP8960w7X6vg25JOY394ynHKpUYSOrz9jHXFnVd3EH3sBF5+D9g3rfTZHUquARmocAt8GxQ/jx8o2TtSKEMn/L4M4EauhvC7kvs0jf291TEUPWnphgPMkp0CJ7/fKjwuRX71i3YXo2k6MZuSuXRXxRFPYTdLitkxX6XFRgTPy754qnEv0r7PBhDAgaTIxLjuSfaz+NzuTJuGNolB+/uPbbMTZM+JGmalVRbwBTYNV7ucmYO0yTc8uwscND5OQkPK40ZP1jtBbRFgVYiEwbU4f/j3aLOQZnC+xhEyx1lhR+Fzrh2M9j0F1usvJzlyu6lzRwZ7EtPT+AHe/+lHOHhVkScvc+FgA+R63lGQOuVNqqOi5mXjSpZSbykjewZZqBl8qYFtCGEdH6nzxGzddMwtacvm8b8FlXKYn5+2n4J0qBrZ3zRdOhhN4xp1MZdQV1UvgfBFTU8u4rtP23UVQuuz6FS+KVBizxPnbCwrIDqpojMMUSenePJqBCcyROtc7yJlAZ/13QAP1jp9RXnbNbEHXo04vQ1PlkmnfrEyZE1/7qmX9yK5j7C0YmSi6Z1sXGTaiEhtf5fo1DmlcQforJmaaBENLBjR15Jp5vrUUD0310a4QcE5CazKmjEb9jzSXYMrIZi4lHxUkzzt3wLr91fcu+tXpdGTNEytrZFQ/kmGvN0oa/SuW1adRIjQK1gkUCz7qtXa3YwTbxOAkYmaPkqPYAtalB0MjeaScm/U6EPQ31zSms9toeaFxiGiPYa4fksufm4PS1jflK7vtc3DFSRjRJJaO11uvOvvYB5mYg7ip4AMyh7c4cZz7/icdNFUjXwZFs2CGanHte3BHd1WlTD35bG75QlLIjpCtMSQwPEkkdpmdCkP4AShdjzCYu6A7ilk1fIVhC7bf1galu71Gp1aTYn5sWZYNb5Q8MC+uUp69s2HeCPYGdPwzi29AYTZKJsI+P2myFB3bhYHgyVCbKYAQ+ulsj+9eM56Ymeca2bS9NVIViYsWOcnInBmK4qRbXJ6wKwvARCo5zCBusOUK0lN6l3x5qYjk1NhoL3JbW/SdA65rY09u0sweKuh2+LhooB8Rn2/7QWeIYUsPfr5aEefFwqcmbai/DDs7nAhjJHbQH+APQOcuKx9Fyw5Uz4vnmMNmjLWDqhHmuaZHOEwJDmdtZ6MPAswlPN2xbX+2IPXmitqHmMWGbGGR92QMLUj7O5j2jKUS7nTYD/aKB5n1kbvM+rN01fu6tkgcieBngZgxb91Yy3Xx+6kof6ewQUb8hhaSoQ2foBGvKWnHZhXwM9tMCO5tf4PH5tjFJBsdtITzYTrVq0HO0mefbwT0jn/OouOL7CMxBCYc6aLbmLF4HwIbfybGvWUEpdchiDO+vL+ifhbat8ivMMAdVfol+cQBj8ti8f97MyGi3zIuSEiNHAsf9NWili/o1Qkr0ulrylkVq0JgxcLlZA1a0UPEBqhw/ftbIna3K07Oio+efpfw1yD1x+N0HLHtZ1mKMdZtOhZha4YgglF/khCIEhcXIYoq/q4iPuy8PJEoqqZzVrzKHlsHnkRoxiFbbpk+TjkRxmSR4Sqb5Ywm4TtwcWWLiW622U9/dT+yy3e1mylpwhWIkTbjRigKE00raDsLLq2KLdOo5YX9OVca+fzrkI1V14jewVNb0mBI7VOUR+sa1f8jJWvHa6k1pzbkK9UdrGYxmBxbncW3i6nqH0PpL+lPqCO0arWe9MqKPzIq9zw/Xc5TwxyyMEL7oGZdv8IJ5GriD+scrVTwlxqltDB1AyfiyX3StVBYBDL+ZWyQg9Z0v1lqhWQmpNFkHp34vmA4WtGSmFmJPk50CeIzLemtgoP2gauc8LcXxdd5GNSxCByI6tEEVov0sg/ewAEQ1wXvlmTFo++Z8D36aulE/w6L4w17W/LE4IN50TU8NTydmOWYTtqR2/wOSWtuExzgY6ByfouZbtsCb9pfXKUUsjGYJn960Tfx8rRoTlAMG0VAd72zQL9Vifw9NpTda3qgSsQULKPVwebbB9O8mlSrdO5ULmfhwZiODoVw25ZKrWyM3cboPdQov8SSag0iYCYKjOvX3Kv08MbcjT2MkQ/7cScVeSzruHLyUBgVAyVNZQhFSBozqBo+opuQ8potUj4cDSNhy/FTGl6miwStfrRoXFnMDYW9Wc2zz9RMTvsP+xOEzn14ZBNeL2xt9kj1qn+MPZXiKfF6WSuYaP49875SiUtnkY7O11ZV/PSfNg5Zp+WaogsszR/UpBcCsZXsANL7EF4n+6/ldr0SLqcpWFOZUhXxQVExduEK+wZb7c6sI28h8tGEPnzobP/J1lo8JmIPdngWSYl6YZ8xIJW7W81dJP24avPUOsm708PtUpPDv1htVkHK2UwI9IXV0qfIAYJVcJY5EATYtK9oFUWj6pRr47875eh4gzQQDPDgpu/uC8W03RedAgLouz8tTaayerAheqipIfPU4v0exZaPJIJr4oZVXuJUwGdvMj+8V2Sc4ua2IrwqGtL9WNNxK1SKbFBY5QhxJCHzFXaRnO37dLSfBRN5uJLlk4cnEWfHx9CtKUei3Y8+7IypVVsuh5Vbf8tjSad4vY+2/3rUaYllrwlx2zIubQGh3hWwImk6jHsMmlcW2p1kP/SgNIbEUeWXBXH/MBhi4lbjRSaq7ujG08KaXshEtjRDY6MP+SQfPdfHcZALOL58LC1trlJ92bfTgU7nuDyKSUoF4hEWIaxLW5WIQc48fa4rbRSBzMpWjtxbLH6WremXqHh7vWjxV+vBwmyhkkb662k1FCcfi5PpMN3p565UeoBHdUGWX/lX5TAnN5F2sDbD3JuLca7tR2f+Oo3mUj592g9hruSiicHvc+PPcF9Hca7VbQ2SBvoTIZ9Cm1SKI6rvGrGBIyhEG9zoRi2haJ7r0o2rlhaQ2ACglsrVt+yRPaWES4PiVjMMca5cme8KwQ+9LxrePKYg+mCS+g5728hxGwtBXr7Xynb7VfN31LWsASc1mX+QxYUSYE7t8yj9binm2ObK8H7AHdV8PUqQdZNI+DctZOjbLFBS7mp3au05Cxg2drwiCDfXCFFExWHB46inCB87rDor0H3DKqPwX3g97KvlkUJb/YthVzhsTKSe72QJvhPXhWfUA16ECfkogMxvoS5kYkPwSa3NQceKxHcXQJAidsqQi1kYr9+L89QJ22obmT8Z6lFTpx25NOWWT/OI4pxzBQhcN6K3h2Z9AolIxDKGWydCFetH7UF4e3XL+SaobaBm1pGVq6trQG5Rw0+Rn2cjuLOAT8/7b/X4SfyTRWDegLGxtMmwDbkB7eB8n4PTaVFoKxZtVdOKlZ2iPUQpSyv2f4+gUmKDUKcowJUgpv1Ina99Q0JPXfVDmmMkDF+6EeAlcLdFT5HjWP9K9hOxvYdVU0FsdHZUkkkq9nJjiPoGrangIqlEhfPpM6jsQUNuvQ7jcfh7pXI+1TNUpzVBHDKKL0OJ4MCEXXPN/TaNxE4r+ZLHMPne6UXGCg9aFBzcjBdV9NlD4+gy0UIXzG871BZFA32SuUXqXoO5Xj22GfOc3M4LcOgRDukWLVvnqtbt/YTpHwKr36SIb3nOUr8t3HiOUpz77GXjHBMDsPnxj2FsYkPVIN719bDtUN2cGHKvT7EGdzphc49XRVTv2iNkEQ+YVyEMz2Yi3g8DL9uAlmR/7bMv8Nz36CechIvf79vPXZaRRlhgcYxIbhaOLHpyCqWdbV8LAfrSAqLLKbvSJ/EB5nAz+Torvu7zB1vnr89nCDQFfKURa334QBIJegOtnR16TfugiAuA7gVrT6qn/nZ/N3ZRM+okSn98E16C/sgWr141OYkw8XSZKt2wPaO47p/x2NuGTKKMRsyPtfFNDjjO+nxZTYcCVLVXn7zev+adegnOeR8jVKeVsIlStQbhoXmCS8HABCCX32kjEGjDReSypXdM72oB+u7i74rGZkJzC+0dSaB9mDUWbQnAw+D/FNZY0wV7M0kkYUeHYGpibukuInq5gHO+CptvlrK5gsbX7QxT1lnoX31n6pOMN+//FhH4K0XAMdDzQr3X1QgQNhKv5iTD0L9Yrw5kg7cziqD8ROXXNpmTUH0ndxot8sH+F3rSUvPxZXZSxBzQy1MDBLC4yJk+IV3znlm513fkXmNzlx5Hks62cK3hxYqfmHpxPasecivkcwEtUji1JVHVjKlCe9ocbJAjFGdRaw5eOkUGKO054RqUftuL/yD9IAtYz6CDUceJKzxCzgpV/DtftxHkcU9smAnTeUteSWkA4/ZAjdMYqGYylhG+6RuTgjHFO9QT+P3Vz0F1x9YWL0helUX+43ivF9SWY3JVRZ8b+1ci5MrNdXkCkINUZnD7m1NXb42Pn0SnhkV4kbB/rxVtt8Jk9Zy91T2aud6QBfRIpNuysmr2p8lJ/RPl4+RN3BLBBfWFPyswQ5PhAA38amjPwdZSPWssnBR4QQOjtm/S/OcF8K951hIfJTWCWns2NZ4kaLU4yDizyX1ILCiEhHZME2NEDZRB9QVblCAZoGIhZHFw/9gEKsYkrdHcqC1uPT/V5IY6RnZXEEMi9G/tWA6opIJeNwEepPEud/jYYtctAEpXdLPt5kGeAlfgfxu5tNOLU8lqjV2lUu5f5wKJMImIiFn4mvphzC5YPuf/gznGIOZk3pc2MzbAeYJmbiEf1wepzkd47caoQhKQYOb5Ahzg2A60V+0OYPGhaVOHjjlbRiwK9DacdxXd4B16TgWFbBE0xkIxgP1vIxn3eyJJyTdyN7Ykz/GLcWHb0cfPmcHAsNxJgC7QB/5DteurvTkbgPCRXh0hDZlrJ9YOcAUU+TeWl5MlDeQ3Ipx8v1eZFyDS+42iqx7M4yNIQuPmrw6eFqL8Md1JACMiEfPT9Gt405qGw53zT1O6fl5g35mddU78GL2KWXQPg48V/dzV9hzajhhQsZDY8wG3cFb46o4FK3RbP+MVOaejrlW4QKt6vNR0UmdnOKv+jIzJXX8tfsoR1tFR+qKDXVO2sbn61XU0BXD45dRglfiwcqdZnQKO36gk74LXu4RdZgpmyjUJ0/55tPDX5diR5XHKhbdP86GDckEziI20ll1Jdt/uHjnh0uwI/tlEP4YmRas9PhCEfRfTR2+vTpB4g40viFoi59ijX3zlPVRh5qYue6e+4jdvNWNBSlUJRq6404gDJyjVzXs3z1mMbrpOrjSA7BwJylK3KnFTzOMlmaRE2CGnB0UxvQeq70ApAwhbclvytRr9OyYDGnYn3cgNzdOMCmDX3SvarN19i7FThQKr7VnPfw0XqDY4WkqCfDaW9lGRlZ0zmkH33WJ+L+BAX13LeNpmM1BXYRJ0Mcf21lBr6PfEK77vZ10HVHGVUoXMOtKer397F/H5/Ymj295AfOMpLJBgo+ynHdC4DsJod4nm8DWfM010OFM+H/UuzoR/m3hOmP/9dVPf5hJdwq0r9a6PFh9uRmpC1XrY9Cne1/TMtIp/pyUZVb+MHHVepEMTI43xsdsSMe8wU+1M0QLBNKfY76HvYg7INQTBeHXSIV2T/lZ5uF2wL9MDN58jrNXjA1TdTL2+GfRqtRXZL5cf9AlVq3iFtwCuZSwNRAZMDB64rc3C0gvTMh9GjDYdlQSyS3JRqwFstavY2JLVYbSVpbx3nHRUH/Fpn9wIzrWxbf3tYL2lrLVMekA/OOsdbEqElI3Ue+97Fw/7pDhDoPskZdKYuNupD5BI2Vz+T+CQAznbNp95UwmALw5ku8WHGEVwJ+ReC+/OZLeIsQbFqziEVKPU+0mcsAu3YhKAGVNWLMk4f5gHnV668qu6WWLhDih/HeQ/3xpa0eFl9ybFECUyZ6RORJTs+5imv8L6+9gHEsxI7ksZQidR/k3q6zPofz4K75c+CayAo3bnd9/KfuwUkiB2ITBinObayQnFmrrnAezBk1DqrhjozNiWPTD+dX1Cw8BLmKgzHvqSd7jMSH9C4gr7b5VW7/369atl6fJgg5/mjg0j1uvg/JzREw9QGIaUC4xyuWyAFW5/ls+v92j499xX5dTHdnKC+5wXsS4Cne9pEU4ThYTjUtpnOMTGFTqDKUXDFGkseVTvvKLphoqFKTPuHi2PWbaE+6fn7L126m+xZH5jn3zOFg4v+WS9fobq4b5L5lwjm9psYTenUGVRcRTw9Ig1cBNTSu+4FHqGcWG2wEznysI2qoozwTe45ZKPlBrlXtGcAvVLpqfzVdWrQhiHMEvdhvAaMOj0YJDgtUALOyd2M87O8bU7sDtgigN66ACcn1SXDQnH9eVE/m8FWVlf32b8SO9c5WNUyIiFZKJgWsXTO9KwaUecSSO5KoqJRErm5I327dtfavvF4OnnbD81HQkJaOTW+txQRFwF+2UXoJy0/RuQU8rqUj2YZOV+VYRBBKqdCREA848AFOzCXgVBex2ZybMu9O/9wl6s+F/yb1gu5yPnqhjWLtLoXbBQmAha2bp6oHhfRe+zNAvrOi8F/rbxmqPxR/cUs5hJUYWZen6VfIrcpnZjTpU8FOnBiFiPxncbX+hs3iW2YbkoGoXUlLn92MLWrJSMl5BDCcR+I2B6InXNDM8aDRysjUhrvWULs4F0A2tZcgRIFCd7/rulPVe4ezAoLWHbludhsZDm/3zjBqETl44vP5GT+iY2vMVUu1z8stS/66bs8/us4/il2n+RWRRrNPurjmyWJdq9qvVbxbzIge6LUFH6cHFmRzDn9YMhuwicCG3RxxHLEllB159PUPec+WTpEKdzWHsnW5ln3IZ0jgki/ElWxcv7PsrKByrIgTTn6wDLlob/6l6tdCukiiMFdkcT0vXTDr6FjRV3hPuu/lVG+mKnXOLeZJsPbx8m3djLHRBEmOmT4TtkXFOUBm85D32zior8KT8tkWpOU/yUnnPEL1l6jOglanDk7w8Sf0mcglMJN3aLHIOp8umnC7LZxDmLl3WhXZ2SJKja5Yt2X1x9iDefSxuIcBTVIWCkyVosZg25h/H5yS02H1xyb4CIJvXCvMV6jEOlimS3hIfx4fjN2LrkxGBTeLcv2dVJ2iKzBaZ4VNIrJy02N0aLsJIlgivvDz3PUxdkcycAD82/CMGh1KL8i1D3nj7KEUb67dAbvvm4NmQSfPKepehkINIvpG6iPTRMN/HHnntyU2rAwrUeSi1TT7nGLggCD9bxVYVxmJQxQvfHfl0CLWMjGvllD+Y2YudwR2be+cjczLQjsdwCDqdatwrjCQskilhDZ+MhpTj41XrytevuihspSeuMEOY6blvMgqJF86r6f2MPaBSEW1yjCxY7ZOrd7F2umoEViI+ypa7Er6zn26sZnpqZxIB0nQEElMXHjuIqLhmcAV1Bg6u+qqoas6zjug24u5Wv56xq3La5b+3Ta7n2ThL+lFNoqy/yKgUg9xJ1ocKiJPP+kkQA3zZ0nBhlR7sXG0hShBQF7Wns0u4z4gsE55ecuL0lSmhMdn9gfX2zk6K0hTW+f1UQmpPRBSseFNAusm8DmlmNYVpl+GMk3H0RRLlfqpQaVs9ANrWX4MUh9K6DGs371H8szK0EEXP9KjvUk9YzR6uPBfTVQVRpg55NRnBW/vh72sUmUzwyMzCIUqu0SN4zfKN83wSAjilPe+5uozxu0/fRuyZSqaDTB9wob+4iDWM1E7lZO9YU2+Id4TzWeoVH8u2wavXaLDShwFssnWHtevcTmjdq0kaQAvcJ0NGHPZ2cJ2p5V5xQ23M4h8CSevkO3Fx+FnYuwo+Rsa0TlNH11UCMsVsf2W0lKv54nqaAMIz2dbRb1puPSxDjkpD80uy8JcoGkHeStP0sHXHjx3/d3APn46K/oVnc69QI/eFc5WMIiN2OKCetgQWBRLyZVqaMuMvk2DKxVdrRi6G9x2nh9Rwqs7pk+VqJrEYpFJklIdKTgtd/WvCm7jlfeZKY9vzsnHsPmtfFyLxS55pYqqWtqcD9se2RHDk9rUJKuqUkJSMTwLzorf9c3vZ6fIt9aX2TkcjLXaCX7VzDbdOAyEIcACKBifuxFJZkI1B0yKCew+W6eQ5ELxfH4jvPOdHhX83CTIQM9TbFbuv2KGLjAAP7990R/UppvDlelx3wL+xeoAoXlqzACXh2U/XMeEDeJJHja9wQCaqscoe42Gq4cpVDtKEbqg940rPKeaz9196EarXRr89UpbUWuM/x87Dd2K5KWWkk40+oRmlnNGrjAb0XAkqJ8ZCrFErMDTRTI3PxlYi+3xCMppzhoYF3x4NETPTzOTmpfrUIfdXWGe205DQzGWmhWQ/Ws13+vd5RHvml+gkua2yWznF4VNmDJWhwGiomvxqTLaadYkf/GX9kCGHAJa2GHEsqhTgD5xKEt+bpI5q7sBVPaS3Q1g1RMi3t9Nr8ILjlPLKfEPVAbGiKskAzkYjlWsSrcQzi8vQhXenuQ7gqv7DRJdNFCKF965Cjc9f4mSqMe3rLr4l8+Xh1NxY3aCylyN8ugLe/m2LkaR4HQQGK6jxAcUtRkeYurSAdl5iIbetQ4hTpMnHrWNgZ/cjLBbhPytbwU16X5tXa1rWkAo8OuqQ3eq9EPQQ6MDJ0BdV7xv/Muc+Pani+sEVCIGEjtwiUK7MJc96W+33K+pOPo4xzE0BP+iv2m3MBFu3dN7PDJdQgKTMjyLQ9XsQ4JEj22gRmi0jCnQi1UUiOpAx5uqbYDqkKHrIjXNdHRef9ne3CYazay1rf6sNtpxT5sOLtpNQ9Te8Qf/uyInjSBRYkfT/IXat5+aaIDsntPtcJGWutLrEO8E0zEyyeqWNran3/aFP1mgsGSTK3Veu3AiUIEwLf0Ziz1zBdZb0r23k29RPm34JebB7NDx15U8YD9m3n7+BV290GxCOlPZr9eUwd+fFma9gBtu2eoFL/0L/SS4qyuSHcwGuUa7FIsdbO7lcJXTHFrVEJr8d5N3DZw+lm6Sa4Zmv02I6u3Qe5RIlZ/kRGTY//2XWTvBXslJgVdCBaFFWo4EZDa/9Z/AyCV0H1DinxzgSBciLC1e/oVdgot4h45SClzzm0h8S9RpxQ1ZqBb+zeOVILDvFdXxlTLMcX1dETFBehJc3grVq4blisqWorsHJph5OK9W/UtF7yNAz2A7fvPxjFH+D8n8BUaBrwAuvgZonbbmz8k0upcrpxYwzFBkoq/ZSNLpon2ey7G8IxCPQeJi+EpSK/cv3ZF/i9w6MbsSrpIsNSiOTzgWrK/N+nYYf4Z7rCTp8olnnYnWk9OA1XjFD3xGlMZmqfFd+ZFqItOsT610W/ofblO/GsQeYzOkOLKYSfQ9fFvFzw2DKTeuhyoJzaI5DfV39WeWVAHjYuhbKcdjuqnn4XHcxSDdWj2EXfSbEfbg0qDTYkmxCTmaAEtq+qUNYA25nuQt21dUEyAJJ5rkZgjBzUr1Q089FGBUuSbtdx63sMZ6OstjS4ZuH/qYgjuuCksIHLN3HKo7kDV51qa2JN2V+Z1VSzLZL6ZKXOnUyLnoCXSbxWgq4IOmDiWlYWDVKh5mIxY4zod6JdFXiXjU9yCNk3XqDpaHlGRFZmf0TyluCjF0BVvPX0NTqOcRISeN5E20Y3yv4YzaJsF5a+dD2gJ9u5ohzgm1V2HwT+OG/WDJnN/kaMmxk3fQ0oVTRKGSsYVq5qFiKNaZDylVf5umkmVxrG/kl+O938z4Ug8XDS3uSbP6KChndQdvy9LUb2K5mAwk+YZ4kVmO0Fvjk/M3ICsHKJL5b8CtOMRPqGTF7fll6u9tnbrvpE4kozsC513i32Vuwz8a4phYcVLaomipY8SvZp9zxIPEwJQWePeRPo3BnMjziEnzvGIzhQZyHYUxN0D0On3PRlEzhkMkD2ThOtR6Iz9gasZPA/ZJ30an6GyR544u6mDEM3Iyr3yk2mmFcM8Ze5wOYAUmL6AxY/EgOmds6707J5wlcMRfq6BLay8jt5hTx9u3ithlmGlyZx5mz9xBEGrv+TuyA0LEUrl/++4pZPt7WuHhX0KUq+RcnhCJNcsMTESMOdpg0rGUJfTXi2XeAZVYzyHjm27hYZH+8/rJxPf+ho8a/sjkrBlN8U7Y5mY2aY4hW/kB/3wzMCytbUEkVt3m2HaLTac9SonOcVNo1qdF06E/yyirM88QndyZlXEhXZhd7oLuNZSZJIEJioWiZV3CVSKubDBDtY9aTa+XERMLhS4m+SPl1743HIpj6w1vwq2LVzU4KwbdkPaMgsAnfCHs2ZmiyYZNpNdHU3L4u8TobCJq0EHfBXHxQcuC7TDG/1HMgSlWYaSuOVNIw4aFJohFf99B+79OKPBnH3x8q/cDR1Khr/4OJe6BYp+UbJ6FpoVeh3q42Y6beJxk88s0fvK++G+5EwhhfLX4GaZNlIFHIQrlgOWrXcD8PDu6AoN5oGCET+cFhIbItclMQUNtb+cbx37ZWvTRkN4dCnovicqMdDNgv+1Et3Uyv9ykDtDw0fGIcXeez0r6Vxg0/MGOX4i95yZ1EbB1FYOaScn/NoyGPbTg40MASW/98V7uPspzTIHRxkeP1sqZHm1Fq5XmZmIngVhc5kMBZiEi/cSFz0R/lFzb6dJpOUXMbRgeRJz1MhW29b755qJXhxnnuZWAs5CGe6/F0R3CmUF6uOfOzvIp2WhSJUlgNf2Av0eIkyRTvvcD195SQLQeuNUhMgWGX7xqJqBUSlpiK+j4/B3qF9lyozPedkaxHaaggTHpHCUhkDzJOMdW24Y6YJgkx14576Ds9a2VGsVrHm+GScDmk/ajqNdHMgwSOOoEMFlKVjCDuKkF9YWl9LDoEmFvWWmA0CEdZVocA3vLHh1jXizYFnBcuCCbqYSaDDtd/YHtEaAnOzJnpITYBCJ31IgevGle1lVfM8/qA2USZRIlR27TdLsedoxWdPd8e7aLDB5Hcrb2bDc8tXvxhG4mpTB2HzaqL45eru5xgrVAn4a3W8hoXmV5nBU+0ikRLx7T9uRA0QwU3RE8Sg5frWxEFl1g4pv/tguF/bJ0O6Nqhns3SovuF4nZpIKdvZZ7l03tNfaytanjpiQrb7sN2vRAO0eNDWg5f9oP2fRSkYTaAIFUxc/bkYWigyWMKj3qTzS2tB0UyAsZZO7KzaaT3QxjfTy6ltbZt8U3ut1hFAlAJFMu5yEWrGcY4CdnSJjpWdMw2Cn5J7/q580d6gZLDiQyavezbAnqM4GBKdkmU2O57YUeBOwWK7eT2CXE/C/5KdT1ezRpZskCbhmKo4+d1CPmZ8q44eJjxTUARkPIBkNBQ13f/VlbcoFPyMXzuFy8N+YdI6bXDpTM3lr0qOdQCOwJdbMGu6xqUKRxNQGP2vw9nq+hjaowy94MEuF2+d8fZqjNJmOTmD6h0plmmkZT9BxXQ/xKKUGSG5CI+uk2S0R8Joy5E7czzYdJJA49igPKjAcT4q6oNbJ18DbkZYwZ7/aFz4ho42aP6XNrw7GoulX61kH6+FWOj+BYoD7To4yy4rQ5080d2KQhbpI9Kk1Qv1a4LXwLQSyKWIHUHP5pJN3z73+he+pyxgKipypdrhrNg672u8T9vxI8louWrATmy92uPnLyTKQQDMdw70PWj46NzNvrkD9y9ItnbqaJfxC+fDmFZw/46w5Uwkjfkj+LPEXYds2oBeBj/V+ejlGGT92ORUTnAvgoGz1RRQrBsZ2/3MHJ4t0148CvId7Gx+Q5DulQvGielWiNW3dFpcA8KxRHXaxmk+VYFqRJA0QiMoxxnCywoa4gZ13dt8k4Jcic2OOFRdSMipC8uj4W3YXm/SJisze0RNVbmMAyCYlR8hXqyyvPjZPjf4qwmkawKdrasC2gHQKFihyvrIi/WJY5SEemo93jax5XMxcqXZ8EoiNRVF0qUUp33LreF1OaKU7gLwCwLj3++zGeEIkm0Evg8lbqs1s/NSvFSsj8IbVEOulhelOLhSZW47pznyXHwIk9fmrb3m9JyWdCNQ1qb25SrXET73COp27HFApMy8RTJuCqB+HocAyBMhSSEVdv9gfsMRSeus/U8OKq+ROEcuaMh2pT/N4Dm6G/35eO743+xFiMlavGLU3v4jBXhF+21DXSDAKQFfNw+T0osSiMiU1uFmDr6Ce2wtBlbaQKPwPwzNaMejYQr6AgBJAY31IpghntvsaokmVYNpvC9duWOlMv5CL0RD3muw0Qf2dhSwW7T2O6bMH4THIRXSvcP49MHv5Flr2hMMjj4IeYHM1JMIKNonxRUYWYM+Aus2yf3TuNXFdEZC/3kr5Y3daJO6pownTulUMWOZjyt/BcHuXwZqn9yaWokSXiT0FIBMiQDhBQJN22ctht6kD3jA/Chu+zLKVp1YS421vltyqVaczM+OlblDb6J5NcegpXmmZdC4kSRqkbxu3Ux4Gn05pxp4m/901km9rLN0NA4QzDttn6wjE3FY1cmxP03IyVV9G4drSssq7KcWZ5znV2DeLVkf+SgHe6sjYnNQj9j0CFpZXT0vnR2706F+7Rh2HTNrjrPLlcE4+0va5CSIMQRMssqacOXIT2zGlf45KJdmaxulFu4wFl4NinOkcson03+3o5ERQCuxNMtQ32eZnEzf5Efe31lQ8KlPpPM98mWABnFFtsonf2wyR5mEdfzMIDReo6he8XQRneNdI+Nw4Fyl3veovR3ysBhghB8z+7HP78QhPqxIcerb31WVBXlUXgPVg9+D2TB41anUfgv+7iZL3W2HTu/zZe8YLMFQ9n9F83dpxzYZGoc8q+tkp6Uu0y3sT5Jq4Xn6RrBVJSJBoQOMwrK6YjsR0gHRqUoyismjLxQLK7+JL7sK0JnldpdNBICo5kwYjEfceVuCGRvtMpnBKxQf9Bny1eEZ6BiLVviFCyOKJN6X4DqGWsfeSVIRwjO0eIqlUB/hDpvYVaK7KCFzDm9RfUcRbON+UDMQEDOlCHUw7e4ks5AAVb3bpTNoV9DgbJXV3EThJrqEHSD4P1oPUEZzK0c8fmA+z6u3Knwh4D2bdFDQ6ZjzmJGRjcSEt1nPNGJxYKSG7s0QS7vZf1WY3scau9Fz+lXtl8fRCV60dhxVU8p05ok/Zq1q/bSI1DOIsvuKptQoobCirt7mxj3+8J0XkikX+0YIu1ybunpUK/5cHZmfwZXfUxsiPbb2qzo1yTRcbVaDmdFdMAnNzX3X25h8a63LFJqqqnd8ntMHxu/mbNZxFI607nGUZCP5jlx2g0iGc/pbzxQ2BQfGRty2QXQuDM8YjMa8dsTv0zf4MSs0s3vW7GDcJcW8EFqLLDnujqTdbMKzNp8TD8Ca2d4BtJUGdl685SzS2vNt7ZELgQ1IemE0q3fdd5iQwDiulX5Q7fuG157DsuGpeJC17svDsz4zN0EHMVynu5mWsYuAqyXTBvXwKBHHTxssYsIY2uKapDih5YSj7Lk8AKrvv3Mz5Vovj7cQhJ2TaTqhHrTBZ50vDd6C5sUfhppP4Txef3r0qfw2uyQwOcw/W1rm3Q2C7piP7NsfzjpkfsnSgNgYMUQqW4T6ti3bMQRquo8rAC2V4d1qRF0TFmL1KheCxZnW/u6k+vbarBP8Des34idkvMNt4GdSbxwrJOeQf7fKVZN6OUZUVf2jR+nv1BTUjUH5kFtzYDOVoa/hhqUPg3E9SzsQfa4G9ZpbpCtY55EjScNp7big7pKGgUrlhHIprfRImfg96+zee7vQdUcmRbGpa3kDbLaWolw4cwZIi3R76qc3kyQ+rD5PhwCfIDMIdx/c33oFtErAhrCnAxp6ns2E/MdmbZU/aponlT+IHFSlrCxxvkiBSLKwbqsKc4kM1X0Eji61X6RbXFk/lsrYhundE6XKQ3bOhyBFt7gH8ak5Ynjb1VL1M3kHPHqxFsTF8NpKPXh6IesQlWEoBVp8izcXf/S3K09hYYSiNT0hOTKKBmR285n+H/veDuAXPPMk8w7Qy/Bv41wP6GrusOjg7K9VMF5gu3YDHabRSPP1yTS/1PPJNV8uzNtCjrB964aLTTFcxZ3nMYMhaYtpamYbtkPhnDML8N/douwDCQ9IzUQJKAuGPzsv7C5n3eAQhf4ZNnDA5o8jkZj44l6KFJ9ZJd1HejiDzh1Cm/M6EavBo4ToTLufYpZLPyjsMWDFd6j8Y3LV9P37/yHjT+kTLQflmA0fsvijCxk1JHqkW1kz04InNkMiJBEMih5Tkj8BEN2rdrTl2V2gKnSCqam99A5w0DGPT8RO/muLa2jUT9Edj3h7QxnLKa9cz5xsFFa+kzhL4WktnQx3vEYdCqFrTdZPeRRdnqMR/o2q/RIMahZuGd0TBX4sBoKvbjjMNl1VquxP2oPED85IEFNC8r+wBZ4F8mHaFqbCFxuAfF9YxUpLXyXkb2UpMcH/vemH9umyPZksnQ/r9bsT6NmNVqe50gMCGorDQDT4xON4GTU/HvXrymcydqK2ZErfJZV5Y/vkHaEAsAPg4M6ff7qN+MmDIGIkZQBnL2WYnGoBdNlM34WWF+7aq0yJB1AEl09kFji2TNVOitNZrTTydYyrjEFY2c/U8vNRAhumxn7DYPvwNi3R5TNvvTOi2mWu97Xm6UCoh/QUo33qfgsL/dYHVNrXnE01gHkQDCht6qMM9lJCaevGH4wLteLQvf+CIQYyeP/Em6dNnWYj3PTXjPbN9GJ5ID8MBIGlzTjKn2l0Dlg4mBQwtKwC5jip2kPACSzBq9vf27p/yID9bpP+nxPBbpd+zAtMS1+DpUE9ZXjTGw0nZGSJe2VfB0u+wy5eHRVecMkUn4kFd5FuOnTGZRxN9b8yguPwT6LK/C/9Zt6kgpN2eNgr+xadrGZzsVgyFfHpjaH4gvG/JXmAAc6NiLiL04u//bKgy1Y1TUykWnaWWQtNMj79anmszOOTKMujZbENkUQ2Kjg7Z2sGcg4cYS+ouXhWgkrJD2SfpH9VBCd4OprY/kJdDf5eccQg54zZAxK5IySPbSRwHhUhv4CJjJoN2Y6hJAlF8xNoTNAMEH9cul9agKY/LmDleLHgmFdModbVAGmXthB7DhphfL70nqsB8eDP1ulsHmwbvfT4FoGo8l4QqoyRLEEMTDW+imZRaQWyp8QU8BWtMuE7mm5rbmao+Libey7m1je2ApqmIS0TcYdYcyNmVf+sBrMfXhJYx/IW0ITjSxQ+Zg9gcnC11KOETRRZtjGzsoT79fFPkwPYljI/Rh+Igv/Wl2fH7R9EfEi7HJKCmS3oKurUUGkIwWG88w6plEhlBhOg9UiVJxMlj7EUJEFGgg9Bc6puPT0G1mF1l72ZHDKs8yxZohxRw2/PUn+nzRPmzgrGboSD5Qp1g6mrAgkH7cA0i+TPJRUB0qsO+8PWMzK0RBbtVEFvk8dt7wY76uvBOYaDT/Jt6UOBjgsFDS8vG5ELjuns5iU0XXYMYhbdO59+fpe6tVg6myp9xTQWQgxgQPPqLuFk61Bp76syXf7g5ASpZJJtYr+aKne265hGHlh61GBLNGs3Pl8TRe2FkvO1OypbTLLJfNXv8GUkgVyu2apChfrwOBjsYXtgX2UASVK7vOEmpmi8EsrOOuUytICZhNSO51z7KVDE+VzwdIagS9Z30KSNdN5HQm7yD+HTGGGZ9nhtvfzeKqQa1Ai5QOlgJQqPPaHoWVpyWZ8n3aAj8FyCPe1BSmZdbq6x8N/MDiKQKm6O85p4qBQY+7ymsF/6daEkyEj42EWl9G0FeIwBmMu9R88fQIjwJ75U/RCnV4Asb2zrBb+zntxxjHUFqKqVIQf3wlHvOSlDyPQHsra57q2HJEzo5Fv5593e47hCvM3vFB96xtUZA3PDg8y/ypWWBZTfZevR3w0lC/rHT2QJAx5DjEbbrbluLulkXnO+rEshqJBWZFu5uofKMhSOTVHpTBTseH3piDXOoNWGrXVcC7kxawjojW/anIliNBlXmu6TaZ/bDaptpWYvjuVN8tKiW3L48XDY+wWbZipC9wr1Qdg9OHDOfaB97p5vTXquPNAlB3iLeAWpFa4qkKpI/nKLCy3Q3j9V+JqXftdHHQxYFK7wGS2mxamrA0/1BLMpIvlW6FmwqM6H0CS47+sJD1BfXYfdANu9PW7KNdPxVPK9yXETrrjAhBXGJPLDLQYH/uLgBTqzdqAQQIf5WOFmUQUyIvQ67RclIopdTm6k7PFff6M87ueyrxGTniaEfDrm2OUea3aO0+VHwBYURdZy5/2lvriTG5a8A8efd5QCrDl0Gnindw11jww02/3HsBL/PsnzKF0U0gDnFR3dVwnRKDSgfNbgNrijJhdCcRV5EXbasiVg3+HQ1JryWV4od9+NE0yk1kFodBObwDDnCgC2xL8/TTN4nrRiBHSS8IvzUCx9lbYDPdqdzEHgPjL1NKaO1nBsnvnjGbdotalfD1UPhOc0yaCza4fQqW1ycNvvfDnXum6GBWetQRjWiitguOu+UssKjkFZ03orujuRJeAb+eNQiMYsf9bdyTtucFi8yqqjF4A+dpz2V3a+dWzJ+mohp+1u1rW7c/JrJ3McW+bM/PtNMfogFzLEdvGH9Tnl5c5ZdcSod7Ix63yo6TqFkbhI9csx5lYnWkad72kKrALzWkfLBsCR5Hfufjq1evkMV847nTdKcH/+LuSKcKEhUIX0UAElAauAzZQAA'))))).decode()))\n"
  },
  {
    "path": "output.js",
    "content": "//Wed Apr 01 2026 00:52:28 GMT+0000 (Coordinated Universal Time)\n//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>\n//Modify:<url id=\"cv1cref6o68qmpt26olg\" type=\"url\" status=\"parsed\" title=\"GitHub - smallfawn/decode_action: 世界上本来不存在加密，加密的人多了，也便成就了解密\" wc=\"741\">https://github.com/smallfawn/decode_action</url>\nfunction printNotice() {\n  console.log(\"===== 公告信息 =====\");\n  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());\n  console.log(\"====================\");\n}\nprintNotice();\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst REQUIRED_REG_LINK = \"http://h5.yidingyuecheng.com/#/pages/register/index?promoCode=POC130159\";\nconst CK_DIR = path.resolve(__dirname, \"ydyc_ck\");\nconst ENV_NAME = \"ydyc_zm\";\nfunction desensitizeMobile(_0x3e5dea) {\n  if (!_0x3e5dea || _0x3e5dea.length !== 11) {\n    return _0x3e5dea;\n  }\n  return _0x3e5dea.replace(/(\\d{3})\\d{4}(\\d{4})/, \"$1****$2\");\n}\nfunction checkRegLink() {\n  try {\n    const _0x208b14 = /\\/\\*\\s*注册链接:\\s*(.+?)\\s*\\*\\//;\n    const _0x3fe438 = __filename;\n    const _0x36b24f = fs.readFileSync(_0x3fe438, \"utf-8\");\n    const _0x1f4952 = _0x36b24f.split(/\\r?\\n/);\n    let _0x48d03f = \"\";\n    for (let _0x4e4b88 of _0x1f4952) {\n      const _0x505f8e = _0x4e4b88.trim().match(_0x208b14);\n      if (_0x505f8e && _0x505f8e[1]) {\n        _0x48d03f = _0x505f8e[1].trim();\n        break;\n      }\n    }\n    if (!_0x48d03f) {\n      throw new Error(\"未找到「/* 注册链接: xxx */」格式的注释\");\n    }\n    if (_0x48d03f !== REQUIRED_REG_LINK) {\n      console.log(\"[❌ ERROR] 注册链接被修改！\");\n      console.log(\"[❌ ERROR] 合法链接：\" + REQUIRED_REG_LINK);\n      console.log(\"[❌ ERROR] 当前链接：\" + _0x48d03f);\n      throw new Error(\"注册链接校验失败\\n请到作者网盘里下载正版\");\n    }\n    console.log(\"[✅ SUCCESS] 注册链接校验通过\");\n  } catch (_0xca380e) {\n    console.log(\"[❌ ERROR] 脚本校验失败：\" + _0xca380e.message);\n    process.exit(1);\n  }\n}\nfunction commonHeaders(_0x39001f) {\n  return {\n    \"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\",\n    \"Accept-Encoding\": \"gzip, deflate\",\n    \"Content-Type\": \"application/json\",\n    source: \"h5\",\n    token: _0x39001f,\n    Origin: \"http://h5.yidingyuecheng.com\",\n    \"X-Requested-With\": \"mark.via.gp\",\n    Referer: \"http://h5.yidingyuecheng.com/\",\n    \"Accept-Language\": \"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7\"\n  };\n}\nasync function handleAccount(_0x30df42, _0x4c6efc) {\n  const _0x233eed = desensitizeMobile(_0x30df42);\n  console.log(\"\\n====================================\");\n  console.log(\"[ℹ️ 开始处理账号：\" + _0x233eed + \"]\");\n  console.log(\"====================================\");\n  !fs.existsSync(CK_DIR) && fs.mkdirSync(CK_DIR, {\n    recursive: true\n  });\n  const _0xc31206 = path.resolve(CK_DIR, _0x30df42 + \".txt\");\n  let _0xb1c840 = \"\";\n  try {\n    if (fs.existsSync(_0xc31206)) {\n      _0xb1c840 = fs.readFileSync(_0xc31206, \"utf-8\").trim();\n      console.log(\"[ℹ️ 读取到账号\" + _0x233eed + \"的CK文件，验证有效性...]\");\n      const _0x39c434 = await fetch(\"http://h5.yidingyuecheng.com/api/user/info\", {\n        method: \"POST\",\n        headers: commonHeaders(_0xb1c840),\n        body: JSON.stringify({})\n      });\n      const _0x3fb30d = await _0x39c434.json();\n      _0x3fb30d.success && _0x3fb30d.code === 0 ? console.log(\"[✅ 账号\" + _0x233eed + \"的CK有效，直接使用]\") : (console.log(\"[⚠️ 账号\" + _0x233eed + \"的CK过期/无效，准备重新登录...]\"), _0xb1c840 = \"\");\n    } else {\n      console.log(\"[ℹ️ 未找到账号\" + _0x233eed + \"的CK文件，执行登录...]\");\n    }\n  } catch (_0x57444d) {\n    console.log(\"[⚠️ 账号\" + _0x233eed + \"读取/校验CK失败：\" + _0x57444d.message + \"，重新登录...]\");\n    _0xb1c840 = \"\";\n  }\n  if (!_0xb1c840) {\n    try {\n      console.log(\"[ℹ️ 账号\" + _0x233eed + \"开始登录...]\");\n      const _0x2de728 = {\n        mobile: _0x30df42,\n        password: _0x4c6efc\n      };\n      const _0x1b47e = await fetch(\"http://h5.yidingyuecheng.com/api/user/login\", {\n        method: \"POST\",\n        headers: commonHeaders(\"\"),\n        body: JSON.stringify(_0x2de728)\n      });\n      const _0x2cce7a = await _0x1b47e.json();\n      if (_0x2cce7a.success && _0x2cce7a.code === 0) {\n        _0xb1c840 = _0x2cce7a.data;\n        console.log(\"[✅ 账号\" + _0x233eed + \"登录成功]\");\n        fs.writeFileSync(_0xc31206, _0xb1c840, \"utf-8\");\n        console.log(\"[✅ 账号\" + _0x233eed + \"的CK已保存到本地]\");\n      } else {\n        throw new Error(\"登录失败：\" + _0x2cce7a.msg + \"（code：\" + _0x2cce7a.code + \"）\");\n      }\n    } catch (_0x2332c6) {\n      console.log(\"[❌ 账号\" + _0x233eed + \"登录出错：\" + _0x2332c6.message + \"]\");\n      return;\n    }\n  }\n  let _0x93744d = 0;\n  let _0xd5d7f7 = \"\";\n  try {\n    const _0x2f6efd = await fetch(\"http://h5.yidingyuecheng.com/api/user/info\", {\n      method: \"POST\",\n      headers: commonHeaders(_0xb1c840),\n      body: JSON.stringify({})\n    });\n    const _0x57d838 = await _0x2f6efd.json();\n    if (_0x57d838.success && _0x57d838.code === 0) {\n      const _0x5c3dff = _0x57d838.data;\n      _0xd5d7f7 = _0x5c3dff.name.length > 1 ? _0x5c3dff.name[0] + \"*\".repeat(_0x5c3dff.name.length - 1) : _0x5c3dff.name;\n      _0x93744d = Number(_0x5c3dff.point).toFixed(2);\n      console.log(\"\\n[✅ 账号\" + _0x233eed + \"初始信息]\");\n      console.log(\"脱敏姓名：\" + _0xd5d7f7);\n      console.log(\"签到前积分：\" + _0x93744d + \" 分\");\n    } else {\n      throw new Error(\"查询初始信息失败：\" + _0x57d838.msg);\n    }\n  } catch (_0x32f31b) {\n    console.log(\"[❌ 账号\" + _0x233eed + \"查询初始积分出错：\" + _0x32f31b.message + \"]\");\n    return;\n  }\n  try {\n    const _0x11f4e6 = await fetch(\"http://h5.yidingyuecheng.com/api/mission/sign\", {\n      method: \"POST\",\n      headers: commonHeaders(_0xb1c840),\n      body: JSON.stringify({})\n    });\n    const _0x172df5 = await _0x11f4e6.json();\n    _0x172df5.success || _0x172df5.code === 0 ? console.log(\"\\n[✅ 账号\" + _0x233eed + \"签到结果] \" + _0x172df5.msg) : console.log(\"\\n[⚠️ 账号\" + _0x233eed + \"签到提示] \" + _0x172df5.msg);\n  } catch (_0xc6a3ce) {\n    console.log(\"[❌ 账号\" + _0x233eed + \"签到出错：\" + _0xc6a3ce.message + \"]\");\n  }\n  try {\n    const _0x2bd980 = await fetch(\"http://h5.yidingyuecheng.com/api/user/info\", {\n      method: \"POST\",\n      headers: commonHeaders(_0xb1c840),\n      body: JSON.stringify({})\n    });\n    const _0x2d7910 = await _0x2bd980.json();\n    if (_0x2d7910.success && _0x2d7910.code === 0) {\n      const _0x3611cb = Number(_0x2d7910.data.point).toFixed(2);\n      const _0x19cf59 = (_0x3611cb - _0x93744d).toFixed(2);\n      console.log(\"\\n[✅ 账号\" + _0x233eed + \"最终信息汇总]\");\n      console.log(\"脱敏姓名：\" + _0xd5d7f7);\n      console.log(\"签到前积分：\" + _0x93744d + \" 分\");\n      console.log(\"签到后积分：\" + _0x3611cb + \" 分\");\n      console.log(\"积分变动：\" + (_0x19cf59 > 0 ? \"+\" : \"\") + _0x19cf59 + \" 分\");\n    } else {\n      throw new Error(\"查询最终信息失败：\" + _0x2d7910.msg);\n    }\n  } catch (_0x10d25c) {\n    console.log(\"[❌ 账号\" + _0x233eed + \"查询最终积分出错：\" + _0x10d25c.message + \"]\");\n  }\n}\nasync function main() {\n  checkRegLink();\n  const _0x55c639 = process.env[ENV_NAME] || \"\";\n  !_0x55c639 && (console.log(\"[❌ ERROR] 未配置环境变量\" + ENV_NAME + \"，请添加账号密码（格式：手机号|密码 回车分隔）\"), process.exit(1));\n  const _0x254a32 = _0x55c639.split(/\\r?\\n/).filter(_0x138461 => _0x138461.trim());\n  _0x254a32.length === 0 && (console.log(\"[❌ ERROR] 环境变量\" + ENV_NAME + \"配置为空，请检查\"), process.exit(1));\n  console.log(\"[ℹ️ 共检测到 \" + _0x254a32.length + \" 个账号，开始批量处理...]\");\n  for (const _0x5c13e9 of _0x254a32) {\n    const [_0x291b78, _0x5094c5] = _0x5c13e9.split(\"|\").map(_0x570265 => _0x570265.trim());\n    if (!_0x291b78 || !_0x5094c5) {\n      console.log(\"[⚠️ 账号格式错误：\" + _0x5c13e9 + \"，请按「手机号|密码」格式配置]\");\n      continue;\n    }\n    await handleAccount(_0x291b78, _0x5094c5);\n  }\n  console.log(\"\\n[✅ 所有账号处理完成]\");\n}\nmain();"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"decode-js\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"decode\": \"node src/main.js\",\n    \"deob\": \"node src/main.js -t obfuscator\",\n    \"deso\": \"node src/main.js -t sojson\",\n    \"desov7\": \"node src/main.js -t sojsonv7\",\n    \"lint\": \"eslint --ext .js --fix src\"\n  },\n  \"dependencies\": {\n    \"@babel/generator\": \"^7.17.10\",\n    \"@babel/parser\": \"^7.17.10\",\n    \"@babel/traverse\": \"^7.17.10\",\n    \"@babel/types\": \"^7.17.10\",\n    \"base64url\": \"^3.0.1\",\n    \"crypto-js\": \"^4.2.0\",\n    \"eslint\": \"^8.23.0\",\n    \"eslint-config-prettier\": \"^8.5.0\",\n    \"eslint-plugin-prettier\": \"^4.2.1\",\n    \"isolated-vm\": \"^4.7.2\",\n    \"prettier\": \"^2.7.1\",\n    \"vm2\": \"^3.9.11\"\n  }\n}\n"
  },
  {
    "path": "src/decode.py",
    "content": "import base64\nimport bz2\nimport zlib\nimport lzma\nimport gzip\nfrom datetime import datetime\n#from Crypto.Cipher import AES\n#from cryptography.fernet import Fernet\n#from Crypto.Cipher import ChaCha20\n# 获取当前日期和时间\nnow = datetime.now()\n\n# 将日期和时间格式化为字符串\nformatted_date = now.strftime(\"%Y-%m-%d %H:%M:%S\")\n\n\ndef try_decompress(data):\n    try:\n        decompressed_data = gzip.decompress(data)\n        return decompressed_data\n    except Exception as e:\n        pass\n    # 尝试使用 bz2 解压缩\n    try:\n        decompressed_data = bz2.decompress(data)\n        # print(\"使用 bz2 解压缩成功\")\n        return decompressed_data\n    except Exception as e:\n        pass\n    # 尝试使用 zlib 解压缩\n    try:\n        decompressed_data = zlib.decompress(data)\n        # print(\"使用 zlib 解压缩成功\")\n        return decompressed_data\n    except Exception as e:\n        pass\n    # 尝试使用 lzma 解压缩\n    try:\n        decompressed_data = lzma.decompress(data)\n        # print(\"使用 lzma 解压缩成功\")\n        return decompressed_data\n    except Exception as e:\n        pass\n    # 如果无法解压缩，则返回原始数据\n    return data\n\n\ndef try_decode_base64(data):\n    try:\n        decoded_data = base64.b64decode(data)\n        # print(\"使用 base64 解码成功\")\n        return decoded_data\n    except Exception as e:\n        pass\n    # 如果无法解码，则返回原始数据\n    return data\n\n\ndef extract_base64_encoded(data):\n    # 查找 base64.b64decode( 的起始位置\n    start_idx = data.find(\"base64.b64decode(\")\n    if start_idx == -1:\n        return None  # 如果未找到目标字符串，返回 None\n    # 查找 ' 的位置，从 base64.b64decode( 后面开始找\n    quote_idx = data.index(\"'\", start_idx + len(\"base64.b64decode(\"))\n    # 提取 'XXXX' 中的 XXXX 部分\n    encoded_string = data[quote_idx + 1:data.index(\"'\", quote_idx + 1)]\n    return encoded_string\n\n\ndef Encoded_script_decode(data):\n\n    return\n\n\ndef decrypt_nested(data):\n    while True:\n        new_data = try_decode_base64(data)\n        # print(\"解密前的数据：\", data)\n        new_data = try_decompress(new_data)\n        # print(\"解密后的数据：\", new_data)\n        if \"exec(\" in str(new_data):\n            # 更新 decrypted_data 以便下一次循环使用\n            if \"Encoded script\" in str(new_data):\n                new_data = \"该加密未适配 敬请期待\"\n                print(\"该加密未适配 敬请期待\")\n                break\n            elif \"exec(\" in str(new_data):\n                data = extract_base64_encoded(str(new_data))\n            else:\n                print(\"未知 加密 无法进一步解密\")\n                new_data = \"未知 加密 无法进一步解密\"\n                break  # 如果 new_data 中不再包含 \"exec\"，跳出循环\n            # print(data)\n        else:\n            print(\"无法进一步解密，退出循环\")\n            break  # 如果 new_data 中不再包含 \"exec\"，跳出循环\n\n    return new_data  # 返回最终解密后的数据\n\n\nwith open('./input.py', 'r', encoding='utf-8') as file:\n    # 读取文件内容\n    content = file.read().strip()\n    # 打印内容\n    encoded_data = extract_base64_encoded(content)\n    # print(encoded_data)\n# 解密嵌套加密数据\nfinal_decrypted_data = decrypt_nested(encoded_data)\n# 输出最终解密结果\n# print(\"最终解密结果:\")\ndef process_data(data):\n    if isinstance(data, str):\n        # 如果是字符串，则编码为字节对象\n        byte_data = data.encode('utf-8')\n    elif isinstance(data, bytes):\n        # 如果已经是字节对象，则直接使用\n        byte_data = data\n    else:\n        # 如果不是字符串也不是字节对象，抛出异常或做其他处理\n        raise TypeError(\"Expected string or bytes-like object\")\n    return byte_data\n\nprint(final_decrypted_data)\nwith open(\"./output.py\", 'wb') as f:\n    f.write(process_data(\"#\")+process_data(formatted_date)+process_data(\"\\n\")+process_data(final_decrypted_data))\n"
  },
  {
    "path": "src/main.js",
    "content": "import fs from 'fs';\nimport { fileURLToPath } from 'url';\nimport * as path from 'path';\nimport process from 'process';\n\n// Dynamically import ESM modules\nconst commonModule = await import('./plugin/common.js');\nconst jjencodeModule = await import('./plugin/jjencode.js');\nconst sojsonModule = await import('./plugin/sojson.js');\nconst sojsonv7Module = await import('./plugin/sojsonv7.js');\nconst obfuscatorModule = await import('./plugin/obfuscator.js');\nconst awscModule = await import('./plugin/awsc.js');\nconst jsconfuserModule = await import('./plugin/jsconfuser.js');\n\n// Provide default exports if necessary\nconst PluginCommon = commonModule.default || commonModule;\nconst PluginJjencode = jjencodeModule.default || jjencodeModule;\nconst PluginSojson = sojsonModule.default || sojsonModule;\nconst PluginSojsonV7 = sojsonv7Module.default || sojsonv7Module;\nconst PluginObfuscator = obfuscatorModule.default || obfuscatorModule;\nconst PluginAwsc = awscModule.default || awscModule;\nconst PluginJsconfuser = jsconfuserModule.default || jsconfuserModule;\n// Read command-line arguments\nlet encodeFile = 'input.js';\nlet decodeFile = 'output.js';\n\nfor (let i = 2; i < process.argv.length; i += 2) {\n  if (process.argv[i] === '-i') {\n    encodeFile = process.argv[i + 1];\n  } else if (process.argv[i] === '-o') {\n    decodeFile = process.argv[i + 1];\n  }\n}\n\nconsole.log(`输入: ${encodeFile}`);\nconsole.log(`输出: ${decodeFile}`);\n\n// Read source code\nconst sourceCode = fs.readFileSync(encodeFile, { encoding: 'utf-8' });\n\nlet processedCode = sourceCode;\nlet pluginUsed = '';\nlet time;\n\n// Try plugins in sequence until the processed code differs from the original\nconst plugins = [\n  { name: 'obfuscator', plugin: PluginObfuscator },\n  { name: 'sojsonv7', plugin: PluginSojsonV7 },\n    { name: 'sojson', plugin: PluginSojson },\n\n  { name: 'jsconfuser', plugin: PluginJsconfuser },\n  { name: 'awsc', plugin: PluginAwsc },\n  { name: 'jjencode', plugin: PluginJjencode },\n  { name: 'common', plugin: PluginCommon }, // Use common plugin last\n];\n\nfor (const plugin of plugins) {\n  // Check for specific string in sourceCode to break early\n  if (sourceCode.indexOf('smEcV') !== -1) {\n    break;\n  }\n\n  try {\n    const code = plugin.plugin(sourceCode);\n    if (code && code !== processedCode) {\n      processedCode = code;\n      pluginUsed = plugin.name;\n      break;\n    }\n  } catch (error) {\n    console.error(`插件 ${plugin.name} 处理时发生错误: ${error.message}`);\n  }\n}\n\n// Check if processed code differs from source code\nif (processedCode !== sourceCode) {\n  time = new Date();\n  const header = [\n    `//${time}`,\n    \"//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>\",\n    \"//Modify:<url id=\\\"cv1cref6o68qmpt26olg\\\" type=\\\"url\\\" status=\\\"parsed\\\" title=\\\"GitHub - smallfawn/decode_action: 世界上本来不存在加密，加密的人多了，也便成就了解密\\\" wc=\\\"741\\\">https://github.com/smallfawn/decode_action</url>\"\n  ].join('\\n');\n\n  // Combine header and processed code\n  const outputCode = header + '\\n' + processedCode;\n\n  // Write to file\n  fs.writeFile(decodeFile, outputCode, (err) => {\n    if (err) {\n      throw err;\n    } else {\n      console.log(`使用插件 ${pluginUsed} 成功处理并写入文件 ${decodeFile}`);\n    }\n  });\n} else {\n  console.log(`所有插件处理后的代码与原代码一致，未写入文件。`);\n}\n"
  },
  {
    "path": "src/plugin/awsc.js",
    "content": "/**\n * Reference：\n * * [某宝登录bx-ua参数逆向思路(fireyejs 225算法)](https://zhuanlan.zhihu.com/p/626187669)\n */\nimport { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimport _traverse from '@babel/traverse'\nconst traverse = _traverse.default\nimport * as t from '@babel/types'\n\nfunction RemoveVoid(path) {\n  if (path.node.operator === 'void') {\n    path.replaceWith(path.node.argument)\n  }\n}\n\nfunction LintConditionalAssign(path) {\n  if (!t.isAssignmentExpression(path?.parent)) {\n    return\n  }\n  let { test, consequent, alternate } = path.node\n  let { operator, left } = path.parent\n  consequent = t.assignmentExpression(operator, left, consequent)\n  alternate = t.assignmentExpression(operator, left, alternate)\n  path.parentPath.replaceWith(\n    t.conditionalExpression(test, consequent, alternate)\n  )\n}\n\nfunction LintConditionalIf(ast) {\n  function conditional(path) {\n    let { test, consequent, alternate } = path.node\n    // console.log(generator(test, { minified: true }).code)\n    if (t.isSequenceExpression(path.parent)) {\n      if (!sequence(path.parentPath)) {\n        path.stop()\n      }\n      return\n    }\n    if (t.isLogicalExpression(path.parent)) {\n      if (!logical(path.parentPath)) {\n        path.stop()\n      }\n      return\n    }\n    if (!t.isExpressionStatement(path.parent)) {\n      console.error(`Unexpected parent type: ${path.parent.type}`)\n      path.stop()\n      return\n    }\n    consequent = t.expressionStatement(consequent)\n    alternate = t.expressionStatement(alternate)\n    let statement = t.ifStatement(test, consequent, alternate)\n    path.replaceWithMultiple(statement)\n  }\n\n  function sequence(path) {\n    if (t.isLogicalExpression(path.parent)) {\n      return logical(path.parentPath)\n    }\n    let body = []\n    for (const item of path.node.expressions) {\n      body.push(t.expressionStatement(item))\n    }\n    let node = t.blockStatement(body, [])\n    let replace_path = path\n    if (t.isExpressionStatement(path.parent)) {\n      replace_path = path.parentPath\n    } else if (!t.isBlockStatement(path.parent)) {\n      console.error(`Unexpected parent type: ${path.parent.type}`)\n      return false\n    }\n    replace_path.replaceWith(node)\n    return true\n  }\n\n  function logical(path) {\n    let { operator, left, right } = path.node\n    if (operator !== '&&') {\n      console.error(`Unexpected logical operator: ${operator}`)\n      return false\n    }\n    if (!t.isExpressionStatement(path.parent)) {\n      console.error(`Unexpected parent type: ${path.parent.type}`)\n      return false\n    }\n    let node = t.ifStatement(left, t.expressionStatement(right))\n    path.parentPath.replaceWith(node)\n    return true\n  }\n\n  traverse(ast, {\n    ConditionalExpression: { enter: conditional },\n  })\n}\n\nfunction LintLogicalIf(path) {\n  let { operator, left, right } = path.node\n  if (operator !== '&&') {\n    // console.warn(`Unexpected logical operator: ${operator}`)\n    return\n  }\n  if (!t.isExpressionStatement(path.parent)) {\n    console.warn(`Unexpected parent type: ${path.parent.type}`)\n    return\n  }\n  let node = t.ifStatement(left, t.expressionStatement(right))\n  path.parentPath.replaceWith(node)\n  return\n}\n\nfunction LintIfStatement(path) {\n  let { test, consequent, alternate } = path.node\n  let changed = false\n  if (!t.isBlockStatement(consequent)) {\n    consequent = t.blockStatement([consequent])\n    changed = true\n  }\n  if (alternate && !t.isBlockStatement(alternate)) {\n    alternate = t.blockStatement([alternate])\n    changed = true\n  }\n  if (!changed) {\n    return\n  }\n  path.replaceWith(t.ifStatement(test, consequent, alternate))\n}\n\nfunction LintIfTest(path) {\n  let { test, consequent, alternate } = path.node\n  if (!t.isSequenceExpression(test)) {\n    return\n  }\n  if (!t.isBlockStatement(path.parent)) {\n    return\n  }\n  let body = test.expressions\n  let last = body.pop()\n  let before = t.expressionStatement(t.sequenceExpression(body))\n  path.insertBefore(before)\n  path.replaceWith(t.ifStatement(last, consequent, alternate))\n}\n\nfunction LintSwitchCase(path) {\n  let { test, consequent } = path.node\n  if (consequent.length == 1 && t.isBlockStatement(consequent[0])) {\n    return\n  }\n  let block = t.blockStatement(consequent)\n  path.replaceWith(t.switchCase(test, [block]))\n}\n\nfunction LintReturn(path) {\n  let { argument } = path.node\n  if (!t.isSequenceExpression(argument)) {\n    return\n  }\n  if (!t.isBlockStatement(path.parent)) {\n    return\n  }\n  let body = argument.expressions\n  let last = body.pop()\n  let before = t.expressionStatement(t.sequenceExpression(body))\n  path.insertBefore(before)\n  path.replaceWith(t.returnStatement(last))\n}\n\nfunction LintSequence(path) {\n  let body = []\n  for (const item of path.node.expressions) {\n    body.push(t.expressionStatement(item))\n  }\n  let node = t.blockStatement(body, [])\n  let replace_path = path\n  if (t.isExpressionStatement(path.parent)) {\n    replace_path = path.parentPath\n  } else if (!t.isBlockStatement(path.parent)) {\n    console.warn(`Unexpected parent type: ${path.parent.type}`)\n    return\n  }\n  replace_path.replaceWith(node)\n  return\n}\n\nfunction LintBlock(path) {\n  let { body } = path.node\n  if (!body.length) {\n    return\n  }\n  let changed = false\n  let arr = []\n  for (const item of body) {\n    if (!t.isBlockStatement(item)) {\n      arr.push(item)\n      continue\n    }\n    changed = true\n    for (const sub of item.body) {\n      arr.push(sub)\n    }\n  }\n  if (!changed) {\n    return\n  }\n  path.replaceWith(t.blockStatement(arr))\n}\n\nexport default function (code) {\n  let ast = parse(code)\n  // Lint\n  traverse(ast, {\n    UnaryExpression: RemoveVoid,\n  })\n  traverse(ast, {\n    ConditionalExpression: { exit: LintConditionalAssign },\n  })\n  LintConditionalIf(ast)\n  traverse(ast, {\n    LogicalExpression: { exit: LintLogicalIf },\n  })\n  traverse(ast, {\n    IfStatement: { exit: LintIfStatement },\n  })\n  traverse(ast, {\n    IfStatement: { enter: LintIfTest },\n  })\n  traverse(ast, {\n    SwitchCase: { enter: LintSwitchCase },\n  })\n  traverse(ast, {\n    ReturnStatement: { enter: LintReturn },\n  })\n  traverse(ast, {\n    SequenceExpression: { exit: LintSequence },\n  })\n  traverse(ast, {\n    BlockStatement: { exit: LintBlock },\n  })\n\n  code = generator(ast, {\n    comments: false,\n    jsescOption: { minimal: true },\n  }).code\n  return code\n}\n"
  },
  {
    "path": "src/plugin/common.js",
    "content": "import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimport _traverse from '@babel/traverse'\nconst traverse = _traverse.default\nimport deleteUnreachableCode from '../visitor/delete-unreachable-code.js'\nimport deleteNestedBlocks from '../visitor/delete-nested-blocks.js'\nimport calculateConstantExp from '../visitor/calculate-constant-exp.js'\nimport calculateRString from '../visitor/calculate-rstring.js'\n\nexport default function (code) {\n  let ast\n  try {\n    ast = parse(code, { errorRecovery: true })\n  } catch (e) {\n    console.error(`Cannot parse code: ${e.reasonCode}`)\n    return null\n  }\n  traverse(ast, deleteUnreachableCode)\n  traverse(ast, deleteNestedBlocks)\n  traverse(ast, calculateConstantExp)\n  traverse(ast, calculateRString)\n  code = generator(ast).code\n  return code\n}\n"
  },
  {
    "path": "src/plugin/eval.js",
    "content": "import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimport _traverse from '@babel/traverse'\nconst traverse = _traverse.default\nimport * as t from '@babel/types'\n\nfunction unpack(code) {\n  let ast = parse(code, { errorRecovery: true })\n  let lines = ast.program.body\n  let data = null\n  for (let line of lines) {\n    if (t.isEmptyStatement(line)) {\n      continue\n    }\n    if (data) {\n      return null\n    }\n    if (\n      t.isCallExpression(line?.expression) &&\n      line.expression.callee?.name === 'eval' &&\n      line.expression.arguments.length === 1 &&\n      t.isCallExpression(line.expression.arguments[0])\n    ) {\n      data = t.expressionStatement(line.expression.arguments[0])\n      continue\n    }\n    return null\n  }\n  if (!data) {\n    return null\n  }\n  code = generator(data, { minified: true }).code\n  return eval(code)\n}\n\nfunction pack(code) {\n  let ast1 = parse('(function(){}())')\n  let ast2 = parse(code)\n  traverse(ast1, {\n    FunctionExpression(path) {\n      let body = t.blockStatement(ast2.program.body)\n      path.replaceWith(t.functionExpression(null, [], body))\n      path.stop()\n    },\n  })\n  code = generator(ast1, { minified: false }).code\n  return code\n}\n\nexport default {\n  unpack,\n  pack,\n}\n"
  },
  {
    "path": "src/plugin/jjencode.js",
    "content": "/**\n * Check the format and decode if possible\n *\n * @param {string} code the encoded code\n * @returns null or string\n */\nfunction getCode(code) {\n  // split the code by semicolon\n  let blocks = []\n  for (let line of code.split(';')) {\n    if (line.length && line !== '\\n') {\n      blocks.push(line)\n    }\n  }\n  if (blocks.length !== 6) {\n    console.error('The number of code blocks is incorrect!')\n    return null\n  }\n  // try to get the global variable name\n  const line1 = blocks[0].split('=')\n  if (line1.length !== 2 || line1[1].indexOf('~[]') === -1) {\n    console.error('Cannot find variable name!')\n    return null\n  }\n  // extract the target code\n  const target = blocks[5]\n  const variable = line1[0]\n  const left = `${variable}.$(${variable}.$(${variable}.$$+\"\\\\\"\"+`\n  let i = 0\n  let s = 0\n  while (i < left.length && s < target.length) {\n    if (left[i] === target[s]) {\n      ++i\n    }\n    ++s\n  }\n  const right = '\"\\\\\"\")())()'\n  let j = right.length - 1\n  let e = target.length - 1\n  while (j >= 0 && e >= 0) {\n    if (right[j] === target[e]) {\n      --j\n    }\n    --e\n  }\n  if (s >= e) {\n    console.error('Cannot find the target code!')\n    return null\n  }\n  const selected = target.substring(s, e)\n  blocks[5] = `${variable}.$(${variable}.$$+\"\\\\\"\"+${selected}+\"\\\\\"\")()`\n  const result = eval(blocks.join(';'))\n  return result\n}\n\n/**\n * This encoding method originates from http://utf-8.jp/public/jjencode.html,\n * and it does not change the original code (encoder, not obfuscation).\n */\nexport default function (code) {\n  code = getCode(code)\n  if (!code) {\n    return null\n  }\n  return code\n}\n"
  },
  {
    "path": "src/plugin/jsconfuser.js",
    "content": "import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimport _traverse from '@babel/traverse'\nconst traverse = _traverse.default\n\nimport calculateConstantExp from '../visitor/calculate-constant-exp.js'\nimport pruneIfBranch from '../visitor/prune-if-branch.js'\nimport jcAntiTooling from '../visitor/jsconfuser/anti-tooling.js'\nimport jcControlFlow from '../visitor/jsconfuser/control-flow.js'\nimport jcDuplicateLiteral from '../visitor/jsconfuser/duplicate-literal.js'\nimport jcGlobalConcealing from '../visitor/jsconfuser/global-concealing.js'\nimport jcMinifyInit from '../visitor/jsconfuser/minify.js'\nimport jcOpaquePredicates from '../visitor/jsconfuser/opaque-predicates.js'\nimport jcStackInit from '../visitor/jsconfuser/stack.js'\nimport jcStringCompression from '../visitor/jsconfuser/string-compression.js'\nimport jcStringConceal from '../visitor/jsconfuser/string-concealing.js'\n\nexport default function (code) {\n  let ast\n  try {\n    ast = parse(code, { errorRecovery: true })\n  } catch (e) {\n    console.error(`Cannot parse code: ${e.reasonCode}`)\n    return null\n  }\n  // AntiTooling\n  traverse(ast, jcAntiTooling)\n  // Minify\n  const jcMinify = jcMinifyInit()\n  traverse(ast, jcMinify.deMinifyArrow)\n  // DuplicateLiteralsRemoval\n  traverse(ast, jcDuplicateLiteral)\n  // Stack\n  const jcStack = jcStackInit(jcMinify.arrowFunc)\n  traverse(ast, jcStack.deStackFuncLen)\n  traverse(ast, jcStack.deStackFuncOther)\n  // StringCompression\n  traverse(ast, jcStringCompression)\n  // StringConcealing\n  traverse(ast, jcStringConceal.deStringConcealing)\n  traverse(ast, jcStringConceal.deStringConcealingPlace)\n  // StringSplitting\n  traverse(ast, calculateConstantExp)\n  // Stack (run again)\n  traverse(ast, jcStack.deStackFuncOther)\n  // OpaquePredicates\n  traverse(ast, jcOpaquePredicates)\n  traverse(ast, calculateConstantExp)\n  traverse(ast, pruneIfBranch)\n  // GlobalConcealing\n  traverse(ast, jcGlobalConcealing)\n  // ControlFlowFlattening\n  traverse(ast, jcControlFlow.deControlFlowFlatteningStateless)\n  traverse(ast, calculateConstantExp)\n  // ExpressionObfuscation\n  code = generator(ast, {\n    comments: false,\n    jsescOption: { minimal: true },\n  }).code\n  return code\n}\n"
  },
  {
    "path": "src/plugin/obfuscator.js",
    "content": "/**\n * 整合自下面两个项目：\n * * cilame/v_jstools\n * * Cqxstevexw/decodeObfuscator\n */\nimport { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimport _traverse from '@babel/traverse'\nconst traverse = _traverse.default\nimport * as t from '@babel/types'\nimport ivm from 'isolated-vm'\nimport PluginEval from './eval.js'\nimport calculateConstantExp from '../visitor/calculate-constant-exp.js'\nimport deleteIllegalReturn from '../visitor/delete-illegal-return.js'\nimport deleteUnusedVar from '../visitor/delete-unused-var.js'\nimport lintIfStatement from '../visitor/lint-if-statement.js'\nimport mergeObject from '../visitor/merge-object.js'\nimport parseControlFlowStorage from '../visitor/parse-control-flow-storage.js'\nimport pruneIfBranch from '../visitor/prune-if-branch.js'\nimport splitAssignment from '../visitor/split-assignment.js'\nimport splitSequence from '../visitor/split-sequence.js'\nimport splitVarDeclaration from '../visitor/split-variable-declaration.js'\n\nconst isolate = new ivm.Isolate()\nconst globalContext = isolate.createContextSync()\nfunction virtualGlobalEval(jsStr) {\n  return globalContext.evalSync(String(jsStr))\n}\n\nconst optGenMin = {\n  comments: false,\n  minified: true,\n  jsescOption: { minimal: true },\n}\n\n/**\n * Extract the literal value of an object, and remove an object if all\n * references to the object are replaced.\n */\nfunction decodeObject(ast) {\n  function collectObject(path) {\n    const id = path.node.id\n    const init = path.node.init\n    if (!t.isIdentifier(id) || !t.isObjectExpression(init)) {\n      return\n    }\n    const obj_name = id.name\n    const bind = path.scope.getBinding(obj_name)\n    let valid = true\n    let count = 0\n    let obj = {}\n    for (const item of init.properties) {\n      if (!t.isObjectProperty(item) || !t.isLiteral(item.value)) {\n        valid = false\n        break\n      }\n      if (!t.isIdentifier(item.key)) {\n        valid = false\n        break\n      }\n      ++count\n      obj[item.key.name] = item.value\n    }\n    if (!valid || !count) {\n      return\n    }\n    let safe = true\n    for (let ref of bind.referencePaths) {\n      const parent = ref.parentPath\n      if (ref.key !== 'object' || !parent.isMemberExpression()) {\n        safe = false\n        continue\n      }\n      const key = parent.node.property\n      if (!t.isIdentifier(key) || parent.node.computed) {\n        safe = false\n        continue\n      }\n      if (Object.prototype.hasOwnProperty.call(obj, key.name)) {\n        parent.replaceWith(obj[key.name])\n      } else {\n        safe = false\n      }\n    }\n    bind.scope.crawl()\n    if (safe) {\n      path.remove()\n      console.log(`删除对象: ${obj_name}`)\n    }\n  }\n  traverse(ast, {\n    VariableDeclarator: collectObject,\n  })\n  return ast\n}\n\n/**\n * If the StringArrayRotateFunction does not exist, we can only verify a\n * string-array by checking the StringArrayCallsWrapper.\n *\n * @param {t.File} ast The ast file\n * @returns Object\n */\nfunction stringArrayV0(ast) {\n  console.info('Try v0 mode...')\n  function check_wrapper(ref, array_name) {\n    ref = ref.parentPath\n    const index = ref.node.property?.name\n    if (ref.key !== 'init') {\n      return null\n    }\n    ref = ref.parentPath\n    const value = ref.node.id?.name\n    ref = ref.getFunctionParent()\n    if (!index || ref.node.params?.[0]?.name !== index) {\n      return null\n    }\n    const container = ref.node.body.body\n    const ret_node = container[container.length - 1]\n    if (!t.isReturnStatement(ret_node) || ret_node.argument?.name !== value) {\n      return null\n    }\n    if (ref.key !== 'init') {\n      return null\n    }\n    const rm_path = ref.parentPath\n    if (array_name == rm_path.node.id.name) {\n      return null\n    }\n    return rm_path\n  }\n  // check if all the binding references are wrapper\n  function check_other_function(path, array_name) {\n    const binding = path.scope.getBinding(array_name)\n    if (!binding.referencePaths) {\n      return\n    }\n    const ob_func_str = []\n    const ob_dec_call = []\n    for (const ref of binding.referencePaths) {\n      if (ref.findParent((path) => path.removed)) {\n        continue\n      }\n      if (ref.parentPath.isMemberExpression() && ref.key === 'object') {\n        const rm_path = check_wrapper(ref, array_name)\n        if (!rm_path) {\n          console.error('Unexpected reference')\n          return null\n        }\n        const code = generator(rm_path.node, optGenMin).code\n        rm_path.get('body').replaceWith(t.blockStatement([]))\n        ob_func_str.push(code)\n        ob_dec_call.push({ name: rm_path.node.id.name, path: rm_path })\n      } else {\n        console.error('Unexpected reference')\n        return null\n      }\n    }\n    if (!ob_func_str.length) {\n      return null\n    }\n    ob_func_str.push(generator(path.node, optGenMin).code)\n    path.remove()\n    return {\n      version: 0,\n      stringArrayName: array_name,\n      stringArrayCodes: ob_func_str,\n      stringArrayCalls: ob_dec_call,\n    }\n  }\n  let ret_obj = {\n    version: 0,\n    stringArrayName: null,\n    stringArrayCodes: [],\n    stringArrayCalls: [],\n  }\n  function check_string_array(path) {\n    if (path.getFunctionParent()) {\n      return\n    }\n    const init = path.get('init')\n    if (!init.isArrayExpression()) {\n      return\n    }\n    if (!init.node.elements.length) {\n      return\n    }\n    const array_name = path.node.id.name\n    const obj = check_other_function(path, array_name)\n    if (obj) {\n      ret_obj = obj\n      path.stop()\n    }\n  }\n  traverse(ast, {\n    VariableDeclarator: check_string_array,\n  })\n  return ret_obj\n}\n\n/**\n * Before version 2.19.0, the string-array is a single array.\n * Hence, we have to find StringArrayRotateFunction instead.\n *\n * @param {t.File} ast The ast file\n * @returns Object\n */\nfunction stringArrayV2(ast) {\n  console.info('Try v2 mode...')\n  let obj = {\n    version: 2,\n    stringArrayName: null,\n    stringArrayCodes: [],\n    stringArrayCalls: [],\n  }\n  // Function to rotate string list (\"func2\")\n  function find_rotate_function(path) {\n    const callee = path.get('callee')\n    const args = path.node.arguments\n    if (\n      !callee.isFunctionExpression() ||\n      callee.node.params.length !== 2 ||\n      args.length == 0 ||\n      args.length > 2 ||\n      !t.isIdentifier(args[0])\n    ) {\n      return\n    }\n    const arr = callee.node.params[0].name\n    const cmpV = callee.node.params[1].name\n    // >= 2.10.0\n    const fp1 = `(){try{if()break${arr}push(${arr}shift())}catch(){${arr}push(${arr}shift())}}`\n    // < 2.10.0\n    const fp2 = `=function(){while(--){${arr}push(${arr}shift)}}${cmpV}`\n    const code = '' + callee.get('body')\n    if (!checkPattern(code, fp1) && !checkPattern(code, fp2)) {\n      return\n    }\n    obj.stringArrayName = args[0].name\n    // The string array can be found by its binding\n    const bind = path.scope.getBinding(obj.stringArrayName)\n    const def = t.variableDeclaration('var', [bind.path.node])\n    obj.stringArrayCodes.push(generator(def, optGenMin).code)\n    // The calls can be found by its references\n    for (let ref of bind.referencePaths) {\n      if (ref?.listKey === 'arguments') {\n        // This is the rotate function\n        continue\n      }\n      if (ref.findParent((path) => path.removed)) {\n        continue\n      }\n      // the key is 'object'\n      let up1 = ref.getFunctionParent()\n      if (up1.node.id) {\n        // 2.12.0 <= v < 2.15.4\n        // The `stringArrayCallsWrapperName` is included in the definition\n        obj.stringArrayCodes.push(generator(up1.node, optGenMin).code)\n        up1.node.body = t.blockStatement([])\n        obj.stringArrayCalls.push({ name: up1.node.id.name, path: up1 })\n        continue\n      }\n      if (up1.key === 'init') {\n        // v < 2.12.0\n        // The `stringArrayCallsWrapperName` is defined by VariableDeclarator\n        up1 = up1.parentPath\n        const node = t.variableDeclaration('var', [up1.node])\n        obj.stringArrayCodes.push(generator(node, optGenMin).code)\n        up1.node.init = null\n        obj.stringArrayCalls.push({ name: up1.node.id.name, path: up1 })\n        continue\n      }\n      // 2.15.4 <= v < 2.19.0\n      // The function includes another function with the same name\n      up1 = up1.parentPath\n      const wrapper = up1.node.left.name\n      let up2 = up1.getFunctionParent()\n      if (!up2 || up2.node?.id?.name !== wrapper) {\n        console.warn('Unexpected reference!')\n        continue\n      }\n      obj.stringArrayCodes.push(generator(up2.node, optGenMin).code)\n      up1.remove()\n      up2.node.body = t.blockStatement([])\n      obj.stringArrayCalls.push({ name: wrapper, path: up2 })\n    }\n    // Remove the string array\n    bind.path.remove()\n    // Add the rotate function\n    const node = t.expressionStatement(path.node)\n    obj.stringArrayCodes.push(generator(node, optGenMin).code)\n    path.stop()\n    if (path.parentPath.isUnaryExpression()) {\n      path.parentPath.remove()\n    } else {\n      path.remove()\n    }\n  }\n  traverse(ast, { CallExpression: find_rotate_function })\n  if (obj.stringArrayCodes.length < 3 || !obj.stringArrayCalls.length) {\n    console.error('Essential code missing!')\n    obj.stringArrayName = null\n  }\n  return obj\n}\n\n/**\n * Find the string-array codes by matching string-array function\n * (valid version >= 2.19.0)\n *\n * @param {t.File} ast The ast file\n * @returns Object\n */\nfunction stringArrayV3(ast) {\n  console.info('Try v3 mode...')\n  let ob_func_str = []\n  let ob_dec_name = []\n  let ob_string_func_name = null\n  // Normally, the string array func (\"func1\") follows the template below:\n  // function aaa() {\n  //   const bbb = [...]\n  //   aaa = function () {\n  //     return bbb;\n  //   };\n  //   return aaa();\n  // }\n  // In some cases (lint), the assignment is merged into the ReturnStatement\n  // After finding the possible func1, this method will check all the binding\n  // references and put the child encode function into list.\n  function find_string_array_function(path) {\n    if (path.getFunctionParent()) {\n      return\n    }\n    if (\n      !t.isIdentifier(path.node.id) ||\n      path.node.params.length ||\n      !t.isBlockStatement(path.node.body)\n    ) {\n      return\n    }\n    const body = path.node.body.body\n    if (body.length < 2 || body.length > 3) {\n      return\n    }\n    const name_func = path.node.id.name\n    let string_var = -1\n    try {\n      if (\n        body[0].declarations.length != 1 ||\n        !(string_var = body[0].declarations[0].id.name) ||\n        !t.isArrayExpression(body[0].declarations[0].init)\n      ) {\n        return\n      }\n      const nodes = [...body]\n      nodes.shift()\n      const code = generator(t.BlockStatement(nodes), optGenMin).code\n      const fp = `${name_func}=function(){return${string_var}}${name_func}()`\n      if (!checkPattern(code, fp)) {\n        return\n      }\n    } catch {\n      return\n    }\n    const binding = path.scope.getBinding(name_func)\n    if (!binding.referencePaths) {\n      return\n    }\n    let paths = binding.referencePaths\n    let nodes = []\n    // The sorting function maybe missing in some config\n    function find2(refer_path) {\n      if (\n        refer_path.parentPath.isCallExpression() &&\n        refer_path.listKey === 'arguments' &&\n        refer_path.key === 0\n      ) {\n        let rm_path = refer_path.parentPath\n        if (rm_path.parentPath.isExpressionStatement()) {\n          rm_path = rm_path.parentPath\n        }\n        nodes.push([rm_path.node, 'func2'])\n        rm_path.remove()\n      }\n    }\n    paths.map(find2)\n    function find3(refer_path) {\n      if (refer_path.findParent((path) => path.removed)) {\n        return\n      }\n      if (\n        refer_path.parentPath.isCallExpression() &&\n        refer_path.key === 'callee'\n      ) {\n        let rm_path = refer_path.parentPath.getFunctionParent()\n        if (name_func == rm_path.node.id.name) {\n          return\n        }\n        const code = generator(rm_path.node, optGenMin).code\n        rm_path.node.body = t.blockStatement([])\n        nodes.push([code, 'func3', rm_path])\n      } else {\n        console.error('Unexpected reference')\n      }\n    }\n    paths.map(find3)\n    if (!name_func) {\n      return\n    }\n    ob_string_func_name = name_func\n    ob_func_str.push(generator(path.node, optGenMin).code)\n    nodes.map(function (item) {\n      if (item[1] == 'func3') {\n        ob_func_str.push(item[0])\n        ob_dec_name.push({ name: item[2].node.id.name, path: item[2] })\n        return\n      }\n      let node = item[0]\n      if (t.isCallExpression(node)) {\n        node = t.expressionStatement(node)\n      }\n      ob_func_str.push(generator(node, optGenMin).code)\n    })\n    path.stop()\n    path.remove()\n  }\n  traverse(ast, { FunctionDeclaration: find_string_array_function })\n  return {\n    version: 3,\n    stringArrayName: ob_string_func_name,\n    stringArrayCodes: ob_func_str,\n    stringArrayCalls: ob_dec_name,\n  }\n}\n\nfunction decodeGlobal(ast) {\n  let obj = stringArrayV3(ast)\n  if (!obj.stringArrayName) {\n    obj = stringArrayV2(ast)\n  }\n  if (!obj.stringArrayName) {\n    obj = stringArrayV0(ast)\n  }\n  if (!obj.stringArrayName) {\n    console.error('Cannot find string list!')\n    return false\n  }\n  console.log(`String List Name: ${obj.stringArrayName}`)\n  let ob_func_str = obj.stringArrayCodes\n  let ob_dec_call = obj.stringArrayCalls\n  try {\n    virtualGlobalEval(ob_func_str.join(';'))\n  } catch (e) {\n    // issue #31\n    if (e.name === 'ReferenceError') {\n      let lost = e.message.split(' ')[0]\n      traverse(ast, {\n        Program(path) {\n          let loc = path.scope.getBinding(lost).path\n          let obj = t.variableDeclaration(loc.parent.kind, [loc.node])\n          ob_func_str.unshift(generator(obj, optGenMin).code)\n          loc.node.init = null\n          ob_dec_call.push({ name: lost, path: loc })\n          path.stop()\n        },\n      })\n      virtualGlobalEval(ob_func_str.join(';'))\n    }\n  }\n\n  // 递归删除混淆函数\n  function getChild(father) {\n    if (father.key !== 'argument' || !father.parentPath.isReturnStatement()) {\n      console.error(`Unexpected chained call: ${father}`)\n      return null\n    }\n    const func = father.getFunctionParent()\n    let name = func.node.id?.name\n    let root\n    let code\n    if (name) {\n      // FunctionDeclaration\n      // function A (...) { return function B (...) }\n      root = func\n      code = generator(root.node, optGenMin).code\n    } else {\n      // FunctionExpression\n      // var A = function (...) { return function B (...) }\n      root = func.parentPath\n      code = generator(t.variableDeclaration('var', [root])).code\n      name = root.node.id.name\n    }\n    return {\n      name: name,\n      path: root,\n      code: code,\n    }\n  }\n  function dfs(stk, item) {\n    stk.push(item)\n    const cur_val = item.name\n    console.log(`Enter sub ${stk.length}:${cur_val}`)\n    let pfx = ''\n    for (let parent of stk) {\n      pfx += parent.code + ';'\n    }\n    virtualGlobalEval(pfx)\n    let scope = item.path.scope\n    if (item.path.isFunctionDeclaration()) {\n      scope = item.path.parentPath.scope\n    }\n    const binding = scope.getBinding(cur_val)\n    binding.scope.crawl()\n    const refs = binding.scope.bindings[cur_val].referencePaths\n    const refs_next = []\n    // 有4种链式调用情况：\n    // - VariableDeclarator和FunctionDeclaration为原版\n    // - AssignmentExpression 出现于 #50\n    // - FunctionExpression 出现于 #94\n    for (let ref of refs) {\n      const parent = ref.parentPath\n      if (ref.key === 'callee') {\n        // CallExpression\n        let old_call = parent + ''\n        try {\n          // 运行成功则说明函数为直接调用并返回字符串\n          let new_str = virtualGlobalEval(old_call)\n          console.log(`map: ${old_call} -> ${new_str}`)\n          parent.replaceWith(t.StringLiteral(new_str))\n        } catch (e) {\n          // 运行失败则说明函数为其它混淆函数的子函数\n          console.log(`sub: ${old_call}`)\n          const ret = getChild(parent)\n          if (ret) {\n            refs_next.push(ret)\n          }\n        }\n      } else if (ref.key === 'init') {\n        // VariableDeclarator\n        refs_next.push({\n          name: ref.parent.id.name,\n          path: ref.parentPath,\n          code: 'var ' + ref.parentPath,\n        })\n      } else if (ref.key === 'right') {\n        // AssignmentExpression\n        // 这种情况尚不完善 可能会产生额外替换\n        refs_next.push({\n          name: ref.parent.left.name,\n          path: ref.parentPath,\n          code: 'var ' + ref.parentPath,\n        })\n      }\n    }\n    for (let ref of refs_next) {\n      dfs(stk, ref)\n    }\n    binding.scope.crawl()\n    console.log(`Exit sub ${stk.length}:${cur_val}`)\n    stk.pop()\n    if (!item.path.parentPath.isCallExpression()) {\n      item.path.remove()\n      binding.scope.crawl()\n      return\n    }\n    // 只会出现在AssignmentExpression情况下 需要再次运行\n    item.path.replaceWith(t.identifier(cur_val))\n    item.path = binding.path\n    binding.scope.crawl()\n    dfs(stk, item)\n  }\n  for (let item of ob_dec_call) {\n    item.code = ''\n    dfs([], item)\n  }\n  return true\n}\n\nfunction stringArrayLite(ast) {\n  const visitor = {\n    VariableDeclarator(path) {\n      const name = path.node.id.name\n      if (!path.get('init').isArrayExpression()) {\n        return\n      }\n      const elements = path.node.init.elements\n      for (const element of elements) {\n        if (!t.isLiteral(element)) {\n          return\n        }\n      }\n      const bind = path.scope.getBinding(name)\n      if (!bind.constant) {\n        return\n      }\n      for (const ref of bind.referencePaths) {\n        if (\n          !ref.parentPath.isMemberExpression() ||\n          ref.key !== 'object' ||\n          ref.parentPath.key == 'left' ||\n          !t.isNumericLiteral(ref.parent.property)\n        ) {\n          return\n        }\n      }\n      console.log(`Extract string array: ${name}`)\n      for (const ref of bind.referencePaths) {\n        const i = ref.parent.property.value\n        ref.parentPath.replaceWith(elements[i])\n      }\n      bind.scope.crawl()\n      path.remove()\n    },\n  }\n  traverse(ast, visitor)\n}\n\nfunction decodeCodeBlock(ast) {\n  // 合并字面量\n  traverse(ast, calculateConstantExp)\n  // 先合并分离的Object定义\n  traverse(ast, mergeObject)\n  // 在变量定义完成后判断是否为代码块加密内容\n  traverse(ast, parseControlFlowStorage)\n  // 合并字面量(在解除区域混淆后会出现新的可合并分割)\n  traverse(ast, calculateConstantExp)\n  return ast\n}\n\nfunction cleanSwitchCode(path) {\n  // 扁平控制：\n  // 会使用一个恒为true的while语句包裹一个switch语句\n  // switch语句的执行顺序又while语句上方的字符串决定\n  // 首先碰断是否符合这种情况\n  const node = path.node\n  let valid = false\n  if (t.isBooleanLiteral(node.test) && node.test.value) {\n    valid = true\n  }\n  if (t.isArrayExpression(node.test) && node.test.elements.length === 0) {\n    valid = true\n  }\n  if (!valid) {\n    return\n  }\n  if (!t.isBlockStatement(node.body)) {\n    return\n  }\n  const body = node.body.body\n  if (\n    !t.isSwitchStatement(body[0]) ||\n    !t.isMemberExpression(body[0].discriminant) ||\n    !t.isBreakStatement(body[1])\n  ) {\n    return\n  }\n  // switch语句的两个变量\n  const swithStm = body[0]\n  const arrName = swithStm.discriminant.object.name\n  const argName = swithStm.discriminant.property.argument.name\n  // 在while上面的节点寻找这两个变量\n  let arr = []\n  let rm = []\n  path.getAllPrevSiblings().forEach((pre_path) => {\n    if (!pre_path.isVariableDeclaration()) {\n      return\n    }\n    for (let i = 0; i < pre_path.node.declarations.length; ++i) {\n      const declaration = pre_path.get(`declarations.${i}`)\n      let { id, init } = declaration.node\n      if (arrName == id.name) {\n        if (t.isStringLiteral(init?.callee?.object)) {\n          arr = init.callee.object.value.split('|')\n          rm.push(declaration)\n        }\n      }\n      if (argName == id.name) {\n        if (t.isLiteral(init)) {\n          rm.push(declaration)\n        }\n      }\n    }\n  })\n  if (rm.length !== 2) {\n    return\n  }\n  rm.forEach((pre_path) => {\n    pre_path.remove()\n  })\n  console.log(`扁平化还原: ${arrName}[${argName}]`)\n  // 重建代码块\n  const caseList = swithStm.cases\n  let resultBody = []\n  arr.map((targetIdx) => {\n    // 从当前序号开始直到遇到continue\n    let valid = true\n    targetIdx = parseInt(targetIdx)\n    while (valid && targetIdx < caseList.length) {\n      const targetBody = caseList[targetIdx].consequent\n      const test = caseList[targetIdx].test\n      if (!t.isStringLiteral(test) || parseInt(test.value) !== targetIdx) {\n        console.log(`switch中出现乱序的序号: ${test.value}:${targetIdx}`)\n      }\n      for (let i = 0; i < targetBody.length; ++i) {\n        const s = targetBody[i]\n        if (t.isContinueStatement(s)) {\n          valid = false\n          break\n        }\n        if (t.isReturnStatement(s)) {\n          valid = false\n          resultBody.push(s)\n          break\n        }\n        if (t.isBreakStatement(s)) {\n          console.log(`switch中出现意外的break: ${arrName}[${argName}]`)\n        } else {\n          resultBody.push(s)\n        }\n      }\n      targetIdx++\n    }\n  })\n  // 替换整个while语句\n  path.replaceInline(resultBody)\n}\n\nfunction cleanDeadCode(ast) {\n  traverse(ast, calculateConstantExp)\n  traverse(ast, pruneIfBranch)\n  traverse(ast, { WhileStatement: { exit: cleanSwitchCode } })\n  return ast\n}\n\nfunction standardIfStatement(path) {\n  const consequent = path.get('consequent')\n  const alternate = path.get('alternate')\n  const test = path.get('test')\n  const evaluateTest = test.evaluateTruthy()\n\n  if (!consequent.isBlockStatement()) {\n    consequent.replaceWith(t.BlockStatement([consequent.node]))\n  }\n  if (alternate.node !== null && !alternate.isBlockStatement()) {\n    alternate.replaceWith(t.BlockStatement([alternate.node]))\n  }\n\n  if (consequent.node.body.length == 0) {\n    if (alternate.node == null) {\n      path.replaceWith(test.node)\n    } else {\n      consequent.replaceWith(alternate.node)\n      alternate.remove()\n      path.node.alternate = null\n      test.replaceWith(t.unaryExpression('!', test.node, true))\n    }\n  }\n\n  if (alternate.isBlockStatement() && alternate.node.body.length == 0) {\n    alternate.remove()\n    path.node.alternate = null\n  }\n\n  if (evaluateTest === true) {\n    path.replaceWithMultiple(consequent.node.body)\n  } else if (evaluateTest === false) {\n    alternate.node === null\n      ? path.remove()\n      : path.replaceWithMultiple(alternate.node.body)\n  }\n}\n\nfunction standardLoop(path) {\n  const node = path.node\n  if (!t.isBlockStatement(node.body)) {\n    node.body = t.BlockStatement([node.body])\n  }\n}\n\nfunction purifyCode(ast) {\n  // 标准化if语句\n  traverse(ast, { IfStatement: standardIfStatement })\n  // 标准化for语句\n  traverse(ast, { ForStatement: standardLoop })\n  // 标准化while语句\n  traverse(ast, { WhileStatement: standardLoop })\n  // 删除空语句\n  traverse(ast, {\n    EmptyStatement: (path) => {\n      path.remove()\n    },\n  })\n  traverse(ast, splitAssignment)\n  // 删除未使用的变量\n  traverse(ast, deleteUnusedVar)\n  // 替换索引器\n  function FormatMember(path) {\n    let curNode = path.node\n    if (!t.isStringLiteral(curNode.property)) {\n      return\n    }\n    if (curNode.computed === undefined || !curNode.computed === true) {\n      return\n    }\n    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(curNode.property.value)) {\n      return\n    }\n    curNode.property = t.identifier(curNode.property.value)\n    curNode.computed = false\n  }\n  traverse(ast, { MemberExpression: FormatMember })\n\n  // 替换类和对象的计算方法和计算属性\n  // [\"method\"](){} -> \"method\"(){}\n  function FormatComputed(path) {\n    let curNode = path.node\n    if (!t.isStringLiteral(curNode.key)) {\n      return\n    }\n    curNode.computed = false\n  }\n  // \"method\"(){} -> method(){}\n  function stringLiteralToIdentifier(path) {\n    let curNode = path.node\n    if (!t.isStringLiteral(curNode.key) || curNode.computed === true) {\n      return\n    }\n    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(curNode.key.value)) {\n      return\n    }\n    curNode.key = t.identifier(curNode.key.value)\n  }\n  traverse(ast, {\n    'Method|Property': (path) => {\n      FormatComputed(path)\n      stringLiteralToIdentifier(path)\n    },\n  })\n\n  // 拆分语句\n  traverse(ast, splitSequence)\n  return ast\n}\n\nfunction checkPattern(code, pattern) {\n  let i = 0\n  let j = 0\n  while (i < code.length && j < pattern.length) {\n    if (code[i] == pattern[j]) {\n      ++j\n    }\n    ++i\n  }\n  return j == pattern.length\n}\n\nconst deleteSelfDefendingCode = {\n  VariableDeclarator(path) {\n    const { id, init } = path.node\n    const selfName = id.name\n    if (!t.isCallExpression(init)) {\n      return\n    }\n    if (!t.isIdentifier(init.callee)) {\n      return\n    }\n    const callName = init.callee.name\n    const args = init.arguments\n    if (\n      args.length != 2 ||\n      !t.isThisExpression(args[0]) ||\n      !t.isFunctionExpression(args[1])\n    ) {\n      return\n    }\n    const block = generator(args[1]).code\n    const patterns = [\n      // @7920538\n      `return${selfName}.toString().search().toString().constructor(${selfName}).search()`,\n      // @7135b09\n      `const=function(){const=.constructor()return.test(${selfName})}return()`,\n      // #94\n      `var=function(){var=.constructor()return.test(${selfName})}return()`,\n    ]\n    let valid = false\n    for (let pattern of patterns) {\n      valid |= checkPattern(block, pattern)\n    }\n    if (!valid) {\n      return\n    }\n    const refs = path.scope.bindings[selfName].referencePaths\n    for (let ref of refs) {\n      if (ref.key == 'callee') {\n        ref.parentPath.remove()\n        break\n      }\n    }\n    path.remove()\n    console.info(`Remove SelfDefendingFunc: ${selfName}`)\n    const scope = path.scope.getBinding(callName).scope\n    scope.crawl()\n    const bind = scope.bindings[callName]\n    if (bind.referenced) {\n      console.error(`Call func ${callName} unexpected ref!`)\n    }\n    bind.path.remove()\n    console.info(`Remove CallFunc: ${callName}`)\n  },\n}\n\nconst deleteDebugProtectionCode = {\n  FunctionDeclaration(path) {\n    const { id, params, body } = path.node\n    if (\n      !t.isIdentifier(id) ||\n      params.length !== 1 ||\n      !t.isIdentifier(params[0]) ||\n      !t.isBlockStatement(body) ||\n      body.body.length !== 2 ||\n      !t.isFunctionDeclaration(body.body[0]) ||\n      !t.isTryStatement(body.body[1])\n    ) {\n      return\n    }\n    const debugName = id.name\n    const ret = params[0].name\n    const subNode = body.body[0]\n    if (\n      !t.isIdentifier(subNode.id) ||\n      subNode.params.length !== 1 ||\n      !t.isIdentifier(subNode.params[0])\n    ) {\n      return\n    }\n    const subName = subNode.id.name\n    const counter = subNode.params[0].name\n    const code = generator(body).code\n    const pattern = `function${subName}(${counter}){${counter}debugger${subName}(++${counter})}try{if(${ret})return${subName}${subName}(0)}catch(){}`\n    if (!checkPattern(code, pattern)) {\n      return\n    }\n    const scope1 = path.parentPath.scope\n    const refs = scope1.bindings[debugName].referencePaths\n    for (let ref of refs) {\n      if (ref.findParent((path) => path.removed)) {\n        continue\n      }\n      if (ref.key == 0) {\n        // DebugProtectionFunctionInterval @e8e92c6\n        const rm = ref.getFunctionParent().parentPath\n        rm.remove()\n        continue\n      }\n      // ref.key == 'callee'\n      const up1 = ref.getFunctionParent()\n      const callName = up1.parent.callee.name\n      if (callName === 'setInterval') {\n        // DebugProtectionFunctionInterval @51523c0\n        const rm = up1.parentPath\n        rm.remove()\n        continue\n      }\n      const up2 = up1.getFunctionParent()?.parentPath\n      if (up2) {\n        // DebugProtectionFunctionCall\n        const scope2 = up2.scope.getBinding(callName).scope\n        up2.remove()\n        scope1.crawl()\n        scope2.crawl()\n        const bind = scope2.bindings[callName]\n        bind.path.remove()\n        console.info(`Remove CallFunc: ${callName}`)\n        continue\n      }\n      // exceptions #95\n      const rm = ref.parentPath\n      rm.remove()\n    }\n    path.remove()\n    console.info(`Remove DebugProtectionFunc: ${debugName}`)\n  },\n}\n\nconst deleteConsoleOutputCode = {\n  VariableDeclarator(path) {\n    const { id, init } = path.node\n    const selfName = id.name\n    if (!t.isCallExpression(init)) {\n      return\n    }\n    if (!t.isIdentifier(init.callee)) {\n      return\n    }\n    const callName = init.callee.name\n    const args = init.arguments\n    if (\n      args.length != 2 ||\n      !t.isThisExpression(args[0]) ||\n      !t.isFunctionExpression(args[1])\n    ) {\n      return\n    }\n    const block = generator(args[1]).code\n    const pattern = `console=console=log,warn,info,error,for(){${callName}constructor.prototype.bind${callName}${callName}bind${callName}}`\n    if (!checkPattern(block, pattern)) {\n      return\n    }\n    const refs = path.scope.bindings[selfName].referencePaths\n    for (let ref of refs) {\n      if (ref.key == 'callee') {\n        ref.parentPath.remove()\n        break\n      }\n    }\n    path.remove()\n    console.info(`Remove ConsoleOutputFunc: ${selfName}`)\n    const scope = path.scope.getBinding(callName).scope\n    scope.crawl()\n    const bind = scope.bindings[callName]\n    if (bind.referenced) {\n      console.error(`Call func ${callName} unexpected ref!`)\n    }\n    bind.path.remove()\n    console.info(`Remove CallFunc: ${callName}`)\n  },\n}\n\nfunction unlockEnv(ast) {\n  //可能会误删一些代码，可屏蔽\n  traverse(ast, deleteSelfDefendingCode)\n  traverse(ast, deleteDebugProtectionCode)\n  traverse(ast, deleteConsoleOutputCode)\n  return ast\n}\n\nexport default function (code) {\n  let ret = PluginEval.unpack(code)\n  let global_eval = false\n  if (ret) {\n    global_eval = true\n    code = ret\n  }\n  let ast\n  try {\n    ast = parse(code, { errorRecovery: true })\n  } catch (e) {\n    console.error(`Cannot parse code: ${e.reasonCode}`)\n    return null\n  }\n  // IllegalReturn\n  traverse(ast, deleteIllegalReturn)\n  // Lint before split statements\n  traverse(ast, lintIfStatement)\n  // Split declarations to avoid bugs\n  traverse(ast, splitVarDeclaration)\n  // 清理二进制显示内容\n  traverse(ast, {\n    StringLiteral: ({ node }) => {\n      delete node.extra\n    },\n    NumericLiteral: ({ node }) => {\n      delete node.extra\n    },\n  })\n  console.log('还原数值...')\n  if (!decodeObject(ast)) {\n    return null\n  }\n  console.log('处理全局加密...')\n  if (!decodeGlobal(ast)) {\n    return null\n  }\n  console.log('提高代码可读性...')\n  ast = purifyCode(ast)\n  console.log('处理代码块加密...')\n  stringArrayLite(ast)\n  ast = decodeCodeBlock(ast)\n  console.log('清理死代码...')\n  ast = cleanDeadCode(ast)\n  // 刷新代码\n  ast = parse(generator(ast, optGenMin).code, { errorRecovery: true })\n  console.log('提高代码可读性...')\n  ast = purifyCode(ast)\n  console.log('解除环境限制...')\n  ast = unlockEnv(ast)\n  console.log('净化完成')\n  code = generator(ast, { jsescOption: { minimal: true } }).code\n  if (global_eval) {\n    code = PluginEval.pack(code)\n  }\n  return code\n}\n"
  },
  {
    "path": "src/plugin/sojson.js",
    "content": "/**\n * 在 babel_asttool.js 的基础上修改而来\n */\nimport { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimport _traverse from '@babel/traverse'\nconst traverse = _traverse.default\nimport * as t from '@babel/types'\nimport ivm from 'isolated-vm'\nimport PluginEval from './eval.js'\nimport calculateConstantExp from '../visitor/calculate-constant-exp.js'\nimport deleteUnusedVar from '../visitor/delete-unused-var.js'\nimport parseControlFlowStorage from '../visitor/parse-control-flow-storage.js'\nimport pruneIfBranch from '../visitor/prune-if-branch.js'\nimport splitSequence from '../visitor/split-sequence.js'\n\nconst isolate = new ivm.Isolate()\nconst globalContext = isolate.createContextSync()\nfunction virtualGlobalEval(jsStr) {\n  return globalContext.evalSync(String(jsStr))\n}\n\nfunction decodeGlobal(ast) {\n  // 清理空语句\n  let i = 0\n  while (i < ast.program.body.length) {\n    if (t.isEmptyStatement(ast.program.body[i])) {\n      ast.program.body.splice(i, 1)\n    } else {\n      ++i\n    }\n  }\n  // 前3句非空语句分别为签名信息、预处理函数、解密函数。\n  if (i < 3) {\n    console.log('Error: code too short')\n    return false\n  }\n  // 分离解密语句与内容语句\n  let decrypt_code = ast.program.body.slice(0, 3)\n  if (!t.isVariableDeclaration(decrypt_code[0])) {\n    console.log('Error: line 1 is not variable declaration')\n    return false\n  }\n  let decrypt_fun = decrypt_code[2]\n  if (t.isExpressionStatement(decrypt_fun)) {\n    decrypt_fun = decrypt_code[1]\n  }\n  let decrypt_val\n  if (t.isVariableDeclaration(decrypt_fun)) {\n    decrypt_val = decrypt_fun.declarations[0].id.name\n  } else if (t.isFunctionDeclaration(decrypt_fun)) {\n    decrypt_val = decrypt_fun.id.name\n  } else {\n    console.log('Error: cannot find decrypt variable')\n    return false\n  }\n  console.log(`主加密变量: ${decrypt_val}`)\n  let content_code = ast.program.body.slice(3)\n  // 运行解密语句\n  ast.program.body = decrypt_code\n  let { code } = generator(ast, {\n    compact: true,\n  })\n  virtualGlobalEval(code)\n  // 遍历内容语句\n  function funToStr(path) {\n    let node = path.node\n    if (!t.isIdentifier(node.callee, { name: decrypt_val })) {\n      return\n    }\n    let tmp = path.toString()\n    let value = virtualGlobalEval(tmp)\n    // console.log(`还原前：${tmp} 还原后：${value}`)\n    path.replaceWith(t.valueToNode(value))\n  }\n  function memToStr(path) {\n    let node = path.node\n    if (!t.isIdentifier(node.object, { name: decrypt_val })) {\n      return\n    }\n    let tmp = path.toString()\n    let value = virtualGlobalEval(tmp)\n    // console.log(`还原前：${tmp} 还原后：${value}`)\n    path.replaceWith(t.valueToNode(value))\n  }\n  ast.program.body = content_code\n  traverse(ast, {\n    CallExpression: funToStr,\n    MemberExpression: memToStr,\n  })\n  return ast\n}\n\nfunction cleanSwitchCode(path) {\n  // 扁平控制：\n  // 会使用一个恒为true的while语句包裹一个switch语句\n  // switch语句的执行顺序又while语句上方的字符串决定\n  // 首先碰断是否符合这种情况\n  const node = path.node\n  if (!(t.isBooleanLiteral(node.test) || t.isUnaryExpression(node.test))) {\n    return\n  }\n  if (!(node.test.prefix || node.test.value)) {\n    return\n  }\n  if (!t.isBlockStatement(node.body)) {\n    return\n  }\n  const body = node.body.body\n  if (\n    !t.isSwitchStatement(body[0]) ||\n    !t.isMemberExpression(body[0].discriminant) ||\n    !t.isBreakStatement(body[1])\n  ) {\n    return\n  }\n  // switch语句的两个变量\n  const swithStm = body[0]\n  const arrName = swithStm.discriminant.object.name\n  const argName = swithStm.discriminant.property.argument.name\n  console.log(`扁平化还原: ${arrName}[${argName}]`)\n  // 在while上面的节点寻找这两个变量\n  let arr = []\n  path.getAllPrevSiblings().forEach((pre_path) => {\n    const { declarations } = pre_path.node\n    let { id, init } = declarations[0]\n    if (arrName == id.name) {\n      arr = init.callee.object.value.split('|')\n      pre_path.remove()\n    }\n    if (argName == id.name) {\n      pre_path.remove()\n    }\n  })\n  // 重建代码块\n  const caseList = swithStm.cases\n  let resultBody = []\n  arr.map((targetIdx) => {\n    // 从当前序号开始直到遇到continue\n    let valid = true\n    targetIdx = parseInt(targetIdx)\n    while (valid && targetIdx < caseList.length) {\n      const targetBody = caseList[targetIdx].consequent\n      const test = caseList[targetIdx].test\n      if (!t.isStringLiteral(test) || parseInt(test.value) !== targetIdx) {\n        console.log(`switch中出现乱序的序号: ${test.value}:${targetIdx}`)\n      }\n      for (let i = 0; i < targetBody.length; ++i) {\n        const s = targetBody[i]\n        if (t.isContinueStatement(s)) {\n          valid = false\n          break\n        }\n        if (t.isReturnStatement(s)) {\n          valid = false\n          resultBody.push(s)\n          break\n        }\n        if (t.isBreakStatement(s)) {\n          console.log(`switch中出现意外的break: ${arrName}[${argName}]`)\n        } else {\n          resultBody.push(s)\n        }\n      }\n      targetIdx++\n    }\n  })\n  // 替换整个while语句\n  path.replaceInline(resultBody)\n}\n\nfunction cleanDeadCode(ast) {\n  traverse(ast, calculateConstantExp)\n  traverse(ast, pruneIfBranch)\n  traverse(ast, { WhileStatement: { exit: cleanSwitchCode } })\n  return ast\n}\n\nfunction checkPattern(code, pattern) {\n  let i = 0\n  let j = 0\n  while (i < code.length && j < pattern.length) {\n    if (code[i] == pattern[j]) {\n      ++j\n    }\n    ++i\n  }\n  return j == pattern.length\n}\n\n/**\n * Two RegExp tests will be conducted here:\n * * If '\\n' exists (code formatted)\n * * If '\\u' or '\\x' does not exist (literal formatted)\n *\n * An infinite call stack will appear if either of the test fails.\n * (by replacing the 'e' with '\\u0435')\n */\nconst deleteSelfDefendingCode = {\n  VariableDeclarator(path) {\n    const { id, init } = path.node\n    const selfName = id.name\n    if (!t.isCallExpression(init)) {\n      return\n    }\n    if (!t.isIdentifier(init.callee)) {\n      return\n    }\n    const callName = init.callee.name\n    const args = init.arguments\n    if (\n      args.length != 2 ||\n      !t.isThisExpression(args[0]) ||\n      !t.isFunctionExpression(args[1])\n    ) {\n      return\n    }\n    const block = generator(args[1]).code\n    const pattern = `RegExp()return.test(.toString())RegExp()return.test(.toString())\\u0435\\u0435`\n    if (!checkPattern(block, pattern)) {\n      return\n    }\n    const refs = path.scope.bindings[selfName].referencePaths\n    for (let ref of refs) {\n      if (ref.key == 'callee') {\n        ref.parentPath.remove()\n        break\n      }\n    }\n    path.remove()\n    console.info(`Remove SelfDefendingFunc: ${selfName}`)\n    const scope = path.scope.getBinding(callName).scope\n    scope.crawl()\n    const bind = scope.bindings[callName]\n    if (bind.referenced) {\n      console.error(`Call func ${callName} unexpected ref!`)\n    }\n    bind.path.remove()\n    console.info(`Remove CallFunc: ${callName}`)\n  },\n}\n\n/**\n * A \"debugger\" will be inserted by:\n * * v5: directly.\n * * v6: calling Function constructor twice.\n */\nconst deleteDebugProtectionCode = {\n  FunctionDeclaration(path) {\n    const { id, params, body } = path.node\n    if (\n      !t.isIdentifier(id) ||\n      params.length !== 1 ||\n      !t.isIdentifier(params[0]) ||\n      !t.isBlockStatement(body) ||\n      body.body.length !== 2 ||\n      !t.isFunctionDeclaration(body.body[0]) ||\n      !t.isTryStatement(body.body[1])\n    ) {\n      return\n    }\n    const debugName = id.name\n    const ret = params[0].name\n    const subNode = body.body[0]\n    if (\n      !t.isIdentifier(subNode.id) ||\n      subNode.params.length !== 1 ||\n      !t.isIdentifier(subNode.params[0])\n    ) {\n      return\n    }\n    const subName = subNode.id.name\n    const counter = subNode.params[0].name\n    const code = generator(body).code\n    const pattern = `function${subName}(${counter}){${counter}debug${subName}(++${counter})}try{if(${ret})return${subName}${subName}(0)}catch(){}`\n    if (!checkPattern(code, pattern)) {\n      return\n    }\n    const scope1 = path.parentPath.scope\n    const refs = scope1.bindings[debugName].referencePaths\n    for (let ref of refs) {\n      if (ref.findParent((path) => path.removed)) {\n        continue\n      }\n      let parent = ref.getFunctionParent()\n      if (parent.key == 0) {\n        // DebugProtectionFunctionInterval\n        // window.setInterval(Function(), ...)\n        const rm = parent.parentPath\n        rm.remove()\n        continue\n      }\n      // DebugProtectionFunctionCall\n      const callName = parent.parent.callee.name\n      const up2 = parent.getFunctionParent().parentPath\n      const scope2 = up2.scope.getBinding(callName).scope\n      up2.remove()\n      scope1.crawl()\n      scope2.crawl()\n      const bind = scope2.bindings[callName]\n      bind.path.remove()\n      console.info(`Remove CallFunc: ${callName}`)\n    }\n    path.remove()\n    console.info(`Remove DebugProtectionFunc: ${debugName}`)\n  },\n}\n\nconst deleteConsoleOutputCode = {\n  VariableDeclarator(path) {\n    const { id, init } = path.node\n    const selfName = id.name\n    if (!t.isCallExpression(init)) {\n      return\n    }\n    if (!t.isIdentifier(init.callee)) {\n      return\n    }\n    const callName = init.callee.name\n    const args = init.arguments\n    if (\n      args.length != 2 ||\n      !t.isThisExpression(args[0]) ||\n      !t.isFunctionExpression(args[1])\n    ) {\n      return\n    }\n    const body = args[1].body.body\n    if (body.length !== 3) {\n      return\n    }\n    if (\n      !t.isVariableDeclaration(body[0]) ||\n      !t.isVariableDeclaration(body[1]) ||\n      !t.isIfStatement(body[2])\n    ) {\n      return\n    }\n    const feature = [\n      [],\n      ['window', 'process', 'require', 'global'],\n      [\n        'console',\n        'log',\n        'warn',\n        'debug',\n        'info',\n        'error',\n        'exception',\n        'trace',\n      ],\n    ]\n    let valid = true\n    for (let i = 1; i < 3; ++i) {\n      const { code } = generator(body[i])\n      feature[i].map((key) => {\n        if (code.indexOf(key) == -1) {\n          valid = false\n        }\n      })\n    }\n    if (!valid) {\n      return\n    }\n    const refs = path.scope.bindings[selfName].referencePaths\n    for (let ref of refs) {\n      if (ref.key == 'callee') {\n        ref.parentPath.remove()\n        break\n      }\n    }\n    path.remove()\n    console.info(`Remove ConsoleOutputFunc: ${selfName}`)\n    const scope = path.scope.getBinding(callName).scope\n    scope.crawl()\n    const bind = scope.bindings[callName]\n    if (bind.referenced) {\n      console.error(`Call func ${callName} unexpected ref!`)\n    }\n    bind.path.remove()\n    console.info(`Remove CallFunc: ${callName}`)\n  },\n}\n\nconst deleteVersionCheck = {\n  StringLiteral(path) {\n    const msg = '删除版本号，js会定期弹窗，还请支持我们的工作'\n    if (path.node.value !== msg) {\n      return\n    }\n    let fnPath = path.getFunctionParent().parentPath\n    if (!fnPath.isCallExpression()) {\n      return\n    }\n    fnPath.remove()\n    console.log('Remove VersionCheck')\n  },\n}\n\nfunction unlockEnv(ast) {\n  // 查找并删除`自卫模式`函数\n  traverse(ast, deleteSelfDefendingCode)\n  // 查找并删除`禁止控制台调试`函数\n  traverse(ast, deleteDebugProtectionCode)\n  // 清空`禁止控制台输出`函数\n  traverse(ast, deleteConsoleOutputCode)\n  // 删除版本号检测\n  traverse(ast, deleteVersionCheck)\n  return ast\n}\n\nfunction purifyFunction(path) {\n  const node = path.node\n  if (!t.isIdentifier(node.left) || !t.isFunctionExpression(node.right)) {\n    return\n  }\n  const name = node.left.name\n  if (node.right.body.body.length !== 1) {\n    return\n  }\n  let retStmt = node.right.body.body[0]\n  if (!t.isReturnStatement(retStmt)) {\n    return\n  }\n  if (!t.isBinaryExpression(retStmt.argument, { operator: '+' })) {\n    return\n  }\n  try {\n    const fnPath = path.getFunctionParent() || path.scope.path\n    fnPath.traverse({\n      CallExpression: function (_path) {\n        const _node = _path.node.callee\n        if (!t.isIdentifier(_node, { name: name })) {\n          return\n        }\n        let args = _path.node.arguments\n        _path.replaceWith(t.binaryExpression('+', args[0], args[1]))\n      },\n    })\n    path.remove()\n    console.log(`拼接类函数: ${name}`)\n  } catch {\n    let code = generator(path.node, { minified: true }).code\n    console.warn('Purify function failed: ' + code)\n  }\n}\n\nfunction purifyCode(ast) {\n  // 净化拼接字符串的函数\n  traverse(ast, { AssignmentExpression: purifyFunction })\n  // 计算常量表达式\n  traverse(ast, calculateConstantExp)\n  // 替换索引器\n  function FormatMember(path) {\n    // _0x19882c['removeCookie']['toString']()\n    //  |\n    //  |\n    //  |\n    //  v\n    // _0x19882c.removeCookie.toString()\n    let curNode = path.node\n    if (!t.isStringLiteral(curNode.property)) {\n      return\n    }\n    if (curNode.computed === undefined || !curNode.computed === true) {\n      return\n    }\n    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(curNode.property.value)) {\n      return\n    }\n    curNode.property = t.identifier(curNode.property.value)\n    curNode.computed = false\n  }\n  traverse(ast, { MemberExpression: FormatMember })\n  // 分割表达式\n  traverse(ast, splitSequence)\n  // 删除空语句\n  traverse(ast, {\n    EmptyStatement: (path) => {\n      path.remove()\n    },\n  })\n  // 删除未使用的变量\n  traverse(ast, deleteUnusedVar)\n  return ast\n}\n\nexport default function (code) {\n  let ret = PluginEval.unpack(code)\n  let global_eval = false\n  if (ret) {\n    global_eval = true\n    code = ret\n  }\n  let ast = parse(code)\n  // 清理二进制显示内容\n  traverse(ast, {\n    StringLiteral: ({ node }) => {\n      delete node.extra\n    },\n  })\n  traverse(ast, {\n    NumericLiteral: ({ node }) => {\n      delete node.extra\n    },\n  })\n  console.log('处理全局加密...')\n  ast = decodeGlobal(ast)\n  if (!ast) {\n    return null\n  }\n  console.log('处理代码块加密...')\n  traverse(ast, parseControlFlowStorage)\n  console.log('清理死代码...')\n  ast = cleanDeadCode(ast)\n  // 刷新代码\n  ast = parse(\n    generator(ast, {\n      comments: false,\n      jsescOption: { minimal: true },\n    }).code\n  )\n  console.log('提高代码可读性...')\n  ast = purifyCode(ast)\n  console.log('解除环境限制...')\n  ast = unlockEnv(ast)\n  console.log('净化完成')\n  code = generator(ast, {\n    comments: false,\n    jsescOption: { minimal: true },\n  }).code\n  if (global_eval) {\n    code = PluginEval.pack(code)\n  }\n  return code\n}\n"
  },
  {
    "path": "src/plugin/sojsonv7.js",
    "content": "/**\n * For jsjiami.com.v7\n */\nimport { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimport _traverse from '@babel/traverse'\nconst traverse = _traverse.default\nimport * as t from '@babel/types'\nimport ivm from 'isolated-vm'\nimport PluginEval from './eval.js'\nimport calculateConstantExp from '../visitor/calculate-constant-exp.js'\nimport deleteIllegalReturn from '../visitor/delete-illegal-return.js'\nimport deleteUnusedVar from '../visitor/delete-unused-var.js'\nimport parseControlFlowStorage from '../visitor/parse-control-flow-storage.js'\nimport pruneIfBranch from '../visitor/prune-if-branch.js'\nimport splitSequence from '../visitor/split-sequence.js'\n\nconst isolate = new ivm.Isolate()\nconst globalContext = isolate.createContextSync()\nfunction virtualGlobalEval(jsStr) {\n  return globalContext.evalSync(String(jsStr))\n}\nfunction evalOneTime(str) {\n  const vm = new ivm.Isolate()\n  const ret = vm.createContextSync().evalSync(String(str))\n  vm.dispose()\n  return ret\n}\n\nfunction decodeGlobal(ast) {\n  // 清理空语句\n  let i = 0\n  while (i < ast.program.body.length) {\n    if (t.isEmptyStatement(ast.program.body[i])) {\n      ast.program.body.splice(i, 1)\n    } else {\n      ++i\n    }\n  }\n  // line 1: version and string table\n  // line x: preprocessing function of string table\n  // line y: main encode function containing the var of string table\n  if (i < 3) {\n    console.log('Error: code too short')\n    return false\n  }\n  // split the first line\n  traverse(ast, {\n    Program(path) {\n      path.stop()\n      const l1 = path.get('body.0')\n      if (!l1.isVariableDeclaration()) {\n        return\n      }\n      const defs = l1.node.declarations\n      const kind = l1.node.kind\n      for (let i = defs.length - 1; i; --i) {\n        l1.insertAfter(t.VariableDeclaration(kind, [defs[i]]))\n        l1.get(`declarations.${i}`).remove()\n      }\n      l1.scope.crawl()\n    },\n  })\n  // find the main encode function\n  // [version, string-array, call, ...]\n  let decrypt_code = []\n  for (let i = 0; i < 3; ++i) {\n    decrypt_code.push(t.EmptyStatement())\n  }\n  const first_line = ast.program.body[0]\n  let var_version\n  if (t.isVariableDeclaration(first_line)) {\n    if (first_line.declarations.length) {\n      var_version = first_line.declarations[0].id.name\n    }\n  } else if (t.isCallExpression(first_line?.expression)) {\n    let call_func = first_line.expression.callee?.name\n    let i = ast.program.body.length\n    let find = false\n    while (--i) {\n      let part = ast.program.body[i]\n      if (!t.isFunctionDeclaration(part) || part?.id?.name !== call_func) {\n        continue\n      }\n      if (find) {\n        // remove duplicate definition\n        ast.program.body[i] = t.emptyStatement()\n        continue\n      }\n      find = true\n      let obj = part.body.body[0]?.expression?.left\n      if (!obj || !t.isMemberExpression(obj) || obj.object?.name !== 'global') {\n        break\n      }\n      var_version = obj.property?.value\n      decrypt_code.push(part)\n      ast.program.body[i] = t.emptyStatement()\n      continue\n    }\n  }\n  if (!var_version) {\n    console.error('Line 1 is not version variable!')\n    return false\n  }\n  console.info(`Version var: ${var_version}`)\n  decrypt_code[0] = first_line\n  ast.program.body.shift()\n\n  // iterate and classify all refs of var_version\n  const refs = {\n    string_var: null,\n    string_path: null,\n    def: [],\n  }\n  traverse(ast, {\n    Identifier: (path) => {\n      const name = path.node.name\n      if (name !== var_version) {\n        return\n      }\n      const up1 = path.parentPath\n      if (up1.isVariableDeclarator()) {\n        refs.def.push(path)\n      } else if (up1.isArrayExpression()) {\n        let node_table = path.getFunctionParent()\n        while (node_table.getFunctionParent()) {\n          node_table = node_table.getFunctionParent()\n        }\n        let var_string_table = null\n        if (node_table.node.id) {\n          var_string_table = node_table.node.id.name\n        } else {\n          while (!node_table.isVariableDeclarator()) {\n            node_table = node_table.parentPath\n          }\n          var_string_table = node_table.node.id.name\n        }\n        let valid = true\n        up1.traverse({\n          MemberExpression(path) {\n            valid = false\n            path.stop()\n          },\n        })\n        if (valid) {\n          refs.string_var = var_string_table\n          refs.string_path = node_table\n        } else {\n          console.info(`Drop string table: ${var_string_table}`)\n        }\n      } else if (up1.isAssignmentExpression() && path.key === 'left') {\n        // We don't need to execute this reference\n        // Instead, we can delete it directly\n        const up2 = up1.parentPath\n        up2.replaceWith(up2.node.left)\n      } else {\n        console.warn(`Unexpected ref var_version: ${up1}`)\n      }\n    },\n  })\n  // check if contains string table\n  let var_string_table = refs.string_var\n  if (!var_string_table) {\n    console.error('Cannot find string table')\n    return false\n  }\n  //  check if contains rotate function and decrypt variable\n  let decrypt_val\n  let decrypt_path\n  let binds = refs.string_path.scope.getBinding(var_string_table)\n  function parse_main_call(path) {\n    decrypt_path = path\n    const node = path.node\n    const copy = t.functionDeclaration(node.id, node.params, node.body)\n    node.body = t.blockStatement([])\n    return copy\n  }\n  // remove path of string table\n  if (refs.string_path.isVariableDeclarator()) {\n    decrypt_code[1] = t.variableDeclaration('var', [refs.string_path.node])\n  } else {\n    decrypt_code[1] = refs.string_path.node\n  }\n  refs.string_path.remove()\n  // iterate refs\n  let cache = undefined\n  for (let bind of binds.referencePaths) {\n    if (bind.findParent((path) => path.removed)) {\n      continue\n    }\n    const parent = bind.parentPath\n    if (parent.isCallExpression() && bind.listKey === 'arguments') {\n      // This is the rotate function.\n      // If it's in a sequence expression, it can be handled together.\n      // Or, we should handle it after this iteration.\n      cache = parent\n      continue\n    }\n    if (parent.isSequenceExpression()) {\n      // rotate function\n      decrypt_code.push(t.expressionStatement(parent.node))\n      const up2 = parent.parentPath\n      if (up2.isIfStatement()) {\n        // In the new version, rotate function will be enclosed by an\n        // empty IfStatement\n        up2.remove()\n      } else {\n        parent.remove()\n      }\n      continue\n    }\n    if (parent.isVariableDeclarator()) {\n      // main decrypt val\n      let top = parent.getFunctionParent()\n      while (top.getFunctionParent()) {\n        top = top.getFunctionParent()\n      }\n      decrypt_code[2] = parse_main_call(top)\n      decrypt_val = top.node.id.name\n      continue\n    }\n    if (parent.isCallExpression() && !parent.node.arguments.length) {\n      // main decrypt val\n      if (!t.isVariableDeclarator(parent.parentPath.node)) {\n        continue\n      }\n      let top = parent.getFunctionParent()\n      while (top.getFunctionParent()) {\n        top = top.getFunctionParent()\n      }\n      decrypt_code[2] = parse_main_call(top)\n      decrypt_val = top.node.id.name\n      continue\n    }\n    if (parent.isExpressionStatement()) {\n      parent.remove()\n      continue\n    }\n    console.warn(`Unexpected ref var_string_table: ${parent}`)\n  }\n  // If rotateFunction is detected but not handled, we should handle it now.\n  if (decrypt_code.length === 3 && cache) {\n    if (cache.parentPath.isExpressionStatement()) {\n      decrypt_code.push(cache.parent)\n      cache = cache.parentPath\n    } else {\n      decrypt_code.push(t.expressionStatement(cache.node))\n    }\n    cache.remove()\n  }\n  decrypt_path.parentPath.scope.crawl()\n  if (!decrypt_val) {\n    console.error('Cannot find decrypt variable')\n    return\n  }\n  console.log(`Main call wrapper name: ${decrypt_val}`)\n\n  // 运行解密语句\n  let content_code = ast.program.body\n  ast.program.body = decrypt_code\n  let { code } = generator(ast, {\n    compact: true,\n  })\n  virtualGlobalEval(code)\n  // 遍历内容语句\n  ast.program.body = content_code\n  function funToStr(path) {\n    let tmp = path.toString()\n    let value = virtualGlobalEval(tmp)\n    // console.log(`还原前：${tmp} 还原后：${value}`)\n    path.replaceWith(t.valueToNode(value))\n  }\n  function memToStr(path) {\n    let tmp = path.toString()\n    let value = virtualGlobalEval(tmp)\n    // console.log(`还原前：${tmp} 还原后：${value}`)\n    path.replaceWith(t.valueToNode(value))\n  }\n  function dfs(stk, item) {\n    stk.push(item)\n    const cur_val = item.name\n    console.log(`Enter sub ${stk.length}:${cur_val}`)\n    let pfx = ''\n    for (let parent of stk) {\n      pfx += parent.code + ';'\n    }\n    virtualGlobalEval(pfx)\n    let scope = item.path.scope\n    if (item.path.isFunctionDeclaration()) {\n      scope = item.path.parentPath.scope\n    }\n    // var is function scoped and let is block scoped\n    // Hence, var may not be in the current scope, e.g., in a for-loop\n    const binding = scope.getBinding(cur_val)\n    scope = binding.scope\n    const refs = binding.referencePaths\n    const refs_next = []\n    for (let ref of refs) {\n      const parent = ref.parentPath\n      if (ref.key === 'init') {\n        // VariableDeclarator\n        refs_next.push({\n          name: parent.node.id.name,\n          path: parent,\n          code: 'var ' + parent,\n        })\n      } else if (ref.key === 'right') {\n        // AssignmentExpression\n        refs_next.push({\n          name: parent.node.left.name,\n          path: parent,\n          code: 'var ' + parent,\n        })\n      } else if (ref.key === 'object') {\n        // MemberExpression\n        memToStr(parent)\n      } else if (ref.key === 'callee') {\n        // CallExpression\n        funToStr(parent)\n      } else {\n        console.error('Unexpected reference')\n      }\n    }\n    for (let ref of refs_next) {\n      dfs(stk, ref)\n    }\n    scope.crawl()\n    item.path.remove()\n    scope.crawl()\n    console.log(`Exit sub ${stk.length}:${cur_val}`)\n    stk.pop()\n  }\n  const root = {\n    name: decrypt_val,\n    path: decrypt_path,\n    code: '',\n  }\n  dfs([], root)\n  return ast\n}\n\nfunction cleanSwitchCode1(path) {\n  // 扁平控制：\n  // 会使用一个恒为true的while语句包裹一个switch语句\n  // switch语句的执行顺序又while语句上方的字符串决定\n  // 首先碰断是否符合这种情况\n  const node = path.node\n  if (!(t.isBooleanLiteral(node.test) || t.isUnaryExpression(node.test))) {\n    return\n  }\n  if (!(node.test.prefix || node.test.value)) {\n    return\n  }\n  if (!t.isBlockStatement(node.body)) {\n    return\n  }\n  const body = node.body.body\n  if (\n    !t.isSwitchStatement(body[0]) ||\n    !t.isMemberExpression(body[0].discriminant) ||\n    !t.isBreakStatement(body[1])\n  ) {\n    return\n  }\n  // switch语句的两个变量\n  const swithStm = body[0]\n  const arrName = swithStm.discriminant.object.name\n  const argName = swithStm.discriminant.property.argument.name\n  console.log(`扁平化还原: ${arrName}[${argName}]`)\n  // 在while上面的节点寻找这两个变量\n  let arr = []\n  path.getAllPrevSiblings().forEach((pre_path) => {\n    const { declarations } = pre_path.node\n    let { id, init } = declarations[0]\n    if (arrName == id.name) {\n      arr = init.callee.object.value.split('|')\n      pre_path.remove()\n    }\n    if (argName == id.name) {\n      pre_path.remove()\n    }\n  })\n  // 重建代码块\n  const caseList = swithStm.cases\n  let resultBody = []\n  arr.map((targetIdx) => {\n    // 从当前序号开始直到遇到continue\n    let valid = true\n    targetIdx = parseInt(targetIdx)\n    while (valid && targetIdx < caseList.length) {\n      const targetBody = caseList[targetIdx].consequent\n      const test = caseList[targetIdx].test\n      if (!t.isStringLiteral(test) || parseInt(test.value) !== targetIdx) {\n        console.log(`switch中出现乱序的序号: ${test.value}:${targetIdx}`)\n      }\n      for (let i = 0; i < targetBody.length; ++i) {\n        const s = targetBody[i]\n        if (t.isContinueStatement(s)) {\n          valid = false\n          break\n        }\n        if (t.isReturnStatement(s)) {\n          valid = false\n          resultBody.push(s)\n          break\n        }\n        if (t.isBreakStatement(s)) {\n          console.log(`switch中出现意外的break: ${arrName}[${argName}]`)\n        } else {\n          resultBody.push(s)\n        }\n      }\n      targetIdx++\n    }\n  })\n  // 替换整个while语句\n  path.replaceInline(resultBody)\n}\n\nfunction cleanSwitchCode2(path) {\n  // 扁平控制：\n  // 会使用一个空的for语句包裹一个switch语句\n  // switch语句的执行顺序由for语句上方的字符串决定\n  // 首先判断是否符合这种情况\n  const node = path.node\n  if (node.init || node.test || node.update) {\n    return\n  }\n  if (!t.isBlockStatement(node.body)) {\n    return\n  }\n  const body = node.body.body\n  if (\n    !t.isSwitchStatement(body[0]) ||\n    !t.isMemberExpression(body[0].discriminant) ||\n    !t.isBreakStatement(body[1])\n  ) {\n    return\n  }\n  // switch语句的两个变量\n  const swithStm = body[0]\n  const arrName = swithStm.discriminant.object.name\n  const argName = swithStm.discriminant.property.argument.name\n  // 在while上面的节点寻找这两个变量\n  let arr = null\n  for (let pre_path of path.getAllPrevSiblings()) {\n    if (!pre_path.isVariableDeclaration()) {\n      continue\n    }\n    let test = '' + pre_path\n    try {\n      arr = evalOneTime(test + `;${arrName}.join('|')`)\n      arr = arr.split('|')\n    } catch {\n      //\n    }\n  }\n  if (!Array.isArray(arr)) {\n    return\n  }\n  console.log(`扁平化还原: ${arrName}[${argName}]`)\n  // 重建代码块\n  const caseMap = {}\n  for (let item of swithStm.cases) {\n    caseMap[item.test.value] = item.consequent\n  }\n  let resultBody = []\n  arr.map((targetIdx) => {\n    // 从当前序号开始直到遇到continue\n    let valid = true\n    while (valid && targetIdx < arr.length) {\n      const targetBody = caseMap[targetIdx]\n      for (let i = 0; i < targetBody.length; ++i) {\n        const s = targetBody[i]\n        if (t.isContinueStatement(s)) {\n          valid = false\n          break\n        }\n        if (t.isReturnStatement(s)) {\n          valid = false\n          resultBody.push(s)\n          break\n        }\n        if (t.isBreakStatement(s)) {\n          console.log(`switch中出现意外的break: ${arrName}[${argName}]`)\n        } else {\n          resultBody.push(s)\n        }\n      }\n      targetIdx++\n    }\n  })\n  // 替换整个while语句\n  path.replaceInline(resultBody)\n}\n\nfunction cleanDeadCode(ast) {\n  traverse(ast, calculateConstantExp)\n  traverse(ast, pruneIfBranch)\n  traverse(ast, { WhileStatement: { exit: cleanSwitchCode1 } })\n  traverse(ast, { ForStatement: { exit: cleanSwitchCode2 } })\n  return ast\n}\n\nfunction removeUniqueCall(path) {\n  let up1 = path.parentPath\n  let decorator = up1.node.callee.name\n  console.info(`Remove decorator: ${decorator}`)\n  let bind1 = up1.scope.getBinding(decorator)\n  bind1.path.remove()\n  if (up1.key === 'callee') {\n    up1.parentPath.remove()\n  } else if (up1.key === 'init') {\n    let up2 = up1.parentPath\n    let call = up2.node.id.name\n    console.info(`Remove call: ${call}`)\n    let bind2 = up2.scope.getBinding(call)\n    up2.remove()\n    for (let ref of bind2.referencePaths) {\n      if (ref.findParent((path) => path.removed)) {\n        continue\n      }\n      if (ref.key === 'callee') {\n        let rm = ref.parentPath\n        if (rm.key === 'expression') {\n          rm = rm.parentPath\n        }\n        rm.remove()\n      } else {\n        console.warn(`Unexpected ref key: ${ref.key}`)\n      }\n    }\n  }\n}\n\nfunction unlockDebugger(path) {\n  const decl_path = path.getFunctionParent()?.getFunctionParent()\n  if (!decl_path) {\n    return\n  }\n  // Check if contains inf-loop\n  let valid = false\n  path.getFunctionParent().traverse({\n    WhileStatement(path) {\n      if (t.isBooleanLiteral(path.node.test) && path.node.test) {\n        valid = true\n      }\n    },\n  })\n  if (!valid) {\n    return\n  }\n  const name = decl_path.node.id.name\n  const bind = decl_path.scope.getBinding(name)\n  console.info(`Debug test and inf-loop: ${name}`)\n  for (let ref of bind.referencePaths) {\n    if (ref.findParent((path) => path.removed)) {\n      continue\n    }\n    if (ref.listKey === 'arguments') {\n      // setInterval\n      let rm = ref.getFunctionParent().parentPath\n      if (rm.key === 'expression') {\n        rm = rm.parentPath\n      }\n      rm.remove()\n    } else if (ref.key === 'callee') {\n      // lint test for this method\n      let rm = ref.getFunctionParent()\n      removeUniqueCall(rm)\n    } else {\n      console.warn(`Unexpected ref key: ${ref.key}`)\n    }\n  }\n  decl_path.remove()\n  path.stop()\n}\n\nfunction unlockConsole(path) {\n  if (!t.isArrayExpression(path.node.init)) {\n    return\n  }\n  let pattern = 'log|warn|debug|info|error|exception|table|trace'\n  let count = 0\n  for (let ele of path.node.init.elements) {\n    if (~pattern.indexOf(ele.value)) {\n      ++count\n      continue\n    }\n    return\n  }\n  if (count < 5) {\n    return\n  }\n  let left1 = path.getSibling(0)\n  const code = generator(left1.node, { minified: true }).code\n  pattern = ['window', 'process', 'require', 'global']\n  pattern.map((key) => {\n    if (code.indexOf(key) == -1) {\n      return\n    }\n  })\n  let rm = path.getFunctionParent()\n  removeUniqueCall(rm)\n}\n\nfunction unlockLint(path) {\n  if (path.findParent((up) => up.removed)) {\n    return\n  }\n  if (path.node.value !== '(((.+)+)+)+$') {\n    return\n  }\n  let rm = path.getFunctionParent()\n  removeUniqueCall(rm)\n}\n\nfunction unlockDomainLock(path) {\n  const array_list = [\n    '[7,116,5,101,3,117,0,100]',\n    '[5,110,0,100]',\n    '[7,110,0,108]',\n    '[7,101,0,104]',\n  ]\n  const checkArray = (node) => {\n    const trim = node.split(' ').join('')\n    for (let i = 0; i < 4; ++i) {\n      if (array_list[i] == trim) {\n        return i + 1\n      }\n    }\n    return 0\n  }\n  if (path.findParent((up) => up.removed)) {\n    return\n  }\n  let mask = 1 << checkArray('' + path)\n  if (mask == 1) {\n    return\n  }\n  let rm = path.getFunctionParent()\n  rm.traverse({\n    ArrayExpression: function (item) {\n      mask = mask | (1 << checkArray('' + item))\n    },\n  })\n  if (mask & 0b11110) {\n    console.info('Find domain lock')\n    removeUniqueCall(rm)\n  }\n}\n\nfunction unlockEnv(ast) {\n  // 删除`禁止控制台调试`函数\n  traverse(ast, { DebuggerStatement: unlockDebugger })\n  // 删除`禁止控制台输出`函数\n  traverse(ast, { VariableDeclarator: unlockConsole })\n  // 删除`禁止换行`函数\n  traverse(ast, { StringLiteral: unlockLint })\n  // 删除`安全域名`函数\n  traverse(ast, { ArrayExpression: unlockDomainLock })\n}\n\n/**\n * If a function acts as follows:\n * A = function (p1, p2) { return p1 + p2 }\n *\n * Convert its call to a binary expression:\n * A(a, b) => a + b\n */\nfunction purifyFunction(path) {\n  const left = path.get('left')\n  const right = path.get('right')\n  if (!left.isIdentifier() || !right.isFunctionExpression()) {\n    return\n  }\n  const name = left.node.name\n  const params = right.node.params\n  if (params.length !== 2) {\n    return\n  }\n  const name1 = params[0].name\n  const name2 = params[1].name\n  if (right.node.body.body.length !== 1) {\n    return\n  }\n  let retStmt = right.node.body.body[0]\n  if (!t.isReturnStatement(retStmt)) {\n    return\n  }\n  if (!t.isBinaryExpression(retStmt.argument, { operator: '+' })) {\n    return\n  }\n  if (\n    retStmt.argument.left?.name !== name1 ||\n    retStmt.argument.right?.name !== name2\n  ) {\n    return\n  }\n  const fnPath = path.getFunctionParent() || path.scope.path\n  fnPath.traverse({\n    CallExpression: function (_path) {\n      const _node = _path.node.callee\n      if (!t.isIdentifier(_node, { name: name })) {\n        return\n      }\n      let args = _path.node.arguments\n      _path.replaceWith(t.binaryExpression('+', args[0], args[1]))\n    },\n  })\n  path.remove()\n  console.log(`拼接类函数: ${name}`)\n}\n\nfunction purifyCode(ast) {\n  // 净化拼接字符串的函数\n  traverse(ast, { AssignmentExpression: purifyFunction })\n  // 计算常量表达式\n  traverse(ast, calculateConstantExp)\n  // 替换索引器\n  function FormatMember(path) {\n    // _0x19882c['removeCookie']['toString']()\n    //  |\n    //  |\n    //  |\n    //  v\n    // _0x19882c.removeCookie.toString()\n    let curNode = path.node\n    if (!t.isStringLiteral(curNode.property)) {\n      return\n    }\n    if (curNode.computed === undefined || !curNode.computed === true) {\n      return\n    }\n    if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(curNode.property.value)) {\n      return\n    }\n    curNode.property = t.identifier(curNode.property.value)\n    curNode.computed = false\n  }\n  traverse(ast, { MemberExpression: FormatMember })\n  // 分割表达式\n  traverse(ast, splitSequence)\n  // 删除空语句\n  traverse(ast, {\n    EmptyStatement: (path) => {\n      path.remove()\n    },\n  })\n  // 删除未使用的变量\n  traverse(ast, deleteUnusedVar)\n}\n\nexport default function (code) {\n  let ret = PluginEval.unpack(code)\n  let global_eval = false\n  if (ret) {\n    global_eval = true\n    code = ret\n  }\n  let ast\n  try {\n    ast = parse(code, { errorRecovery: true })\n  } catch (e) {\n    console.error(`Cannot parse code: ${e.reasonCode}`)\n    return null\n  }\n  // IllegalReturn\n  traverse(ast, deleteIllegalReturn)\n  // 清理二进制显示内容\n  traverse(ast, {\n    StringLiteral: ({ node }) => {\n      delete node.extra\n    },\n  })\n  traverse(ast, {\n    NumericLiteral: ({ node }) => {\n      delete node.extra\n    },\n  })\n  console.log('处理全局加密...')\n  ast = decodeGlobal(ast)\n  if (!ast) {\n    return null\n  }\n  console.log('处理代码块加密...')\n  traverse(ast, parseControlFlowStorage)\n  console.log('清理死代码...')\n  ast = cleanDeadCode(ast)\n  // 刷新代码\n  ast = parse(\n    generator(ast, {\n      comments: false,\n      jsescOption: { minimal: true },\n    }).code\n  )\n  console.log('提高代码可读性...')\n  purifyCode(ast)\n  ast = parse(generator(ast, { comments: false }).code)\n  console.log('解除环境限制...')\n  unlockEnv(ast)\n  console.log('净化完成')\n  code = generator(ast, {\n    comments: false,\n    jsescOption: { minimal: true },\n  }).code\n  if (global_eval) {\n    code = PluginEval.pack(code)\n  }\n  return code\n}\n"
  },
  {
    "path": "src/utility/check-func.js",
    "content": "function checkPattern(code, pattern) {\n  let i = 0\n  let j = 0\n  while (i < code.length && j < pattern.length) {\n    if (code[i] == pattern[j]) {\n      ++j\n    }\n    ++i\n  }\n  return j == pattern.length\n}\n\nexport default {\n  checkPattern,\n}\n"
  },
  {
    "path": "src/utility/safe-func.js",
    "content": "import * as t from '@babel/types'\n\nfunction safeDeleteNode(name, path) {\n  let binding\n  if (path.isFunctionDeclaration()) {\n    binding = path.parentPath.scope.getBinding(name)\n  } else {\n    binding = path.scope.getBinding(name)\n  }\n  if (!binding) {\n    return false\n  }\n  binding.scope.crawl()\n  binding = binding.scope.getBinding(name)\n  if (binding.references) {\n    return false\n  }\n  for (const item of binding.constantViolations) {\n    item.remove()\n  }\n  const decl = binding.path\n  if (decl.removed) {\n    return true\n  }\n  if (!decl.isVariableDeclarator() && !decl.isFunctionDeclaration()) {\n    return true\n  }\n  binding.path.remove()\n  return true\n}\n\nfunction safeGetLiteral(path) {\n  if (path.isUnaryExpression()) {\n    if (path.node.operator === '-' && path.get('argument').isNumericLiteral()) {\n      return -1 * path.get('argument').node.value\n    }\n    return null\n  }\n  if (path.isLiteral()) {\n    return path.node.value\n  }\n  return null\n}\n\nfunction safeGetName(path) {\n  if (path.isIdentifier()) {\n    return path.node.name\n  }\n  if (path.isLiteral()) {\n    return path.node.value\n  }\n  return null\n}\n\nfunction safeReplace(path, value) {\n  if (typeof value === 'string') {\n    path.replaceWith(t.stringLiteral(value))\n    return\n  }\n  if (typeof value === 'number') {\n    path.replaceWith(t.numericLiteral(value))\n    return\n  }\n  path.replaceWithSourceString(value)\n}\n\nexport default {\n  safeDeleteNode,\n  safeGetLiteral,\n  safeGetName,\n  safeReplace,\n}\n"
  },
  {
    "path": "src/visitor/calculate-constant-exp.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nfunction checkLiteral(node) {\n  if (t.isNumericLiteral(node)) {\n    return 'positive'\n  }\n  if (t.isLiteral(node)) {\n    return 'literal'\n  }\n  if (!t.isUnaryExpression(node)) {\n    return false\n  }\n  if (!t.isNumericLiteral(node.argument)) {\n    return false\n  }\n  if (node.operator === '-') {\n    return 'negative'\n  }\n  return false\n}\n\n/**\n * Calculate BinaryExpression if left and right are both literals.\n * Otherwise, the expression can't be simplified.\n * Note that negative numbers are UnaryExpressions.\n */\nfunction calculateBinaryExpression(path) {\n  const { left, right } = path.node\n  if (!checkLiteral(left) || !checkLiteral(right)) {\n    return\n  }\n  const code = generator(path.node).code\n  try {\n    const ret = eval(code)\n    // The strings cannot use replaceWithSourceString\n    // For example, string \"ab\" will be parsed as identifier ab\n    if (typeof ret === 'string') {\n      path.replaceWith(t.stringLiteral(ret))\n    } else {\n      path.replaceWithSourceString(ret)\n    }\n  } catch {\n    //\n  }\n}\n\n/**\n * Calculate UnaryExpression:\n * - the operator is `!` and the argument is ArrayExpression or Literal.\n * - the operator is `-` and the argument is a negative number\n * - the operator is `+`, or `~`, and the argument is a number\n * - the operator is 'void' and the argument is Literal.\n * - the operator is 'typeof' and the argument is Literal.\n *\n * Otherwise, the expression can't be simplified.\n * For example, `typeof window` can be calculated but it's not constant.\n */\nfunction calculateUnaryExpression(path) {\n  const node0 = path.node\n  const node1 = node0.argument\n  const isLiteral = checkLiteral(node1)\n  if (node0.operator === '!') {\n    if (t.isArrayExpression(node1)) {\n      if (node1.elements.length === 0) {\n        path.replaceWith(t.booleanLiteral(false))\n      }\n    }\n    if (isLiteral) {\n      const code = generator(node0).code\n      path.replaceWith(t.booleanLiteral(eval(code)))\n    }\n    return\n  }\n  if (node0.operator === '-') {\n    if (isLiteral === 'negative') {\n      const code = generator(node0).code\n      path.replaceWithSourceString(eval(code))\n    }\n    return\n  }\n  if (node0.operator === '+' || node0.operator === '~') {\n    if (isLiteral === 'negative' || isLiteral === 'positive') {\n      const code = generator(node0).code\n      path.replaceWithSourceString(eval(code))\n    }\n    return\n  }\n  if (node0.operator === 'void') {\n    if (isLiteral) {\n      path.replaceWith(t.identifier('undefined'))\n    }\n    return\n  }\n  if (node0.operator === 'typeof') {\n    if (isLiteral) {\n      const code = generator(node0).code\n      path.replaceWith(t.stringLiteral(eval(code)))\n    }\n    return\n  }\n}\n\nexport default {\n  BinaryExpression: { exit: calculateBinaryExpression },\n  UnaryExpression: { exit: calculateUnaryExpression },\n}\n"
  },
  {
    "path": "src/visitor/calculate-rstring.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\n/**\n * \"sh\".split(\"\").reverse().join(\"\") -> \"hs\"\n */\nexport default {\n  StringLiteral(path) {\n    if (path.key !== 'object') {\n      return\n    }\n    let root = path\n    let count = 6\n    while (root.parentPath && count) {\n      if (\n        root.parentPath.isMemberExpression() ||\n        root.parentPath.isCallExpression()\n      ) {\n        root = root.parentPath\n        --count\n      } else {\n        break\n      }\n    }\n    if (count) {\n      return\n    }\n    const code = generator(root.node).code\n    try {\n      const ret = eval(code)\n      root.replaceWith(t.stringLiteral(ret))\n    } catch {\n      //\n    }\n  },\n}\n"
  },
  {
    "path": "src/visitor/delete-extra.js",
    "content": "/**\n * 0x10 -> 16, \"\\u0058\" -> \"X\"\n * not ASCII-safe (disable jsescOption:minimal to keep ASCII-safe)\n */\nexport default {\n  StringLiteral: ({ node }) => {\n    delete node.extra\n  },\n  NumericLiteral: ({ node }) => {\n    delete node.extra\n  },\n}\n"
  },
  {
    "path": "src/visitor/delete-illegal-return.js",
    "content": "/**\n * delete ReturnStatement in Program scope\n */\nexport default {\n  ReturnStatement(path) {\n    if (!path.getFunctionParent()) {\n      path.remove()\n    }\n  },\n}\n"
  },
  {
    "path": "src/visitor/delete-nested-blocks.js",
    "content": "const isIntersect = (path, bindings) => {\n  path.scope.crawl()\n  for (const key of Object.keys(bindings)) {\n    if (path.scope.hasBinding(key)) {\n      return true\n    }\n  }\n  return false\n}\n\n/**\n * Avoid nested blocks.\n *\n * This is slightly different from the @putout/plugin-remove-nested-blocks :\n * https://github.com/coderaiser/putout/issues/224#issuecomment-2614051528\n */\nexport default {\n  BlockStatement: (path) => {\n    const { parentPath } = path\n    if (!parentPath.isBlockStatement() && !parentPath.isProgram()) {\n      return\n    }\n    let valid = path.container.length === 1\n    if (!isIntersect(parentPath, path.scope.bindings)) {\n      valid = true\n    }\n    if (!valid) {\n      return\n    }\n    path.replaceWithMultiple(path.node.body)\n    path.scope.crawl()\n  },\n}\n"
  },
  {
    "path": "src/visitor/delete-unreachable-code.js",
    "content": "import * as t from '@babel/types'\n\n/**\n * DFS the BlockStatement to find and return the location of the first\n * ReturnStatement, which is not inside a Conditional Block.\n */\nconst checkReturnLocation = (body) => {\n  for (let i = 0; i < body.length; ++i) {\n    if (t.isReturnStatement(body[i])) {\n      return i\n    }\n    if (t.isBlockStatement(body[i])) {\n      const ret = checkReturnLocation(body[i].body)\n      if (~ret) {\n        return i\n      }\n    }\n  }\n  return -1\n}\n\n/**\n * Remove the codes after the first ReturnStatement, which is not inside a\n * Conditional Block. The FunctionDeclaration will be preserved.\n *\n * This is slightly different from the @putout/plugin-remove-unreachable-code :\n * https://github.com/coderaiser/putout/issues/224#issuecomment-2614051528\n */\nexport default {\n  BlockStatement: (path) => {\n    const body = path.node.body\n    const loc = checkReturnLocation(body)\n    if (loc == -1) {\n      return\n    }\n    for (let i = body.length - 1; i > loc; --i) {\n      if (t.isFunctionDeclaration(body[i])) {\n        continue\n      }\n      body.splice(i, 1)\n    }\n    if (loc === 0 && t.isBlockStatement(body[0])) {\n      const inner = body.shift()\n      while (inner.body.length) {\n        body.unshift(inner.body.pop())\n      }\n    }\n    path.scope.crawl()\n  },\n}\n"
  },
  {
    "path": "src/visitor/delete-unused-var.js",
    "content": "import * as t from '@babel/types'\n\n/**\n * Delete unused variables with the following exceptions:\n *\n * - ForOfStatement\n * - ForInStatement\n *\n */\nexport default {\n  VariableDeclarator: (path) => {\n    const { node, scope } = path\n    const name = node.id.name\n    const binding = scope.getBinding(name)\n    if (!binding || binding.referenced || !binding.constant) {\n      return\n    }\n    if (node.init && !t.isLiteral(node.init)) {\n      return\n    }\n    const up1 = path.parentPath\n    const up2 = up1?.parentPath\n    if (t.isForOfStatement(up2)) {\n      return\n    }\n    if (t.isForInStatement(up2)) {\n      return\n    }\n    console.log(`Unused variable: ${name}`)\n    if (up1.node.declarations.length === 1) {\n      up1.remove()\n      up1.scope.crawl()\n    } else {\n      path.remove()\n      scope.crawl()\n    }\n  },\n}\n"
  },
  {
    "path": "src/visitor/jsconfuser/anti-tooling.js",
    "content": "import * as t from '@babel/types'\n\nfunction deAntiToolingCheckFunc(path) {\n  if (path.node.params.length) {\n    return false\n  }\n  const body = path.node.body\n  if (!t.isBlockStatement(body)) {\n    return false\n  }\n  if (body.body.length) {\n    return false\n  }\n  return true\n}\n\nfunction deAntiToolingExtract(path, func_name) {\n  let binding = path.scope.getBinding(func_name)\n  for (let ref of binding.referencePaths) {\n    if (!ref.parentPath.isCallExpression() || !ref.key === 'callee') {\n      continue\n    }\n    const call = ref.parentPath\n    if (!call.listKey === 'body') {\n      continue\n    }\n    for (let node of call.node.arguments) {\n      call.insertBefore(node)\n    }\n    call.remove()\n  }\n  binding.scope.crawl()\n  binding = path.scope.getBinding(func_name)\n  if (binding.references === 0) {\n    path.remove()\n  }\n}\n\nconst deAntiTooling = {\n  FunctionDeclaration(path) {\n    const func_name = path.node.id?.name\n    if (!func_name) {\n      return\n    }\n    if (!deAntiToolingCheckFunc(path)) {\n      return\n    }\n    console.log(`AntiTooling Func Name: ${func_name}`)\n    deAntiToolingExtract(path, func_name)\n  },\n}\n\nexport default deAntiTooling\n"
  },
  {
    "path": "src/visitor/jsconfuser/control-flow.js",
    "content": "import safeFunc from '../../utility/safe-func.js'\nconst safeGetLiteral = safeFunc.safeGetLiteral\nconst safeGetName = safeFunc.safeGetName\nconst safeReplace = safeFunc.safeReplace\n\nfunction checkControlVar(path) {\n  const parent = path.parentPath\n  if (path.key !== 'right' || !parent.isAssignmentExpression()) {\n    return false\n  }\n  const var_path = parent.get('left')\n  const var_name = var_path.node?.name\n  if (!var_name) {\n    return false\n  }\n  let root_path = parent.parentPath\n  if (root_path.isExpressionStatement) {\n    root_path = root_path.parentPath\n  }\n  const binding = parent.scope.getBinding(var_name)\n  for (const ref of binding.referencePaths) {\n    if (ref === var_path) {\n      continue\n    }\n    let cur = ref\n    let valid = false\n    while (cur && cur !== root_path) {\n      if (cur.isSwitchCase() || cur === path) {\n        valid = true\n        break\n      }\n      cur = cur.parentPath\n    }\n    if (!valid) {\n      return false\n    }\n    if (ref.key === 'object') {\n      const prop = ref.parentPath.get('property')\n      if (!prop.isLiteral() && !prop.isIdentifier()) {\n        return false\n      }\n      continue\n    }\n    if (ref.key === 'right') {\n      const left = ref.parentPath.get('left')\n      if (!left.isMemberExpression()) {\n        return false\n      }\n      const obj = safeGetName(left.get('object'))\n      if (obj !== var_name) {\n        return false\n      }\n      continue\n    }\n  }\n  return true\n}\n\n/**\n * Process the constant properties in the controlVar\n *\n * Template:\n * ```javascript\n * controlVar = {\n *   // strings\n *   key_string: 'StringLiteral',\n *   // numbers\n *   key_number: 'NumericLiteral',\n * }\n * ```\n *\n * Some kinds of deadCode may in inserted to the fake chunks:\n *\n * ```javascript\n * controlVar = false\n * controlVar = undefined\n * controlVar[randomControlKey] = undefined\n * delete controlVar[randomControlKey]\n * ```\n */\nconst deControlFlowFlatteningStateless = {\n  ObjectExpression(path) {\n    if (!checkControlVar(path)) {\n      return\n    }\n    const parent = path.parentPath\n    const var_name = parent.get('left').node?.name\n    console.log(`[ControlFlowFlattening] parse stateless in obj: ${var_name}`)\n    const props = {}\n    const prop_num = path.node.properties.length\n    for (let i = 0; i < prop_num; ++i) {\n      const prop = path.get(`properties.${i}`)\n      const key = safeGetName(prop.get('key'))\n      const value = safeGetLiteral(prop.get('value'))\n      if (!key || !value) {\n        continue\n      }\n      props[key] = value\n    }\n    const binding = parent.scope.getBinding(var_name)\n    for (const ref of binding.referencePaths) {\n      if (ref.key !== 'object') {\n        continue\n      }\n      const prop = safeGetName(ref.parentPath.get('property'))\n      if (!prop) {\n        continue\n      }\n      if (!Object.prototype.hasOwnProperty.call(props, prop)) {\n        continue\n      }\n      const upper = ref.parentPath\n      if (upper.key === 'left' && upper.parentPath.isAssignmentExpression()) {\n        // this is in the fake chunk\n        ref.parentPath.parentPath.remove()\n        continue\n      }\n      safeReplace(ref.parentPath, props[prop])\n    }\n    binding.scope.crawl()\n  },\n}\n\n/**\n *\n * Template:\n * ```javascript\n * flaggedLabels = {\n *   currentLabel: { flagKey: 'xxx', flagValue : 'true or false' }\n * }\n * labelToStates[chunk[i].label] = stateValues: [] => caseStates[i]\n * initStateValues = labelToStates[startLabel]\n * endState\n * chunks = [\n *   {\n *     body: [\n *       {\n *         type: \"GotoStatement\",\n *         label: \"END_LABEL\",\n *       }\n *     ],\n *   }\n *   {\n *     label: \"END_LABEL\",\n *     body: [],\n *   }\n * ]\n * while (stateVars) {\n *   switch (stateVars) {\n *     // fake assignment expression\n *     case fake_assignment: {\n *       stateVar = 'rand'\n *       // 'GotoStatement label'\n *     }\n *     // clone chunks\n *     case fake_clone: {\n *       // contain a real chunk\n *     }\n *     // fake jumps\n *     case real_1: {\n *       if (false) {\n *         // 'GotoStatement label'\n *       }\n *       // follow with real statements\n *     }\n *   }\n * }\n * The key may exist in its parent's map\n * ```\n */\nconst deControlFlowFlatteningState = {\n  ObjectExpression(path) {\n    if (!checkControlVar(path)) {\n      return\n    }\n  },\n}\n\nexport default {\n  deControlFlowFlatteningStateless,\n  deControlFlowFlatteningState,\n}\n"
  },
  {
    "path": "src/visitor/jsconfuser/duplicate-literal.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport ivm from 'isolated-vm'\nconst isolate = new ivm.Isolate()\n\nimport safeFunc from '../../utility/safe-func.js'\nconst safeReplace = safeFunc.safeReplace\n\nfunction checkArrayName(path) {\n  if (path.key !== 'argument') {\n    return null\n  }\n  const ret_path = path.parentPath\n  if (!ret_path.isReturnStatement() || ret_path.key !== 0) {\n    return null\n  }\n  const array_fn_path = ret_path.getFunctionParent()\n  const array_fn_name = array_fn_path.node.id?.name\n  if (!array_fn_name) {\n    return null\n  }\n  const binding = array_fn_path.parentPath.scope.bindings[array_fn_name]\n  if (binding.references !== 1) {\n    return null\n  }\n  let ref = binding.referencePaths[0]\n  while (ref && !ref.isAssignmentExpression()) {\n    ref = ref.parentPath\n  }\n  if (!ref) {\n    return null\n  }\n  const array_name = ref.node.left?.name\n  if (!array_name) {\n    return null\n  }\n  return {\n    func_name: array_fn_name,\n    func_path: array_fn_path,\n    array_name: array_name,\n    array_path: ref,\n  }\n}\n\nfunction parseArrayWarp(vm, path) {\n  let func = path.getFunctionParent(path)\n  let name = null\n  let binding = null\n  if (func.isArrowFunctionExpression()) {\n    func = func.parentPath\n    name = func.node.id.name\n    binding = func.scope.getBinding(name)\n  } else {\n    name = func.node.id.name\n    binding = func.parentPath.scope.getBinding(name)\n  }\n  console.log(`Process array warp function: ${name}`)\n  vm.evalSync(generator(func.node).code)\n  for (const ref of binding.referencePaths) {\n    const call = ref.parentPath\n    if (ref.key !== 'callee') {\n      console.warn(`Unexpected ref of array warp function: ${call}`)\n      continue\n    }\n    const value = vm.evalSync(generator(call.node).code)\n    safeReplace(call, value)\n  }\n  binding.scope.crawl()\n  binding = binding.scope.getBinding(name)\n  if (!binding.references) {\n    func.remove()\n  }\n}\n\n/**\n * Template:\n * ```javascript\n * var arrayName = getArrayFn()\n * function getArrayFn (){\n *   return [...arrayExpression]\n * }\n * ```\n */\nconst deDuplicateLiteral = {\n  ArrayExpression(path) {\n    let obj = checkArrayName(path)\n    if (!obj) {\n      return\n    }\n    console.log(`Find arrayName: ${obj.array_name}`)\n    let decl_node = t.variableDeclarator(\n      obj.array_path.node.left,\n      obj.array_path.node.right\n    )\n    decl_node = t.variableDeclaration('var', [decl_node])\n    const code = [generator(obj.func_path.node).code, generator(decl_node).code]\n    let binding = obj.array_path.scope.getBinding(obj.array_name)\n    for (const ref of binding.referencePaths) {\n      const vm = isolate.createContextSync()\n      vm.evalSync(code[0])\n      vm.evalSync(code[1])\n      parseArrayWarp(vm, ref)\n    }\n    binding.scope.crawl()\n    binding = binding.scope.bindings[obj.array_name]\n    if (!binding.references) {\n      obj.array_path.remove()\n      binding.path.remove()\n    }\n    binding = obj.func_path.parentPath.scope.getBinding(obj.func_name)\n    binding.scope.crawl()\n    binding = binding.scope.getBinding(obj.func_name)\n    if (!binding.references) {\n      obj.func_path.remove()\n    }\n  },\n}\n\nexport default deDuplicateLiteral\n"
  },
  {
    "path": "src/visitor/jsconfuser/global-concealing.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport findGlobalFn from './global.js'\nimport safeFunc from '../../utility/safe-func.js'\nconst safeDeleteNode = safeFunc.safeDeleteNode\nimport checkFunc from '../../utility/check-func.js'\nconst checkPattern = checkFunc.checkPattern\n\nfunction findGlobalVar(glo_name, glo_path) {\n  let tmp_path = glo_path.parentPath.getFunctionParent()\n  if (\n    !tmp_path ||\n    !tmp_path.parentPath.isMemberExpression() ||\n    !tmp_path.parentPath.parentPath.isCallExpression()\n  ) {\n    return null\n  }\n  const tmp_body = tmp_path.node.body.body\n  tmp_path = tmp_path.parentPath.parentPath\n  const ret_node = tmp_body[tmp_body.length - 1]\n  if (\n    !t.isReturnStatement(ret_node) ||\n    !t.isAssignmentExpression(ret_node.argument)\n  ) {\n    return null\n  }\n  const code = generator(ret_node.argument.right).code\n  const template = `${glo_name}call(this)`\n  if (!checkPattern(code, template)) {\n    return null\n  }\n  const glo_var = ret_node.argument.left.name\n  const binding = glo_path.scope.getBinding(glo_var)\n  for (const ref of binding.referencePaths) {\n    if (\n      !ref.parentPath.isMemberExpression() ||\n      !ref.parentPath.parentPath.isReturnStatement()\n    ) {\n      continue\n    }\n    const func_path = ref.getFunctionParent()\n    const func_name = func_path.node.id.name\n    return {\n      glo_var: glo_var,\n      tmp_path: tmp_path,\n      glo_fn_name: func_name,\n      glo_fn_path: func_path,\n    }\n  }\n  return null\n}\n\nfunction getGlobalConcealingNames(glo_fn_path) {\n  const obj = {}\n  glo_fn_path.traverse({\n    SwitchCase(path) {\n      const code = generator(path.node.test).code\n      const key = parseInt(code)\n      if (Number.isNaN(key)) {\n        console.error(`[GlobalConcealing] concealed key: ${code}`)\n        obj['invalid'] = true\n        return\n      }\n      let consequent = path.node.consequent[0]\n      if (t.isReturnStatement(consequent)) {\n        obj[key] = consequent.argument.property.value\n      } else {\n        if (t.isExpressionStatement(consequent)) {\n          consequent = consequent.expression\n        }\n        obj[key] = consequent.right.left.value\n      }\n    },\n  })\n  return obj\n}\n\n/**\n * Hide the global vars found by module GlobalAnalysis\n *\n * Template:\n * ```javascript\n * // Add to head:\n * var globalVar, tempVar = function () {\n *   getGlobalVariableFnName = createGetGlobalTemplate()\n *   return globalVar = getGlobalVariableFnName.call(this)\n * }[\"call\"]()\n * // Add to foot:\n * function globalFn (indexParamName) {\n *   var returnName\n *   switch (indexParamName) {\n *     case state_x: {\n *       return globalVar[name]\n *     }\n *     case state_y: {\n *       returnName = name || globalVar[name]\n *       break\n *     }\n *   }\n *   return globalVar[returnName]\n * }\n * // References:\n * // name -> globalFn(state)\n * ```\n */\nconst deGlobalConcealing = {\n  FunctionDeclaration(path) {\n    const glo_obj = findGlobalFn(path)\n    if (!glo_obj) {\n      return null\n    }\n    const obj = findGlobalVar(glo_obj.glo_fn_name, glo_obj.glo_fn_path)\n    if (!obj) {\n      return null\n    }\n    console.log(`[GlobalConcealing] globalVar: ${obj.glo_var}`)\n    const glo_vars = getGlobalConcealingNames(obj.glo_fn_path)\n    console.log(`[GlobalConcealing] globalFn: ${obj.glo_fn_name}`)\n    let binding = obj.glo_fn_path.parentPath.scope.getBinding(obj.glo_fn_name)\n    let remain = false\n    for (const ref of binding.referencePaths) {\n      const repl_path = ref.parentPath\n      if (ref.key !== 'callee' || !repl_path.isCallExpression()) {\n        continue\n      }\n      const key = parseInt(generator(repl_path.node.arguments[0]).code)\n      if (glo_vars[key]) {\n        repl_path.replaceWith(t.identifier(glo_vars[key]))\n      } else {\n        remain = true\n      }\n    }\n    if (!remain && safeDeleteNode(obj.glo_fn_name, obj.glo_fn_path)) {\n      obj.tmp_path.remove()\n    }\n  },\n}\n\nexport default deGlobalConcealing\n"
  },
  {
    "path": "src/visitor/jsconfuser/global.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport safeFunc from '../../utility/safe-func.js'\nconst safeGetName = safeFunc.safeGetName\nimport checkFunc from '../../utility/check-func.js'\nconst checkPattern = checkFunc.checkPattern\n\n/**\n * GlobalTemplate 1 (currently not support):\n * ```javascript\n * function {getGlobalFnName}(){\n *   var localVar = false;\n *   eval(${transform.jsConfuserVar(\"localVar\")} + \" = true\")\n *   if (!localVar) {\n *     {countermeasures}\n *   }\n *   const root = eval(\"this\");\n *   return root;\n * }\n * ```\n * GlobalTemplate 2:\n * ```javascript\n * function {getGlobalFnName}(array = [a, b, c, d]){\n *   var bestMatch\n *   var itemsToSearch = []\n *   try {\n *     bestMatch = Object\n *     itemsToSearch[\"push\"]((\"\")[\"__proto__\"][\"constructor\"][\"name\"])\n *   } catch(e) {\n *   }\n *   // ...\n *   return bestMatch || this;\n * }\n * ```\n */\nfunction findGlobalFn(path) {\n  const glo_fn_name = path.node.id?.name\n  if (!glo_fn_name) {\n    return null\n  }\n  let node = path.node.params?.[0]\n  if (\n    !node ||\n    !t.isAssignmentPattern(node) ||\n    !t.isArrayExpression(node.right) ||\n    node.right.elements.length !== 4\n  ) {\n    return null\n  }\n  const array_name = node.left.name\n  const code = generator(path.node.body).code\n  const template =\n    'try{=Objectpush(__proto__constructorname)}catch{}' +\n    `:for(;<${array_name}length;)try{=${array_name}[]()` +\n    'for()if(typeof)continue}catch{}return||this'\n  if (!checkPattern(code, template)) {\n    return\n  }\n  const deps = []\n  const array = path.get('params.0.right')\n  for (let i = 0; i < 4; ++i) {\n    const ele_name = safeGetName(array.get(`elements.${i}`))\n    const binding = path.scope.getBinding(ele_name)\n    deps.push({\n      name: ele_name,\n      path: binding.path,\n      pos: binding.path.node.start,\n    })\n  }\n  deps.push({\n    name: glo_fn_name,\n    path: path,\n    pos: path.node.start,\n  })\n  return {\n    glo_fn_name: glo_fn_name,\n    glo_fn_path: path,\n    deps: deps,\n  }\n}\n\nexport default findGlobalFn\n"
  },
  {
    "path": "src/visitor/jsconfuser/minify.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\n\nfunction checkArrowWrap(path) {\n  if (path.node?.name !== 'arguments') {\n    return null\n  }\n  if (!path.parentPath.isSpreadElement()) {\n    return null\n  }\n  const call = path.parentPath.parentPath\n  if (path.parentPath.listKey !== 'arguments' || !call.isCallExpression()) {\n    return null\n  }\n  if (call.key !== 'argument' || !call.parentPath.isReturnStatement()) {\n    return null\n  }\n  const func_name = call.node.callee?.name\n  if (!func_name) {\n    return null\n  }\n  let wrap = call.getFunctionParent()\n  if (wrap.key !== 'init') {\n    return null\n  }\n  wrap = wrap.parentPath\n  const wrap_name = wrap.node.id?.name\n  wrap = wrap.parentPath\n  if (\n    wrap.listKey !== 'body' ||\n    wrap.key !== 0 ||\n    wrap.container.length !== 2\n  ) {\n    return null\n  }\n  const str = generator(wrap.container[1]).code\n  if (str.indexOf(wrap_name) === -1) {\n    return null\n  }\n  wrap = wrap.getFunctionParent()\n  const arrow_name = wrap.node?.id?.name\n  if (!arrow_name || wrap.node.params?.[0]?.name !== func_name) {\n    return null\n  }\n  return {\n    name: arrow_name,\n    path: wrap,\n  }\n}\n\n/**\n * Template:\n * ```javascript\n * function arrowFunctionName (arrowFn, functionLength = 0){\n *   var functionObject = function(){ return arrowFn(...arguments) };\n *   return Object.defineProperty(functionObject, \"length\", {\n *     \"value\": functionLength,\n *     \"configurable\": true\n *   });\n * }\n * ```\n */\nexport default function () {\n  let arrowFunc = null\n  const deMinifyArrow = {\n    Identifier(path) {\n      let obj = checkArrowWrap(path)\n      if (!obj) {\n        return\n      }\n      arrowFunc = obj.name\n      console.log(`Find arrowFunctionName: ${obj.name}`)\n      let binding = obj.path.parentPath.scope.bindings[obj.name]\n      for (const ref of binding.referencePaths) {\n        if (ref.key !== 'callee') {\n          console.warn(`Unexpected ref of arrowFunctionName: ${obj.name}`)\n          continue\n        }\n        const repl_path = ref.parentPath\n        repl_path.replaceWith(repl_path.node.arguments[0])\n      }\n      binding.scope.crawl()\n      binding = obj.path.parentPath.scope.bindings[obj.name]\n      if (!binding.references) {\n        obj.path.remove()\n      }\n    },\n  }\n  return {\n    arrowFunc,\n    deMinifyArrow,\n  }\n}\n"
  },
  {
    "path": "src/visitor/jsconfuser/opaque-predicates.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport ivm from 'isolated-vm'\nconst isolate = new ivm.Isolate()\n\nimport safeFunc from '../../utility/safe-func.js'\nconst safeDeleteNode = safeFunc.safeDeleteNode\nconst safeGetName = safeFunc.safeGetName\nconst safeReplace = safeFunc.safeReplace\nimport checkFunc from '../../utility/check-func.js'\nconst checkPattern = checkFunc.checkPattern\n\nfunction checkOpaqueObject(path) {\n  const parent = path.parentPath\n  if (!parent.isAssignmentExpression()) {\n    return null\n  }\n  const tmp_name = safeGetName(parent.get('left'))\n  const func_path = parent.getFunctionParent()\n  if (\n    !func_path ||\n    func_path.key !== 'callee' ||\n    !func_path.parentPath.isCallExpression()\n  ) {\n    return null\n  }\n  const func_body = func_path.node.body?.body\n  if (!func_body || func_body.length < 2) {\n    return null\n  }\n  const last_node = func_body[func_body.length - 1]\n  if (\n    !t.isReturnStatement(last_node) ||\n    last_node.argument?.name !== tmp_name\n  ) {\n    return null\n  }\n  const root_path = func_path.parentPath.parentPath\n  if (!root_path.isAssignmentExpression()) {\n    return null\n  }\n  const pred_name = safeGetName(root_path.get('left'))\n  const obj = {\n    pred_name: pred_name,\n    pred_path: root_path,\n    props: {},\n  }\n  for (const prop of path.node.properties) {\n    const key = prop.key.name\n    const value = prop.value\n    if (t.isNumericLiteral(value)) {\n      obj.props[key] = {\n        type: 'number',\n      }\n      continue\n    }\n    if (t.isStringLiteral(value)) {\n      obj.props[key] = {\n        type: 'string',\n      }\n      continue\n    }\n    if (t.isArrayExpression(value)) {\n      if (value.elements.length === 0) {\n        obj.props[key] = {\n          type: 'array_dep',\n        }\n      }\n      continue\n    }\n    if (t.isArrowFunctionExpression(value) || t.isFunctionExpression(value)) {\n      const param = value.params?.[0]?.left?.name\n      if (!param) {\n        continue\n      }\n      const code = generator(value).code\n      const template =\n        `(${param}=){if(${pred_name}[0])${pred_name}push()` +\n        `return${pred_name}${param}}`\n      if (checkPattern(code, template)) {\n        obj.props[key] = {\n          type: 'array',\n        }\n      }\n      continue\n    }\n  }\n  return obj\n}\n\n/**\n * Template:\n * ```javascript\n * // This is defined in the global space\n * var predicateName = (function () {\n *   var tempName = {\n *     prop_array_1: [],\n *     prop_array: function (paramName = 'length') {\n *       if (!predicateName[prop_array_1][0]) {\n *          predicateName[prop_array_1][0].push(rand1)\n *       }\n *       return predicateName[prop_array_1][paramName]\n *     },\n *     prop_number: rand2,\n *     prop_string: rand_str,\n *   }\n *   return tempName\n * })()\n * // Below will appear multiple times\n * predicateName[prop_array]() ? test : fake\n * predicateName[prop_number] > rand3 ? test : fake\n * predicateName[prop_string].charAt(index) == real_char ? test : fake\n * predicateName[prop_string].charCodeAt(index) == real_char ? test : fake\n * ```\n */\nconst deOpaquePredicates = {\n  ObjectExpression(path) {\n    const obj = checkOpaqueObject(path)\n    if (!obj) {\n      return\n    }\n    console.log(`[OpaquePredicates] predicateName : ${obj.pred_name}`)\n    const vm = isolate.createContextSync()\n    const code = generator(obj.pred_path.node).code\n    vm.evalSync('var ' + code)\n    obj.pred_path.get('right').replaceWith(t.numericLiteral(0))\n    let binding = obj.pred_path.scope.getBinding(obj.pred_name)\n    binding.scope.crawl()\n    binding = binding.scope.getBinding(obj.pred_name)\n    for (const ref of binding.referencePaths) {\n      if (ref.key !== 'object') {\n        continue\n      }\n      const member = ref.parentPath\n      const prop = member.get('property')\n      if (!prop || !Object.prototype.hasOwnProperty.call(obj.props, prop)) {\n        continue\n      }\n      let expr = member\n      while (\n        expr.parentPath.isCallExpression() ||\n        expr.parentPath.isMemberExpression()\n      ) {\n        expr = expr.parentPath\n      }\n      const test = generator(expr.node).code\n      const res = vm.evalSync(test)\n      safeReplace(expr, res)\n    }\n    safeDeleteNode(obj.pred_name, obj.pred_path)\n  },\n}\n\nexport default deOpaquePredicates\n"
  },
  {
    "path": "src/visitor/jsconfuser/stack.js",
    "content": "import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport ivm from 'isolated-vm'\nconst isolate = new ivm.Isolate()\n\nimport calculateConstantExp from '../calculate-constant-exp.js'\n\nimport safeFunc from '../../utility/safe-func.js'\nconst safeGetName = safeFunc.safeGetName\nconst safeReplace = safeFunc.safeReplace\n\nlet arrowFunc = null\n\nfunction checkFuncLen(path) {\n  if (path.node?.name !== 'configurable' || path.key !== 'key') {\n    return null\n  }\n  const prop = path.parentPath\n  if (!prop.isObjectProperty() || prop.key !== 1) {\n    return null\n  }\n  const obj = prop.parentPath\n  if (obj.node.properties.length !== 2) {\n    return null\n  }\n  if (obj.node.properties[0]?.key?.name !== 'value') {\n    return null\n  }\n  if (obj.listKey !== 'arguments') {\n    return null\n  }\n  const arg_num = obj.container.length\n  if (obj.key !== arg_num - 1) {\n    return null\n  }\n  const func_name = obj.container[arg_num - 3]?.name\n  const warp = obj.getFunctionParent()\n  if (warp.node.params?.[0]?.name !== func_name) {\n    return null\n  }\n  const func_len_name = warp.node?.id?.name\n  if (!func_len_name || func_len_name === arrowFunc) {\n    return null\n  }\n  return {\n    name: func_len_name,\n    path: warp,\n  }\n}\n\n/**\n * type: param, value, ref, invalid\n */\nfunction initStackCache(len) {\n  const cache = {}\n  for (let i = 0; i < len; ++i) {\n    cache[i] = {\n      type: 'param',\n    }\n  }\n  return cache\n}\n\nfunction processAssignLeft(vm, cache, path, prop_name, stk_name) {\n  const father = path.parentPath\n  const right = father.get('right')\n  if (right.isBinaryExpression()) {\n    cache[prop_name] = {\n      type: 'invalid',\n    }\n    return\n  }\n  if (right.isLiteral()) {\n    vm.evalSync(generator(father.node).code)\n    cache[prop_name] = {\n      type: 'value',\n      value: right.node,\n    }\n    return\n  }\n  if (right.isArrayExpression()) {\n    const elements = right.node.elements\n    if (elements.length === 1 && elements[0]?.value === 'charCodeAt') {\n      cache[prop_name] = {\n        type: 'value',\n        value: right.node,\n      }\n      return\n    }\n  }\n  if (right.isUnaryExpression() && right.node.operator === '-') {\n    vm.evalSync(generator(father.node).code)\n    cache[prop_name] = {\n      type: 'value',\n      value: right.node,\n    }\n    return\n  }\n  if (right.isMemberExpression() && right.node.object?.name === stk_name) {\n    const right_prop = right.get('property')\n    if (right_prop.isBinaryExpression()) {\n      return\n    }\n    let ref = safeGetName(right_prop)\n    if (!Object.prototype.hasOwnProperty.call(cache, ref)) {\n      cache[prop_name] = {\n        type: 'invalid',\n      }\n      return\n    }\n    while (cache[ref].type === 'ref') {\n      ref = cache[ref].value\n    }\n    if (cache[ref].type === 'value') {\n      right.replaceWith(cache[ref].value)\n      vm.evalSync(generator(father.node).code)\n      cache[prop_name] = {\n        type: 'value',\n        value: cache[ref].value,\n      }\n    } else {\n      cache[prop_name] = {\n        type: 'ref',\n        value: ref,\n      }\n    }\n    return\n  }\n  cache[prop_name] = {\n    type: 'invalid',\n  }\n}\n\nfunction processAssignInvalid(cache, path, prop_name) {\n  cache[prop_name] = {\n    type: 'invalid',\n  }\n}\n\nfunction processReplace(cache, path, prop_name) {\n  const value = cache[prop_name].value\n  const type = cache[prop_name].type\n  if (type === 'ref') {\n    path.node.computed = true\n    safeReplace(path.get('property'), value)\n    return true\n  }\n  if (type === 'value') {\n    path.replaceWith(value)\n    return true\n  }\n  return false\n}\n\nfunction checkStackInvalid(path, invalid) {\n  const stk_name = path.node.params[0].argument.name\n  const body_path = path.get('body')\n  body_path.traverse({\n    MemberExpression: {\n      exit(path) {\n        if (path.node.object.name !== stk_name) {\n          return\n        }\n        const father = path.parentPath\n        const prop = path.get('property')\n        const prop_name = safeGetName(prop)\n        if (father.isUpdateExpression()) {\n          invalid[prop_name] = 1\n          return\n        }\n        if (body_path.scope == father.scope) {\n          return\n        }\n        if (!father.isAssignmentExpression() || path.key !== 'left') {\n          return\n        }\n        invalid[prop_name] = 1\n      },\n    },\n  })\n  return invalid\n}\n\nfunction checkChangeValid(invalid, used) {\n  let valid = true\n  Object.keys(used).forEach(function (key) {\n    if (Object.prototype.hasOwnProperty.call(invalid, key)) {\n      valid = false\n    }\n  })\n  return valid\n}\n\nfunction tryStackReplace(path, len, invalid, used) {\n  const stk_name = path.node.params[0].argument.name\n  const body_path = path.get('body')\n  const cache = initStackCache(len)\n  const vm = isolate.createContextSync()\n  vm.evalSync(`var ${stk_name} = []`)\n  let changed = false\n  body_path.traverse({\n    MemberExpression: {\n      exit(path) {\n        if (path.node.object.name !== stk_name) {\n          return\n        }\n        const prop = path.get('property')\n        if (prop.isBinaryExpression()) {\n          return\n        }\n        const prop_name = safeGetName(prop)\n        if (!prop_name) {\n          return\n        }\n        if (Object.prototype.hasOwnProperty.call(invalid, prop_name)) {\n          processAssignInvalid(cache, path, prop_name)\n          return\n        }\n        const exist = Object.prototype.hasOwnProperty.call(cache, prop_name)\n        if (exist && cache[prop_name].type === 'param') {\n          return\n        }\n        const father = path.parentPath\n        if (father.isAssignmentExpression() && path.key === 'left') {\n          processAssignLeft(vm, cache, path, prop_name, stk_name)\n        } else if (exist) {\n          used[prop_name] = 1\n          changed |= processReplace(cache, path, prop_name)\n        }\n      },\n    },\n  })\n  const binding = body_path.scope.getBinding(stk_name)\n  binding.scope.crawl()\n  return changed\n}\n\nfunction getStackParamLen(path) {\n  const stk_name = path.node.params?.[0]?.argument?.name\n  if (!stk_name) {\n    return 'unknown'\n  }\n  const body_path = path.get('body')\n  let len = 'unknown'\n  body_path.traverse({\n    MemberExpression: {\n      exit(path) {\n        if (path.node.object.name !== stk_name) {\n          return\n        }\n        const prop = path.get('property')\n        if (prop.isBinaryExpression()) {\n          return\n        }\n        const prop_name = safeGetName(prop)\n        if (!prop_name || prop_name !== 'length') {\n          return\n        }\n        const father = path.parentPath\n        if (!father.isAssignmentExpression() || path.key !== 'left') {\n          return\n        }\n        const right = father.get('right')\n        if (right.isBinaryExpression()) {\n          return\n        }\n        if (!right.isLiteral()) {\n          return\n        }\n        len = right.node.value\n        path.stop()\n      },\n    },\n  })\n  return len\n}\n\nfunction processStackParam(path, len) {\n  if (path.isArrowFunctionExpression()) {\n    console.log(`[Stack] Process arrowFunctionExpression, len: ${len}`)\n  } else if (path.isFunctionExpression()) {\n    console.log(`[Stack] Process functionExpression, len: ${len}`)\n  } else {\n    console.log(`[Stack] Process Function ${path.node.id.name}, len: ${len}`)\n  }\n  const orig_code = generator(path.node).code\n  let changed = true\n  const invalid = {}\n  let used = {}\n  while (changed) {\n    checkStackInvalid(path, invalid)\n    if (!checkChangeValid(invalid, used)) {\n      path.replaceWith(parse(orig_code).program.body[0])\n      used = {}\n    }\n    changed = tryStackReplace(path, len, invalid, used)\n    path.traverse(calculateConstantExp)\n  }\n}\n\nconst deStackFuncLen = {\n  Identifier(path) {\n    let obj = checkFuncLen(path)\n    if (!obj) {\n      return\n    }\n    console.log(`[Stack] Find functionLengthName: ${obj.name}`)\n    let binding = obj.path.parentPath.scope.bindings[obj.name]\n    for (const ref of binding.referencePaths) {\n      if (ref.key !== 'callee') {\n        console.warn(\n          `[Stack] Unexpected ref of functionLengthName: ${obj.name}`\n        )\n        continue\n      }\n      const repl_path = ref.parentPath\n      const arg = repl_path.node.arguments[0]\n      const len = repl_path.node.arguments[1].value\n      if (t.isIdentifier(arg)) {\n        const func_name = arg.name\n        const func_decl = repl_path.scope.getBinding(func_name).path\n        processStackParam(func_decl, len)\n        repl_path.remove()\n      } else {\n        repl_path.replaceWith(arg)\n        processStackParam(repl_path, len)\n      }\n    }\n    binding.scope.crawl()\n    binding = obj.path.parentPath.scope.bindings[obj.name]\n    if (!binding.references) {\n      obj.path.remove()\n    }\n  },\n}\n\nconst deStackFuncOther = {\n  RestElement(path) {\n    if (path.listKey !== 'params') {\n      return\n    }\n    const func = path.getFunctionParent()\n    const len = getStackParamLen(func)\n    if (len === 'unknown') {\n      return\n    }\n    processStackParam(func, len)\n  },\n}\n\nexport default function (func) {\n  arrowFunc = func\n  return {\n    deStackFuncLen,\n    deStackFuncOther,\n  }\n}\n"
  },
  {
    "path": "src/visitor/jsconfuser/string-compression.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\n\nimport ivm from 'isolated-vm'\nconst isolate = new ivm.Isolate()\n\nimport safeFunc from '../../utility/safe-func.js'\nconst safeReplace = safeFunc.safeReplace\nimport checkFunc from '../../utility/check-func.js'\nconst checkPattern = checkFunc.checkPattern\n\nfunction findStringDecoder(path) {\n  if (path.node?.name !== 'charCodeAt' || path.key !== 'property') {\n    return null\n  }\n  let loop = path\n  while (loop && !loop.isForStatement()) {\n    loop = loop.parentPath\n  }\n  const i = loop?.node?.update?.argument?.name\n  if (!i) {\n    return null\n  }\n  const func = loop.getFunctionParent()\n  const param = func.node.params?.[0]?.name\n  if (!param) {\n    return null\n  }\n  const code = generator(func.node).code\n  const template =\n    `function(${param}){var=${param}.split()for(${i}=1;${i}<.length;${i}++)` +\n    `[${i}].charCodeAt(0)[${i}].push().charAt(0)return.join().split()}`\n  if (!checkPattern(code, template)) {\n    return null\n  }\n  return {\n    name: func.node.id.name,\n    path: func,\n  }\n}\n\nfunction findStringGet(path) {\n  const decoder_name = path.node.id.name\n  let binding = path.parentPath.scope.getBinding(decoder_name)\n  if (!binding || binding.references !== 1) {\n    return null\n  }\n  const ref = binding.referencePaths[0]\n  if (ref.key !== 1 || ref.listKey !== 'arguments') {\n    return null\n  }\n  const get_ref_path = ref.parentPath.get('arguments.0')\n  const get_name = get_ref_path.node?.name\n  if (!get_name) {\n    return null\n  }\n  binding = get_ref_path.scope.getBinding(get_name)\n  return {\n    name: get_name,\n    path: binding.path,\n    ref: get_ref_path,\n  }\n}\n\nfunction findStringSplit(path) {\n  while (path && !path.isAssignmentExpression()) {\n    path = path.parentPath\n  }\n  const split_name = path?.node?.left?.name\n  if (!split_name) {\n    return null\n  }\n  const binding = path.scope.getBinding(split_name)\n  return {\n    name: split_name,\n    path: path,\n    def: binding.path,\n  }\n}\n\nfunction findStringFn(path, name) {\n  const binding = path.scope.getBinding(name)\n  const ref = binding.referencePaths?.[0]\n  if (!ref) {\n    return null\n  }\n  const fn_path = ref.getFunctionParent(name)\n  const fn_name = fn_path.node.id.name\n  return {\n    name: fn_name,\n    path: fn_path,\n  }\n}\n\n/**\n * Template:\n * ```javascript\n * var split = (function (getStringParamName, decoderParamName) {\n *   return decoderParamName(getStringParamName())\n * })(getStringName, decoder)\n * function getStringName () {\n *   var str = splits[0]\n *   var objectToTest = {}\n *   if ('testingFor' in objectToTest) {\n *     str += splits[1]\n *   }\n *   return str\n * }\n * function decoder (b) {\n *   // DecodeTemplate\n * }\n * function fnName (index) {\n *   return split[index]\n * }\n * ```\n */\nconst deStringCompression = {\n  Identifier(path) {\n    const decoder_obj = findStringDecoder(path)\n    if (!decoder_obj) {\n      return\n    }\n    const get_obj = findStringGet(decoder_obj.path)\n    if (!get_obj) {\n      return\n    }\n    const split_obj = findStringSplit(get_obj.ref)\n    if (!get_obj) {\n      return\n    }\n    const fn_obj = findStringFn(split_obj.path, split_obj.name)\n    if (!get_obj) {\n      return\n    }\n    console.log(`Find stringCompression Fn: ${fn_obj.name}`)\n    const vm = isolate.createContextSync()\n    vm.evalSync(generator(decoder_obj.path.node).code)\n    vm.evalSync(generator(get_obj.path.node).code)\n    vm.evalSync('var ' + generator(split_obj.path.node).code)\n    vm.evalSync(generator(fn_obj.path.node).code)\n    let binding = fn_obj.path.parentPath.scope.getBinding(fn_obj.name)\n    for (const ref of binding.referencePaths) {\n      if (ref.key !== 'callee') {\n        console.warn(\n          `Unexpected ref of stringCompression Fn: ${ref.parentPath}`\n        )\n        continue\n      }\n      const repl_path = ref.parentPath\n      try {\n        const value = vm.evalSync(generator(repl_path.node).code)\n        safeReplace(repl_path, value)\n      } catch (e) {\n        console.warn(\n          `Unexpected ref of stringCompression Fn: ${ref.parentPath}`\n        )\n      }\n    }\n    binding.scope.crawl()\n    binding = binding.scope.bindings[fn_obj.name]\n    if (!binding.references) {\n      fn_obj.path.remove()\n    }\n    binding.scope.crawl()\n    binding = split_obj.path.scope.getBinding(split_obj.name)\n    if (!binding.references) {\n      split_obj.path.remove()\n      split_obj.def.remove()\n    }\n    binding.scope.crawl()\n    binding = get_obj.path.scope.getBinding(get_obj.name)\n    if (!binding.references) {\n      get_obj.path.remove()\n    }\n    binding.scope.crawl()\n    binding = decoder_obj.path.scope.getBinding(decoder_obj.name)\n    if (!binding.references) {\n      decoder_obj.path.remove()\n    }\n  },\n}\n\nexport default deStringCompression\n"
  },
  {
    "path": "src/visitor/jsconfuser/string-concealing.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nimport ivm from 'isolated-vm'\nconst isolate = new ivm.Isolate()\n\nimport findGlobalFn from './global.js'\nimport safeFunc from '../../utility/safe-func.js'\nconst safeDeleteNode = safeFunc.safeDeleteNode\nconst safeGetName = safeFunc.safeGetName\nconst safeReplace = safeFunc.safeReplace\n\nfunction insertDepItemVar(deps, name, path) {\n  const binding = path.scope.getBinding(name)\n  if (binding.path === path) {\n    deps.push({\n      name: name,\n      path: binding.path,\n      node: t.variableDeclaration('var', [binding.path.node]),\n      pos: binding.path.node.start,\n    })\n    return\n  }\n  deps.push({\n    name: name,\n    path: path,\n    pos: path.node.start,\n  })\n  deps.push({\n    name: name,\n    path: binding.path,\n    node: t.variableDeclaration('var', [binding.path.node]),\n    pos: binding.path.node.start,\n  })\n}\n\n/**\n * Template:\n * ```javascript\n * var __globalObject = {getGlobalFnName}() || {};\n * var __TextDecoder = __globalObject[\"TextDecoder\"];\n * var __Uint8Array = __globalObject[\"Uint8Array\"];\n * var __Buffer = __globalObject[\"Buffer\"];\n * var __String = __globalObject[\"String\"] || String;\n * var __Array = __globalObject[\"Array\"] || Array;\n * ```\n */\nfunction findGlobalFnRef(obj) {\n  const path = obj.glo_fn_path\n  const glo_fn_name = obj.glo_fn_name\n  let binding = path.parentPath.scope.getBinding(glo_fn_name)\n  let glo_fn_ref = binding.referencePaths[0]\n  while (!glo_fn_ref.isAssignmentExpression()) {\n    glo_fn_ref = glo_fn_ref.parentPath\n  }\n  const glo_obj_name = glo_fn_ref.node.left.name\n  obj.glo_obj_name = glo_obj_name\n  obj.glo_obj_path = glo_fn_ref\n  obj.glo_obj_ref = {}\n  insertDepItemVar(obj.deps, glo_obj_name, glo_fn_ref)\n  binding = glo_fn_ref.scope.getBinding(glo_obj_name)\n  for (const ref of binding.referencePaths) {\n    const prop = safeGetName(ref.parentPath.get('property'))\n    if (!prop) {\n      continue\n    }\n    let root = ref\n    while (!root.isAssignmentExpression()) {\n      root = root.parentPath\n    }\n    const ref_name = safeGetName(root.get('left'))\n    obj.glo_obj_ref[prop] = ref_name\n    insertDepItemVar(obj.deps, ref_name, root)\n  }\n  return\n}\n\n/**\n * Template:\n * ```javascript\n * var utf8ArrayToStr = (function () {\n *   // ...\n * })();\n * function bufferToStringName () {\n *   if(typeof __TextDecoder !== \"undefined\" && __TextDecoder) {\n *     return new __TextDecoder()[\"decode\"](new __Uint8Array(buffer));\n *   } else if(typeof __Buffer !== \"undefined\" && __Buffer) {\n *     return __Buffer[\"from\"](buffer)[\"toString\"](\"utf-8\");\n *   } else {\n *     return utf8ArrayToStr(buffer);\n *   }\n * }\n * ```\n */\nfunction findBufferToString(obj) {\n  const path = obj.glo_obj_path\n  const ref_array = obj.glo_obj_ref['Array']\n  let binding = path.scope.getBinding(ref_array)\n  for (const ref of binding.referencePaths) {\n    if (ref.key !== 'callee') {\n      continue\n    }\n    let a2s_path = ref.getFunctionParent()\n    while (!a2s_path.isAssignmentExpression()) {\n      a2s_path = a2s_path.parentPath\n    }\n    obj.a2s_name = safeGetName(a2s_path.get('left'))\n    obj.a2s_path = a2s_path\n    insertDepItemVar(obj.deps, obj.a2s_name, obj.a2s_path)\n    break\n  }\n  if (!obj.a2s_name) {\n    return false\n  }\n  binding = obj.a2s_path.scope.getBinding(obj.a2s_name)\n  const b2s_path = binding.referencePaths[0].getFunctionParent()\n  obj.b2s_name = safeGetName(b2s_path.get('id'))\n  obj.b2s_path = b2s_path\n  obj.deps.push({\n    name: obj.b2s_name,\n    path: b2s_path,\n    pos: b2s_path.node.start,\n  })\n  binding = b2s_path.parentPath.scope.getBinding(obj.b2s_name)\n  const child = []\n  for (const ref of binding.referencePaths) {\n    const decode_fn = ref.getFunctionParent()\n    let valid = false\n    decode_fn.traverse({\n      StringLiteral(path) {\n        if (path.node.value.length === 91) {\n          valid = true\n          path.stop()\n        }\n      },\n    })\n    if (!valid) {\n      return false\n    }\n    child.push({\n      name: decode_fn.node.id.name,\n      decoder: decode_fn,\n    })\n  }\n  obj.child = child\n  return true\n}\n\nfunction generatorStringConcealingDepCode(obj) {\n  obj.deps.sort((a, b) => {\n    return a.pos - b.pos\n  })\n  const dep_node = t.program([])\n  for (const item of obj.deps) {\n    if (item.node) {\n      dep_node.body.push(item.node)\n    } else {\n      dep_node.body.push(item.path.node)\n    }\n  }\n  obj.dep_code = generator(dep_node).code\n}\n\nfunction renameProperty(member) {\n  const obj_name = safeGetName(member.get('object'))\n  const prop_name = safeGetName(member.get('property'))\n  const new_name = member.scope.generateUidIdentifier(`_tmp_local_`)\n  const binding = member.scope.getBinding(obj_name)\n  let first = true\n  for (const ref of binding.referencePaths) {\n    const item = ref.parentPath\n    const prop = safeGetName(item.get('property'))\n    if (prop !== prop_name) {\n      continue\n    }\n    if (first) {\n      let body = item\n      while (body.listKey !== 'body') {\n        body = body.parentPath\n      }\n      body.container.unshift(\n        t.variableDeclaration('var', [t.variableDeclarator(new_name)])\n      )\n      body.scope.crawl()\n      first = false\n    }\n    item.replaceWith(new_name)\n  }\n  member.scope.crawl()\n}\n\n/**\n * Template:\n * ```javascript\n * var cacheName = [], arrayName = []\n * // Below will appear multiple times\n * var getterFnName = (x, y, z, a, b)=>{\n *   if ( x !== y ) {\n *     return b[x] || (b[x] = a(arrayName[x]))\n *   }\n *   // Add fake ifStatements\n *   if(typeof a === \"undefined\") {\n *     a = decodeFn\n *   }\n *   if(typeof b === \"undefined\") {\n *     b = cacheName\n *   }\n * }\n * // Base91 Algo\n * function decodeFn (str){\n *   var table = {__strTable__};\n *   var raw = \"\" + (str || \"\");\n *   var len = raw.length;\n *   var ret = [];\n *   var b = 0;\n *   var n = 0;\n *   var v = -1;\n *   for (var i = 0; i < len; i++) {\n *     var p = table.indexOf(raw[i]);\n *     if (p === -1) continue;\n *     if (v < 0) {\n *       v = p;\n *     } else {\n *       v += p * 91;\n *       b |= v << n;\n *       n += (v & 8191) > 88 ? 13 : 14;\n *       do {\n *         ret.push(b & 0xff);\n *         b >>= 8;\n *         n -= 8;\n *       } while (n > 7);\n *       v = -1;\n *     }\n *   }\n *   if (v > -1) {\n *     ret.push((b | (v << n)) & 0xff);\n *   }\n *   return bufferToStringName(ret);\n * }\n * ```\n */\nfunction processSingleGetter(obj, decoder_name, decoder_path) {\n  const decoder_code = generator(decoder_path.node).code\n  let binding = decoder_path.parentPath.scope.getBinding(decoder_name)\n  let getter_path = binding.referencePaths[0].getFunctionParent()\n  while (\n    !getter_path.isAssignmentExpression() &&\n    !getter_path.isVariableDeclarator()\n  ) {\n    getter_path = getter_path.parentPath\n  }\n  let getter_name\n  if (getter_path.isAssignmentExpression()) {\n    if (getter_path.get('left').isMemberExpression()) {\n      renameProperty(getter_path.get('left'))\n    }\n    getter_name = safeGetName(getter_path.get('left'))\n  } else {\n    getter_name = safeGetName(getter_path.get('id'))\n  }\n  console.log(\n    `[StringConcealing] getter: ${getter_name} decoder: ${decoder_name}`\n  )\n  const getter_code = 'var ' + generator(getter_path.node).code\n  binding = getter_path.scope.getBinding(getter_name)\n  if (getter_path.isAssignmentExpression()) {\n    getter_path.get('right').replaceWith(t.numericLiteral(0))\n  } else {\n    getter_path.get('init').replaceWith(t.numericLiteral(0))\n  }\n  binding.scope.crawl()\n  binding = getter_path.scope.getBinding(getter_name)\n  let complete = false\n  while (!complete) {\n    complete = true\n    const vm = isolate.createContextSync()\n    vm.evalSync(obj.dep_code)\n    try {\n      for (const ref of binding.referencePaths) {\n        if (ref.findParent((path) => path.removed)) {\n          continue\n        }\n        let repl_path = ref.parentPath\n        if (repl_path.isCallExpression()) {\n          const args = repl_path.node.arguments\n          if (args.length !== 1 || !t.isLiteral(args[0])) {\n            console.warn(`[StringConcealing] Unexpected call: ${repl_path}`)\n            continue\n          }\n        } else if (repl_path.isMemberExpression()) {\n          repl_path = repl_path.parentPath\n        } else {\n          console.warn(`[StringConcealing] Unexpected ref: ${repl_path}`)\n          continue\n        }\n        const eval_code = generator(repl_path.node).code\n        // The name of getter can be the same as other dep functions\n        const value = vm.evalSync(\n          `(function (){${decoder_code}\\n${getter_code}\\nreturn ${eval_code}})()`\n        )\n        safeReplace(repl_path, value)\n      }\n    } catch (e) {\n      if (e.name !== 'ReferenceError') {\n        console.warn(`[StringConcealing] Unexpected exception: ${e.message}`)\n        return\n      }\n      complete = false\n      const lost = e.message.split(' ')[0]\n      const binding = getter_path.scope.getBinding(lost)\n      if (!binding) {\n        console.warn(`[StringConcealing] Missing cache or array: ${lost}`)\n        return\n      }\n      let count = binding.constantViolations.length\n      if (count) {\n        console.warn(`[StringConcealing] Invalid violations ${lost} : ${count}`)\n        return\n      }\n      count = binding.path.node.init.elements.length\n      if (count) {\n        console.log(`[StringConcealing] Add array : ${lost}`)\n        obj.array_name = lost\n        obj.array_path = binding.path\n      } else {\n        console.log(`[StringConcealing] Add cache : ${lost}`)\n        obj.cache_name = lost\n        obj.cache_path = binding.path\n      }\n      insertDepItemVar(obj.deps, lost, binding.path)\n      generatorStringConcealingDepCode(obj)\n    }\n  }\n  safeDeleteNode(getter_name, getter_path)\n  safeDeleteNode(decoder_name, decoder_path)\n}\n\nconst deStringConcealing = {\n  FunctionDeclaration(path) {\n    const obj = findGlobalFn(path)\n    if (!obj) {\n      return null\n    }\n    if (obj.glo_fn_path.parentPath.getFunctionParent()) {\n      return null\n    }\n    findGlobalFnRef(obj)\n    if (!findBufferToString(obj)) {\n      return\n    }\n    generatorStringConcealingDepCode(obj)\n    for (const item of obj.child) {\n      processSingleGetter(obj, item.name, item.decoder)\n    }\n    safeDeleteNode(obj.array_name, obj.array_path)\n    safeDeleteNode(obj.cache_name, obj.cache_path)\n    // a2s and b2s are pairs\n    if (safeDeleteNode(obj.b2s_name, obj.b2s_path)) {\n      obj.a2s_path.remove()\n      obj.a2s_path.scope.crawl()\n    }\n  },\n}\n\nfunction tryStringConcealingPlace(path) {\n  const parent = path.parentPath\n  if (!parent.isAssignmentExpression()) {\n    return\n  }\n  const name = safeGetName(parent.get('left'))\n  let binding = parent.scope.getBinding(name)\n  if (binding?.constantViolations?.length !== 1) {\n    return\n  }\n  for (const ref of binding.referencePaths) {\n    if (ref.key !== 'object' || ref.parentPath.key === 'callee') {\n      return\n    }\n  }\n  const code = generator(parent.node).code\n  const vm = isolate.createContextSync()\n  vm.evalSync('var ' + code)\n  for (const ref of binding.referencePaths) {\n    if (ref.key !== 'object') {\n      continue\n    }\n    const test = generator(ref.parent).code\n    const res = vm.evalSync(test)\n    safeReplace(ref.parentPath, res)\n  }\n  safeDeleteNode(name, parent)\n}\n\nconst deStringConcealingPlace = {\n  StringLiteral(path) {\n    if (path.key !== 'right' || !path.parentPath.isAssignmentExpression()) {\n      return\n    }\n    const name = safeGetName(path.parentPath.get('left'))\n    if (!name) {\n      return\n    }\n    const binding = path.scope.getBinding(name)\n    if (binding.constantViolations.length !== 1) {\n      return\n    }\n    for (const ref of binding.referencePaths) {\n      if (ref.node.start < path.node.start) {\n        continue\n      }\n      ref.replaceWith(path.node)\n    }\n    safeDeleteNode(name, path.parentPath)\n  },\n  ArrayExpression(path) {\n    let valid = true\n    if (path.node.elements.length === 0) {\n      return\n    }\n    for (const ele of path.node.elements) {\n      if (!t.isStringLiteral(ele)) {\n        valid = false\n        break\n      }\n    }\n    if (!valid) {\n      return\n    }\n    tryStringConcealingPlace(path)\n  },\n  ObjectExpression(path) {\n    let valid = true\n    if (path.node.properties.length === 0) {\n      return\n    }\n    for (const ele of path.node.properties) {\n      if (!t.isStringLiteral(ele.value)) {\n        valid = false\n        break\n      }\n    }\n    if (!valid) {\n      return\n    }\n    tryStringConcealingPlace(path)\n  },\n}\n\nexport default {\n  deStringConcealing,\n  deStringConcealingPlace,\n}\n"
  },
  {
    "path": "src/visitor/lint-if-statement.js",
    "content": "import * as t from '@babel/types'\n\nfunction LintIfStatement(path) {\n  let { test, consequent, alternate } = path.node\n  let changed = false\n  if (!t.isBlockStatement(consequent)) {\n    consequent = t.blockStatement([consequent])\n    changed = true\n  }\n  if (alternate && !t.isBlockStatement(alternate)) {\n    alternate = t.blockStatement([alternate])\n    changed = true\n  }\n  if (!changed) {\n    return\n  }\n  path.replaceWith(t.ifStatement(test, consequent, alternate))\n}\n\nexport default {\n  IfStatement: {\n    exit: LintIfStatement,\n  },\n}\n"
  },
  {
    "path": "src/visitor/merge-object.js",
    "content": "import * as t from '@babel/types'\n\nfunction mergeObject(path) {\n  const { id, init } = path.node\n  if (!t.isObjectExpression(init)) {\n    // 判断是否是定义对象\n    return\n  }\n  let name = id.name\n  let scope = path.scope\n  let binding = scope.getBinding(name)\n  const start = path.node.end\n  let end = -1\n  let violation = null\n  if (!binding.constant) {\n    // Find the first constantViolation after this declaration\n    for (let item of binding.constantViolations) {\n      if (item.node.start <= start) {\n        continue\n      }\n      if (item.isVariableDeclarator()) {\n        end = item.node.start\n        violation = item\n        break\n      }\n      if (item.isAssignmentExpression()) {\n        end = item.node.start\n        violation = item\n        break\n      }\n      return\n    }\n  }\n  // 添加已有的key\n  let keys = {}\n  let properties = init.properties\n  for (let prop of properties) {\n    let key = null\n    if (t.isStringLiteral(prop.key)) {\n      key = prop.key.value\n    }\n    if (t.isIdentifier(prop.key)) {\n      key = prop.key.name\n    }\n    if (key) {\n      keys[key] = true\n    }\n  }\n  // 遍历作用域检测是否含有局部混淆特征并合并成员\n  let merges = []\n  const container = path.parentPath.parentPath\n  let cur = 0\n  let valid = true\n  // Check references in sequence\n  while (cur < binding.references) {\n    const ref = binding.referencePaths[cur]\n    // Ignore the references before this declaration\n    if (ref.node.start <= start) {\n      ++cur\n      continue\n    }\n    // Ignore the references after the first constantViolation\n    if (end >= 0 && ref.node.end >= end) {\n      break\n    }\n    if (ref.key !== 'object' || !ref.parentPath.isMemberExpression()) {\n      break\n    }\n    const me = ref.parentPath\n    if (me.key !== 'left' || !me.parentPath.isAssignmentExpression()) {\n      break\n    }\n    const ae = me.parentPath\n    let bk = ae\n    let stop = false\n    while (bk.parentPath !== container) {\n      if (\n        bk.parentPath.isSequenceExpression() ||\n        bk.parentPath.isVariableDeclarator() ||\n        bk.parentPath.isVariableDeclaration() ||\n        bk.parentPath.isExpressionStatement()\n      ) {\n        bk = bk.parentPath\n        continue\n      }\n      stop = true\n      break\n    }\n    if (stop) {\n      break\n    }\n    const property = me.node.property\n    let key = null\n    if (t.isStringLiteral(property)) {\n      key = property.value\n    }\n    if (t.isIdentifier(property)) {\n      key = property.name\n    }\n    if (!key) {\n      valid = false\n      break\n    }\n    // 不允许出现重定义\n    if (Object.prototype.hasOwnProperty.call(keys, key)) {\n      valid = false\n      break\n    }\n    // 添加到列表\n    properties.push(t.ObjectProperty(t.valueToNode(key), ae.node.right))\n    keys[key] = true\n    merges.push(ae)\n    ++cur\n  }\n  if (!merges.length || !valid) {\n    return\n  }\n  // Remove code\n  console.log(`尝试性合并: ${name}`)\n  for (let ref of merges) {\n    const left = ref.node.left\n    if (ref.parentPath.isSequenceExpression() && ref.container.length === 1) {\n      ref = ref.parentPath\n    }\n    if (\n      ref.parentPath.isVariableDeclarator() ||\n      ref.parentPath.isAssignmentExpression()\n    ) {\n      ref.replaceWith(left)\n    } else {\n      ref.remove()\n    }\n  }\n  // Check the remaining references\n  const ref1 = binding.referencePaths[cur++]\n  if (!ref1) {\n    scope.crawl()\n    return\n  }\n  const ref2 = binding.referencePaths[cur]\n  // Don't replace the declarator if there exists more than one reference\n  if (ref2 && ref2.node.end < end) {\n    scope.crawl()\n    return\n  }\n  // Check if the only reference is an assignment\n  let key = ref1.key\n  let up1 = ref1.parentPath\n  if (up1.isSequenceExpression() && ref1.container.length === 1) {\n    key = up1.key\n    up1 = up1.parentPath\n  }\n  if (!up1.isVariableDeclarator() || key !== 'init') {\n    scope.crawl()\n    return\n  }\n  // Move the definition to its reference\n  up1.node.init = path.node.init\n  // Delete the original definition\n  if (violation?.isAssignmentExpression()) {\n    path.node.init = undefined\n  } else {\n    path.remove()\n  }\n  binding.scope.crawl()\n}\n\n/**\n * Collect the properties of one object and move it back to the declaration.\n *\n * One example made by ObjectExpressionKeysTransformer:\n *\n * ```javascript\n * var _0xb28de8 = {};\n * _0xb28de8[\"abcd\"] = function(_0x22293f, _0x5a165e) {\n *     return _0x22293f == _0x5a165e;\n * };\n * _0xb28de8.dbca = function(_0xfbac1e, _0x23462f, _0x556555) {\n *     return _0xfbac1e(_0x23462f, _0x556555);\n * };\n * _0xb28de8.aaa = function(_0x57e640) {\n *     return _0x57e640();\n * };\n * _0xb28de8[\"bbb\"] = \"eee\";\n * var _0x15e145 = _0xb28de8;\n * ```\n *\n * The result:\n *\n * ```javascript\n * var _0x15e145 = {\n *   \"abcd\": function (_0x22293f, _0x5a165e) {\n *     return _0x22293f == _0x5a165e;\n *   },\n *   \"dbca\": function (_0xfbac1e, _0x23462f, _0x556555) {\n *     return _0xfbac1e(_0x23462f, _0x556555);\n *   },\n *   \"aaa\": function (_0x57e640) {\n *     return _0x57e640();\n *   },\n *   \"bbb\": \"eee\"\n * };\n * ```\n *\n * Note:\n * - Constant objects in the original code can be splitted\n * - AssignmentExpression can be moved to ReturnStatement\n */\nexport default {\n  VariableDeclarator: mergeObject,\n}\n"
  },
  {
    "path": "src/visitor/parse-control-flow-storage.js",
    "content": "import _generate from '@babel/generator'\nconst generator = _generate.default\nimport * as t from '@babel/types'\n\nfunction parseObject(path) {\n  let node = path.node\n  // 变量必须定义为Object类型才可能是代码块加密内容\n  if (!t.isObjectExpression(node.init)) {\n    return\n  }\n  let objPropertiesList = node.init.properties\n  if (objPropertiesList.length == 0) {\n    return\n  }\n  // 遍历Object 判断每个元素是否符合格式\n  let objName = node.id.name\n  let objKeys = {}\n  // 有时会有重复的定义\n  let replCount = 0\n  objPropertiesList.map(function (prop) {\n    if (!t.isObjectProperty(prop)) {\n      return\n    }\n    let key\n    if (t.isIdentifier(prop.key)) {\n      key = prop.key.name\n    } else {\n      key = prop.key.value\n    }\n    if (t.isFunctionExpression(prop.value)) {\n      // 符合要求的函数必须有且仅有一条return语句\n      if (prop.value.body.body.length !== 1) {\n        return\n      }\n      let retStmt = prop.value.body.body[0]\n      if (!t.isReturnStatement(retStmt)) {\n        return\n      }\n      // 检测是否是3种格式之一\n      let repfunc = null\n      if (t.isBinaryExpression(retStmt.argument)) {\n        // 二元运算类型\n        repfunc = function (_path, args) {\n          _path.replaceWith(\n            t.binaryExpression(retStmt.argument.operator, args[0], args[1])\n          )\n        }\n      } else if (t.isLogicalExpression(retStmt.argument)) {\n        // 逻辑判断类型\n        repfunc = function (_path, args) {\n          _path.replaceWith(\n            t.logicalExpression(retStmt.argument.operator, args[0], args[1])\n          )\n        }\n      } else if (t.isCallExpression(retStmt.argument)) {\n        // 函数调用类型 调用的函数必须是传入的第一个参数\n        if (!t.isIdentifier(retStmt.argument.callee)) {\n          return\n        }\n        if (retStmt.argument.callee.name !== prop.value.params[0]?.name) {\n          return\n        }\n        repfunc = function (_path, args) {\n          _path.replaceWith(t.callExpression(args[0], args.slice(1)))\n        }\n      }\n      if (repfunc) {\n        objKeys[key] = repfunc\n        ++replCount\n      }\n    } else if (t.isStringLiteral(prop.value)) {\n      let retStmt = prop.value.value\n      objKeys[key] = function (_path) {\n        _path.replaceWith(t.stringLiteral(retStmt))\n      }\n      ++replCount\n    } else if (t.isMemberExpression(prop.value)) {\n      let retStmt = prop.value\n      objKeys[key] = function (_path) {\n        _path.replaceWith(retStmt)\n      }\n      ++replCount\n    }\n  })\n  // 如果Object内的元素不全符合要求 很有可能是普通的字符串类型 不需要替换\n  if (!replCount) {\n    return\n  }\n  if (objPropertiesList.length !== replCount) {\n    console.log(\n      `不完整替换: ${objName} ${replCount}/${objPropertiesList.length}`\n    )\n    return\n  }\n  // 遍历作用域进行替换 分为函数调用和字符串调用\n  console.log(`处理代码块: ${objName}`)\n  let objUsed = {}\n  function getReplaceFunc(_node) {\n    // 这里开始所有的调用应该都在列表中\n    let key = null\n    if (t.isStringLiteral(_node.property)) {\n      key = _node.property.value\n    } else if (t.isIdentifier(_node.property)) {\n      key = _node.property.name\n    } else {\n      // Maybe the code was obfuscated more than once\n      const code = generator(_node.property, { minified: true }).code\n      console.log(`意外的调用: ${objName}[${code}]`)\n      return null\n    }\n    if (!Object.prototype.hasOwnProperty.call(objKeys, key)) {\n      // 这里应该是在死代码中 因为key不存在\n      return null\n    }\n    objUsed[key] = true\n    return objKeys[key]\n  }\n  let bind = path.scope.getBinding(objName)?.referencePaths\n  let usedCount = 0\n  // Replace reversely to handle nested cases correctly\n  for (let i = bind.length - 1; i >= 0; --i) {\n    let ref = bind[i]\n    let up1 = ref.parentPath\n    if (up1.isMemberExpression() && ref.key === 'object') {\n      if (up1.key === 'left' && t.isAssignmentExpression(up1.parent)) {\n        continue\n      }\n      let func = getReplaceFunc(up1.node)\n      if (!func) {\n        continue\n      }\n      ++usedCount\n      let up2 = up1.parentPath\n      if (up1.key === 'callee') {\n        func(up2, up2.node.arguments)\n      } else {\n        func(up1)\n      }\n    }\n  }\n  path.scope.crawl()\n  // 如果没有全部使用 就先不删除\n  if (usedCount !== bind.length) {\n    console.log(`不完整使用: ${objName} ${usedCount}/${bind.length}`)\n  } else {\n    path.remove()\n  }\n}\n\n/**\n * Parse control flow object\n *\n * Several kinds of expressions are collected, transformed, and merged into\n * the controlFlowStorage object by method FunctionControlFlowTransformer:\n *\n * - BinaryExpression\n * - CallExpression\n * - LogicalExpression\n * - Literal\n *\n * ```javascript\n * var _0xb28de8 = {\n *     \"abcd\": function(_0x22293f, _0x5a165e) {\n *         return _0x22293f == _0x5a165e;\n *     },\n *     \"dbca\": function(_0xfbac1e, _0x23462f, _0x556555) {\n *         return _0xfbac1e(_0x23462f, _0x556555);\n *     },\n *     \"aaa\": function(_0x57e640) {\n *         return _0x57e640();\n *     },\n *     \"bbb\": \"eee\",\n *     \"ccc\": A[x][y][...]\n * };\n * ```\n *\n * This visitor can parse such objects and undo the transformation.\n *\n * ```javascript\n * // From\n * var aa = _0xb28de8[\"abcd\"](123, 456);\n * var bb = _0xb28de8[\"dbca\"](bcd, 11, 22);\n * var cc = _0xb28de8[\"aaa\"](dcb);\n * var dd = _0xb28de8[\"bbb\"];\n * var ee = _0xb28de8[\"ccc\"];\n * // To\n * var aa = 123 == 456;\n * var bb = bcd(11, 22);\n * var cc = dcb();\n * var dd = \"eee\";\n * var ee = A[x][y][...];\n * ```\n */\nexport default {\n  VariableDeclarator: {\n    exit: parseObject,\n  },\n}\n"
  },
  {
    "path": "src/visitor/prune-if-branch.js",
    "content": "function pruneIfBranch(path) {\n  function clear(path, toggle) {\n    // 判定成立\n    if (toggle) {\n      path.replaceWith(path.node.consequent)\n      return\n    }\n    // 判定不成立\n    if (!path.node.alternate) {\n      path.remove()\n      return\n    }\n    path.replaceWith(path.node.alternate)\n  }\n  // 判断判定是否恒定\n  const test = path.node.test\n  const types = ['StringLiteral', 'NumericLiteral', 'BooleanLiteral']\n  if (test.type === 'BinaryExpression') {\n    if (\n      types.indexOf(test.left.type) !== -1 &&\n      types.indexOf(test.right.type) !== -1\n    ) {\n      const left = JSON.stringify(test.left.value)\n      const right = JSON.stringify(test.right.value)\n      clear(path, eval(left + test.operator + right))\n    }\n  } else if (types.indexOf(test.type) !== -1) {\n    clear(path, eval(JSON.stringify(test.value)))\n  }\n}\n\n/**\n * Prune the branch if the test is constant\n *\n * The code must be reloaded to update the references\n */\nexport default {\n  IfStatement: pruneIfBranch,\n  ConditionalExpression: pruneIfBranch,\n}\n"
  },
  {
    "path": "src/visitor/split-assignment.js",
    "content": "import * as t from '@babel/types'\n\n/**\n * Split the AssignmentExpressions. For example:\n *\n * - In the test of IfStatement\n * - In the VariableDeclaration\n */\nexport default {\n  IfStatement(path) {\n    if (!path.parentPath.isBlockStatement() && !path.parentPath.isProgram()) {\n      return\n    }\n    let test = path.get('test')\n    if (test.isAssignmentExpression()) {\n      path.insertBefore(t.expressionStatement(test.node))\n      test.replaceWith(test.node.left)\n      path.scope.crawl()\n      return\n    }\n    if (test.isMemberExpression()) {\n      let object = test.get('object')\n      if (object.isAssignmentExpression()) {\n        path.insertBefore(t.expressionStatement(object.node))\n        object.replaceWith(object.node.left)\n      }\n      let property = test.get('property')\n      if (property.isAssignmentExpression()) {\n        path.insertBefore(t.expressionStatement(property.node))\n        property.replaceWith(property.node.left)\n      }\n      path.scope.crawl()\n    }\n  },\n  VariableDeclaration(path) {\n    if (!path.parentPath.isBlockStatement() && !path.parentPath.isProgram()) {\n      return\n    }\n    for (let i = 0; i < path.node.declarations.length; ++i) {\n      const declaration = path.get(`declarations.${i}`)\n      const init = declaration.node.init\n      if (!t.isAssignmentExpression(init)) {\n        continue\n      }\n      path.insertBefore(t.ExpressionStatement(init))\n      declaration.get('init').replaceWith(init.left)\n    }\n    path.scope.crawl()\n  },\n}\n"
  },
  {
    "path": "src/visitor/split-member-object.js",
    "content": "function splitMemberObject(path) {\n  const object = path.get('object')\n  if (!object.isAssignmentExpression()) {\n    return\n  }\n  let insertPath = path\n  while (!insertPath?.listKey) {\n    if (insertPath.parentPath.isAssignmentExpression()) {\n      insertPath = insertPath.parentPath\n      continue\n    }\n    if (insertPath.parentPath.isExpressionStatement()) {\n      insertPath = insertPath.parentPath\n      continue\n    }\n    return\n  }\n  insertPath.insertBefore(object.node)\n  object.replaceWith(object.node.left)\n  insertPath.scope.crawl()\n}\n\n/**\n * Split assignment operation in member object\n *\n * From:\n * ```javascript\n * (a = {})['b'] = c;\n * ```\n * To:\n * ```javascript\n * a = {}\n * a['b'] = c;\n * ```\n */\nexport default {\n  MemberExpression: splitMemberObject,\n}\n"
  },
  {
    "path": "src/visitor/split-sequence.js",
    "content": "import * as t from '@babel/types'\n\nfunction doSplit(insertPath, path) {\n  const expressions = path.node.expressions\n  const lastExpression = expressions.pop()\n  while (expressions.length) {\n    insertPath.insertBefore(t.expressionStatement(expressions.shift()))\n  }\n  path.replaceWith(lastExpression)\n  insertPath.scope.crawl()\n}\n\nfunction splitSequence(path) {\n  let { parentPath } = path\n  if (parentPath.isVariableDeclarator()) {\n    // Skip if it's not the first VariableDeclarator\n    if (parentPath.key !== 0) {\n      return\n    }\n    let insertPath = parentPath.parentPath\n    // Skip if the container of the VariableDeclaration is not an array\n    if (!insertPath.listKey) {\n      return\n    }\n    doSplit(insertPath, path)\n    return\n  }\n  if (parentPath.isReturnStatement()) {\n    if (!parentPath.listKey) {\n      return\n    }\n    doSplit(parentPath, path)\n    return\n  }\n  if (parentPath.isExpressionStatement()) {\n    if (!parentPath.listKey) {\n      return\n    }\n    doSplit(parentPath, path)\n    return\n  }\n}\n\n/**\n * The sequenceExpressions inside certain statements are splitted if possible:\n *\n * - VariableDeclarator\n * - ReturnStatement\n * - ExpressionStatement\n */\nexport default {\n  SequenceExpression: splitSequence,\n}\n"
  },
  {
    "path": "src/visitor/split-variable-declaration.js",
    "content": "import * as t from '@babel/types'\n\nfunction splitVariableDeclaration(path) {\n  // The scope of a for statement is its body\n  if (path.parentPath.isFor()) {\n    return\n  }\n  // The container must be an array\n  if (!path.listKey) {\n    return\n  }\n  const kind = path.node.kind\n  const list = path.node.declarations\n  if (list.length == 1) {\n    return\n  }\n  for (let item of list) {\n    path.insertBefore(t.variableDeclaration(kind, [item]))\n  }\n  path.remove()\n  path.scope.crawl()\n}\n\n/**\n * Split the VariableDeclaration if it has more than one VariableDeclarator\n *\n * This operation will only be performed when its container is an array\n */\nexport default {\n  VariableDeclaration: splitVariableDeclaration,\n}\n"
  }
]