如何强化 iPhone/Android 应用程序,使其难以逆向工程?

信息安全 应用安全 移动的 攻击预防 逆向工程
2021-08-12 02:09:39

这些是我想到的以下目标:

  1. 使应用程序难以破解,因为二进制文件将包含一些秘密令牌。
  2. 如果它仍然可以被破解,应用程序是否可以通过任何方式告诉某人或它自己它已被破解(例如检查一些校验和或证书或为应用程序执行它的操作系统)并采取一些措施?当程序在内存中时,应用程序二进制文件可能会被感染,或者恶意代码也可能被注入,因此建议的方法应该能够处理这两种情况。

我正在使用这些平台:

  • iPhone [操作系统:iOS,语言:Objective-C]
  • Android [操作系统:Android,语言:Java]

请不要考虑成本和开发时间,因为这对其受众来说是一个关键的应用程序。如果我能回答两个平台的问题,那就太好了。感谢您的时间和关注。

编辑 1这些保护类是否没有记录?我在 Apple 文档下看不到任何文档。有人知道这些是否有用吗?

编辑 2:Mac OS X 的 codesign 带有一个-o kill用于签署二进制文件的选项,这样每当二进制文件与证书中的校验和不匹配时,操作系统就会杀死它。这里详细介绍[Ctrl-F 'option flags']。iOS有这样的东西吗?

编辑3:我正在考虑结合使用这些想法。因此,如果有人来这里看,这可能会有所帮助。单向函数不经意转移

4个回答

基本上,目标“1”是行不通的;逆向工程效果太好了。使用 Android 版本将特别容易,因为 Java 非常易于反编译(在 Android 上,使用特定的字节码格式,因为 Java 实现是Dalvik,而不是“真正的”Java,但原则仍然存在)。但是,iPhone 版本也不会那么难(Objective-C 编译为 ARM 代码,这不是最难反汇编和弄清楚的事情)。

目标“2”也不起作用。首先,逆向工程和数据提取只是被动的,因此应用程序没有改变——因此,应用程序无法检测到这种逆向工程是否发生。此外,很容易修改一些应用程序代码以绕过任何内部测试;这是自 1980 年代初(至少)以来用于解决游戏复制保护问题的第一大破解技术。

您可以期望的最好的结果是将用户分为两类:越狱的和没有越狱的。在未越狱的 iPhone 上,只能安装和执行 Apple 尽职尽责地批准的应用程序,因此这可以防止安装经过修改的非官方应用程序,该应用程序会以您不希望的方式使用您的“秘密令牌”。但越狱正是人们安装未经批准的应用程序所做的事情,而且似乎相对容易。

您还可以通过经常更新您的秘密令牌并不断发布您的应用程序的新版本来尝试使攻击者的生活更加艰难。这就是游戏机供应商所做的,不断有新的“固件版本”,用户必须下载这些版本才能玩较新的游戏、观看较新的电影或连接到游戏玩家网络。这也让用户感到烦恼,因此这样做并不便宜。

如果您可以将“秘密令牌”安排为特定于用户的,事情对您来说会容易得多 - 这样,如果令牌值被不当提取,您仍然可以在您的服务器上将其列入黑名单(我假设您的应用程序使用连接到某处服务器的令牌)。

请注意,在已部署的应用程序中对数据保密,或者在未能做到这一点的情况下可靠地检测到对所述数据的欺诈性使用,是全球所有游戏编辑和电影发行商的圣杯。上次听说,索尼、华纳等大玩家还没有找到,也不是没有尝试。

如何强化 iPhone/Android 应用程序,使其难以逆向工程?

防止软件的逆向工程是一个难题。

在硬件辅助较弱的通用计算平台(PC、iPhone 或 Android 手机)上,在相当长的一段时间(数月)内防止应用软件的逆向工程是一个非常困难的问题。

通常,代替保护整个应用程序,一种常见的替代方法是尝试保护应用程序使用的关键数据并放弃保护整个应用程序。

[1] 使应用程序难以破解,因为二进制文件将包含一些秘密令牌。

您虽然在这里暴露了“保护数据”的概念。您真正希望应用程序保护的是一些关键数据:秘密令牌。

当应用程序无法保护自己时,您如何保护数据?

您将责任传递给操作系统。操作系统可以提供两种类型的保护:访问控制和加密。当我们允许或拒绝读取数据或执行特定操作时,我们使用的大多数保护措施都是访问控制。

访问控制将操作和想要执行该操作的标识符作为输入。它的输出是批准或拒绝执行操作的权限。访问控制使用一组或规则或表来确定是否应允许标识符执行操作。

