从“去除签名验证”说起

数天前坊间传闻有人破解了太极,最后发现它其实是使用 MT 管理器的“去除签名验证”这功能绕过了太极的签名检查,然后把界面做了一定的调整;并没有对太极的内核做出什么实质性的破解。既然如此,今天我就跟大家聊聊,这个“去除签名验证”到底是怎么回事;然后给大家一些思路,如何去防范自己的应用被某些不怀好意的人莫名其妙地给篡改了。

首先我们需要知道的是,Android 应用程序的安装包 APK 文件都必须有自己的签名,没有签名的 APK 是无法直接安装到系统的。这个签名实际上是“数字签名”,它是现代密码学非对称加密的重要应用,是当今互联网安全的基石。

这里不打算仔细说明数字签名等技术性的内容,简单理解,你可以认为开发者通过公开的规则根据 APK文件的内容给 APK 文件打上一个烙印,由非对称加密保证这种形状的烙印只有开发者自己能打出来;Android 系统在安装应用的时候会通过这种公开的规则计算烙印的形状,然后检查 APK 文件和它的烙印是否对应,不对应就安装失败。一旦有人对 APK 文件的内容有涂改,那么这个烙印就会发生变化,导致系统在安装的时候发现烙印形状不对而拒绝安装。

那么如果非要修改 APK 的内容并且能成功安装到系统,我们可以有若干种方法:

修改 APK 之后,我们打一个跟原来开发者一模一样的烙印。这种方式除非开发者自己把他打烙印的方式告诉你,不然基本不可能。这是由非对称加密本身保证的,也许有人觉得很神奇;打个比方,我告诉你一个数 10000323402614653,你能用简单的办法告诉我这是哪两个大于1的数字相乘得到的吗?对我来说就是一个简单的乘法,几秒钟搞定;对不知道这两个数的人来说,找出这两个数可能要花几天甚至基本不可能。

我们把这个烙印抹除掉,根据那个公开的规则打上一个自己的烙印;这样修改 APK的时候安装到系统,系统会发现我们新打上去的这个烙印是符合规则的,允许我们安装。

破坏掉系统的验证机制,让系统不要验证这个烙印对不对,不管三七二十一都给我装上去(这就是Xposed 插件:核心破解的原理)。

也许有人看到这里就会发现,这个系统的验证机制貌似没有什么用啊?它只能保证 APK 文件必须有合法的签名,并没有办法保证 APK 文件不被篡改!你看,别人要修改你的APK,只需要把你的名字抹掉,然后签上他自己的名字就可以了。怎么办呢?

我们必须在应用程序的内部自己计算 APK文件的签名,然后跟我们自己的签名信息做验证。一旦 APK 文件被修改,那么计算出来的签名不一致,我们就会发现 APK 被篡改,这时候采取其他的处理措施。

Android 系统的这一整套签名机制,包含了从开发工具链(打包过程),Android系统本身(安装时签名验证)以及运行时(动态获取程序签名)因此,在“自己计算APK文件的签名”这里,系统提供了公开的接口,不再需要我们自己去计算。这个公开的接口就是 PackageManager 的 getPackageInfo 等一系列方法。我们只需要在应用程序内部通过此接口获取签名然后验证签名就能发现应用程序是否被篡改了。

如果整个事情有这么简单,那这个世界就没那么好玩儿了。道高一尺,魔高一丈。

假设,如果别人修改了你的 APK 文件内容之后:

直接删去你做签名验证的部分代码,然后重新签名。

修改getPackageInfo的返回值,让他返回他自己的签名。

这样,你的签名验证就形同虚设了。因此,针对上面第一个破解之法,我们最好:

不要在 Java 代码中验证签名,最好在 native 验证;并且为了防止别人直接删除 native 函数调用,必须在 app 功能的核心逻辑的主要链路之中做验证;这样 native 函数调用删除之后,app 压根无法工作。

针对第二条,我们有很多种应对方案:

不使用系统提供的getPackageInfo接口,改用自己写代码解析和验证签名。

检测getPackageInfo调用是否被篡改,如果情况不对也视为被篡改。

