let Fs = require('fs'); let Path = require('path'); let JavascriptObfuscator = require('javascript-obfuscator'); let fmbuild = require('./fmbuild'); let defaultConfig = { auto: false, files: ['/src/project.js'], useAbsPath: false, preset: 'lower', options: {} }; let presetFileUrl = 'packages://ccc-obfuscated-code/preset.json'; let presets = null; let list = ['DemoNewWeixin', 'FMViewBase', 'FMViewWudianMoveBlinkBase', 'FMButton', 'FMItemLayout', 'FMScrollViewLoop', 'FMTouchMaskView', 'AppConfig', 'AppSwitchConfig', 'StoreConfig', 'EventEnum', 'EventMgr', 'FMInterface', 'Main', 'BundleMgr', 'GameMgr', 'RYPlatformMgr', 'RemoteMgr', 'SoundMgr', 'StorageMgr', 'TimerUtils', 'VibrateMgr', 'WXADMgr', 'WudianMgr', 'AesTools', 'HttpUnit', 'NetConfig', 'aes', 'ALD', 'MaiLiang', 'OPPOAPI', 'CachedQQBannerAd', 'QQMiniGameAPI', 'ShareAd', 'TTAPI', 'VIVOAPI', 'CachedWXBannerAd', 'WXAPI', 'RYAD', 'RYSDK', 'RYSTAT', 'User', 'AppPlatform', 'Common', 'DateUtils', 'LogUtils', 'Utilit', 'WXBannerView', 'CrazyClickView', 'Export1View', 'Export2View', 'Export3View', 'ADGameBanner', 'ADListView', 'ADSingle', 'ADWXBanner', 'KRQ_Base', 'LoadingView', 'MiniGameItemPrefab', 'MiniGameView', 'OPPONativeAdViewTemplate', 'ButWudianPrefab', 'QQCrazyClickView', 'QQCrazyClickView2', 'QQGameSettleViewTemplate', 'QQGameViewTemplate', 'QQMainViewTemplate', 'TTExport2Template', 'TTGameSettleViewTemplate', 'TTGameViewTemplate', 'TTMainViewTemplate', 'TTMoreReward', 'TTRelive', 'TTReward', 'TTRewardBox', 'TTShareRecord', 'TTSignIn', 'TTStartTry', 'VVNativeAd1View', 'VVNativeAd2View', 'SettleLayoutBase', 'WCGameSettleViewTemplate', 'WCGameViewTemplate', 'WCMainViewTemplate', 'FMAdItemLayout', 'FMAdViewBase', 'FMAdViewFixed', 'FMAdViewLoop', 'FMViewGameSettleWx', 'FMViewGameWx', 'FMViewHaowanWx', 'FMViewHaowanWx2', 'FMViewLoopExportBig', 'FMViewMainWx', 'FMViewReviveWx', 'FMViewSkinWx', 'FMGameFailScene', 'FMGameScene', 'FMGameWinScene', 'FMMainScene'] let listRandom = []; let rProIndex = 0; let tabProRand = {} let identifiersPrefixName = "" let csrywTab = {} /** * 保存加工的配置 * @param {*} config */ function saveConfigProcess(config) { let projectPath = Editor.Project.path || Editor.projectPath; Editor.log(projectPath) let configDirPath = Path.join(projectPath, '/packages/ccc-obfuscated-code/local/'); Editor.log(configDirPath) if (!Fs.existsSync(configDirPath)) Fs.mkdirSync(configDirPath); let configFilePath = Path.join(configDirPath, 'configProcess.json'); let object = {}; // // 读取本地配置 // if (Fs.existsSync(configFilePath)) { // object = JSON.parse(Fs.readFileSync(configFilePath, 'utf8')); // } // // 写入配置 // for (let key in config) { // object[key] = config[key]; // } let classTab = {} for (let index = 0; index < list.length; index++) { const element = list[index]; const element2 = listRandom[index]; classTab[element] = element2 } object.time = (new Date()).toLocaleString(); object.identifiersPrefixName = identifiersPrefixName; object.class = classTab //object.csryw = tabProRand object.csryw = csrywTab let string = JSON.stringify(object, null, 2); Fs.writeFileSync(configFilePath, string); Editor.log('[CC] 混淆数据保存', configFilePath); } /** * 保存配置 * @param {*} config */ function saveConfig(config) { let projectPath = Editor.Project.path || Editor.projectPath; Editor.log(projectPath) let configDirPath = Path.join(projectPath, '/packages/ccc-obfuscated-code/local/'); Editor.log(configDirPath) if (!Fs.existsSync(configDirPath)) Fs.mkdirSync(configDirPath); let configFilePath = Path.join(configDirPath, 'ccc-obfuscated-code.json'); let object = {}; // 读取本地配置 if (Fs.existsSync(configFilePath)) { object = JSON.parse(Fs.readFileSync(configFilePath, 'utf8')); } // 写入配置 for (let key in config) { object[key] = config[key]; } let string = JSON.stringify(object, null, 2); Fs.writeFileSync(configFilePath, string); Editor.log('[CC] 配置文件路径', configFilePath); } /** * 读取配置 */ function getConfig() { let projectPath = Editor.Project.path || Editor.projectPath; let configFilePath = Path.join(projectPath, '/packages/ccc-obfuscated-code/local/ccc-obfuscated-code.json'); let config = null; if (Fs.existsSync(configFilePath)) { config = JSON.parse(Fs.readFileSync(configFilePath, 'utf8')); // 删除旧版本(1.0.0)的配置文件 let projectName = Editor.Project.name || projectPath.slice(projectPath.lastIndexOf('\\') + 1); if (config[projectName]) { Fs.unlinkSync(configFilePath); config = null; } } if (!config) { config = defaultConfig; config.options = getPreset('off'); if (config.preset !== 'off') { let preset = getPreset(config.preset); for (let key in preset) { config.options[key] = preset[key]; } } } return config; }; /** * 读取预设参数 */ function getPreset(type) { if (presets) { return presets[type]; } else { let presetFilePath = Editor.url(presetFileUrl); if (Fs.existsSync(presetFilePath)) { presets = JSON.parse(Fs.readFileSync(presetFilePath, 'utf8')); return presets[type]; } else { return null; } } }; /** * 混淆 * @param {*} path 文件路径 * @param {*} options 混淆参数 */ function obfuscate(path, options) { let sourceCode = Fs.readFileSync(path, 'utf8'); let obfuscationResult = JavascriptObfuscator.obfuscate(sourceCode, options); let obfuscatedCode = obfuscationResult.getObfuscatedCode(); Fs.writeFileSync(path, obfuscatedCode); } //读取js 文件 function readFileList(dir, filesList = []) { const files = Fs.readdirSync(dir); files.forEach((item, index) => { var fullPath = Path.join(dir, item); const stat = Fs.statSync(fullPath); if (stat.isDirectory()) { readFileList(Path.join(dir, item), filesList); //递归读取文件 } else { if (Path.extname(fullPath) === '.js') { filesList.push(fullPath); } } }); return filesList; } //随机串 function randomString() { let num = Math.floor(Math.random() * 5); num = num + 7; var t = "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", a = t.length, n = ""; for (i = 0; i < num; i++) n += t.charAt(Math.floor(Math.random() * a)); return n } //随机串 class name function randomStringClassName(num) { var t = "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", a = t.length, n = ""; for (i = 0; i < num; i++) n += t.charAt(Math.floor(Math.random() * a)); return n } //随机串 IdentifiersPrefix function randomStringIdentifiersPrefix(num) { var t = "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", a = t.length, n = ""; for (i = 0; i < num; i++) n += t.charAt(Math.floor(Math.random() * a)); return n } //替换 function replace_csryw(path) { let projectPath = Editor.Project.path || Editor.projectPath; let configFilePath = Path.join(projectPath, path); var filesList = []; readFileList(configFilePath, filesList); for (let index = 0; index < filesList.length; index++) { const element = filesList[index]; let data = Fs.readFileSync(element); if (data) { data = data.toString() + ""; for (var key in tabProRand) { data = data.replace(new RegExp("." + key, "gm"), tabProRand[key]); } Fs.writeFileSync(element, data); } } } //混淆 assets subpackages function replaceFile(actualPlatform) { if (actualPlatform == "wechatgame") { //查找关键字 Editor.log("开始查找") tabProRand = {}; rProIndex = Math.floor(Math.random() * 20); find_csryw_file("/build/wechatgame/assets/", tabProRand); find_csryw_file("/build/wechatgame/subpackages/", tabProRand); Editor.log(tabProRand); Editor.log("总共找到关键字") // //替换的字符 // let cstr = randomString() // Editor.warn("替换 _csryw 为 " + cstr); replace_csryw("/build/wechatgame/assets/") replace_csryw("/build/wechatgame/subpackages/") } else { Editor.warn("当前平台没有处理 关键字 替换" + actualPlatform); } } //替换 function replace_className(path) { let projectPath = Editor.Project.path || Editor.projectPath; let configFilePath = Path.join(projectPath, path); var filesList = []; readFileList(configFilePath, filesList); for (let index = 0; index < filesList.length; index++) { const element = filesList[index]; let data = Fs.readFileSync(element); if (data) { data = data.toString() + ""; for (var i = 0; i < list.length; i++) { data = data.replace(new RegExp(",\'" + list[i] + "\'", "gm"), ",\'" + listRandom[i] + "\'"); data = data.replace(new RegExp(":\'" + list[i] + "\'", "gm"), ":\'" + listRandom[i] + "\'"); data = data.replace(new RegExp("\'" + list[i] + "\':", "gm"), "\'" + listRandom[i] + "\':"); data = data.replace("\[\'" + list[i] + "\'", "\[\'" + listRandom[i] + "\'"); //data = data.replace(new RegExp("/" + list[i] + "", "gm"), "/" + listRandom[i] + ""); } Fs.writeFileSync(element, data); } } } //文件中查找 function find_csryw_file(path, array) { let projectPath = Editor.Project.path || Editor.projectPath; let configFilePath = Path.join(projectPath, path); var filesList = []; readFileList(configFilePath, filesList); Editor.log(filesList) for (let index = 0; index < filesList.length; index++) { const element = filesList[index]; let data = Fs.readFileSync(element); if (data) { data = data.toString() + ""; let cnextid = data.indexOf('_csryw'); // 字符出现的位置 while (cnextid !== -1) { let cid1 = data.lastIndexOf(".", cnextid); let cid2 = data.lastIndexOf("\"", cnextid); let cid3 = data.lastIndexOf("{", cnextid); let cid4 = data.lastIndexOf(",", cnextid); let cid = -1; cid = cid1 > cid ? cid1 : cid; cid = cid2 > cid ? cid2 : cid; cid = cid3 > cid ? cid3 : cid; cid = cid4 > cid ? cid4 : cid; if (cid != -1) { let str = data.slice(cid + 1, cnextid + 6); if (!array[str]) { let strRandName = randomStringClassName(4); let nstr = strRandName.slice(0, 2) + rProIndex + strRandName.slice(2); rProIndex = rProIndex + Math.floor(Math.random() * 10); array[str] = nstr; } } cnextid = data.indexOf('_csryw', cnextid + 1); // 从字符串出现的位置的下一位置开始继续查找 } } } } //查找 _csryw function find_csryw() { } function test(actualPlatform) { if (actualPlatform == "wechatgame") { listRandom = []; let cindex = Math.floor(Math.random() * 100); for (var i = 0; i < list.length; i++) { let str = randomStringClassName(2); let nstr = str.slice(0, 1) + cindex + str.slice(1); cindex = cindex + Math.floor(Math.random() * 40); listRandom.push(nstr); } //Editor.log(listRandom) Editor.warn("替换 class name "); replace_className("/build/wechatgame/assets/") replace_className("/build/wechatgame/subpackages/") } else { Editor.warn("当前平台没有处理 class 关键字 替换" + actualPlatform); } } //第一次混淆 function javaObfuscator(filePath) { let sourceCode = Fs.readFileSync(filePath, 'utf8'); let obfuscationResult = JavascriptObfuscator.obfuscate(sourceCode, { "compact": true, "controlFlowFlattening": false, "controlFlowFlatteningThreshold": 0.75, "deadCodeInjection": false, "deadCodeInjectionThreshold": 0.4, "debugProtection": false, "debugProtectionInterval": false, "disableConsoleOutput": false, "domainLock": [], "identifierNamesGenerator": "mangled", "identifiersDictionary": [], "identifiersPrefix": "", "inputFileName": "", "log": false, "renameGlobals": false, "reservedNames": [], "reservedStrings": [], "rotateStringArray": true, "seed": "", "selfDefending": false, "shuffleStringArray": false, "sourceMap": false, "sourceMapBaseUrl": "", "sourceMapFileName": "", "sourceMapMode": "separate", "splitStrings": false, "splitStringsChunkLength": 10, "stringArray": false, "stringArrayEncoding": false, "stringArrayThreshold": 0.75, "target": "browser", "transformObjectKeys": false, "unicodeEscapeSequence": false }); let obfuscatedCode = obfuscationResult.getObfuscatedCode(); Fs.writeFileSync(filePath, obfuscatedCode); } //文件中查找 function find_csryw_file2(path) { let data = Fs.readFileSync(path); if (data) { data = data.toString() + ""; let cnextid = data.indexOf('_csryw'); // 字符出现的位置 while (cnextid !== -1) { let cid1 = data.lastIndexOf(".", cnextid); let cid2 = data.lastIndexOf("\"", cnextid); let cid3 = data.lastIndexOf("{", cnextid); let cid4 = data.lastIndexOf(",", cnextid); let cid5 = data.lastIndexOf(" ", cnextid); let cid6 = data.lastIndexOf("(", cnextid); let cid7 = data.lastIndexOf("[", cnextid); let cid = -1; cid = cid1 > cid ? cid1 : cid; cid = cid2 > cid ? cid2 : cid; cid = cid3 > cid ? cid3 : cid; cid = cid4 > cid ? cid4 : cid; cid = cid5 > cid ? cid5 : cid; cid = cid6 > cid ? cid6 : cid; cid = cid7 > cid ? cid7 : cid; if (cid != -1) { let str = data.slice(cid + 1, cnextid + 6); if (!csrywTab[str]) { let strRandName = randomStringClassName(4); let nstr = strRandName.slice(0, 2) + rProIndex + strRandName.slice(2); rProIndex = rProIndex + Math.floor(Math.random() * 10); csrywTab[str] = nstr; } } cnextid = data.indexOf('_csryw', cnextid + 1); // 从字符串出现的位置的下一位置开始继续查找 } } } function rmdir(filePath) { if (Fs.existsSync(filePath)) { if (Fs.statSync(filePath).isDirectory()) { let files = Fs.readdirSync(filePath); files.forEach((file, idx) => { var curPath = filePath + Path.sep + file; if (Fs.statSync(curPath).isDirectory()) { rmdir(curPath); } else { Fs.unlinkSync(curPath); } }); Fs.rmdirSync(filePath); } else { Fs.unlinkSync(filePath); } } } function copyFolder2(srcDir, tarDir) { // Editor.log(srcDir + " " + tarDir) if (!Fs.existsSync(tarDir)) Fs.mkdirSync(tarDir); let files = Fs.readdirSync(srcDir); for (let i = 0; i < files.length; i++) { var srcPath = Path.join(srcDir, files[i]); var tarPath = Path.join(tarDir, files[i]); let result = Fs.statSync(srcPath); if (result.isFile()) { Fs.copyFileSync(srcPath, tarPath); } else if (result.isDirectory()) { copyFolder(srcPath, tarPath, null); } } } //! 将srcDir文件下的文件、文件夹递归的复制到tarDir下 function copyFolder(srcDir, tarDir, cb) { // Editor.log(srcDir + " " + tarDir) if (!Fs.existsSync(tarDir)) Fs.mkdirSync(tarDir); let files = Fs.readdirSync(srcDir); for (let i = 0; i < files.length; i++) { var srcPath = Path.join(srcDir, files[i]); var tarPath = Path.join(tarDir, files[i]); let result = Fs.statSync(srcPath); if (result.isFile()) { let name = Path.extname(srcPath); if (name == ".js") {//|| name == ".json" Fs.copyFileSync(srcPath, tarPath); } if (name == ".js") { //Editor.log("处理 " + srcPath) find_csryw_file2(srcPath); } } else if (result.isDirectory()) { copyFolder(srcPath, tarPath, null); } } if (cb) { cb(); } } //替换 文件关键字 function replace_csryw_write(srcPath) { let name = Path.extname(srcPath); if (name == ".js" || name == ".json") { let data = Fs.readFileSync(srcPath); if (data) { data = data.toString() + ""; for (var key in csrywTab) { data = data.replace(new RegExp(key, "gm"), csrywTab[key]); } Fs.writeFileSync(srcPath, data); } } } //替换关键字 function replace_csryw2(srcDir) { let result = Fs.statSync(srcDir); if (result.isFile()) { replace_csryw_write(srcDir); } else if (result.isDirectory()) { let files = Fs.readdirSync(srcDir); for (let i = 0; i < files.length; i++) { var srcPath = Path.join(srcDir, files[i]); let result = Fs.statSync(srcPath); if (result.isFile()) { replace_csryw_write(srcPath); } else if (result.isDirectory()) { replace_csryw2(srcPath); } } } } // function findCsryw2(srcDir, cb) { // let files = Fs.readdirSync(srcDir); // for (let i = 0; i < files.length; i++) { // var srcPath = Path.join(srcDir, files[i]); // let result = Fs.statSync(srcPath); // if (result.isFile()) { // let name = Path.extname(srcPath); // if (name == ".js") { // find_csryw_file2(srcPath); // } // } else if (result.isDirectory()) { // findCsryw2(srcPath, null); // } // } // if (cb) { // cb(); // } // } //还原 function test3() { let projectPath = Editor.Project.path || Editor.projectPath; let configFilePath = Path.join(projectPath, "/library/"); let srcPath = Path.join(configFilePath, "/imports/"); let decPath = Path.join(configFilePath, "/importsOther/"); copyFolder2(decPath, srcPath) Editor.log("还原完成。。。") } function test2() { let projectPath = Editor.Project.path || Editor.projectPath; let configFilePath = Path.join(projectPath, "/library/"); let srcPath = Path.join(configFilePath, "/imports/"); let decPath = Path.join(configFilePath, "/importsOther/"); Editor.log("文件准备处理中。。。。") //重置 csrywTab = {} rmdir(decPath); Editor.log("清理" + decPath) //创建文件夹 if (!Fs.existsSync(decPath)) { Fs.mkdirSync(decPath); Editor.log("创建 " + decPath) } //拷贝过去 并 找到关键字 随机 copyFolder(srcPath, decPath) Editor.log("拷贝查找关键字") // csrywTab["AUTO_VIEW_csryw"] = "123" // csrywTab["ExportViewType_csryw"] = "1244443" //let str = Path.join(decPath, "/14/147d4940-1ed7-44fb-bbfc-1fb26eb8d73a.js"); replace_csryw2(srcPath) Editor.log("文件处理结束啦") } module.exports = { load() { Editor.Builder.on('build-start', this.onBuildStart); // Editor.Builder.on('build-finished', this.onBuildFinished); //Editor.Builder.on('before-change-files', this.onBeforeChangeFiles); Editor.Builder.on('build-finished', this.onBeforeChangeFiles); }, unload() { Editor.Builder.removeListener('build-start', this.onBuildStart); // Editor.Builder.removeListener('build-finished', this.onBuildFinished); // Editor.Builder.removeListener('before-change-files', this.onBeforeChangeFiles); Editor.Builder.removeListener('build-finished', this.onBeforeChangeFiles); }, messages: { 'open-panel'() { Editor.log('[CC] 代码混淆工具/构建后自动混淆'); Editor.Panel.open('ccc-obfuscated-code'); }, // TODO // 'open-panel'() { // Editor.log('[CC] 代码混淆工具/主动混淆'); // Editor.Panel.open('ccc-obfuscated-code-do'); // }, 'save-config'(event, config) { Editor.log('[CC] 保存配置'); saveConfig(config); event.reply(null, true); //new fmbuild(); }, 'read-config'(event) { Editor.log('[CC] 读取配置'); let config = getConfig(); event.reply(null, config); }, 'get-preset'(event, name) { Editor.log('[CC] 读取预设', name); let preset = getPreset(name); if (preset) { event.reply(null, preset); } else { Editor.log('[CC] 预设文件已丢失'); Editor.log('[CC] 文件下载地址 https://gitee.com/ifaswind/ccc-obfuscated-code/blob/master/preset.json'); event.reply(null, {}); } } }, onBuildStart(options, callback) { let config = getConfig(); if (config.auto) { Editor.log('[CC] 将在项目构建完成后自动混淆代码') try { test2(); } catch (err) { Editor.error(err) } } callback(); }, onBeforeChangeFiles(options, callback) { let config = getConfig(); if (config.auto) { //replaceFile(options.actualPlatform); Editor.log('[CC] 正在混淆代码'); // try { // for (let i = 0; i < config.files.length; i++) { // if (config.files[i] === '') continue; // let path = config.useAbsPath ? config.files[i] : Path.join(options.dest, config.files[i]); // if (Fs.existsSync(path)) { // Editor.log('[CC] 混淆文件', path); // //javaObfuscator(path) // obfuscate(path, config.options); // } else { // Editor.warn('[CC] 文件不存在', path); // } // } // test3(); // } catch (err) { // Editor.error(err) // } try { test3();//还原 test(options.actualPlatform);//替换class identifiersPrefixName = randomStringIdentifiersPrefix(7) Editor.log("identifiersPrefixName " + identifiersPrefixName) config.options.identifiersPrefix = identifiersPrefixName config.options.renameGlobals = true; for (let i = 0; i < config.files.length; i++) { if (config.files[i] === '') continue; let path = config.useAbsPath ? config.files[i] : Path.join(options.dest, config.files[i]); if (Fs.existsSync(path)) { Editor.log('[CC] 混淆文件', path); obfuscate(path, config.options); } else { Editor.warn('[CC] 文件不存在', path); } } } catch (err) { Editor.error(err) } saveConfigProcess(); Editor.warn('[CC] 混淆已结束'); } callback(); }, }