Android apk反编译学习【天天酷跑】

639 查看

微信游戏出来之后,网上出现很多破解教程和攻略,修改最多的有无敌打飞机,无限弹药,刷分数,天天酷跑刷金币等。看了下破解方法不外乎以下两种:

  1. 反编译游戏apk包,修改smali字节码,重新打包。
  2. 暂停运行时软件,在内存中查找关键游戏数值,直接修改内存数据。

(此外听朋友说起其他破解方式,如修改本地cache文件中的程序数据,但前提得是数据没有存储于服务端)

工作时长期疲于应对应用层开发,早该学习一些基础的破解和防破解技术,也有助于保护自己开发的应用,且最近痴迷《天天酷跑》,天天努力赶超小伙伴,遂尝试将其作为“破解”对象娱乐一下(这可比玩出高分再向小伙伴炫耀有趣多啦),由于之前没有任何破解经验,并不知道能破解到什么程度,抱着学习的态度一步步尝试之。

动手之前先学习前辈经验,这里有一篇打飞机破解文章,讲解详细清晰:http://www.blogjava.net/zh-weir/archive/2013/08/14/402821.html

归纳一下文章内容,微信客户端和其游戏是平台与插件关系,整个打飞机游戏是一个jar包,位于微信apk的assets目录下,微信通过动态加载的方式运行,其中包含classex.dexresso库(用JD-GUI打开这个jar包可以看到)。动态加载时会拷贝该jar包到data目录,解压后将其中的资源释放出来。拷贝之前将进行一次签名校验,如果jar包被修改过,签名校验将无法通过,想要破解游戏,必须对jar包中的代码进行修改,那就得找到绕过签名校验的方法。

开始之前先学习一下签名校验的原理和如何绕过签名校验。

Android签名生成步骤和原理:

我们在App开发完成后将其编译为apk包,这个时候包中没有签名信息,如果直接通过adb install安装到手机上,将会报错:[INSTALL_PARSE_FAILED_NO_CERTIFICATES]

解压apk包可以发现未签名的apk中不包含META-INF目录,该目录在签名后生成,其中包含签名信息相关的3个文件CERT.RSACERT.SFMANIFEST.MF,签名过程就是这3个文件的生成过程。

签名第一步:遍历apk中所有文件(entry),逐一生成SHA-1数字签名,然后通过Base64编码,写入MANIFEST.MF文件。

vim查看MANIFEST.MF文件内容如下:

Manifest-Version: 1.0
Created-By: 1.0 (Android)

Name: res/drawable-hdpi/m03_0.png
SHA1-Digest: Odo4l7R6YcmMl9HtbhLpb8hChts=

Name: res/layout/shop_fav_item.xml
SHA1-Digest: CVvGtpAo1cLWo7OQfR3tZQwYOY4=
......

第二步:使用SHA1-RSA算法,用私钥对MANIFEST.MF摘要文件签名,并对其中每个文件摘要签名,生成CERT.SF文件

vim查看其内容如下:

Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA1-Digest-Manifest: 4pIv24erc5QG+i/kLOOhG6n+EdY=

Name: res/drawable-hdpi/m03_0.png
SHA1-Digest: 9ZYIfLFplSPcDEIxLJl0JUzmJLs=

Name: res/layout/shop_fav_item.xml
SHA1-Digest: nqQ+nd6yZn3sB5LfHltWOYlXJMA=

......

(SHA1-Digest-Manifest:是对MANIFEST.MF文件的摘要签名后的值)

第三步:生成CERT.RSA文件,其中记录了RSA公钥,加密算法等信息用于签名校验,该文件是二进制格式,有兴趣可以研究下。

插入学习一下RSA加密和签名原理:

RSA有2种应用方式,加密和验证,这里只用到了验证,为了使整个实现过程更加清晰明了,先学习下其原理,这里我们可以假定加密和验证过程无法被破解,通过私钥加密的信息唯有公钥才能解密。

RSA加密过程(以客户端A发送一条消息给客户端B为例,其中A持有公钥,B持有私钥):

  1. 客户端A消息原文:msg
  2. 将msg映射为整数(实际上分为很多段整数):Int(msg)
  3. 用公钥进行加密:En( Int(msg) ) )
  4. ————发送加密后的信息————
  5. 客户端B收到加密信息:En( Int(msg) )
  6. 用私钥解密:De( En( Int( msg) ) ) = Int(msg)
  7. 还原msg:UnInt( Int(msg) ) = msg

(通过公钥加密的信息唯有私钥才能解密,可以确保信息不会泄漏)

RSA签名与验证过程(客户端A发送一条签名后消息给客户端B,B验证该消息是否为A发出或者未被篡改,其中A持有私钥,B持有公钥):

  1. 客户端A消息原文:msg
  2. 对msg提取摘要:Dig(msg)
  3. 对摘要Base64编码:Base64( Dig(msg) )
  4. 用私钥进行签名(把摘要当作已经加密后的内容,用私钥解密):Sign( Base64( Dig(msg) ) )
  5. ————发送消息和签名————
  6. 客户端B收到信息和签名:msg 和 Sign( Base64( Dig(msg) ) )
  7. 对msg提取摘要:Dig(msg)
  8. 对摘要Base64编码:Base64( Dig(msg) )
  9. 将摘要当作未加密过的内容,用公钥加密:En( Base64( Dig(msg) ) )
  10. 签名验证,即判断En( Base64( Dig(msg) ) ) 和Sign( Base64( Dig(msg) ) )是否相等。(这里的Sign跟上面的De一模一样)

(通过公钥加密的信息唯有私钥才能解密,可以验证这条消息肯定是A发出来的)

在Android里面签名验证的实现:

覆盖安装时用旧版本的公钥对新安装apk包中MANIFEST.MF进行加密,生成新的MANIFEST.MF'文件,再和原始的MANIFEST.MF文件对比所有的摘要信息是否一致,以此确保是否为同一个机构发布的升级app。(实际当然不是生成新的MANIFEST.MF'文件,可以这么理解)

了解签名原理之后,再结合一下签名机制的应用场景:

  1. 个人开发者或机构想要通过Android Market发布app,必须使用官方提供的keystore证书为程序签名,作用是防止你的app被恶意覆盖安装。(app升级时若签名不一致将不能覆盖安装)。
  2. 运行时程序自检签名(如果签名不符可以直接crash,强行退出,插件调用不起来等)。

回到如何绕过打飞机插件签名验证问题,首先破解微信之后不可能用私钥重新签名,所以覆盖安装不考虑,卸载正版微信即可。其次则是考虑如何绕过打飞机插件加载时的签名校验,作者尝试利用ANDROID-8219321漏洞(修改插件包内容而签名校验时计算出的校验值保持不变),但利用该漏洞将会在插件包中产生2个classex.dex文件,导致文件改动,而打飞机插件包的文件名中巧妙的包含了该jar包的md5值,任何jar包的改动均将导致md5值发生改变,微信插件加载时将进行签名+插件md5值双重校验,要想绕过只有修改微信的smali code,找到md5校验方法然后屏蔽掉(利用漏洞的话签名就不用屏蔽)。

作者因为没找到md5校验代码,最终采用了系统app+屏蔽签名校验的方法(系统app签名校验时只会校验mainfest.xml文件,但是必须安装在system/app目录下,手机需要root然后将system分区mount为可写),也即jar包可以修改,修改后计算出新的md5并且重命名即可, 安装时微信的签名校验依旧可以通过,同时屏蔽微信对插件的签名校验,大功告成!接来下就是对打飞机插件的smali code一顿狂改了。

仔细分析了好久发现作者在绕过签名这里有些多余,既然不可能使用私钥签名破解后的app,绕过签名的意义就在于可以覆盖安装正版微信(签名保持“不变”,利用ANDROID-8219321漏洞,或者安装到system/app目录下作为系统应用,然而既然决定安装到system/app目录下,考虑安装时微信的签名校验依旧可以通过这个问题纯属多余)。我们完全可以使用自签名,给破解后的微信app和插件用自己的证书签名。这样微信app和插件均可以随意修改之。

从签名的验证和一系列绕过办法可以发现比较靠谱的安全策略是程序运行时使用navite代码进行签名自检,比如将1.0版本apk中MANIFEST.MF文件的签名值存储在云端,程序运行时通过native代码将用公钥解密后的值发送至云端进行对比,只有这种方法破解难度较大。

搞清楚了打飞机的破解原理,接下来尝试破解天天酷跑,然而实际情况却有很多不一样。

首先天天酷跑和打飞机两者的插件机制不一样,天天酷跑是一个独立应用,和微信属于app间相互跳转关系。此外天天酷跑的游戏代码几乎全在native层实现(NPEEngine),基本上无法修改,令人沮丧,刚开始的时候并没有意识到这一点,但是无论无何还是要玩一玩的。

使用apktool反编译之后,可以修改其中的res资源,ogg声音,配置文件,smali code。

然后重新打包,使用自签名,重新安装。运行之后通过微信好友连接玩不了-_-!,应该是签名校验或者其他的什么校验没通过(没有进一步研究),但是可以连接qq好友,qq使用sso登录,校验竟然通过了,也算是发现个漏洞。

游戏大部分资源都在assets目录中,其中的图片,配置,地图等都是二进制格式的无法修改。只有少量几个json和xml的配置文件可以改改,但都涉及不到核心,只能简单修改下默认角色的重力,弹跳速度等,动态性做得太好,很多资源都是运行时从服务端load,比如新增的角色,升级信息,宠物,坐骑等。

其次学习了下smali code,这个简单又好玩,最后附上修改的截图,只能拿去逗一下小伙伴...(另外换了下icon,恶搞的声音,角色弹跳属性)

启动时加个对话框:

邀请或分享给好友时,修改展示文案,并把下载链接替换为破解app的下载地址