作为独立开发者,应用被破解是一件非常让人烦恼的事情。之前有同学在我的一篇博文下面问,有没有一些 Android 防破解的方法。在多次加固、破解、再加固、再破解的过程中,我也积累了一些思路和方法。这里分享一下,如果需要用到,可以作一个参考。
先说一个结论,也是我在 Stackoverflow 上面的一个国外程序员的答案,
anti_debug.png
就是说,APK 包已经在别人手上了,我们能做的不过是提升被破解的难度,如果真的遇到非常“执着”的,要破解一样被破解。如果逻辑非常值钱,那么最好还是把逻辑放到服务器上面。此外,加固也是一个可选的方案。不过目前市面上专业的加固价格并不美丽,各大平台年费从 3 万至 8 万不等,并且对个人开发者并不友好。
下面是我开发过程中为了防止应用被破解采取的一些策略。
首先,别人要破解你的软件。如果只是在自己的手机上面使用,那么他可以修改系统的一些方法进行破解。这种不在我的考虑范围内,因为他们的修改只在自己的手机上生效,构不成传播。我关注的是 APK 文件被破解的情况。
我们在加密的时候会用到一些加密或者编码方法。常见的有,非对称加密算法 RSA 等;对称加密算法 DES、3DES 和 AES 等;不可逆的加密 MD5、SHA256 等。
另外,我们会把重要的加密逻辑放到 Native 层来实现,所以一些 JNI 编程的方法也是需要的。不过,如果仅仅是用来作加密的话,对 C/C++ 的要求是没那么高的。对在 Android 中使用 JNI,可以参考我之前的文章《在 Android 中使用 JNI 的总结》。
在应用和 so 中作签名校验可以说是最基本的安全策略。在应用中作签名校验可以防止应用被二次打包。因为如果别人修改你的代码,肯定要重新打包,此时签名必然会改变。对 so 作签名校验是很有必要的,除了防止应用被打包,也可以防止你的 so 被别人盗用。
可以使用如下的代码在 java 中进行签名校验,
private static String getAppSignatureHash(final String packageName, final String algorithm) {
if (StringUtils.isSpace(packageName)) return "";
Signature[] signature = getAppSignature(packageName);
if (signature == null || signature.length <= 0) return "";
return StringUtils.bytes2HexString(EncryptUtils.hashTemplate(signature[0].toByteArray(), algorithm))
.replaceAll("(?<=[0-9A-F]{2})[0-9A-F]{2}", ":$0");
}
对于在 Native 层作签名校验,将上述方法翻译成对应的 JNI 调用即可,这里就不赘述了。
上面是签名校验的逻辑,看似美好,实际上稍微碰到有点破解的经验的就顶不住了。我之前遇到的一种破解上述签名校验的方法是,在自定义 Application 的 onCreate()
方法中读取 APK 的签名并存储到全局变量中,然后 Hook 获取应用签名的方法,并把上述读取到的真实的签名信息返回,以此绕过签名校验逻辑。
针对上述这种破解方式,我想到的第一个方法是对当前应用的 Application 类型作校验。因为他们加载 Hook 的逻辑是在自定义的 Application 中完成的,如果他们的 Application 和我们自己的 Application 类路径不一致,那么可以认定应用为破解版。
不过,这种方式作用也有限。我当时采用这种策略是考虑到有的破解者可能就是用一个脚本破解所有应用,所以改动一下可以防止这类破解者。但是,后来我也遇到一些“狠人”。因为我的软件用了 360 加固,所以如果加固壳工程的 Application 也认为是合法的。于是,我就看到了有的破解者在我的加固包之上又做了一层加固…
上述签名校验容易被 Hook 绕过,我们还可以采用另一种签名校验方法。
记得之前在《使用 APT 开发组件化框架的若干细节问题》 这篇文章中提到过,ARouter 在加载 APT 生成的路由信息的时候,一种方式是获取软件的 APK,然后从 APK 的 dex 中获取指定包名下的类文件。那么,我们是不是也可以借鉴这种方式来直接对 APK 进行签名校验呢?
首先,你可以采用下面的方法获取软件的 APK,
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
获取 APK 签名信息的方法比较多,这里我提供的是 Android 源码中的打包文件的签名代码,代码位置是:
https://android.googlesource.com/platform/tools/apksig/+/master
这样,当我们拿到 APK 之后,使用上述方法直接对 APK 的签名信息进行校验即可。
上述我们提到了一些常用的加密方法,这里介绍下我在设计软件和系统的时候是如何对用户的重要信息作加密处理的。
首先,我的应用在做用户鉴权的时候是通过服务器下发的字段来验证的。为了防止服务器返回的信息被篡改以及在本地被用户篡改,我为返回的鉴权信息增加了签名字段。逻辑是这样的,
除了上述方法之外,为服务器配置 SSL 证书也是必不可少的。现在很多云平台都会提供一年免费的 Trust Asia 的证书(到期可再续费),免费使用即可。
为了防止应用的逻辑被破解,当某些重要的信息(比如上面的鉴权信息)写入到本地的时候,除了做上述处理,我对存储到 SharedPreference 中的键也做了一层处理。主要是使用设备 ID 和键名称拼接,做 SHA256 加密之后作为键值对的键。这里的设备 ID 就是 ANDROID_ID. 虽然 ANDROID_ID 用作设备 ID 并不可靠,但是在这个场景中它可以保证大部分用户存储到本地的键值对中的键是不同的,也就增加了破解者针对某个键值对进行破解的难度。
在代码中直接使用字符串很容易被别人搜索到,一般对于重要的字符串信息,我们可以将其先转换为整数数组。然后再在代码中通过数组得到最终的字符串。比如下面的代码用来将字符串转换为 short 类型的数组,
static short[] getShortsFromBytes(String from) {
byte[] bytesFrom = from.getBytes();
int size = bytes.length%2==0 ? bytes.length/2 : bytes.length/2+1;
short[] shorts = new short[size];
int i = 0;
short s = 0;
for (byte b : bytes) {
if (i % 2 == 0) {
s = (short) (b << 8);
} else {
s = (short) (s | b);
}
shorts[i/2] = s;
i++;
}
return shorts;
}
除了上面的一些方法之外,Android 的 Jetpack 对数据安全开发了 Security 库,适用于运行 Android 6.0 和更高版本的设备。Security 库针对的是 Android 应用中读写文件的安全性。详情可以阅读官方文档相关的内容:
更安全地处理数据:https://developer.android.com/topic/security/data
混淆之后可以让别人反编译我们的代码之后阅读起来更加困难。这在一定程度上可以增强应用的安全性。默认的混淆字典是 abc
等英文字母组成,还是具有一定的可读性的。我们可以通过配置混淆字典进一步增加阅读的难度:使用特殊符号、0oO
这种相近的字符甚至 java 的关键字来增加阅读的难度。配置的方式是,
# 方法名等混淆指定配置
-obfuscationdictionary dict.txt
# 类名混淆指定配置
-classobfuscationdictionary dict.txt
# 包名混淆指定配置
-packageobfuscationdictionary dict.txt
一般来说,当我们自定义混淆字典的时候需要从下面两个方面考虑,
对于 o0O
这种虽然可读性变差了,但是代码长度相比于默认混淆字典要长一些,这会增加我们应用的包体积。我在选择混淆字典的时候使用的是比较难以记忆的字符。我把混淆字典放到了 Github 上面,需要的可以自取,
混淆字典:https://github.com/Shouheng88/LeafNote-Community/blob/main/dict.txt
下面是混淆之后的效果,
QQ截图20220216230706.png
这既可以保证包体积不会增大,又增加了阅读的难度。不过当我们反混淆的时候可能会遇到反混淆乱码的问题,比如 SDK 默认的反混淆工具就有这个问题(工具本身的问题)。
对 so 的破解,我现在也没有特别好的方法。之前我已经把一些需要高级权限的逻辑搬到了 native 层,但是最终一样被破解。如果是专业的加固,会对 so 同时做加固。我个人目前对 so 也不是特别熟,之前被破解也是因为 so 的内容被修改。后面会对 so 相关的内容做进一步学习和补充。上面提到的 so 的签名校验可以作为安全性检查之一,下面还有一些开发过程中的其他建议可以做参考。
使用布尔类型作为 native 方法的返回值的一个不好的地方是,别人破解起来会非常容易。因为对于布尔类型,它只有 true 和 false 两种情况。所以,破解者可以很容易地通过将类的方法修改为直接返回 true 或者 false 来绕开校验的逻辑。相对来说更好的方式是返回一个整数或者字符串。
如果一个方法是 native 方法,我们可以通过判断方法的属性信息来判断这个方法是否被修改。上面提到了有些 native 方法如果直接返回布尔类型,可能直接会被篡改为直接返回 true
/false
的形式。此时,破解者就把 native 方法修改为普通的方法。所以,我们可以通过判断方法的 native 特性,来判断这个方法是否被别人做了手脚。下面是一个示例方法,
val method = cls.getMethod("method", Int::class.java)
Modifier.isNative(method.modifiers)
把一套逻辑封装成一个方法对于常规业务的开发是一个好的习惯。但是把权限校验的逻辑封装到一个方法中就不一定了。因为别人只要把注意力放在你的这一个方法上面就足够了。这样,只要破解了这一个方法就可以破解你的应用中所有的安全校验逻辑。
但是如果把同一个权限校验的逻辑在所有需要做权限校验的地方都拷贝一份,后续代码维护起来也会非常困难。那么有没有比较折衷的手段,既可以实现逻辑集中维护,又可以把权限校验的逻辑分散到各个需要做权限校验的地方呢?答案是有,只不过要求应用中使用的是 kotlin 语言。
使用 inline 实现权限校验集中管理和分散调用:inline 是 kotlin 的一个关键字,效果类似于 C 语言中的内联。编译的时候会将 inline 方法中的逻辑内联到调用的地方。我们只需要将我们的权限校验的逻辑写到 inline 方法中,然后在需要鉴权的地方调用这个 inline 方法,就可以实现权限校验集中管理和分散调用。这样如果需要破解我们的校验逻辑,需要到每个地方依次进行破解。
此外,
1、权限校验的逻辑最好和业务代码交织在一起而不是分开写。原因如上,分开写别人只要破解这一个方法就够了。 2、C/C++ 层也可以尝试使用 inline 方法。
上面也说了最好的安全措施还是把重要的逻辑放到后端。不过,对于我开发的应用,因为它本身基本是离线使用的,所以,无法在操作过程中使用服务器做鉴权。对此,我使用了两个方案来让服务器参与到防破解中。
其一是,启用版本配置,在应用配置中下发强制升级信息。最初为应用设计服务器的时候我就设计了应用从后端拉取配置信息的接口。这个接口也会同时下发应用的版本信息以及升级的类型。如果是强制升级,那么会弹出一个无法取消的对话框。这样这个版本基本就无法继续使用了。通过这个配置,我们可以通过服务器配置直接禁用被破解的应用版本。
其二,在执行需要高级权限的操作的时候上报服务器。服务器通过后端存储的用户信息判断该用户是否具备该权限。如果不具备权限,那么增加一条违规记录,并记录违规用户的用户信息。后台通过可以配置的形式对单一用户进行禁用。至于这里为什么不直接对用户进行禁用的问题。正如《七武士》中的一个桥段一样,好的防守总是会留一个入口。直接禁用很容易被破解者发现并做相应处理。
另外,最好不要直接抛出异常,弹出的 toast 不要使用明文字符串。因为,上述两种方式都很容易让别人直接定位到我们校验的逻辑的位置。如果不得不抛异常,建议触发 OOM!
写了那么多东西,我也无奈,破解比反破解要容易得多,以上是我在实践过程中总结的一些基本的技巧。对于 Android 应用安全,我还有很多东西需要学习和了解。毕竟,对于应用层开发来说,安全是另一个专业领域的事情。我也只能“防君子不防小人”。后续我学习了更多的内容,做了更多的攻防战,总结更多经验之后再补充。唉,“本是同根生,相煎何太急”!
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/BQBevqzY0VUVvb7NZWGBKw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。