目前很多所谓的“一键去除签名验证”就是通过动态代理修改getPackageInfo的返回值。说来也巧,大约在三年前,我在自己的个人博客上详细介绍了这种方式的实现原理,其中部分代码如下图:

当我打开所谓的“破解版太极”的时候,我发现有部分代码异常熟悉,仔细查阅一番之后发现应该是使用 MT管理器 的“去除签名验证”,其中部分代码应该源自这里:

看到这里的时候我觉得有点搞笑,拿着俺的代码去破解俺的应用。

好了扯远了,动态代理 Package service 的方式可以修改getPackageInfo的返回值,进而欺骗过应用程序内部的签名验证。针对这种破解方法,有一种很简单的方案,那就是检测 package service 是否被动态代理。一旦我们发现 package service 被代理,我们就可以认为有人在搞事情。

我们上面还提到,除了通过 getPackageInfo 获取签名信息之外,我们还可以通过自己计算签名来验证签名。实际上,有很多 app 也是通过这种方式验证签名的,有没有破解之法呢?答案是肯定的,因为只要计算签名,就必须读取 APK 文件,我们可以拦截 APK 文件读取的过程,让他返回原来正确的 apk 文件就能绕过这个过程。MT管理器的“去除签名验证”增强版应该就是这种方案的具体实现。那么,如何反掉这种“拦截文件读取过程”的实现呢?方案自然是有的,因为一般的方式是拦截 libc 中的 io 函数调用,只要我们采取更底层的方式读取文件,就能拿到未被篡改过的数据;或者直接检测关键函数是否被hook就可以。

总结一下,为了避免应用被轻松破解,我们应该这么做:

必须在App内部自己读取签名然后验证,并且保证这部分逻辑存在于 app 核心功能的主要链路中;最好把这部分逻辑使用 native 改写避免被一锅端。

验证 app 内部 package / activity 等关键 service 是否被篡改。

直接读取apk文件手动解析签名,然后做签名对比。

验证系统 io 关键函数是否被 inline hook。

今天说了这么多有关“破解”的东西,实际上我是很反感把这种技术到处滥用的。特别是把这种“一键破除签名”做成傻瓜式的功能提供给用户使用的行为,我真是不敢恭维。很多开发者不会也压根没有时间去对自己的应用做完善的签名验证,这种傻瓜式工具的出现直接导致了破解成本的大大降低,很多基本没入门的小白开发者就能拿着这种所谓的工具去明目张胆地破解各种应用。不仅损害了原作者的利益,还让破解者背负潜在的法律风险。

另外,我得给大家普及一下法律知识。破解应用并传播给他人使用是属于违反刑法的行为:

第五条 以营利为目的,实施刑法第二百一十七条所列侵犯著作权行为之一,违法所得数额在三万元以上的,属于“违法所得数额较大”;具有下列情形之一的,属于“有其他严重情节”,应当以侵犯著作权罪判处三年以下有期徒刑或者拘役,并处或者单处罚金:

(一)非法经营数额在五万元以上的;

(二)未经著作权人许可,复制发行其文字作品、音乐、电影、电视、录像作品、计算机软件及其他作品,复制品数量合计在一千张(份)以上的;

(三)其他严重情节的情形。

以营利为目的,实施刑法第二百一十七条所列侵犯著作权行为之一,违法所得数额在十五万元以上的,属于“违法所得数额巨大”;具有下列情形之一的,属于“有其他特别严重情节”,应当以侵犯著作权罪判处三年以上七年以下有期徒刑,并处罚金:

(一)非法经营数额在二十五万元以上的;

(二)未经著作权人许可,复制发行其文字作品、音乐、电影、电视、录像作品、计算机软件及其他作品,复制品数量合计在五千张(份)以上的;

(三)其他特别严重情节的情形。

刑法跟民法不同,那可不是闹着玩儿的。注意到没有,就算不是以盈利为目的,复制1000份以上就算严重情节了,做了个破解版别兴冲冲地去分享,一不小心你就得进去了。

好了今天就到这里了,大家晚安!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20190828A00L1G00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券