我正在为Android开发一款支付处理应用程序,我想防止黑客访问APK文件中的任何资源、资产或源代码。
如果有人将.apk扩展名更改为.zip,那么他们可以将其解压缩并轻松访问应用程序的所有资源和资产,并且可以使用dex2jar和Java反编译器访问源代码。对Android APK文件进行反向工程非常容易-有关更多详细信息,请参阅堆栈溢出问题从APK文件到项目的反向工程。
我已经使用了Android SDK提供的Proguard工具。当我对使用签名密钥库和Proguard生成的APK文件进行反向工程时,我得到了混淆的代码。
然而,Android组件的名称保持不变,一些代码(如应用程序中使用的关键值)保持不变。根据Proguard文档,该工具无法混淆Manifest文件中提到的组件。
现在我的问题是:
如何完全防止Android APK的反向工程?这可能吗?我如何保护应用程序的所有资源、资产和源代码,使黑客无法以任何方式破解APK文件?有没有办法让黑客攻击变得更加困难甚至不可能?我还能做什么来保护APK文件中的源代码?
AFAIK,您不能再保护/res目录中的文件,因为它们现在受到保护。
然而,您可以采取一些步骤来保护源代码,或者至少保护它的功能(如果不是所有功能)。
使用ProGuard等工具。这些将使您的代码变得模糊,如果不是不可能的话,在反编译时更难阅读。将服务中最关键的部分从应用程序中移出,并隐藏在服务器端语言(如PHP)后面的Web服务中。例如,如果你有一个花费了一百万美元编写的算法。你显然不希望人们从你的应用程序中窃取它。移动算法,让它在远程服务器上处理数据,然后使用应用程序简单地向它提供数据。或者使用NDK将它们本机写入.so文件,这比apk更不容易被反编译。我认为目前还没有一个.so文件的反编译器(即使存在,也不如Java反编译器)。此外,正如@nikolay在评论中提到的,在服务器和设备之间进行交互时,应该使用SSL。在设备上存储值时,不要以原始格式存储。例如,如果您有一个游戏,并且您正在SharedPreferences中存储用户的游戏货币数量。假设是10000枚硬币。不要直接保存10000,而是使用((货币*2)+1)/13这样的算法保存。因此,您将1538.53846154保存到SharedPreferences中,而不是10000。然而,上面的例子并不完美,你必须努力想出一个不会因舍入误差而失去货币价值的公式。您可以对服务器端任务执行类似的操作。现在举个例子,让我们来看看你的支付处理应用。假设用户必须支付200美元。不要向服务器发送一个200美元的原始值,而是发送一系列较小的预定义值,这些值加起来可以达到200美元。例如,在服务器上有一个文件或表,它将单词与值等同。假设查理相当于47美元,约翰相当于3美元。所以,你不需要寄200美元,而是可以寄查理四次,约翰四次。在服务器上,解释它们的含义并将其相加。这可以防止黑客向您的服务器发送任意值,因为他们不知道哪个单词对应什么值。作为一种额外的安全措施,您可以使用类似于第3点的公式,并每隔n天更改关键字。最后,你可以在你的应用程序中插入随机无用的源代码,这样黑客就可以大海捞针了。插入包含来自互联网的片段的随机类,或者只是用于计算诸如斐波那契序列之类的随机事物的函数。确保这些类可以编译,但不会被应用程序的实际功能所使用。添加足够多的这些虚假类,黑客将很难找到真正的代码。
总而言之,没有办法100%保护您的应用程序。你可以让它更难,但不是不可能。你的网络服务器可能会被破坏,黑客可以通过监控多个交易金额和你发送的关键字来找出你的关键字,黑客可以费力地通过源代码找出哪个代码是伪代码。
你只能反击,但永远不会赢。
完全避免逆向工程是不可能的,但通过在内部使其更加复杂,攻击者可能更难看到应用程序的清晰操作,这可能会减少攻击向量的数量。
如果应用程序处理高度敏感的数据,则存在各种技术,这些技术会增加代码反向工程的复杂性。一种技术是使用C/C++来限制攻击者容易进行的运行时操作。有大量的C和C++库非常成熟,易于集成,Android提供JNI。
攻击者必须首先绕过调试限制,才能在较低级别上攻击应用程序。这增加了攻击的复杂性。Android应用程序应在应用程序清单中设置Android:debuggable=“false”,以防止攻击者或恶意软件轻易地进行运行时操作。
跟踪检查–应用程序可以确定调试器或其他调试工具当前是否正在跟踪它。如果被跟踪,应用程序可以执行任意数量的可能的攻击响应动作,例如丢弃加密密钥以保护用户数据、通知服务器管理员或其他此类类型的响应以试图自卫。这可以通过检查进程状态标志或使用其他技术来确定,如比较ptrace附件的返回值、检查父进程、进程列表中的黑名单调试器或比较程序不同位置的时间戳。
优化-为了隐藏高级数学计算和其他类型的复杂逻辑,利用编译器优化可以帮助混淆目标代码,使其不易被攻击者分解,从而使攻击者更难理解特定代码。在Android中,这可以通过使用NDK本地编译的库更容易地实现。此外,使用LLVM混淆器或任何保护器SDK将提供更好的机器代码混淆。
剥离二进制文件–剥离本机二进制文件是一种有效的方法,可以增加攻击者查看应用程序低级功能组成所需的时间和技能水平。通过剥离二进制文件,二进制文件的符号表被剥离,这样攻击者就无法轻松调试或反向工程应用程序。您可以参考GNU/Linux系统上使用的技术,如sstriping或使用UPX。
最后,您必须了解混淆和ProGuard等工具。
如果我们想让逆向工程(几乎)不可能实现,我们可以将应用程序放在一个高度防篡改的芯片上,该芯片在内部执行所有敏感的内容,并与一些协议通信,使控制GUI在主机上成为可能。即使是防篡改芯片也不能100%防裂;他们只是设定了比软件方法高得多的标准。当然,这是不方便的:该应用程序需要一些小的USB疣来保持芯片插入设备。
这个问题并没有揭示出想要如此谨慎地保护这个应用程序的动机。
如果目的是通过隐藏应用程序可能存在的任何安全缺陷(已知或其他)来提高支付方法的安全性,那么这是完全错误的。如果可行的话,安全敏感位实际上应该是开源的。您应该尽可能方便任何审查您的应用程序的安全研究人员查找这些信息并仔细检查其操作,并与您联系。付款应用程序不应包含任何嵌入证书。也就是说,不应该有任何服务器应用程序仅仅因为设备具有来自工厂的固定证书而信任该设备。支付交易应仅凭用户的凭证进行,使用正确设计的端到端认证协议,避免信任应用程序、平台或网络等。
如果目标是防止克隆,除了防篡改芯片之外,你无法采取任何措施来保护程序不被反向工程和复制,从而使某人将兼容的支付方法融入自己的应用程序,从而导致“未经授权的客户”。有一些方法可以使开发未经授权的客户端变得困难。一种方法是基于程序完整状态的快照创建校验和:所有状态变量,所有内容。GUI、逻辑等等。克隆程序将不具有完全相同的内部状态。当然,它是一个状态机,具有类似的外部可见状态转换(可以通过输入和输出观察到),但几乎没有相同的内部状态。服务器应用程序可以询问程序:您的详细状态是什么?(即给我一个所有内部状态变量的校验和)。这可以与虚拟客户端代码进行比较,虚拟客户端代码在服务器上并行执行,并经过真正的状态转换。第三方克隆人必须复制真实程序的所有相关状态变化,才能给出正确的响应,这将阻碍其开发。