利用抽象语法树还原Uglify打包的js混淆代码

Uglify 是一个用于 JavaScript 代码压缩和混淆的工具,旨在减小文件体积和加密代码,提高网页性能和保护代码安全性.

本文讲一下使用AST(抽象语法树)对Uglify压缩的JS代码还原相关的操作。本文只大概讲还原算法供学习使用,不保证完全当工具使用。后面使用的代码我会提供部分关键java代码。

首先我们看一下Uglify工具打包出来的js代码如下:

laya.wxmini.min.js
window.wxMiniGame=function(e,t){"use strict";class i{static isLocalNativeFile(e){for(var t=0,i=l.nativefiles.length;t<i;t++)if(-1!=e.indexOf(l.nativefiles[t]))return!0;return!1}static getFileInfo(e){var t=e,n=i.fakeObj[t];return null==n?null:n}static read(e,n="utf8",a=null,o="",s=!1,r=""){var l;l=""==o||-1==o.indexOf("http://")&&-1==o.indexOf("https://")?e:i.getFileNativePath(e),l=t.URL.getAdptedFilePath(l),i.fs.readFile({filePath:l,encoding:n,success:function(e){null!=a&&a.runWith([0,e])},fail:function(e){e&&""!=o?i.downFiles(o,n,a,o,s,r):null!=a&&a.runWith([1])}})}static downFiles(e,t="utf8",n=null,a="",o=!1,s="",r=!0){i.wxdown({url:e,success:function(l){200===l.statusCode?i.readFile(l.tempFilePath,t,n,a,o,s,r):403===l.statusCode?null!=n&&n.runWith([0,e]):null!=n&&n.runWith([1,l])},fail:function(e){null!=n&&n.runWith([1,e])}}).onProgressUpdate((function(e){null!=n&&n.runWith([2,e.progress])}))}static readFile(e,n="utf8",a=null,o="",s=!1,r="",d=!0){e=t.URL.getAdptedFilePath(e),i.fs.readFile({filePath:e,encoding:n,success:function(t){(-1!=e.indexOf("http://")||-1!=e.indexOf("https://"))&&(l.autoCacheFile||s)?(null!=a&&a.runWith([0,t]),i.copyFile(e,o,null,n,d)):null!=a&&a.runWith([0,t])},fail:function(e){e&&null!=a&&a.runWith([1,e])}})}static downOtherFiles(e,t=null,n="",a=!1,o=!0){i.wxdown({url:e,success:function(e){200===e.statusCode?(l.autoCacheFile||a)&&-1==n.indexOf("qlogo.cn")&&-1==n.indexOf(".php")?(null!=t&&t.runWith([0,e.tempFilePath]),i.copyFile(e.tempFilePath,n,null,"",o)):null!=t&&t.runWith([0,e.tempFilePath]):null!=t&&t.runWith([1,e])},fail:function(e){null!=t&&t.runWith([1,e])}})}static downLoadFile(e,n="",a=null,o="utf8"){window.navigator.userAgent.indexOf("MiniGame")<0?t.Laya.loader.load(e,a):n==t.Loader.IMAGE||n==t.Loader.SOUND?i.downOtherFiles(e,a,e,!0,!1):i.downFiles(e,o,a,e,!0,n,!1)}static copyFile(e,n,a,o="",s=!0){var r=e.split("/"),d=r[r.length-1],u=n,c=i.getFileInfo(n),h=i.getFileNativePath(d);i.fakeObj[u]={md5:d,readyUrl:n,size:0,times:t.Browser.now(),encoding:o};var f=i.getCacheUseSize();c?c.readyUrl!=n?i.fs.getFileInfo({filePath:e,success:function(t){s&&f+4194304+t.size>=52428800&&(t.size>l.minClearSize&&(l.minClearSize=t.size),i.onClearCacheRes()),i.deleteFile(e,n,a,o,t.size)},fail:function(e){null!=a&&a.runWith([1,e])}}):null!=a&&a.runWith([0]):i.fs.getFileInfo({filePath:e,success:function(t){s&&f+4194304+t.size>=52428800&&(t.size>l.minClearSize&&(l.minClearSize=t.size),i.onClearCacheRes()),i.fs.copyFile({srcPath:e,destPath:h,success:function(e){i.onSaveFile(n,d,!0,o,a,t.size)},fail:function(e){null!=a&&a.runWith([1,e])}})},fail:function(e){null!=a&&a.runWith([1,e])}})}static onClearCacheRes(){var e=l.minClearSize,t=[];for(var n in i.filesListObj)"fileUsedSize"!=n&&t.push(i.filesListObj[n]);i.sortOn(t,"times",i.NUMERIC);for(var a=0,o=1,s=t.length;o<s;o++){var r=t[o];if(a>=e)break;a+=r.size,i.deleteFile("",r.readyUrl)}}static sortOn(e,t,n=0){return n==i.NUMERIC?e.sort((function(e,i){return e[t]-i[t]})):n==(i.NUMERIC|i.DESCENDING)?e.sort((function(e,i){return i[t]-e[t]})):e.sort((function(e,i){return e[t]-i[t]}))}static getFileNativePath(e){return i.fileNativeDir+"/"+e}static deleteFile(e,t="",n=null,a="",o=0){var s=i.getFileInfo(t),r=i.getFileNativePath(s.md5),l=""!=e;i.onSaveFile(t,e,l,a,n,o),i.fs.unlink({filePath:r,success:function(t){if(""!=e){var a=i.getFileNativePath(e);i.fs.copyFile({srcPath:e,destPath:a,success:function(e){},fail:function(e){null!=n&&n.runWith([1,e])}})}},fail:function(e){}})}static deleteAll(){var e=[];for(var t in i.filesListObj)"fileUsedSize"!=t&&e.push(i.filesListObj[t]);for(var n=1,a=e.length;n<a;n++){var o=e[n];i.deleteFile("",o.readyUrl)}i.filesListObj&&i.filesListObj.fileUsedSize&&(i.filesListObj.fileUsedSize=0),i.writeFilesList("",JSON.stringify({}),!1)}static onSaveFile(e,n,a=!0,o="",s=null,r=0){var l=e;if(null==i.filesListObj.fileUsedSize&&(i.filesListObj.fileUsedSize=0),a){i.getFileNativePath(n);i.filesListObj[l]={md5:n,readyUrl:e,size:r,times:t.Browser.now(),encoding:o},i.filesListObj.fileUsedSize=parseInt(i.filesListObj.fileUsedSize)+r,i.writeFilesList(l,JSON.stringify(i.filesListObj),!0),null!=s&&s.runWith([0])}else if(i.filesListObj[l]){var d=parseInt(i.filesListObj[l].size);i.filesListObj.fileUsedSize=parseInt(i.filesListObj.fileUsedSize)-d,i.fakeObj[l].md5==i.filesListObj[l].md5&&delete i.fakeObj[l],delete i.filesListObj[l],i.writeFilesList(l,JSON.stringify(i.filesListObj),!1),null!=s&&s.runWith([0])}}static writeFilesList(e,t,n){var a=i.fileNativeDir+"/"+i.fileListName;i.fs.writeFile({filePath:a,encoding:"utf8",data:t,success:function(e){},fail:function(e){}}),!l.isZiYu&&l.isPosMsgYu&&l.window.wx.postMessage({url:e,data:i.filesListObj[e],isLoad:"filenative",isAdd:n})}static getCacheUseSize(){return i.filesListObj&&i.filesListObj.fileUsedSize?i.filesListObj.fileUsedSize:0}static existDir(e,t){i.fs.mkdir({dirPath:e,success:function(e){null!=t&&t.runWith([0,{data:JSON.stringify({})}])},fail:function(e){-1!=e.errMsg.indexOf("file already exists")?i.readSync(i.fileListName,"utf8",t):null!=t&&t.runWith([1,e])}})}static readSync(e,t="utf8",n=null,a=""){var o,s=i.getFileNativePath(e);try{o=i.fs.readFileSync(s,t),null!=n&&n.runWith([0,{data:o}])}catch(e){null!=n&&n.runWith([1])}}static setNativeFileDir(e){i.fileNativeDir=l.window.wx.env.USER_DATA_PATH+e}}i.fs=window.wx.getFileSystemManager(),i.wxdown=window.wx.downloadFile,i.filesListObj={},i.fakeObj={},i.fileListName="layaairfiles.txt",i.ziyuFileData={},i.ziyuFileTextureData={},i.loadPath="",i.DESCENDING=2,i.NUMERIC=16;class n extends t.SoundChannel{constructor(e,t){super(),this._audio=e,this._miniSound=t,this._onEnd=n.bindToThis(this.__onEnd,this),e.onEnded(this._onEnd)}static bindToThis(e,t){return e.bind(t)}__onEnd(){if(1==this.loops)return this.completeHandler&&(t.Laya.systemTimer.once(10,this,this.__runComplete,[this.completeHandler],!1),this.completeHandler=null),this.stop(),void this.event(t.Event.COMPLETE);this.loops>0&&this.loops--,this.startTime=0,this.play()}play(){this.isStopped=!1,t.SoundManager.addChannel(this),this._audio.play()}set startTime(e){this._audio&&(this._audio.startTime=e)}set autoplay(e){this._audio.autoplay=e}get autoplay(){return this._audio.autoplay}get position(){return this._audio?this._audio.currentTime:0}get duration(){return this._audio?this._audio.duration:0}stop(){this.isStopped=!0,t.SoundManager.removeChannel(this),this.completeHandler=null,this._audio&&(this._audio.stop(),this.loop||(this._audio.offEnded(null),this._miniSound.dispose(),this._audio=null,this._miniSound=null,this._onEnd=null))}pause(){this.isStopped=!0,this._audio.pause()}get loop(){return this._audio.loop}set loop(e){this._audio.loop=e}resume(){this._audio&&(this.isStopped=!1,t.SoundManager.addChannel(this),this._audio.play())}set volume(e){this._audio&&(this._audio.volume=e)}get volume(){return this._audio?this._audio.volume:1}}class a extends t.EventDispatcher{constructor(){super(),this.loaded=!1}static _createSound(){return a._id++,l.window.wx.createInnerAudioContext()}load(e){if(a._musicAudio||(a._musicAudio=a._createSound()),i.isLocalNativeFile(e)){if(-1!=e.indexOf("http://")||-1!=e.indexOf("https://"))if(""!=i.loadPath)e=e.split(i.loadPath)[1];else{var n=""!=t.URL.rootPath?t.URL.rootPath:t.URL._basePath;""!=n&&(e=e.split(n)[1])}}else e=t.URL.formatURL(e);if(this.url=e,this.readyUrl=e,a._audioCache[this.readyUrl])this.event(t.Event.COMPLETE);else if(l.autoCacheFile&&i.getFileInfo(e))this.onDownLoadCallBack(e,0);else if(l.autoCacheFile)if(i.isLocalNativeFile(e)){var o=e;if(""!=(n=""!=t.URL.rootPath?t.URL.rootPath:t.URL._basePath)&&(e=e.split(n)[1]),e||(e=o),l.subNativeFiles&&0==l.subNativeheads.length)for(var s in l.subNativeFiles){var r=l.subNativeFiles[s];l.subNativeheads=l.subNativeheads.concat(r);for(var d=0;d<r.length;d++)l.subMaps[r[d]]=s+"/"+r[d]}if(l.subNativeFiles&&-1!=e.indexOf("/")){var u=e.split("/")[0]+"/";if(u&&-1!=l.subNativeheads.indexOf(u)){var c=l.subMaps[u];e=e.replace(u,c)}}this.onDownLoadCallBack(e,0)}else!i.isLocalNativeFile(e)&&-1==e.indexOf("http://")&&-1==e.indexOf("https://")||-1!=e.indexOf("http://usr/")?this.onDownLoadCallBack(e,0):i.downOtherFiles(e,t.Handler.create(this,this.onDownLoadCallBack,[e]),e);else this.onDownLoadCallBack(e,0)}onDownLoadCallBack(e,n,o=null){if(n)this.event(t.Event.ERROR);else{var s;if(l.autoCacheFile){if(o)s=o;else if(i.isLocalNativeFile(e)){var r=""!=t.URL.rootPath?t.URL.rootPath:t.URL._basePath,d=e;""==r||-1==e.indexOf("http://")&&-1==e.indexOf("https://")||(s=e.split(r)[1]),s||(s=d)}else{var u=i.getFileInfo(e);if(u&&u.md5){var c=u.md5;s=i.getFileNativePath(c)}else s=e}this.url!=t.SoundManager._bgMusic?(this._sound=a._createSound(),this._sound.src=this.url=s):(this._sound=a._musicAudio,this._sound.src=this.url=s)}else this.url!=t.SoundManager._bgMusic?(this._sound=a._createSound(),this._sound.src=e):(this._sound=a._musicAudio,this._sound.src=e);this._sound.onCanplay(a.bindToThis(this.onCanPlay,this)),this._sound.onError(a.bindToThis(this.onError,this))}}onError(e){try{console.log("-----1---------------minisound-----id:"+a._id),console.log(e)}catch(e){console.log("-----2---------------minisound-----id:"+a._id),console.log(e)}this.event(t.Event.ERROR),this._sound.offError(null)}onCanPlay(){this.loaded=!0,this.event(t.Event.COMPLETE),this._sound.offCanplay(null)}static bindToThis(e,t){return e.bind(t)}play(e=0,o=0){var s;if(this.url==t.SoundManager._bgMusic?(a._musicAudio||(a._musicAudio=a._createSound()),s=a._musicAudio):s=a._audioCache[this.readyUrl]?a._audioCache[this.readyUrl]._sound:a._createSound(),!s)return null;if(l.autoCacheFile&&i.getFileInfo(this.url)){var r=i.getFileInfo(this.url).md5;s.src=this.url=i.getFileNativePath(r)}else s.src=this.url;var d=new n(s,this);return d.url=this.url,d.loops=o,d.loop=0===o,d.startTime=e,d.play(),t.SoundManager.addChannel(d),d}get duration(){return this._sound.duration}dispose(){var e=a._audioCache[this.readyUrl];e&&(e.src="",e._sound&&(e._sound.destroy(),e._sound=null,e=null),delete a._audioCache[this.readyUrl]),this._sound&&(this._sound.destroy(),this._sound=null,this.readyUrl=this.url=null)}}a._id=0,a._audioCache={};class o{constructor(){}static _createInputElement(){t.Input._initInput(t.Input.area=t.Browser.createElement("textarea")),t.Input._initInput(t.Input.input=t.Browser.createElement("input")),t.Input.inputContainer=t.Browser.createElement("div"),t.Input.inputContainer.style.position="absolute",t.Input.inputContainer.style.zIndex=1e5,t.Browser.container.appendChild(t.Input.inputContainer),t.Laya.stage.on("resize",null,o._onStageResize),l.window.wx.onWindowResize&&l.window.wx.onWindowResize((function(e){})),t.SoundManager._soundClass=a,t.SoundManager._musicClass=a;var e=l.systemInfo.model,i=l.systemInfo.system;-1!=e.indexOf("iPhone")&&(t.Browser.onIPhone=!0,t.Browser.onIOS=!0,t.Browser.onIPad=!0,t.Browser.onAndroid=!1),-1==i.indexOf("Android")&&-1==i.indexOf("Adr")||(t.Browser.onAndroid=!0,t.Browser.onIPhone=!1,t.Browser.onIOS=!1,t.Browser.onIPad=!1)}static _onStageResize(){t.Laya.stage._canvasTransform.identity().scale(t.Browser.width/t.Render.canvas.width/t.Browser.pixelRatio,t.Browser.height/t.Render.canvas.height/t.Browser.pixelRatio)}static wxinputFocus(e){var i=t.Input.inputElement.target;i&&!i.editable||(l.window.wx.offKeyboardConfirm(),l.window.wx.offKeyboardInput(),l.window.wx.showKeyboard({defaultValue:i.text,maxLength:i.maxChars,multiple:i.multiline,confirmHold:!0,confirmType:i.confirmType||"done",success:function(e){},fail:function(e){}}),l.window.wx.onKeyboardConfirm((function(e){var n=e?e.value:"";i._restrictPattern&&(n=n.replace(/\u2006|\x27/g,""),i._restrictPattern.test(n)&&(n=n.replace(i._restrictPattern,""))),i.text=n,i.event(t.Event.INPUT),o.inputEnter(),i.event("confirm")})),l.window.wx.onKeyboardInput((function(e){var n=e?e.value:"";i.multiline||-1==n.indexOf("\n")?(i._restrictPattern&&(n=n.replace(/\u2006|\x27/g,""),i._restrictPattern.test(n)&&(n=n.replace(i._restrictPattern,""))),i.text=n,i.event(t.Event.INPUT)):o.inputEnter()})))}static inputEnter(){t.Input.inputElement.target.focus=!1}static wxinputblur(){o.hideKeyboard()}static hideKeyboard(){l.window.wx.offKeyboardConfirm(),l.window.wx.offKeyboardInput(),l.window.wx.hideKeyboard({success:function(e){console.log("隐藏键盘")},fail:function(e){console.log("隐藏键盘出错:"+(e?e.errMsg:""))}})}}class s extends t.EventDispatcher{constructor(){super()}_loadResourceFilter(e,n){if(this.sourceUrl=t.URL.formatURL(n),-1==n.indexOf(l.window.wx.env.USER_DATA_PATH)&&(-1!=n.indexOf("http://")||-1!=n.indexOf("https://")))if(""!=i.loadPath)n=n.split(i.loadPath)[1];else{var a=""!=t.URL.rootPath?t.URL.rootPath:t.URL._basePath,o=n;""!=a&&(n=n.split(a)[1]),n||(n=o)}if(l.subNativeFiles&&0==l.subNativeheads.length)for(var r in l.subNativeFiles){var d=l.subNativeFiles[r];l.subNativeheads=l.subNativeheads.concat(d);for(var u=0;u<d.length;u++)l.subMaps[d[u]]=r+"/"+d[u]}if(l.subNativeFiles&&-1!=n.indexOf("/")){var c=n.split("/")[0]+"/";if(c&&-1!=l.subNativeheads.indexOf(c)){var h=l.subMaps[c];n=n.replace(c,h)}}switch(e){case t.Loader.IMAGE:case"htmlimage":case"nativeimage":s._transformImgUrl(n,e,this);break;case t.Loader.SOUND:this._loadSound(n);break;default:this._loadResource(e,n)}}_loadSound(e){var n;if(i.isLocalNativeFile(e)){var a=""!=t.URL.rootPath?t.URL.rootPath:t.URL._basePath,o=e;""==a||-1==e.indexOf("http://")&&-1==e.indexOf("https://")||(n=e.split(a)[1]),n||(n=o),s.onDownLoadCallBack(e,this,0)}else{var r=t.URL.formatURL(e);!i.isLocalNativeFile(e)&&-1==r.indexOf("http://")&&-1==r.indexOf("https://")||-1!=r.indexOf(l.window.wx.env.USER_DATA_PATH)?s.onDownLoadCallBack(e,this,0):i.downOtherFiles(encodeURI(r),t.Handler.create(s,s.onDownLoadCallBack,[r,this]),r)}}static onDownLoadCallBack(e,n,a,o=null){if(a)n.event(t.Event.ERROR,"Load sound failed");else{var s;if(l.autoCacheFile)if(o)s=o;else if(i.isLocalNativeFile(e)){var r=""!=t.URL.rootPath?t.URL.rootPath:t.URL._basePath,d=e;""==r||-1==e.indexOf("http://")&&-1==e.indexOf("https://")||(s=e.split(r)[1]),s||(s=d)}else{var u=i.getFileInfo(e);if(u&&u.md5){var c=u.md5;s=i.getFileNativePath(c)}else s=e}e=s;var h=new t.SoundManager._soundClass;h.load(e),n.onLoaded(h)}}static bindToThis(e,t){return e.bind(t)}complete(e){e instanceof t.Resource?e._setCreateURL(this.sourceUrl):e instanceof t.Texture&&e.bitmap instanceof t.Resource&&e.bitmap._setCreateURL(this.sourceUrl),this.originComplete(e)}_loadHttpRequestWhat(e,n){var a=l.getUrlEncode(e,n);if(t.Loader.preLoadedMap[e])this.onLoaded(t.Loader.preLoadedMap[e]);else{var o=t.URL.formatURL(e);if(i.isLocalNativeFile(e)||i.getFileInfo(o)||-1!=e.indexOf(l.window.wx.env.USER_DATA_PATH)||-1==o.indexOf("http://")&&-1==o.indexOf("https://")||l.AutoCacheDownFile){var r=i.getFileInfo(t.URL.formatURL(e));r?(r.encoding=null==r.encoding?"utf8":r.encoding,i.readFile(i.getFileNativePath(r.md5),a,new t.Handler(s,s.onReadNativeCallBack,[e,n,this]),e)):"image"==this.type||"htmlimage"==this.type?this._transformUrl(e,n):n!=t.Loader.IMAGE&&(-1==o.indexOf("http://")&&-1==o.indexOf("https://")||i.isLocalNativeFile(e))?i.readFile(e,a,new t.Handler(s,s.onReadNativeCallBack,[e,n,this]),e):i.downFiles(o,a,new t.Handler(s,s.onReadNativeCallBack,[e,n,this]),o,!0)}else this._loadHttpRequest(o,n,this,this.onLoaded,this,this.onProgress,this,this.onError)}}static onReadNativeCallBack(e,i=null,n=null,a=0,o=null){var s;a?1==a&&n._loadHttpRequest(e,i,n,n.onLoaded,n,n.onProgress,n,n.onError):(s=i==t.Loader.JSON||i==t.Loader.ATLAS||i==t.Loader.PREFAB||i==t.Loader.PLF?l.getJson(o.data):i==t.Loader.XML?t.Utils.parseXMLFromString(o.data):o.data,!l.isZiYu&&l.isPosMsgYu&&i!=t.Loader.BUFFER&&l.window.wx.postMessage({url:e,data:s,isLoad:"filedata"}),n.onLoaded(s))}static _transformImgUrl(e,n,a){if(l.isZiYu||i.isLocalNativeFile(e))a._loadImage(e,!1);else if(l.autoCacheFile)if(i.isLocalNativeFile(e)||i.getFileInfo(t.URL.formatURL(e)))s.onCreateImage(e,a);else{var o=t.URL.formatURL(e);-1!=e.indexOf(l.window.wx.env.USER_DATA_PATH)||-1==o.indexOf("http://")&&-1==o.indexOf("https://")||l.isZiYu?a._loadImage(e):i.downOtherFiles(o,new t.Handler(s,s.onDownImgCallBack,[e,a]),o)}else a._loadImage(e)}static onDownImgCallBack(e,t,i,n=""){i?t.onError(null):s.onCreateImage(e,t,!1,n)}static onCreateImage(e,n,a=!1,o=""){var s;if(l.autoCacheFile)if(a)if(l.isZiYu){var r=t.URL.formatURL(e);s=i.ziyuFileTextureData[r]?i.ziyuFileTextureData[r]:e}else s=e;else if(""!=o)s=o;else{var d=i.getFileInfo(t.URL.formatURL(e)).md5;s=i.getFileNativePath(d)}else s=a?e:o;n._loadImage(s,!1)}}class r{constructor(){}static __init__(){r.items=r}static setItem(e,t){try{l.window.wx.setStorageSync(e,t)}catch(i){l.window.wx.setStorage({key:e,data:t})}}static getItem(e){return l.window.wx.getStorageSync(e)}static setJSON(e,t){r.setItem(e,t)}static getJSON(e){return r.getItem(e)}static removeItem(e){l.window.wx.removeStorageSync(e)}static clear(){l.window.wx.clearStorageSync()}static getStorageInfoSync(){try{var e=l.window.wx.getStorageInfoSync();return console.log(e.keys),console.log(e.currentSize),console.log(e.limitSize),e}catch(e){}return null}}r.support=!0;class l{static getJson(e){return JSON.parse(e)}static enable(){l.init(t.Laya.isWXPosMsg,t.Laya.isWXOpenDataContext)}static init(e=!1,n=!1){l._inited||(l._inited=!0,l.window=window,l.window.hasOwnProperty("wx")&&(l.window.navigator.userAgent.indexOf("MiniGame")<0||(l.isZiYu=n,l.isPosMsgYu=e,l.EnvConfig={},l.isZiYu||(i.setNativeFileDir("/layaairGame"),i.existDir(i.fileNativeDir,t.Handler.create(l,l.onMkdirCallBack))),l.systemInfo=l.window.wx.getSystemInfoSync(),l.window.focus=function(){},t.Laya._getUrlPath=function(){return""},l.window.logtime=function(e){},l.window.alertTimeLog=function(e){},l.window.resetShareInfo=function(){},l.window.CanvasRenderingContext2D=function(){},l.window.CanvasRenderingContext2D.prototype=l.window.wx.createCanvas().getContext("2d").__proto__,l.window.document.body.appendChild=function(){},l.EnvConfig.pixelRatioInt=0,t.Browser._pixelRatio=l.pixelRatio(),l._preCreateElement=t.Browser.createElement,t.Browser.createElement=l.createElement,t.RunDriver.createShaderCondition=l.createShaderCondition,t.Utils.parseXMLFromString=l.parseXMLFromString,t.Input._createInputElement=o._createInputElement,t.Loader.prototype._loadResourceFilter=s.prototype._loadResourceFilter,t.Loader.prototype._loadSound=s.prototype._loadSound,t.Loader.prototype.originComplete=t.Loader.prototype.complete,t.Loader.prototype.complete=s.prototype.complete,t.Loader.prototype._loadHttpRequestWhat=s.prototype._loadHttpRequestWhat,t.LocalStorage._baseClass=r,r.__init__(),l.window.wx.onMessage&&l.window.wx.onMessage(l._onMessage))))}static _onMessage(e){switch(e.type){case"changeMatrix":t.Laya.stage.transform.identity(),t.Laya.stage._width=e.w,t.Laya.stage._height=e.h,t.Laya.stage._canvasTransform=new t.Matrix(e.a,e.b,e.c,e.d,e.tx,e.ty);break;case"display":t.Laya.stage.frameRate=e.rate||t.Stage.FRAME_FAST;break;case"undisplay":t.Laya.stage.frameRate=t.Stage.FRAME_SLEEP}"opendatacontext"==e.isLoad?e.url&&(i.ziyuFileData[e.url]=e.atlasdata,i.ziyuFileTextureData[e.imgReadyUrl]=e.imgNativeUrl):"openJsondatacontext"==e.isLoad?e.url&&(i.ziyuFileData[e.url]=e.atlasdata):"openJsondatacontextPic"==e.isLoad&&(i.ziyuFileTextureData[e.imgReadyUrl]=e.imgNativeUrl)}static getUrlEncode(e,t){return"arraybuffer"==t?"":"utf8"}static downLoadFile(e,t="",n=null,a="utf8"){i.getFileInfo(e)?null!=n&&n.runWith([0]):i.downLoadFile(e,t,n,a)}static remove(e,t=null){i.deleteFile("",e,t,"",0)}static removeAll(){i.deleteAll()}static hasNativeFile(e){return i.isLocalNativeFile(e)}static getFileInfo(e){return i.getFileInfo(e)}static getFileList(){return i.filesListObj}static exitMiniProgram(){l.window.wx.exitMiniProgram()}static onMkdirCallBack(e,t){e?(i.fakeObj={},i.filesListObj={}):(i.filesListObj=JSON.parse(t.data),i.fakeObj=JSON.parse(t.data)||{}),i.fs.readdir({dirPath:i.fileNativeDir,success:function(e){var t={};for(let e in i.filesListObj)"fileUsedSize"!=e&&(t[i.filesListObj[e].md5]=!0);var n,a=e.files;for(let e=0,s=a.length;e<s;e++)if((n=a[e])!=i.fileListName){var o=i.getFileNativePath(n);t[o]||t[n]||i.fs.unlink({filePath:o,success:function(e){console.log("删除无引用的磁盘文件:"+n)}})}}})}static pixelRatio(){if(!l.EnvConfig.pixelRatioInt)try{return l.EnvConfig.pixelRatioInt=l.systemInfo.pixelRatio,l.systemInfo.pixelRatio}catch(e){}return l.EnvConfig.pixelRatioInt}static createElement(e){var t;if("canvas"==e)return 1==l.idx?l.isZiYu?(t=l.window.sharedCanvas).style={}:t=l.window.canvas:t=l.window.wx.createCanvas(),l.idx++,t;if("textarea"==e||"input"==e)return l.onCreateInput(e);if("div"==e){var i=l._preCreateElement(e);return i.contains=function(e){return null},i.removeChild=function(e){},i}return l._preCreateElement(e)}static onCreateInput(e){var t=l._preCreateElement(e);return t.focus=o.wxinputFocus,t.blur=o.wxinputblur,t.style={},t.value=0,t.parentElement={},t.placeholder={},t.type={},t.setColor=function(e){},t.setType=function(e){},t.setFontFace=function(e){},t.addEventListener=function(e){},t.contains=function(e){return null},t.removeChild=function(e){},t}static createShaderCondition(e){return function(){return this[e.replace("this.","")]}}static sendAtlasToOpenDataContext(e){if(!l.isZiYu){var i=t.Loader.getRes(t.URL.formatURL(e));if(!i)throw"传递的url没有获取到对应的图集数据信息,请确保图集已经过!";i.meta.image.split(",");if(i.meta&&i.meta.image)for(var n=i.meta.image.split(","),a=e.indexOf("/")>=0?"/":"\\",o=e.lastIndexOf(a),s=o>=0?e.substr(0,o+1):"",r=0,d=n.length;r<d;r++)n[r]=s+n[r];else n=[e.replace(".json",".png")];for(r=0;r<n.length;r++){var u=n[r];l.postInfoToContext(t.Laya.URL.formatURL(e),t.Laya.URL.formatURL(u),i)}}}static postInfoToContext(e,n,a){var o={frames:a.frames,meta:a.meta},s=n,r=i.getFileInfo(t.URL.formatURL(n));if(r)var d=r.md5,u=i.getFileNativePath(d);else u=s;if(!u)throw"获取图集的磁盘url路径不存在!";l.window.wx.postMessage({url:e,atlasdata:o,imgNativeUrl:u,imgReadyUrl:s,isLoad:"opendatacontext"})}static sendSinglePicToOpenDataContext(e){var n=t.URL.formatURL(e),a=i.getFileInfo(n);if(a){var o=a.md5,s=i.getFileNativePath(o);e=n}else s=e;if(!s)throw"获取图集的磁盘url路径不存在!";e=t.Laya.URL.formatURL(e),l.window.wx.postMessage({url:e,imgNativeUrl:s,imgReadyUrl:e,isLoad:"openJsondatacontextPic"})}static sendJsonDataToDataContext(e){if(!l.isZiYu){e=t.Laya.URL.formatURL(e);var i=t.Loader.getRes(e);if(!i)throw"传递的url没有获取到对应的图集数据信息,请确保图集已经过!";l.window.wx.postMessage({url:e,atlasdata:i,isLoad:"openJsondatacontext"})}}}l._inited=!1,l.autoCacheFile=!0,l.minClearSize=5242880,l.nativefiles=["layaNativeDir","wxlocal"],l.subNativeFiles=[],l.subNativeheads=[],l.subMaps=[],l.AutoCacheDownFile=!1,l.parseXMLFromString=function(e){var t;e=e.replace(/>\s+</g,"><");try{t=(new l.window.Parser.DOMParser).parseFromString(e,"text/xml")}catch(e){throw"需要引入xml解析库文件"}return t},l.idx=1;class d extends t.EventDispatcher{constructor(){super()}static __init__(){try{var e;if(!(e=t.Accelerator))return;e.prototype.on=d.prototype.on,e.prototype.off=d.prototype.off}catch(e){}}static startListen(e){if(d._callBack=e,!d._isListening){d._isListening=!0;try{l.window.wx.onAccelerometerChange(d.onAccelerometerChange)}catch(e){}}}static stopListen(){d._isListening=!1;try{l.window.wx.stopAccelerometer({})}catch(e){}}static onAccelerometerChange(e){var t;(t={}).acceleration=e,t.accelerationIncludingGravity=e,t.rotationRate={},null!=d._callBack&&d._callBack(t)}on(e,t,i,n=null){return super.on(e,t,i,n),d.startListen(this.onDeviceOrientationChange),this}off(e,t,i,n=!1){return this.hasListener(e)||d.stopListen(),super.off(e,t,i,n)}}d._isListening=!1;class u{constructor(){}static __init__(){l.window.navigator.geolocation.getCurrentPosition=u.getCurrentPosition,l.window.navigator.geolocation.watchPosition=u.watchPosition,l.window.navigator.geolocation.clearWatch=u.clearWatch}static getCurrentPosition(e=null,t=null,i=null){var n;(n={}).success=function(t){null!=e&&e(t)},n.fail=t,l.window.wx.getLocation(n)}static watchPosition(e=null,i=null,n=null){var a;return u._curID++,(a={}).success=e,a.error=i,u._watchDic[u._curID]=a,t.Laya.systemTimer.loop(1e3,null,u._myLoop),u._curID}static clearWatch(e){delete u._watchDic[e],u._hasWatch()||t.Laya.systemTimer.clear(null,u._myLoop)}static _hasWatch(){var e;for(e in u._watchDic)if(u._watchDic[e])return!0;return!1}static _myLoop(){u.getCurrentPosition(u._mySuccess,u._myError)}static _mySuccess(e){var i,n={};for(i in n.coords=e,n.timestamp=t.Browser.now(),u._watchDic)u._watchDic[i].success&&u._watchDic[i].success(n)}static _myError(e){var t;for(t in u._watchDic)u._watchDic[t].error&&u._watchDic[t].error(e)}}u._watchDic={},u._curID=0;e.MiniAccelerator=d,e.MiniAdpter=l,e.MiniFileMgr=i,e.MiniInput=o,e.MiniLoader=s,e.MiniLocalStorage=r,e.MiniLocation=u,e.MiniSound=a,e.MiniSoundChannel=n,e.MiniVideo=class{constructor(e=320,t=240){this.videoend=!1,this.videourl="",this.videoElement=l.window.wx.createVideo({width:e,height:t,autoplay:!0})}static __init__(){}on(e,t,i){"loadedmetadata"==e?(this.onPlayFunc=i.bind(t),this.videoElement.onPlay=this.onPlayFunction.bind(this)):"ended"==e&&(this.onEndedFunC=i.bind(t),this.videoElement.onEnded=this.onEndedFunction.bind(this)),this.videoElement.onTimeUpdate=this.onTimeUpdateFunc.bind(this)}onTimeUpdateFunc(e){this.position=e.position,this._duration=e.duration}get duration(){return this._duration}onPlayFunction(){this.videoElement&&(this.videoElement.readyState=200),console.log("=====视频加载完成========"),null!=this.onPlayFunc&&this.onPlayFunc()}onEndedFunction(){this.videoElement&&(this.videoend=!0,console.log("=====视频播放完毕========"),null!=this.onEndedFunC&&this.onEndedFunC())}off(e,t,i){"loadedmetadata"==e?(this.onPlayFunc=i.bind(t),this.videoElement.offPlay=this.onPlayFunction.bind(this)):"ended"==e&&(this.onEndedFunC=i.bind(t),this.videoElement.offEnded=this.onEndedFunction.bind(this))}load(e){this.videoElement&&(this.videoElement.src=e)}play(){this.videoElement&&(this.videoend=!1,this.videoElement.play())}pause(){this.videoElement&&(this.videoend=!0,this.videoElement.pause())}get currentTime(){return this.videoElement?this.videoElement.initialTime:0}set currentTime(e){this.videoElement&&(this.videoElement.initialTime=e)}get videoWidth(){return this.videoElement?this.videoElement.width:0}get videoHeight(){return this.videoElement?this.videoElement.height:0}get ended(){return this.videoend}get loop(){return!!this.videoElement&&this.videoElement.loop}set loop(e){this.videoElement&&(this.videoElement.loop=e)}get playbackRate(){return this.videoElement?this.videoElement.playbackRate:0}set playbackRate(e){this.videoElement&&(this.videoElement.playbackRate=e)}get muted(){return!!this.videoElement&&this.videoElement.muted}set muted(e){this.videoElement&&(this.videoElement.muted=e)}get paused(){return!!this.videoElement&&this.videoElement.paused}size(e,t){this.videoElement&&(this.videoElement.width=e,this.videoElement.height=t)}get x(){return this.videoElement?this.videoElement.x:0}set x(e){this.videoElement&&(this.videoElement.x=e)}get y(){return this.videoElement?this.videoElement.y:0}set y(e){this.videoElement&&(this.videoElement.y=e)}get currentSrc(){return this.videoElement.src}destroy(){this.videoElement&&this.videoElement.destroy(),this.videoElement=null,this.onEndedFunC=null,this.onPlayFunc=null,this.videoend=!1,this.videourl=null}reload(){this.videoElement&&(this.videoElement.src=this.videourl)}}};

