某页游破解教程
网站:aHR0cHM6Ly93d3cuNDM5OS5jb20vZmxhc2gvMjM3NzM1XzMuaHRt
游戏主逻辑代码在index.js里,js里面有些简单的混淆不影响分析,不过我这里先去混淆后在分析
js去混淆
字符串去混淆
游戏中js字符串混淆,将字符串用unicode编码形式进行显示,比如:
1
|
n.modules().uiControlModule.showDialogBox( "\u6e29\u99a8\u63d0\u793a" , "\u544a\u8bc9\u4f60\u4e00\u4e2a\u6d88\u606f" , !1, null , null , "\u6211\u77e5\u9053\u4e86" ) |
而在AST中可以看到value和rawValue的值是正常,只有raw是编码形式显示,在babel中通常处理方法如下:
1
2
3
4
|
path.node.extra.raw = '"' + path.node.extra.rawValue + '"' path.node.extra.raw = '"' + path.node.value + '"' delete path.node.extra delete path.node.extra.raw |
前两种情况通常用在字符串中没有转义符和”,’的情况,因为path.node.extra.raw和path.node.value前者会在后者的基础上在进行一次转义如 a = “你好\”!” 这条语句:
如果直接替换就会出现 a = “你好“!” ,语法错误。而后两种delete 的情况,使用的时候发现没有效果,最后发现要在
1
|
generate(ast,opts = {jsescOption:{ "minimal" : true }}) |
中加入opts = {jsescOption:{“minimal”:true}才行。
逗号表达式混淆
逗号表达式混淆是将多条语句用, 合并成一条语句 如下:
1
2
3
4
5
|
onUpdate(e) { this .isActive && ! this .isBreak && ( this .cdNum += e, this .cdNum >= this .maxCd && ( this .cdNum = 0, this .bornBoom())) } |
在babel中只需要将SequenceExpression 节点替换成多个子节点就行,不过要注意SequenceExpression位于if 之类节点内的情况,这时直接替换就会导致出错
1
2
3
4
5
6
7
|
SequenceExpression(path) { if (path.parent && !t.isExpressionStatement(path.parent)) { return ; } path.parentPath.replaceWithMultiple(path.node.expressions.map(e => t.expressionStatement(e))); } |
Void+数值 和 !+数值混淆
游戏中会将undefined 使用void + 数值 语句来代替, true和false 也使用 ! + 数值来代替。这种情况直接遍历UnaryExpression表达式判断操作符和操作对象类型就行。
01
02
03
04
05
06
07
08
09
10
11
12
13
|
UnaryExpression(path) { if (path.node.operator === 'void' && path.node.argument.type === 'NumericLiteral' ) { path.replaceWith(t.identifier( "undefined" )); } if (path.node.operator === '!' && path.node.argument.type === 'NumericLiteral' ) { if (path.node.argument.value === 0) { path.replaceWith(t.booleanLiteral( true )); } else { path.replaceWith(t.booleanLiteral( false )); } } } |
声明变量合并
声明变量合并和逗号表达式混淆类似,不过是将多个变量声明合并成一条语句,babel中处理和逗号表达式类似:
1
2
3
4
5
|
VariableDeclaration(path) { if (path.node.declarations.length > 1) { path.replaceWithMultiple(path.node.declarations.map(d => t.variableDeclaration(path.node.kind, [d]))); } } |
逻辑表达式和三目运算混淆
混淆利用逻辑运算短路性质将if else 语句转为逻辑表达式形式如:
1
2
3
4
5
6
|
Init() { console.log( "Init start" ), this .m_gameRecorderManager = a.getGameRecorder(), null === this .m_gameRecorderManager && console.log( "\u5f53\u524d\u7248\u672c App \u4e0d\u652f\u6301\u5f55\u5c4f" ), console.log( "Init over" ) } |
对于 && 运算来说,如果前面语句结果为真 则会执行 && 右侧语句,为假则不会直接(和if语句等效),使用babel去混淆后:
1
2
3
4
5
6
7
8
|
Init() { console.log( "Init start" ); this .m_gameRecorderManager = a.getGameRecorder(); if ( null === this .m_gameRecorderManager) { console.log( "当前版本 App 不支持录屏" ); } console.log( "Init over" ); } |
对这种形式需要构建if 节点:
1
2
3
4
5
6
7
8
|
LogicalExpression(path) { if (!t.isExpressionStatement(path.parent)) { return ; } path.parentPath.replaceWith(t.ifStatement(path.node.left, t.blockStatement([t.expressionStatement(path.node.right)]), null )); } |
三目运算语句的形式跟if else 一样,不过带返回值,所以对它处理也是构建if 节点替换,不过需要注意父节点是赋值语句和return 的情况 ,替换为if语句就不能赋值了。
01
02
03
04
05
06
07
08
09
10
11
|
ConditionalExpression(path) { if (t.isAssignmentExpression(path.parent) || t.isReturnStatement(path.parent)) { return ; } if (!t.isExpressionStatement(path.parent)) { return ; } path.parentPath.replaceWith(t.ifStatement(path.node.test, t.blockStatement([t.expressionStatement(path.node.consequent)]), t.blockStatement([t.expressionStatement(path.node.alternate)]))); path.parentPath.skip(); } |
对游戏做简单修改
Local storage
游戏刷新网页后仍保存体力和宝石的数据,js中有很多storage的字眼,所以很可能是将数据存在了Local storage中。在去混淆的js中搜索localStorage 找到这个
1
|
a. default .getServices().localStorageSrv.setStorage( this .m_VERSION_NOTICE_KEY, this .m_versionNotice); |
这里应该就是设置本地存储数据,在根据localStorageSrv.setStorage搜索可以找到其它值。最后可以找到体力和宝石对应字符串值。