一、资源
RPGMaker为了保护原创作者的游戏,提供了加密工具,可以对游戏内资源进行加密处理,这样就算有不怀好意者获得了资源包,也需要花一定时间才能进行破解。
png->rpgmvp
ogg->rpgmvo
m4a->rpgmvm
以下是rpg_core.js对于加密资源的读取操作:
Bitmap.load = function(url) { //资源加载
var bitmap = new Bitmap();
bitmap._image = new Image();
bitmap._url = url;
bitmap._isLoading = true;
if(!Decrypter.checkImgIgnore(url) && Decrypter.hasEncryptedImages) { //如果资源进行了加密
Decrypter.decryptImg(url, bitmap); //资源解密
} else {
bitmap._image.src = url;
bitmap._image.onload = Bitmap.prototype._onLoad.bind(bitmap);
bitmap._image.onerror = Bitmap.prototype._onError.bind(bitmap);
}
return bitmap;
};
Decrypter.decryptImg = function(url, bitmap) { //解密方法
url = this.extToEncryptExt(url);
var requestFile = new XMLHttpRequest();
requestFile.open("GET", url);
requestFile.responseType = "arraybuffer";
requestFile.send();
requestFile.onload = function () {
if(this.status < Decrypter._xhrOk) {
var arrayBuffer = Decrypter.decryptArrayBuffer(requestFile.response); //对数据流进行解密
bitmap._image.src = Decrypter.createBlobUrl(arrayBuffer); //解密后的资源,形成新的资源对象
bitmap._image.onload = Bitmap.prototype._onLoad.bind(bitmap);
bitmap._image.onerror = Bitmap.prototype._onError.bind(bitmap);
}
};
};
Decrypter.decryptArrayBuffer = function(arrayBuffer) { //解密算法
if (!arrayBuffer) return null;
var header = new Uint8Array(arrayBuffer, 0, this._headerlength);
var i;
var ref = this.SIGNATURE + this.VER + this.REMAIN;
var refBytes = new Uint8Array(16);
for (i = 0; i < this._headerlength; i++) {
refBytes[i] = parseInt("0x" + ref.substr(i * 2, 2), 16);
}
for (i = 0; i < this._headerlength; i++) {
if (header[i] !== refBytes[i]) {
throw new Error("Header is wrong");
}
}
arrayBuffer = this.cutArrayHeader(arrayBuffer, Decrypter._headerlength);
var view = new DataView(arrayBuffer);
this.readEncryptionkey();
if (arrayBuffer) {
var byteArray = new Uint8Array(arrayBuffer);
for (i = 0; i < this._headerlength; i++) {
byteArray[i] = byteArray[i] ^ parseInt(Decrypter._encryptionKey[i], 16); //使用秘钥进行解密
view.setUint8(i, byteArray[i]);
}
}
return arrayBuffer; //返回解密后的数据量,供后续使用
};
可以看出,由于游戏的单机化,_encryptionKey会直接存储至客户端,相当于将秘钥直接提供给了对方。当对方拿到_encryptionKey并利用解密算法,很快加密资源就会被破解。
现在我们知道了_encryptionKey的重要性,就可以对_encryptionKey进行进一步的处理,让那些想破解游戏的人耗费更多的精力,增加破解成本。
Decrypter.readEncryptionkey = function(){ //读取_encryptionKey
this._encryptionKey = $dataSystem.encryptionKey.split(/(.{2})/).filter(Boolean);
};
以上是正常情况下获取_encryptionKey的方法,实质就是System.json中encryptionKey的读取拆分进数组,供解密使用。
我们可以直接将System.json中encryptionKey删除,使其隐藏。
那么我们怎么才能在资源加载的时候进行资源解密呢?
你可以将你的encryptionKey任意拆分并隐藏在任意的代码迷宫中,等使用的时候通过各种方法提取出来使用,这样你的encryptionKey就不会那么明显的直接暴露,当然这样的做法也只是增加破解成本,安了心要破解也是挡不住的。
以下是部分防解密代码:
......
arrayBuffer = this.cutArrayHeader(arrayBuffer, Decrypter._headerlength);
var view = new DataView(arrayBuffer);
if (arrayBuffer) {
this.f_drillUP(view,arrayBuffer);
this.f_DRilluP(view,arrayBuffer);
this.f_dRiLlUp(view,arrayBuffer);
this.f_DrILLUp(view,arrayBuffer);
this.f_drILLup(view,arrayBuffer);
this.f_dRIlLuP(view,arrayBuffer);
}
.......
Decrypter.code_map_drillup = ['3d','58','a4','70','38','5c','81','6c','ad','0c','8c','ad','cd','93','67','68','08','3d','86','23','7b','38','93','67','02','7f','6e','60','41','ee','bb','88','0a','e1','ac','9a','0d','78','38','19','2b','ad','f4','f4','61','bc','7f','1c','dc','75','69','49','93','d7','20','84','e3','e4','e1','c0','52','97','f4','41','62','70','1c','26','c3','09','d3','3e','18','7b','41','84','42','01','47','c3','2c','eb','b0','b5','03','3d','38','93','d4','9f','23','81','da','7c','2b','1b','03','20','6c','41'];
Decrypter.map_code_DrILLup = [32,37,80,24,71,1,87,89,74,84,67,69,9,44,5,61,21,78,2,31,33,27,34,4,70,81,6,58,18,45,54,35];
Decrypter.map_code_DrIlLuP = [66,90,36,44,37,87,75,48,56,68,61,13,84,82,55,98,50,14,46,95,83,60,17,0,27,94,24,38,30,34,45,99];
Decrypter.map_code_dRILlup = [20,40,76,89,5,87,56,94,6,82,31,37,11,50,96,98,55,88,22,81,65,74,19,66,24,90,33,48,57,71,70,83];
Decrypter.map_code_dRiLlup = [0,93,45,79,20,27,77,72,88,71,96,80,8,94,16,15,26,3,61,60,40,41,81,31,2,73,42,34,76,39,86,65];
Decrypter.map_code_drILLup = [36,9,40,79,62,82,57,67,61,94,90,12,98,52,75,28,60,44,4,32,48,64,93,30,51,35,72,88,89,14,3,38];
Decrypter.map_code_DRillup = [61,46,87,31,13,41,33,12,45,71,29,15,79,83,44,39,30,64,97,56,21,25,6,93,3,23,78,85,98,38,59,68];
......
Decrypter.n_dRILLup = function(drILlUP, drIlLuP){ return parseInt(this.code_map_drillup[this.map_code_dRILLUp[drILlUP+9]], 16); }
Decrypter.n_dRillUp = function(drIlLUp, dRilLuP){ return parseInt(this.code_map_drillup[this.map_code_dRILluP[drIlLUp+10]], 16); }
Decrypter.n_drIllUP = function(DRIlLUp, DriLlup){ return parseInt(this.code_map_drillup[this.map_code_drillUP[DRIlLUp+9]], 16); }
Decrypter.n_dRilluP = function(dRillUp, driLluP){ return parseInt(this.code_map_drillup[this.map_code_driLLUp[dRillUp+10]], 16); }
Decrypter.n_driLLUp = function(DRiLLup, DrillUp){ return parseInt(this.code_map_drillup[this.map_code_DRiLluP[DRiLLup+2]], 16); }
Decrypter.n_DrIlLup = function(DrILLuP, DriLluP){ return parseInt(this.code_map_drillup[this.map_code_drILLUP[DrILLuP+11]], 16); }
Decrypter.n_DRIlLuP = function(DRilLup, dRiLLUp){ return parseInt(this.code_map_drillup[this.map_code_DRillup[DRilLup+0]], 16); }
Decrypter.n_DRIlluP = function(DRIllUP, dRilLup){ return parseInt(this.code_map_drillup[this.map_code_DrilLup[DRIllUP+11]], 16); }
Decrypter.n_DrILLup = function(DrIlLUP, drILluP){ return parseInt(this.code_map_drillup[this.map_code_dRILlUp[DrIlLUP+1]], 16); }
......
二、插件
辛苦写的自用插件(开源插件大神勿喷),是否也不想让他人破解,或者插件内有部分与服务端的交互过程,那就更不想让别人破解了。
市面上已经有很多对JS文件进行混淆加密的方法,最近接触了一个号称“最牛加密”的方法,据说绝对绝对无法100%破解,亲试了一波,破解起来确实有难度,相当耗费精力,确实牛。
//源码
var a={},b={};
(function(w, d) {
w.info = "这是一个一系列js操作。";
d.adinfo = "站长接高级 “JS加密” 和 “JS解密” ,保卫你的 js。";
d.warning = "如果您的JS里嵌套了PHP,JSP标签,等等其他非JavaScript的代码,请提取出来再加密。这个工具不能加密php、jsp等模版内容";
})(a, b);
//混淆加密后
;var encode_version = 'sojson.v5', bvale = '', _0x137b=['5YqG6ZiF54mO5pyf5Y2S77++bBTkvbvlrajmnrXlvq/nq6s=','w6U8w4BYbw==','AsOLw6FwEg==','SCjDvsO0w7U=','54mX5py+5Y+R77+BwrQN5L+w5ayq5p2R5b2956qW77676L+S6K6q5pWx5oyg5oq65Lu/55mf5bWl5L20','RnfCsm4i','5Ymw6Zi+54mv5p2k5YyF776fQhnkvqHlr5/mnI/lv5znq78=','w5TCnk9+','w4HDgcK9aEc=','56ug6ZeT5oyP6aue57u7wonigJ1+ReWKj+WspeKDs2nlkp7DouKAjMO8w4nopI/lrLPig70Q776k5L6p5Y6f5L2T55ilWcOUP+ODvw==','5aad5p6O5oKO55mJwpQt6YSm5be85aSZ5LuCUcO/Wu+9kRRyw7vmoJTnrqXvvoznraHnrJnlhLPkurPpn4YyA8OqN8OiwpvDmsOEAcO655ir5LqD56KI772w6K+t5o2l5Y2r5YSV5p6q5YSb5YqF5a+o44KC6Lyt5LiQ5beU5Yav5LiN6IOr5YmJ5a2Nw5M0w4XjgKbDlUJu56y+5qiH54q05YeG5a+E','JTRDLcKUw7pGD8Of','w5svwoLDqMO1wprDri4s','WnLCqw=='];(function(_0x59cc19,_0xa33bc6){var _0x373186=function(_0x22fb36){while(--_0x22fb36){_0x59cc19['push'](_0x59cc19['shift']());}};_0x373186(++_0xa33bc6);}(_0x137b,0x157));var _0x8d27=function(_0x25ef43,_0x21b8e7){_0x25ef43=_0x25ef43-0x0;var _0x8bcc7b=_0x137b[_0x25ef43];if(_0x8d27['initialized']===undefined){(function(){var _0x2a5348=typeof window!=='undefined'?window:typeof process==='object'&&typeof require==='function'&&typeof global==='object'?global:this;var _0x3ccd8f='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x2a5348['atob']||(_0x2a5348['atob']=function(_0x3e8e4b){var _0x3ff844=String(_0x3e8e4b)['replace'](/=+$/,'');for(var _0x28af07=0x0,_0x27609d,_0x5b63d7,_0x31e66f=0x0,_0x4ec419='';_0x5b63d7=_0x3ff844['charAt'](_0x31e66f++);~_0x5b63d7&&(_0x27609d=_0x28af07%0x4?_0x27609d*0x40+_0x5b63d7:_0x5b63d7,_0x28af07++%0x4)?_0x4ec419+=String['fromCharCode'](0xff&_0x27609d>>(-0x2*_0x28af07&0x6)):0x0){_0x5b63d7=_0x3ccd8f['indexOf'](_0x5b63d7);}return _0x4ec419;});}());var _0x266b43=function(_0x4446b9,_0x3fd172){var _0x286dc7=[],_0x3b68a7=0x0,_0x5bd2f9,_0x2be62e='',_0x4653b5='';_0x4446b9=atob(_0x4446b9);for(var _0x4d07e4=0x0,_0x190db4=_0x4446b9['length'];_0x4d07e4<_0x190db4;_0x4d07e4++){_0x4653b5+='%'+('00'+_0x4446b9['charCodeAt'](_0x4d07e4)['toString'](0x10))['slice'](-0x2);}_0x4446b9=decodeURIComponent(_0x4653b5);for(var _0x3eebc8=0x0;_0x3eebc8<0x100;_0x3eebc8++){_0x286dc7[_0x3eebc8]=_0x3eebc8;}for(_0x3eebc8=0x0;_0x3eebc8<0x100;_0x3eebc8++){_0x3b68a7=(_0x3b68a7+_0x286dc7[_0x3eebc8]+_0x3fd172['charCodeAt'](_0x3eebc8%_0x3fd172['length']))%0x100;_0x5bd2f9=_0x286dc7[_0x3eebc8];_0x286dc7[_0x3eebc8]=_0x286dc7[_0x3b68a7];_0x286dc7[_0x3b68a7]=_0x5bd2f9;}_0x3eebc8=0x0;_0x3b68a7=0x0;for(var _0x10f6e0=0x0;_0x10f6e0<_0x4446b9['length'];_0x10f6e0++){_0x3eebc8=(_0x3eebc8+0x1)%0x100;_0x3b68a7=(_0x3b68a7+_0x286dc7[_0x3eebc8])%0x100;_0x5bd2f9=_0x286dc7[_0x3eebc8];_0x286dc7[_0x3eebc8]=_0x286dc7[_0x3b68a7];_0x286dc7[_0x3b68a7]=_0x5bd2f9;_0x2be62e+=String['fromCharCode'](_0x4446b9['charCodeAt'](_0x10f6e0)^_0x286dc7[(_0x286dc7[_0x3eebc8]+_0x286dc7[_0x3b68a7])%0x100]);}return _0x2be62e;};_0x8d27['rc4']=_0x266b43;_0x8d27['data']={};_0x8d27['initialized']=!![];}var _0x3eb94f=_0x8d27['data'][_0x25ef43];if(_0x3eb94f===undefined){if(_0x8d27['once']===undefined){_0x8d27['once']=!![];}_0x8bcc7b=_0x8d27['rc4'](_0x8bcc7b,_0x21b8e7);_0x8d27['data'][_0x25ef43]=_0x8bcc7b;}else{_0x8bcc7b=_0x3eb94f;}return _0x8bcc7b;};var a={},b={};(function(_0x1dd944,_0x1f3eb4){var _0x4be34d={'BRtje':'这是一个一系列js操作。'};_0x1dd944[_0x8d27('0x0','07(1')]=_0x4be34d[_0x8d27('0x1','Q!^g')];_0x1f3eb4['adinfo']=_0x8d27('0x2','U9@6');_0x1f3eb4['warning']=_0x8d27('0x3','Jl1#');}(a,b));;(function(_0x40d200,_0x3bd66e,_0x436c33){var _0x39d952={'khnMW':_0x8d27('0x4','giYA'),'grwTF':function _0x5744af(_0x4e153c,_0x3ecf15){return _0x4e153c===_0x3ecf15;},'WcNOU':_0x8d27('0x5','[%ul'),'DOoHM':function _0xd9761c(_0x300650,_0x4c62d2){return _0x300650!==_0x4c62d2;},'dgdcl':_0x8d27('0x6','os)m'),'eDNQj':_0x8d27('0x7','1)yn')};_0x436c33='al';try{_0x436c33+='ert';_0x3bd66e=encode_version;if(!(typeof _0x3bd66e!==_0x39d952[_0x8d27('0x8','vc#s')]&&_0x39d952[_0x8d27('0x9','MSHW')](_0x3bd66e,_0x39d952['WcNOU']))){if(_0x39d952[_0x8d27('0xa','Y9iI')](_0x39d952['dgdcl'],'kkj')){_0x40d200[_0x436c33]('删除'+_0x8d27('0xb','Jl1#'));}else{_0x40d200[_0x436c33](_0x39d952[_0x8d27('0xc','os)m')]);}}}catch(_0x11f794){_0x40d200[_0x436c33](_0x8d27('0xd','giYA'));}}(window));;encode_version = 'sojson.v5';
三、游戏数据检测
这个方法对破解玩家不是很友好。有两个思路:
1、本地检测,针对一些关键数据,在游戏加载过程中,进行数据(玩家游戏时长与各项成长数值关系,物品、装备等持有量)的校验,如果超出阈值,则提示玩家,强制结束游戏。当然这需要充分考虑游戏的各项数值极限,避免伤害到正常玩家,毕竟玩家体验第一。
2、远端检测,这需要与后端服务进行交互,实际已经不是那么单纯的单机游戏,每个玩家在后端都有唯一的账号,创造者可以定期分析玩家数据,进行非法玩家的探测,及时发现非法玩家,并剔除(封禁)账号。