您的位置:首页 > 其它

Clash of Clans, Hay day, Boom Beach等游戏资源包破解

2015-03-07 18:05 956 查看
一年前做过的事情,现在想回顾一下,温故知新。

入行以来最初接触到的游戏是SC大红大紫的两款游戏COC和Hayday,不论画面表现,游戏性,流畅度都值得同行的程序学习,但是当年觉得只是望洋兴叹,他的技术nb,我该如何去学习呢?后来就尝试去破解他的资源包,往往看他的图片资源是可以看出些端倪的。

首先这件事已经有国外大神分享过了破解资源包的python源码,附上链接:https://github.com/kennytm/CoCCalc/tree/master

但是在13年10月份,SC对旗下的游戏资源包做了改版,一方面加入了新技术,一方面估计是看到了Github上的源码,进行了反破解,所以在此之后的包都无法再破解了,而我做的事就是在国外大神的研究基础上,再去破解新的包。

言归正传,首先下载游戏的安装包(废话),解压,找sc文件夹,ipa文件应该在res目录下,apk应该在assets里,找到后里面会有一堆.sc文件,所有的资源都藏在这里。先插一句,这个东西包括两层,sc为第一层,破解后的文件按照国外大神的命名,就叫.scf吧,这个文件对SC游戏的引擎而言,已经可以用了,但是对我们这些资源破解者而言,还得想办法把scf破解掉,拿到图片,这个作为第二层。

第一层.sc看着玄乎,其实本质上是一个lzma压缩包。一个正常的lzma压缩包前5字节是properties,后8字节是length,再往后是压缩内容。而事实上每个sc文件的length远不能达到8字节那么长,所以SC把第10到13字节的4个0字节删掉了(little-endian),用代码将这4个0字节加上,然后再解压就行,这部分和链接里的代码一致。

重点是第二层.scf,这块内容实在是复杂,所以很佩服国外那个大神,面对一堆数据能从其中摘出每一块每一块的内容,然后找出图片的数据块并且生成出图片。我所做的都是从他的代码里看scf的数据分块,然后对比和老版本文件的区别,一步步解开新的加密的,相比之下实在是微不足道。在这里我会把我自己发现的东西分享一下,懒得把整块代码贴上来了,用到哪贴到哪,和链接里的代码对比一下就会明白。

attributes = [
    None,
    'texture',
    'shape',
    'movie_clip',
    None,
    None,
    None,
    'text_field',
    'matrix',
    'color_transform',
    'movie_clip',
    None,
    'movie_clip',
    'timeline_offset',
    'movie_clip',
    'movie_clip',
    'movie_clip',
    'movie_clip',
    'shape',
]
attributes的变化,这个用于确定是scf文件究竟是哪类数据的,我们要处理的类型是shape(这些名字都是国外那哥们儿命名的,我懒得改,按他的来)。

attribute = attributes[tag_type]
            result = getattr(self, 'parse_' + attribute)(data, tag_type)
            getattr(self, attributes[tag_type] + 's').append(result)
attributes就在这里用到,其实就是说新的shape的attribute变成了18(猥琐地一个个试出来的,不排除有再增加的可能性),检查到属性为18,就认为这块数据是图片内容,然后调用parse_shape方法。

parse_shape方法改动了两处,一处在:

if _ == 0:
                (command_type, nothing1, length) = struct.unpack('<BhI', f.read(7))
            else:
                (nothing1, length) = struct.unpack('<BI', f.read(5))


两处nothing1就是干扰变量,这个值经过大量测试,都是1,所以不存在包含信息的可能,直接弃掉,另一处是解析data的地方:

if length == 50:
                (tex_id, nothing2, *rest) = struct.unpack('<BB8I8h', shape_data)
                commands.append(ShapeDrawBitmapCommand(
                    texture=self.textures[tex_id],
                    xys=((rest[0]/-20, rest[1]/-20),
                        (rest[2]/-20, rest[3]/-20),
                        (rest[4]/-20, rest[5]/-20),
                        (rest[6]/-20, rest[7]/-20)),
                    uvs=((rest[8], rest[9]),
                        (rest[10], rest[11]),
                        (rest[12], rest[13]),
                        (rest[14], rest[15]))
                ))
            elif length == 62:
                (tex_id, nothing2, *rest) = struct.unpack('<BB10I10h', shape_data)
                commands.append(ShapeDrawBitmapCommand(
                    texture=self.textures[tex_id],
                    xys=((rest[0]/-20, rest[1]/-20),
                         (rest[2]/-20, rest[3]/-20),
                         (rest[4]/-20, rest[5]/-20),
                         (rest[6]/-20, rest[7]/-20),
                         (rest[8]/-20, rest[9]/-20)),
                    uvs=((rest[10], rest[11]),
                         (rest[12], rest[13]),
                         (rest[14], rest[15]),
                         (rest[16], rest[17]),
                         (rest[18], rest[19]))
                ))
            elif length == 74:
                (tex_id, nothing2, *rest) = struct.unpack('<BB12I12h', shape_data)
                commands.append(ShapeDrawBitmapCommand(
                    texture=self.textures[tex_id],
                    xys=((rest[0]/-20, rest[1]/-20),
                         (rest[2]/-20, rest[3]/-20),
                         (rest[4]/-20, rest[5]/-20),
                         (rest[6]/-20, rest[7]/-20),
                         (rest[8]/-20, rest[9]/-20),
                         (rest[10]/-20, rest[11]/-20)),
                    uvs=((rest[12], rest[13]),
                         (rest[14], rest[15]),
                         (rest[16], rest[17]),
                         (rest[18], rest[19]),
                         (rest[20], rest[21]),
                         (rest[22], rest[23]))
                ))
            elif length == 86:
                (tex_id, nothing2, *rest) = struct.unpack('<BB14I14h', shape_data)
                commands.append(ShapeDrawBitmapCommand(
                    texture=self.textures[tex_id],
                    xys=((rest[0]/-20, rest[1]/-20),
                         (rest[2]/-20, rest[3]/-20),
                         (rest[4]/-20, rest[5]/-20),
                         (rest[6]/-20, rest[7]/-20),
                         (rest[8]/-20, rest[9]/-20),
                         (rest[10]/-20, rest[11]/-20),
                         (rest[12]/-20, rest[13]/-20)),
                    uvs=((rest[14], rest[15]),
                         (rest[16], rest[17]),
                         (rest[18], rest[19]),
                         (rest[20], rest[21]),
                         (rest[22], rest[23]),
                         (rest[24], rest[25]),
                         (rest[26], rest[27]))
                ))

            elif length == 98:
                (tex_id, nothing2, *rest) = struct.unpack('<BB16I16h', shape_data)
                commands.append(ShapeDrawBitmapCommand(
                    texture=self.textures[tex_id],
                    xys=((rest[0]/-20, rest[1]/-20),
                         (rest[2]/-20, rest[3]/-20),
                         (rest[4]/-20, rest[5]/-20),
                         (rest[6]/-20, rest[7]/-20),
                         (rest[8]/-20, rest[9]/-20),
                         (rest[10]/-20, rest[11]/-20),
                         (rest[12]/-20, rest[13]/-20),
                         (rest[14]/-20, rest[15]/-20)),
                    uvs=((rest[16], rest[17]),
                         (rest[18], rest[19]),
                         (rest[20], rest[21]),
                         (rest[22], rest[23]),
                         (rest[24], rest[25]),
                         (rest[26], rest[27]),
                         (rest[28], rest[29]),
                         (rest[30], rest[31]))
                ))
length的几个取值都是测试出来的,不排除漏掉的可能,另外这里也加入了干扰量nothing2,和nothing1相同,为定值,不包含信息量,直接弃掉,然后就能生成图片了。

最后再提几个细节问题,解压lzma需要安装PYLZMA库,然后进行相关调用来解压,当然嫌麻烦也可以直接将改了之后的.sc后缀改成.lzma直接用系统默认工具解压也可。至于scf,需要Pillow库,目前的切图都是矩形去切的,但是自BoomBeach发布以来,SC使用了新的图片打包技术,他可以做到把小图片放在大图片边角的缝隙里,然后打成一张大图,这个技术要比TexturePacker先进,他这么做,也导致了切图不能按照矩形来切,是不规则多边形去切的,这个技术究竟是怎么实现的目前还没有眉目。还有一点,SC的骨骼动画也都放在scf文件里,但是只是用parse_texture是只能获取每个骨骼的图片的,想要得到骨骼动画各个骨骼间的信息,得去解析别的attribute的数据,这个目前没有可参考的内容,如果单纯分析数据内容工作量是极大的。

总而言之,SC的游戏用的都是自己的引擎,而且这个引擎十分强大,为什么SC的游戏资源看着又细腻又多,但是安装包不算大,秘密都在scf里,他的引擎的相关研究我做的也是十分浅薄的,但是我相信值得借鉴的地方很多,有大牛对此有所研究的还望一起交流。

最后贴几张效果图,按照矩形切的。

BB的:



Hayday的:



COC的:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: