某消消乐手游分析

概述

kx消消乐版本1.52。仅作为学习研究,对本地游戏的一些功能进行了修改。

lua解密

观察 lib\armeabi 下的so文件,发现网上有文章提到了 libhegame。根据资料,游戏代码主要位于加密后的lua脚本中。

加密后的lua的文件名中包含该文件的MD5,例如MainApplication.fa4649711ff8bb0263b0cca9085aa9e4.lua的md5是fa4649711ff8bb0263b0cca9085aa9e4;初始向量IV是原始lua文件的MD5,如GamePlayConfig.200dc27111c390e146d9193be27c2424.lua的前16字节是0462 f05f 926c d538 e9ee e82a 4c5e 76ef,也是原始lua文件的MD5。

解密代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
key = b"\xe9\x74\x7d\x92\xcc\x32\x2e\x7d\x11\x2e\x7c\x34\x51\xd7\xb3\x6a"

def decrypt_lua(c):
IV = c[0:16]
main_data = c[16:]

# 解密
cryptor = AES.new(key, AES.MODE_CBC, IV)
pad_compress_data = cryptor.decrypt(main_data)

# 去除填充字节
pad_compress_data_len = len(pad_compress_data)
padding_len = pad_compress_data[-1]
compress_data = pad_compress_data[0:pad_compress_data_len - padding_len]

# 解压缩
raw_data = zlib.decompress(compress_data)
return raw_data
`

加密代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def get_md5_bytes(data):
return hashlib.md5(data).digest()


def encry_lua(m):

# 压缩
compress_data = zlib.compress(m)

# 填充
padding_len = 16 - (len(compress_data) & 0xf)
compress_data += bytes([padding_len]) * padding_len

# 加密
IV = get_md5_bytes(m)
cryptor = AES.new(key, AES.MODE_CBC, IV)
encry_data = cryptor.encrypt(compress_data)

return (IV, encry_data)

校验

若出现“Your current game version is illgeal”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//\com\happyelements\hellolua\MainActivity.class
public void testSignature()
{
int i = 0;
String[] arrayOfString = new String[5];
arrayOfString[0] = "d12428684ac5205ef3d6db9ec3e62e3d";
arrayOfString[1] = "72226f7801e3c0a5da03157b1df244ce";
arrayOfString[2] = "42a0b767dd6fea9e81b9c4920b959521";
arrayOfString[3] = "ce187ed67e05c2d8879bf66bbfdfc8b9";
arrayOfString[4] = "86cc879de7b5050a44380ff30b829121";
String str = getSignature();
int j = 0;
int k = arrayOfString.length;
while (i < k)
{
if (arrayOfString[i].equalsIgnoreCase(str)) {
j = 1;
}
i++;
}
if (j == 0) {
new AlertDialog.Builder(this).setTitle("Warnning").setMessage("Your current game version is illegal. Please download the official version.").setNegativeButton("Close", new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface paramAnonymousDialogInterface, int paramAnonymousInt) {}
}).show();
}
}
}

根据代码,可以将j初始化为1。

修改 lua 文件后需要加密并以原来的文件名保存,如果更改文件名会有如下错误:

1
2
3
error loading module zoo.data.MetaRef from file src/zoo/data/MetaRef.lua:
fileSize:0
message: zoo.data.MetaRef, unknown error

如果闪退,查看 app 的 crash_log,注释掉引用 CCSpriteExCCNodeEx 的代码,即可启动游戏。

修改

步数修改

网上已有相关资料,直接修改 MoveMode:userMove() 中的代码即可。

1
2
3
4
5
6
7
8
9
--src\zoo\gamePlay\mode\MoveMode.lua
function MoveMode:useMove()
local mainLogic = self.mainLogic;
mainLogic.theCurMoves = mainLogic.theCurMoves + 10;
--mainLogic.theCurMoves = mainLogic.theCurMoves - 1;
if mainLogic.PlayUIDelegate then --------调用UI界面函数显示移动步数
mainLogic.PlayUIDelegate:setMoveOrTimeCountCallback(mainLogic.theCurMoves, false)
end
end

游戏币修改

1
2
3
4
5
6
--src\zoo\data\DataRef.lua
function UserRef:getCoin()
return 666666
--local key = "UserRef.coin"..tostring(self)
--return decrypt_integer(key)
end

风车币 getCash 同理,精力应该也可以照此修改。

新手无限精力有效时间

1
2
3
4
5
6
7
8
9
10
--src\zoo\panelBusLogic\UseEnergyBottleLogic.lua
local oldBuff = UserManager:getInstance().userExtend:getNotConsumeEnergyBuff()
local newBuff = 0
if oldBuff < Localhost:time() then
newBuff = Localhost:time() + 3600 * 1000 * 24 * 365 * 10
--newBuff = Localhost:time() + 3600 * 1000
else
newBuff = oldBuff + 3600 * 1000 * 24 * 365 * 10
--newBuff = oldBuff + 3600 * 1000
end

游戏内道具使用次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--src\zoo\animation\PropListAnimation.lua
function PropListAnimation:getPropUnlimitable(itemId)
-- local summerWeeklyLimitPropList = {GamePropsType.kRandomBird, GamePropsType.kBroom, GamePropsType.kBroom_l}
return true
--[[if self.isUnlimited then
if self.levelType == GameLevelType.kSummerWeekly then
local maxUseTime = kSummerWeeklyPropUseLimit[itemId]
return false, maxUseTime
else
return true
end
else
return false
end]]--
end

游戏外道具购买次数

1
2
3
4
5
6
--src\zoo\panelBusLogic\BuyLogic.lua
-- 返回值:实际价格(可能是折扣价,0即为不可买),可买最高数量(仅与限购相关),折扣前价格(如果折扣了的话)
-- 实际价格为0表示出错,不考虑另外的返回值;可买数量为-1表示不限购买数量;折扣前价格为0表示当前就是原价
function BuyLogic:getPrice()
return realPrice, -1, originalPrice
--return realPrice, buyLimit, originalPrice

参考资料

------ 本文结束 ------

版权声明

Memory is licensed under a Creative Commons BY-NC-SA 4.0 International License.
博客采用知识共享署署名(BY)-非商业性(NC)-相同方式共享(SA)
本文首发于Memory,转载请保留出处。