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

     入行以来最初接触到的游戏是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的数据分块,然后对比和老版本文件的区别,一步步解开新的加密的,相比之下实在是微不足道。在这里我会把我自己发现的东西分享一下,懒得把整块代码贴上来了,用到哪贴到哪,和链接里的代码对比一下就会明白。

[python] view plain copy
  1. attributes = [  
  2.     None,  
  3.     'texture',  
  4.     'shape',  
  5.     'movie_clip',  
  6.     None,  
  7.     None,  
  8.     None,  
  9.     'text_field',  
  10.     'matrix',  
  11.     'color_transform',  
  12.     'movie_clip',  
  13.     None,  
  14.     'movie_clip',  
  15.     'timeline_offset',  
  16.     'movie_clip',  
  17.     'movie_clip',  
  18.     'movie_clip',  
  19.     'movie_clip',  
  20.     'shape',  
  21. ]  
        attributes的变化,这个用于确定是scf文件究竟是哪类数据的,我们要处理的类型是shape(这些名字都是国外那哥们儿命名的,我懒得改,按他的来)。
[python] view plain copy
  1. attribute = attributes[tag_type]  
  2. result = getattr(self'parse_' + attribute)(data, tag_type)  
  3. getattr(self, attributes[tag_type] + 's').append(result)  
        attributes就在这里用到,其实就是说新的shape的attribute变成了18(猥琐地一个个试出来的,不排除有再增加的可能性),检查到属性为18,就认为这块数据是图片内容,然后调用parse_shape方法。

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

[python] view plain copy
  1. if _ == 0:  
  2.     (command_type, nothing1, length) = struct.unpack('<BhI', f.read(7))  
  3. else:  
  4.     (nothing1, length) = struct.unpack('<BI', f.read(5))  

        两处nothing1就是干扰变量,这个值经过大量测试,都是1,所以不存在包含信息的可能,直接弃掉,另一处是解析data的地方:
[python] view plain copy
  1. if length == 50:  
  2.     (tex_id, nothing2, *rest) = struct.unpack('<BB8I8h', shape_data)  
  3.     commands.append(ShapeDrawBitmapCommand(  
  4.         texture=self.textures[tex_id],  
  5.         xys=((rest[0]/-20, rest[1]/-20),  
  6.             (rest[2]/-20, rest[3]/-20),  
  7.             (rest[4]/-20, rest[5]/-20),  
  8.             (rest[6]/-20, rest[7]/-20)),  
  9.         uvs=((rest[8], rest[9]),  
  10.             (rest[10], rest[11]),  
  11.             (rest[12], rest[13]),  
  12.             (rest[14], rest[15]))  
  13.     ))  
  14. elif length == 62:  
  15.     (tex_id, nothing2, *rest) = struct.unpack('<BB10I10h', shape_data)  
  16.     commands.append(ShapeDrawBitmapCommand(  
  17.         texture=self.textures[tex_id],  
  18.         xys=((rest[0]/-20, rest[1]/-20),  
  19.              (rest[2]/-20, rest[3]/-20),  
  20.              (rest[4]/-20, rest[5]/-20),  
  21.              (rest[6]/-20, rest[7]/-20),  
  22.              (rest[8]/-20, rest[9]/-20)),  
  23.         uvs=((rest[10], rest[11]),  
  24.              (rest[12], rest[13]),  
  25.              (rest[14], rest[15]),  
  26.              (rest[16], rest[17]),  
  27.              (rest[18], rest[19]))  
  28.     ))  
  29. elif length == 74:  
  30.     (tex_id, nothing2, *rest) = struct.unpack('<BB12I12h', shape_data)  
  31.     commands.append(ShapeDrawBitmapCommand(  
  32.         texture=self.textures[tex_id],  
  33.         xys=((rest[0]/-20, rest[1]/-20),  
  34.              (rest[2]/-20, rest[3]/-20),  
  35.              (rest[4]/-20, rest[5]/-20),  
  36.              (rest[6]/-20, rest[7]/-20),  
  37.              (rest[8]/-20, rest[9]/-20),  
  38.              (rest[10]/-20, rest[11]/-20)),  
  39.         uvs=((rest[12], rest[13]),  
  40.              (rest[14], rest[15]),  
  41.              (rest[16], rest[17]),  
  42.              (rest[18], rest[19]),  
  43.              (rest[20], rest[21]),  
  44.              (rest[22], rest[23]))  
  45.     ))  
  46. elif length == 86:  
  47.     (tex_id, nothing2, *rest) = struct.unpack('<BB14I14h', shape_data)  
  48.     commands.append(ShapeDrawBitmapCommand(  
  49.         texture=self.textures[tex_id],  
  50.         xys=((rest[0]/-20, rest[1]/-20),  
  51.              (rest[2]/-20, rest[3]/-20),  
  52.              (rest[4]/-20, rest[5]/-20),  
  53.              (rest[6]/-20, rest[7]/-20),  
  54.              (rest[8]/-20, rest[9]/-20),  
  55.              (rest[10]/-20, rest[11]/-20),  
  56.              (rest[12]/-20, rest[13]/-20)),  
  57.         uvs=((rest[14], rest[15]),  
  58.              (rest[16], rest[17]),  
  59.              (rest[18], rest[19]),  
  60.              (rest[20], rest[21]),  
  61.              (rest[22], rest[23]),  
  62.              (rest[24], rest[25]),  
  63.              (rest[26], rest[27]))  
  64.     ))  
  65.   
  66. elif length == 98:  
  67.     (tex_id, nothing2, *rest) = struct.unpack('<BB16I16h', shape_data)  
  68.     commands.append(ShapeDrawBitmapCommand(  
  69.         texture=self.textures[tex_id],  
  70.         xys=((rest[0]/-20, rest[1]/-20),  
  71.              (rest[2]/-20, rest[3]/-20),  
  72.              (rest[4]/-20, rest[5]/-20),  
  73.              (rest[6]/-20, rest[7]/-20),  
  74.              (rest[8]/-20, rest[9]/-20),  
  75.              (rest[10]/-20, rest[11]/-20),  
  76.              (rest[12]/-20, rest[13]/-20),  
  77.              (rest[14]/-20, rest[15]/-20)),  
  78.         uvs=((rest[16], rest[17]),  
  79.              (rest[18], rest[19]),  
  80.              (rest[20], rest[21]),  
  81.              (rest[22], rest[23]),  
  82.              (rest[24], rest[25]),  
  83.              (rest[26], rest[27]),  
  84.              (rest[28], rest[29]),  
  85.              (rest[30], rest[31]))  
  86.     ))  
        length的几个取值都是测试出来的,不排除漏掉的可能,另外这里也加入了干扰量nothing2,和nothing1相同,为定值,不包含信息量,直接弃掉,然后就能生成图片了。

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

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

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

        BB的:


        Hayday的:


        COC的:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值