微信小游戏代码包侵权的避开技巧(含处理脚本代码)

随着微信小游戏平台的普及和完善,越来越多的游戏开发者加入到微信小游戏开发队伍中,很多公司开发者一个团队就开发了几十款游戏。

 

目前,微信小游戏同质化严重,大多是一套代码,换换皮肤就变成了一款新游戏。更有甚者,有些开发者直接反编译别人的游戏包进行简单修改,变成自己的产品申请上架。严重损害著作权所有人的利益和平台规则。

 

为了遏制和打击这种现象,微信小游戏平台有一个环节是机审,审查代码包的相似度,如果代码包相似度超过一定的比例就会被判定为“代码包侵权”,审核不予通过。

 

这本身是好事,但是却产生了大量“误杀”行为。很多公司开发团队用同一套自己研发的游戏引擎开发出不同的产品,申请上线的时候会判定为“侵权”。这就郁闷了,自己侵权自己。

 

虽然微信官方说可以申诉,但是时间不受控制,这不是一个上策。更好的方式是修改代码,混淆代码,让其看起来和别的项目不像,不要触发微信代码机审的“黑机关”。

 

好了,废话了半天,该上菜了!希望各位读者收获满满!

 

混淆代码的思路

1、修改所有代码Class 类名称不要重复

 

2、修改全部全局属性、和至少 1/3的方法名称不要重复(这部分可以写程序去批量改)

 

3、打乱那些基类、工具类里面的方法顺序, 举例 Class A里面有 Function B、C、D,在不同项目里面改乱顺序,项目1里面的 Class A中顺序是B、D、C;项目2里面的 Class A中顺序是D、C、B;

 

4、如果定义了 package包名,包名也改掉;

 

5、每个类里面 随机插入一些废代码(注意,不是直接复制独立的废代码文件到项目中,这种方式无效)

 

6、如果是白鹭引擎,记把exml里面的代码也改改,各种重用的组件比如按钮、Class名称和文件名都改一下。

 

7、如果是 Laya 2.0项目,class 前面 不要用default 定义。

 

按照以上思路,修改到位,经过作者30+款产品上线的经验,通过率99.99%。

 

以上修改思路是有一定工作量的,而且是枯燥的,所以最好写代码脚本去处理,作者一般是手动+自动相结合的方式去混淆代码,以下代码实现了部分混淆代码的功能:

/* 为项目添加干扰代码,主要做这几件事:
1、修改每个类里面的私有方法,添加一个随机数
2、在代码里面随机一行插入一个随机命名的方法体
3、在代码里面随机一行插入一行没有意义的代码
*/
const LINENUM = 20;
const LINENUM_RADOM = 13;
 
var fs = require('fs');
var path = require('path');
 
var filterFils = ["Base64", "ThemeAdAPter","AssetAdapter", "Platform", "wxgamesdk"];//忽略文件
var filterDirs = ["xxxx"];//忽略目录
var traceName = "xxxx";//添加的干扰代码
var funString = '_xxxx_fun(){ console.log("';//添加的方法体
var root_Url = "D:/code/project/src";//项目diam路径
 
fileDisplay(path.resolve(root_Url));
 
/**
 * 文件遍历方法
 * @param filePath 需要遍历的文件路径
 */
function fileDisplay(filePath){
       //根据文件路径读取文件,返回文件列表
       fs.readdir(filePath,function(err,files){
              if(err){
                     console.warn(err)
              }else{
                     //遍历读取到的文件列表
                     files.forEach(function(filename){
                            //获取当前文件的绝对路径
                            var filedir = path.join(filePath,filename);
                            //根据文件路径获取文件信息,返回一个fs.Stats对象
                            fs.stat(filedir,function(eror,stats){
                                   if(eror){
                                          console.warn('获取文件stats失败');
                                   }else{
                                          var isFile = stats.isFile();//是文件
                                          var isDir = stats.isDirectory();//是文件夹
                                          if(isFile){
                                                 console.log(filename,filedir);
                                                 let onlyName = filename.split(".")[0];
                                                 if(filename.indexOf(".ts") != -1 && filterFils.indexOf(onlyName) == -1){
                                                        changeDode(filedir);
                                                 }
                                          }
                                          if(isDir){
                                                 if(!filterDirs || filterDirs.indexOf(filename) == -1){
                                                        fileDisplay(filedir);//递归,如果是文件夹,就继续遍历该文件夹下面的文件
                                                 }
                                          }
                                   }
                            })
                     });
              }
       });
}
 
function changeDode(phppath){
       //const PHPpath = "D:/test/PlayGameCtrl.ts";
       //phppath = "D:/test/HomeTop.ts";
 
       console.log("执行文件:" + phppath);
       let phpcontent = fs.readFileSync(phppath, { encoding: "UTF8" });
       //console.log(phpContent);
 
       //====修改私有方法名==================================================================
       //let arr = phpContent.match(/function\s*(\w+)/);
       let arr = phpContent.match(/private .*?\(/g);
       console.log(arr);
       if(arr){
              let len = "private ".length;
              for(var i=0; i<arr.length; i++){
                     let str = arr[i];
                     if(str.indexOf("private static") != -1) continue;
                     if(str.indexOf("private async") != -1) continue;
                     if(str.indexOf("private get") != -1) continue;
                     if(str.indexOf("private set") != -1) continue;
                     if(str.indexOf("= new ") != -1) continue; //过滤这种:private con:eui.Rect = new eui.Rect();
                     let name = str.substr(len, str.length - len - 1);
                     console.log(name);
                     let number = Math.floor(Math.random() * 9999);
                     let name2;
                     let lastIndex = name.lastIndexOf("_");
                     if(lastIndex == -1){
                            name2 = name + "_" + number;
                     }else{
                            name2 = name.substr(0, lastIndex) + "_" + number;
                     }
                     let name2s = name2 + "(";
                     //phpContent = phpContent.replace("private "+name + "(", "private " + name2s); //不能直接替换需要用正则,因为一个文件里面可能有多个类,可能存在多个同名方法
                     var regExp0 = new RegExp("private "+name + "\\(", 'gi');
                     phpContent = phpContent.replace(regExp0, "private " + name2s);
 
                     var regExp = new RegExp("this."+name + "\\(", 'gi');
                     phpContent = phpContent.replace(regExp, "this." + name2s);
                     regExp = new RegExp("this."+name + "\\," , 'gi');
                     phpContent = phpContent.replace(regExp, "this." + name2 + ",");
                     regExp = new RegExp("this."+name + "\\)" , 'gi');
                     phpContent = phpContent.replace(regExp, "this." + name2 + ")");
 
                     var regExp2 = new RegExp("self."+name + "\\(", 'gi');
                     phpContent = phpContent.replace(regExp2, "self." + name2s);
              }
       }
 
       //====增加干扰代码==================================================================
       let arr2 = phpContent.split("\n");
       //console.log(arr2.length, arr2);
       let isInterFace;
       for(var i=LINENUM; i<arr2.length - 1; i++){
              if(!arr2[i]) continue;
              let formatStr = trim(arr2[i], "g");
              if(formatStr == "" || formatStr == "{") continue;
              arr2[i] = String(arr2[i]);
 
              if(arr2[i].indexOf("return") !=-1) continue;
              if(arr2[i].indexOf("class ") !=-1) continue;
              if(arr2[i].indexOf("super(") !=-1) continue;
              if(arr2[i].indexOf("public constructor") !=-1) continue;
              if(arr2[i].indexOf("else") !=-1) continue;
              if(arr2[i].indexOf("else if") !=-1) continue;
              if(arr2[i].indexOf("//") !=-1) continue;
              if(arr2[i].indexOf("catch(") !=-1) continue;
              if(arr2[i].indexOf(",") !=-1 && arr2[i].indexOf("(") ==-1) continue;//object里面的key value
              if(arr2[i].indexOf("public ") !=-1 && arr2[i].indexOf("{") ==-1) continue;//属性
              if(arr2[i].indexOf("private ") !=-1 && arr2[i].indexOf("{") ==-1) continue;//属性
              if(arr2[i].indexOf("protected ") !=-1 && arr2[i].indexOf("{") ==-1) continue;//属性
              if(arr2[i].indexOf("public ") !=-1 && arr2[i].indexOf("{") !=-1 && arr2[i].indexOf("=") !=-1) continue;//属性
              if(arr2[i].indexOf("private ") !=-1 && arr2[i].indexOf("{") !=-1 && arr2[i].indexOf("=") !=-1) continue;//属性
              if(arr2[i].indexOf("function") !=-1 && arr2[i].indexOf(":") !=-1) continue; //object里面的key value
 
              if(!arr2[i-1]) continue;
              let formatStr2 = Trim(arr2[i-1], "g");
              arr2[i-1] = String(arr2[i-1]);
              if(arr2[i-1].indexOf("return") !=-1) continue;
              if(arr2[i-1].indexOf("if") !=-1 && arr2[i-1].indexOf("{") ==-1) continue;
              if(arr2[i-1].indexOf("else") !=-1 && arr2[i-1].indexOf("{") ==-1) continue;
              if(arr2[i-1].indexOf("for") !=-1 && arr2[i-1].indexOf("{") ==-1) continue;
              if(arr2[i-1].indexOf(",") !=-1 && arr2[i-1].indexOf("(") ==-1) continue;//object里面的key value
              if(formatStr == "})" && formatStr2 == "}") continue;
              if(formatStr == "})" && formatStr2 == "") continue;
 
              let isFun = (arr2[i].indexOf("private ") !=-1 && arr2[i].indexOf("(") !=-1) ||
                                   (arr2[i].indexOf("public ") !=-1 && arr2[i].indexOf("(") !=-1) ||
                                   (arr2[i].indexOf("/**") !=-1);
              if(isFun && (formatStr2 == ""|| formatStr2 == "*/" || arr2[i-1].indexOf("}") != -1 || arr2[i-1].indexOf("//") != -1 || arr2[i-1].indexOf("class") != -1) ) continue;
              if(isFun && arr2[i-1].indexOf("private ") !=-1 ) continue;
              if(isFun && arr2[i-1].indexOf("public ") !=-1 ) continue;
 
              if(arr2[i].indexOf("function") !=-1 && arr2[i-1].indexOf(",") !=-1) continue; //object里面的key value
              if(arr2[i].indexOf("}") !=-1 && arr2[i-1].indexOf(":") !=-1) continue;
 
              let formatStr3 = Trim(arr2[i+1], "g");
              arr2[i+1] = String(arr2[i+1]);
              if(formatStr == "}" && formatStr3 == "}") continue;
              if(formatStr == "}" && formatStr3 == "") continue;
 
              let str = getAddSpace(arr2[i]) + traceName + "(\"" + getRadomStr() + "\");";
              arr2.splice(i,0,str);
              let randomNum = Math.random();
              i = i + LINENUM + ( Math.round(randomNum * LINENUM_RADOM) * (randomNum<0.5 ? 1:-1) );//随机行数添加
       }
 
       //====增加干扰代码 增加干扰方法 ==================================================================
       let pbArr = phpContent.match(/public .*?\(/g);
       let count = (arr ? arr.length : 0) + (pbArr ? pbArr.length : 0);
       let rate = 0.5;
       if(count > 5) rate = 0.4;
       if(count > 10) rate = 0.25;
       if(count > 20) rate = 0.15;
       rate *= 0.75
       for(var i=LINENUM; i<arr2.length - 1; i++){
 
              let formatStr = Trim(arr2[i], "g");
              arr2[i] = String(arr2[i]);
              let formatStr2 = Trim(arr2[i-1], "g");
              arr2[i-1] = String(arr2[i-1]);
              let formatStr3 = Trim(arr2[i+1], "g");
              arr2[i+1] = String(arr2[i+1]);
 
              let canInsert = false;
              let isFun = ( (arr2[i].indexOf("private ") !=-1 || arr2[i].indexOf("public ") !=-1) && arr2[i].indexOf("(") !=-1) && arr2[i].indexOf("=") ==-1;
              var str = '';
              if(isFun && formatStr2 == "}"){
                     canInsert = Math.random() < rate;
                     str = getAddSpace(arr2[i]) + 'private ' + getRadomStr(4, 1) + funString + getRadomStr() + '"); }' + '\n';
              }
              else{
                     let str3 = arr2[i+1];
                     isFun = ( (str3.indexOf("private ") !=-1 || str3.indexOf("public ") !=-1 || str3.indexOf("/**") !=-1) && str3.indexOf("(") !=-1) && str3.indexOf("=") ==-1;
                     if(formatStr == "" && isFun && formatStr2 == "}") {
                            canInsert = Math.random() < rate;
                            str = getAddSpace(arr2[i-1]) + 'private ' + getRadomStr(4, 1) + funString + getRadomStr() + '"); }';
                     }
              }
              if(canInsert){
                     arr2.splice(i,0,str);
              }
       }
 
       phpContent = arr2.join("\n");
       //console.log(phpContent);
 
    fs.writeFileSync(phppath, phpContent, { encoding: "utf8" });
    console.log("执行完成!--" + phppath);
}
 
function getAddSpace(str){
       if(!str) return "";
       let num = str.length - lTrim(str).length;
       let formatStr = Trim(str, "g");
       if(formatStr == "}")
              num += 4;
       let space = "";
       while(num > 0){
              space += " ";
              num--;
       }
       return space;
}
function getRadomStr(len, type){
       len = len || Math.floor(Math.random()*32) + 1;
       var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';    /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
       if(type == 1) $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; //非数字
       var maxPos = $chars.length;
       var pwd = '';
       for (var i = 0; i < len; i++) {
              pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
       }
       return pwd;
}
function Trim(str, is_global) {
       //console.log("==", str)
       str = String(str);
       var result;
       result = str.replace(/(^\s+)|(\s+$)/g,"");
       if(is_global && is_global.toLowerCase()=="g"){
              result = result.replace(/\s/g,"");
       }
       return result;
}
function lTrim(str) {
       str = String(str);
       var result = str.replace(/(^\s+)/g,"");
       return result;
}

 

 


 

 

备注:以上命令用nodejs执行。由于只是辅助工具,并没有写的特别完善,有时会在一些错误的行数插入代码,造成代码格式不对,运行报错,这个需要手动删除该废代码。欢迎有兴趣的读者继续完善!

 

特别说明:本文只是做技术学习,并不是教开发者怎么绕开微信审查进行侵权行为,作为程序员更要尊重自己和别人的劳


相关阅读

添加新评论