可以看出代码被压缩到一块,很难查看代码逻辑。

我们使用代码格式化工具(Nodepad++ jsformat插件,idea等)格式化后的代码再看,代码如下:

laya.wxmini.min.format.js
window.wxMiniGame = function (e, t) {
    "use strict";
    class i {
        static isLocalNativeFile(e) {
            for (var t = 0, i = l.nativefiles.length; t < i; t++)
                if (-1 != e.indexOf(l.nativefiles[t]))
                    return !0;
            return !1
        }
        static getFileInfo(e) {
            var t = e,
            n = i.fakeObj[t];
            return null == n ? null : n
        }
        static read(e, n = "utf8", a = null, o = "", s = !1, r = "") {
            var l;
            l = "" == o || -1 == o.indexOf("http://") && -1 == o.indexOf("https://") ? e : i.getFileNativePath(e),
            l = t.URL.getAdptedFilePath(l),
            i.fs.readFile({
                filePath: l,
                encoding: n,
                success: function (e) {
                    null != a && a.runWith([0, e])
                },
                fail: function (e) {
                    e && "" != o ? i.downFiles(o, n, a, o, s, r) : null != a && a.runWith([1])
                }
            })
        }
        static downFiles(e, t = "utf8", n = null, a = "", o = !1, s = "", r = !0) {
            i.wxdown({
                url: e,
                success: function (l) {
                    200 === l.statusCode ? i.readFile(l.tempFilePath, t, n, a, o, s, r) : 403 === l.statusCode ? null != n && n.runWith([0, e]) : null != n && n.runWith([1, l])
                },
                fail: function (e) {
                    null != n && n.runWith([1, e])
                }
            }).onProgressUpdate((function (e) {
                    null != n && n.runWith([2, e.progress])
                }))
        }
        static readFile(e, n = "utf8", a = null, o = "", s = !1, r = "", d = !0) {
            e = t.URL.getAdptedFilePath(e),
            i.fs.readFile({
                filePath: e,
                encoding: n,
                success: function (t) {
                    (-1 != e.indexOf("http://") || -1 != e.indexOf("https://")) && (l.autoCacheFile || s) ? (null != a && a.runWith([0, t]), i.copyFile(e, o, null, n, d)) : null != a && a.runWith([0, t])
                },
                fail: function (e) {
                    e && null != a && a.runWith([1, e])
                }
            })
        }
        ....
        ....

虽然代码格式明显看起来比之前好的多,但是,有些语句使用“&&”,“?:”等多种表达式把多行代码叠加到一起,依然对代码逻辑阅读、分析有困难。

下面我们逐步来还原Uglify打包出来的代码

第1步:**条件运算符转换为 if-else 结构**:

下面这段 Java 代码片段展示了如何使用 AST(抽象语法树)逐步对 Uglify 压缩后的 JavaScript 代码进行还原,具体来说是将 ConditionalExpression 转换为 if-else 结构。该代码中的关键部分的介绍:

   - 代码中通过遍历 AST,对每个 ConditionalExpression(三元运算符)节点进行转换。

   - 如果 ConditionalExpression 节点的父节点是 ExpressionStatement(表达式语句),则将该节点转换为 if-else 结构。

   - 如果 ConditionalExpression 节点的父节点是 VariableDeclarator(变量声明),则将其转换为 if-else 结构,并插入到对应的位置。

   - 同样,还考虑了 ConditionalExpression 在 AssignmentExpression(赋值表达式)和 ReturnStatement(返回语句)的情况,将其转换为 if-else 结构。

    /**
     * 三元运算符转if-else
     * @param json
     */
    private static void conditionalToIf(JSONObject json) {
        Traverse.walk(json, new JSONArray(), (object, parents) -> {
            
            
            if (!Syntax.ConditionalExpression.isType(object)) {
                return null;
            }
            JSONObject o = (JSONObject) object;
            final Object p0 = parents.get(0);
            final String p0Type = JSONUtils.getType(p0);
            JSONObject parent;
            if (Syntax.ExpressionStatement.isType(p0Type)) {
                
                final JSONObject ifStatement = Gen.IfStatement(o.getJSONObject("test"),
                                                               Gen.toArray(Gen.ExpressionStatement(o.getJSONObject("consequent"))),
                                                               Gen.toArray(Gen.ExpressionStatement(o.getJSONObject("alternate")))
                                                              );
                o.clear();
                o.putAll(ifStatement);
                return null;
            } else if (Syntax.VariableDeclarator.isType(p0Type)) {
                
                parent = (JSONObject) p0;
                if (parents.size() > 4) {
                    final Object parent2 = parents.get(2);
                    if (!Syntax.VariableDeclaration.isType(parent2)) {
                        return null;
                    }
                    final JSONObject parent2Obj = (JSONObject) parent2;
                    if (parent2Obj.getJSONArray("declarations").size() != 1) {
                        return null;
                    }
                    if ("const".equals(parent2Obj.getString("kind"))) {
                        //常量不可分割
                        return null;
                    }
                    final JSON parent3 = (JSON) parents.get(4);
                    if (isContext(parent3)) {
                        final JSONArray blockBody = getBlockBody(parent3);
                        final int index = blockBody.indexOf(parent2Obj);
                        if (index == -1) {
                            return null;
                        }
                        final JSONObject test = o.getJSONObject("test");
                        final JSONObject alternate = o.getJSONObject("alternate");
                        final JSONObject consequent = o.getJSONObject("consequent");
                        final JSONObject id = parent.getJSONObject("id");
                        final JSONObject id2 = Insert.clone(id);
                        
                        final JSONObject newConsequent = Gen.ExpressionStatement(Gen.AssignmentExpression("=", id, consequent));
                        final JSONObject newAlternate = Gen.ExpressionStatement(Gen.AssignmentExpression("=", id2, alternate));
                        final JSONObject ifStatement = Gen.IfStatement(test, Gen.toArray(newConsequent), Gen.toArray(newAlternate));
                        parent.remove("init");
                        blockBody.add(index + 1, ifStatement);
                        
                    }
                }
            } else if (Syntax.AssignmentExpression.isType(p0Type)) {
                if (parents.size() < 4) {
                    return null;
                }
                Object p1 = parents.get(1);
                if (!Syntax.ExpressionStatement.isType(p1)) {
                    return null;
                }
                parent = (JSONObject) p0;
                final JSON parent2 = (JSON) parents.get(3);
                if (isContext(parent2)) {
                    final JSONArray blockBody = getBlockBody(parent2);
                    final int index = blockBody.indexOf(p1);
                    if (index == -1) {
                        return null;
                    }
                    final JSONObject test = o.getJSONObject("test");
                    final JSONObject alternate = o.getJSONObject("alternate");
                    final JSONObject consequent = o.getJSONObject("consequent");
                    final JSONObject id = parent.getJSONObject("left");
                    final JSONObject id2 = Insert.clone(id);
                    
                    final JSONObject newConsequent = Gen.ExpressionStatement(Gen.AssignmentExpression("=", id, consequent));
                    final JSONObject newAlternate = Gen.ExpressionStatement(Gen.AssignmentExpression("=", id2, alternate));
                    final JSONObject ifStatement = Gen.IfStatement(test, Gen.toArray(newConsequent), Gen.toArray(newAlternate));
                    blockBody.remove(index);
                    blockBody.add(index, ifStatement);
                    
                }
                
            } else if (Syntax.ReturnStatement.isType(p0Type)) {
                final Object p1 = parents.get(1);
                if (!(p1 instanceof JSONArray)) {
                    return null;
                }
                JSONArray p1Arr = (JSONArray) p1;
                final int index = p1Arr.indexOf(p0);
                final JSONObject test = o.getJSONObject("test");
                final JSONObject alternate = o.getJSONObject("alternate");
                final JSONObject consequent = o.getJSONObject("consequent");
                final JSONObject newConsequent = Gen.ReturnStatement(consequent);
                final JSONObject newAlternate = Gen.ReturnStatement(alternate);
                final JSONObject ifStatement = Gen.IfStatement(test, Gen.toArray(newConsequent), Gen.toArray(newAlternate));
                p1Arr.remove(index);
                p1Arr.add(index, ifStatement);
                
            } else {
//                logger.debug("conditionalToIf p0Type:{}", p0Type);
            }
            return null;
        });
    }

2:**展开变量声明**:

下面这段 Java 代码展示了如何使用 AST(抽象语法树)逐步对 Uglify 压缩后的 JavaScript 代码进行还原,具体是展开变量声明。该代码中的关键部分的介绍:

   - 代码中通过遍历 AST,对每个 VariableDeclaration(变量声明)节点进行展开处理。

   - 如果发现节点是一个变量声明,且其中包含多个声明(declarations 大于1),则将该声明进行展开处理。

   - 遍历 declarations 数组,将每个声明单独提取,并按照原来的声明类型和属性重新构建为一个新的 VariableDeclaration 节点。

   - 最后,将展开后的新声明依次插入到原来 VariableDeclaration 节点的位置,实现变量声明的展开效果。

public static void expandVariableDeclaration(JSONObject json) {
    Traverse.walk(json, new JSONArray(), (object, parents) -> {
        
        if (Syntax.VariableDeclaration.isType(object)) {
            JSONObject jsonObject = (JSONObject) object;
            JSONArray declarations = jsonObject.getJSONArray("declarations");
            
            if (declarations.size() > 1) {
                String kind = jsonObject.getString("kind");
                final Object o = parents.get(0);
                if (o instanceof JSONArray) {
                    final JSONArray objects = (JSONArray) o;
                    int index = objects.indexOf(object);
                    if (index != -1) {
                        
                        objects.remove(index);
                        for (Object expression1 : declarations) {
                            final JSONObject jsonObject1 = Gen.VariableDeclaration((JSONObject) expression1, kind);
                            objects.add(index++, jsonObject1);
                        }
                    } else {
                        throw new RuntimeException("执行展开变量定义出错");
                    }
                }
            }
        }
        return null;
    });
}

3:**还原代码中的序列表达式**:

下面这段 Java 代码展示了如何使用 AST(抽象语法树)逐步还原 Uglify 压缩后的 JavaScript 代码中的 SequenceExpression(序列表达式)。代码中关键部分的介绍:

   - 通过遍历 AST,对每个 SequenceExpression 节点进行展开处理。SequenceExpression 是由逗号分隔的表达式序列组成的表达式。

   - 如果发现节点是 SequenceExpression,则根据父节点类型进行不同的处理:

     - 如果父节点是 ExpressionStatement(表达式语句),则将序列表达式展开为多个独立的表达式,并依次添加到父节点的数组中。

     - 如果父节点是 ReturnStatement(返回语句),则将序列表达式展开为多个独立的表达式,将前面的表达式作为新的表达式语句,最后一个表达式作为该 ReturnStatement 的结果。

    

private static void expandSequenceExpression(JSONObject json) {
        Traverse.walk(json, new JSONArray(), new EnterCallback() {
            @Override
            public ExitCallback run(JSON object, JSONArray parents) {
                if (!Syntax.SequenceExpression.isType(object)) {
                    return null;
                }
                final JSONObject jsonObject = (JSONObject) object;
                final Object p0 = parents.get(0);
                final Object p1 = parents.get(1);
                if (!(p1 instanceof JSONArray)) {
                    return null;
                }
                JSONArray p1Arr = (JSONArray) p1;
                final String type = JSONUtils.getType(p0);
                if (Syntax.ExpressionStatement.isType(type)) {
                    int index = p1Arr.indexOf(p0);
                    p1Arr.remove(index);
                    final JSONArray expressions = jsonObject.getJSONArray("expressions");
                    for (Object expression : expressions) {
                        p1Arr.add(index++, Gen.ExpressionStatement((JSONObject) expression));
                    }
                } else if (Syntax.ReturnStatement.isType(type)) {
                    final JSONArray expressions = jsonObject.getJSONArray("expressions");
                    int index = p1Arr.indexOf(p0);
                    final int size = expressions.size() - 1;
                    for (int i = 0; i < size; i++) {
                        final JSONObject jsonObject1 = Gen.ExpressionStatement(expressions.getJSONObject(i));
                        p1Arr.add(index++, jsonObject1);
                    }
                    jsonObject.clear();
                    jsonObject.putAll(expressions.getJSONObject(size));
                } else {
//                    logger.debug("expandSequenceExpression run type:{}", type);
                }
                return null;
            }
        });
    }

这段代码片段展示了如何通过 AST 对 Uglify 压缩后的 JavaScript 代码中的 SequenceExpression 进行展开操作,帮助还原代码结构,提高代码的可读性和理解性。逐步还原代码中的序列表达式,有助于深入理解 JavaScript 代码的执行流程和逻辑。

4:**逻辑与(&&)、逻辑或(||)表达式转换为 if 语句**:

下面这段 Java 代码展示了如何使用 AST(抽象语法树)逐步还原 Uglify 压缩后的 JavaScript 代码中的 LogicalExpression(逻辑表达式)。代码中关键部分的介绍:

   - 通过遍历 AST,对每个 ExpressionStatement节点进行展开处理。普通表达式转if-else

- 如果是return语句中的逻辑表达式,特殊处理改为if(xxx)return x else return y的形式。

private static void logicAndOrExpressToIf(JSONObject json) {
        Traverse.walk(json, new JSONArray(), (object, parents) -> {
            
            if (!Syntax.LogicalExpression.isType(object)) {
                return null;
            }
            final Object p0 = parents.get(0);
            final String type = JSONUtils.getType(p0);
            if (Syntax.ExpressionStatement.isType(type)) {
                JSONObject o = (JSONObject) p0;
                
                final JSONObject jsonObject = (JSONObject) object;
                final String operator = jsonObject.getString("operator");
                JSONObject test;
                if (operator.equals("&&")) {
                    
                    test = jsonObject.getJSONObject("left");
                } else if (operator.equals("||")) {
                    test = UglyCodeRestore.UnaryExpressionRevert(jsonObject.getJSONObject("left"));
                } else {
                    return null;
                }
                
                final JSONObject ifStatement = Gen.IfStatement(test, Gen.toArray(Gen.ExpressionStatement(jsonObject.getJSONObject("right"))));
                o.clear();
                o.putAll(ifStatement);
            } else if (Syntax.ReturnStatement.isType(type)) {
                
                final Object p1 = parents.get(1);
                if (!(p1 instanceof JSONArray)) {
                    return null;
                }
                JSONObject jsonObject = (JSONObject) object;
                final String operator = jsonObject.getString("operator");
                JSONObject test = jsonObject.getJSONObject("left");
                JSONObject consequent;
                if (operator.equals("&&")) {
                    
                    test = UglyCodeRestore.UnaryExpressionRevert(test);
                    consequent = Gen.ReturnStatement(Gen.Literal(false));
                } else if (operator.equals("||")) {
                    consequent = Gen.ReturnStatement(Gen.Literal(true));
                } else {
                    return null;
                }
                JSONArray p1Arr = (JSONArray) p1;
                int index = p1Arr.indexOf(p0);
                
                final JSONObject right = jsonObject.getJSONObject("right");
                
                
                final JSONObject ifStatement = Gen.IfStatement(test, Gen.toArray(consequent));
                p1Arr.remove(index);
                p1Arr.add(index++, ifStatement);
                p1Arr.add(index, Gen.ReturnStatement(right));
                
            } else {
//                logger.debug("logicAndExpressToIf type:{}", type);
            }
            
            return null;
        });
    }

private static JSONObject UnaryExpressionRevert(JSONObject test) {
    int nonCount = 0;
    while (Syntax.UnaryExpression.isType(test) && "!".equals(test.getString("operator"))) {
        nonCount++;
        test = test.getJSONObject("argument");
    }
    nonCount++;
    if (nonCount % 2 == 0) {
        return test;
    }
    return Gen.UnaryExpression("!", test);
}

5:**变量名还原**:

下面这段代码用于恢复AST(抽象语法树)中的变量名,函数名和类名的原始名称,并进行相关处理。代码逻辑进行简要解释:

   -  `variableScopeScan(json)`: 给AST的根节点、函数节点附加作用域,即进行变量作用域扫描。

   -  `variableAnalysis(json)`:查找变量、函数、类声明,并将它们添加到所在的作用域中,进行变量分析。

   -  `variableFindRealName(json)`:在作用域内找出变量名、函数名、类名等的原名。通过赋值查找来确定变量、函数和类的原始名称。例如,当某个变量通过赋值操作改变了名称时,在作用域内找到它的真实原名。

   

对AST进行遍历:使用Traverse.walk方法遍历AST,对每个节点进行处理。针对节点类型为Identifier的JSONObject对象,执行以下操作:

    - 获取当前Identifier对象的名称。

    - 获取关于Identifier对象的信息,并判断其是否为类声明、函数声明或需要重命名。

   - 如果符合条件,从其父级作用域中获取原始名称,并将原始名称替换为当前对象的名称。

返回`null`,表示处理完当前节点后继续遍历下一个节点。这段代码主要实现了在AST中恢复变量名、函数名和类名的原始名称的功能,同时对节点进行相应的修改操作。

private static void classNameRestore(JSONObject json) {
    //给ast的root节点,function节点附加作用域
    variableScopeScan(json);
    //查找变量,函数,类声明添加到所在作用域
    variableAnalysis(json);
    //在作用域内找出变量名,函数名,类名等的原名(通过赋值查找)
    // 例如:
    // var t={}; t.xx=1; window.Info=t;
    //我们认为这个t的原名是Info,把这个名字t替换成Info
    variableFindRealName(json);
    Traverse.walk(json, new JSONArray(), (o, p) -> {
        if (o instanceof JSONObject) {
            final JSONObject object = (JSONObject) o;
            if (!Syntax.Identifier.isType(object)) {
                return null;
            }
            final String name = JSONUtils.getName(object);
            IdentifierInfo info = getIdentifierInfo(object, p);
            if (info.isClassDeclaration() || info.isFunctionDeclaration() || object.getBooleanValue($rename)) {
                final String restoreNameFromParent = getScopeRestoreNameFromParent(p, name);
                if (StringUtils.isNotEmpty(restoreNameFromParent)) {
                    object.put("name", restoreNameFromParent);
                }
            }
        }
        return null;
    });
}

第6步:**一元逻辑非表达式的常量数字转换布尔值**:

以下这段代码用于在AST(抽象语法树)中找到特定的一元逻辑非表达式,并将其中的常量数字转换为对应的布尔值。代码逻辑进行简要解释:

   -  AST中查找一元逻辑非表达式,并将其中的常量数字"1"和"0"分别转换为布尔值false和true。如果不是指定格式的表达式,则不做任何操作。

private static void numberToBoolean(JSONObject json) {
    Traverse.walk(json, new JSONArray(), (object, parents) -> {
        if (!Syntax.UnaryExpression.isType(object)) {
            return null;
        }
        JSONObject o = (JSONObject) object;
        if (!"!".equals(o.getString("operator"))) {
            return null;
        }
        JSONObject argument = o.getJSONObject("argument");
        if (!Syntax.Literal.isType(argument)) {
            return null;
        }
        final String raw = argument.getString("raw");
        if (raw.equals("1")) {
            o.clear();
            o.put("type", "Literal");
            o.put("raw", "false");
            o.put("value", false);
        } else if (raw.equals("0")) {
            o.clear();
            o.put("type", "Literal");
            o.put("raw", "true");
            o.put("value", true);
        }
        return null;
    });
}

经过以上步骤,还原的代码格式如下:

js代码还原对比图
JS代码还原对比图

AST还原JS的JAVA原代码已经上传到,请到文章顶部下载 ,关注我,后续文章提供代码还原工具。

  • 69
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值