例如:Alice 在电脑上有一个游戏 FunGame。计算机的访问控制有一个规则,只有 Alice 可以运行 FunGame。Bob 坐在电脑前,尝试运行 FunGame。计算机的访问控制将“run FunGame”和“Bob”作为输入。基于“只有 Alice 可以运行 FunGame”的规则,访问控制的输出被拒绝。

访问控制提供了一种直接且通用的方式来决定允许或拒绝,而密码学提供了一种更间接的允许或拒绝方法。通过加密和解密,我们可以转换数据,使数据有意义或没有意义。

加密和解密至少需要一个加密密钥。密钥的值成为访问控制,具有“允许任何拥有密钥的人获取可理解的数据并使其难以理解”和“允许任何拥有密钥的人获取不可理解的数据并使其易于理解”这两条规则。使数据难以理解只会保护数据不被理解。个人可能仍然能够读取数据,但不会以他们理解的形式出现。

请注意,键而不是参与者的标识符成为关键组件。任何拥有密钥的人,无论他们如何获得它,都可以访问数据。这使得存储密钥成为问题。如果您将密钥与数据存储在同一系统上,则您为任何人提供了访问您数据的方法。这就是为什么密码短语需要记忆的原因。记住密码短语将密钥(密码短语)存储在您的脑海中,而不是系统中。

因此,为了保护您的令牌,您可以使用操作系统的访问控制,或者您可以加密令牌,或两者兼而有之。如果某人的操作系统不强制执行其自己的访问控制(越狱的 iPhone 或 root 的 Android 手机),那么您就不能依赖操作系统的访问控制。但是,只要用于加密令牌的密钥与加密令牌不在同一系统上,即使操作系统受到破坏,您的令牌仍将受到保护。

然而,将密钥远离相关系统是另一个难题,称为密钥管理。

[2] 如果它仍然可以被破解,应用程序有什么方法可以告诉某人或它自己它已被破解(比如检查一些校验和或证书或为应用程序执行的操作系统)并采取一些措施?

当程序在内存中时,应用程序二进制文件可能会被感染或恶意代码也可能被注入,因此建议的方法应该能够处理这两种情况

检查一段数据以查看它自上次检查以来是否未更改的概念称为完整性检查。评估数据块状态的一种有效方法称为加密哈希。要执行完整性检查,您必须首先计算一段数据的加密哈希,然后保护生成的哈希值。稍后在同一条数据上重新计算加密哈希。将重新计算的哈希值与受保护的哈希值进行比较。如果两个值相同,则数据不变。如果两个哈希值不同,则数据已被更改。

您可以将存储中的应用程序二进制文件视为一段数据并对其执行完整性检查。您还可以将内存中的应用程序二进制文件视为一条数据,并对其进行完整性检查。

检查正在运行的程序以查看它是否按设计处理的概念称为证明。检查运行代码使用的所有内存通常是不可行的,因此证明通常检查一些更简单的东西,例如某些数据集的值或运行程序的状态及其状态之间的转换。证明很难实现,因此在商业软件中没有广泛使用。

虽然它们是用于 Obj-C、Java/Dalvik、IPA、JAR 和 APK 的一些非常智能的自我修改(例如打包)和自我检查代码方法——但我相信它们都像它们一样容易被颠覆ASM、C、C++、PE 和 ELF 表亲。逆向工程师的能力在 2012 年是超凡脱俗的。

我的建议是将您的所有知识产权保留在服务器端,并仔细验证和授权每一个动作或不作为,类似于非 rootkit MMORPG 捕获作弊者的方式(例如,但不完全是 PunkBuster)。

移动应用程序应该过于简单,并且永远不应该信任用户——就像每个客户端应用程序一样。

试图真正防止逆向工程可能看起来是徒劳的,但我认为这里的想法是让攻击者更难做到这一点。以下是您作为开发人员可以采取的一些预防措施,以更好地防御 Android 上的反编译:

  1. 用原生代码编写一些核心功能(Android NDK)
  2. 使用加密
  3. 将逻辑转移到服务器端
  4. 使用各种形式的混淆(拆分变量、将标量提升为对象、更改变量生命周期、更改编码、重新排序实例变量、打乱标识符、拆分/折叠/合并数组、修改继承关系)这些只是数据混淆示例。看看控制混淆。你会注意到这些方法促进了草率的代码并增加了维护的开销——但是,逆向你的应用程序的人会讨厌你。

这些只是几个例子,我强烈建议你阅读这篇关于 Java 混淆技术的论文。这些技术可以适应任何语言。祝你好运。

1 ‘‘A Taxonomy of Obfuscating Transformations,’’ Computer Science Technical Reports 148 (1997), https://researchspace.auckland.ac.nz/handle/2292/3491.