如何避免APK文件被反向工程

851

我正在为Android开发一个支付处理应用程序,我希望防止黑客从APK文件中访问任何资源、资产或源代码。

如果有人将.apk扩展名更改为.zip,则可以解压缩它并轻松访问所有应用程序的资源和资产,并使用dex2jar和Java反编译器,他们也可以访问源代码。反向工程 Android APK 文件非常容易 - 有关详细信息,请查看 Stack Overflow 问题 从APK文件到项目的反向工程

我已经使用了Android SDK提供的Proguard工具。当我使用签名密钥库和Proguard生成APK文件时进行反向工程,我得到混淆的代码。

但是,Android组件的名称保持不变,并且一些代码(如应用中使用的键值)保持不变。根据Proguard文档,该工具无法混淆在Manifest文件中提到的组件。

现在我的问题是:

  1. 如何完全防止反向工程Android APK?这可能吗?
  2. 如何保护所有应用程序的资源、资产和源代码,以使黑客无法以任何方式破解APK文件?
  3. 有没有办法使黑客更难或甚至不可能进行破解?我还能做什么来保护APK文件中的源代码?

134
如果你的支付处理方案依赖于客户端操作保持机密,那么听起来你可能正在使用“安全性通过混淆”的方法。 - PeterJ
47
你是否考虑过用C/C++编写代码的重要部分,并将它们作为编译后的库添加?这些库可以反汇编成汇编代码,但从汇编语言中反向工程出一个大型库非常耗时。 - Leo
64
是的,没有人能够反编译 C 代码。 (http://boomerang.sourceforge.net) - dualed
66
欢迎来到创建任何数字资产时所面临的根本问题。黑客可以深入到机器指令级别,因此如果计算机可以读取文件,则可以被盗用/复制,无论多么混淆或DRM都不能完全阻止决心强的黑客。如果需要安全性,请确保私钥永远不在源代码中,并在设计阶段知道只有隔离(远程和/或专用硬件)才能保护它们。 - Keith
17
请注意,根据您的支付处理应用程序的功能,可能会有法规和法律政策影响您的应用程序,并可能使您面临严重罚款:请参阅PCI合规性,起始网址为http://www.pcicomplianceguide.org/pcifaqs.php#11。 - Brenton Fletcher
显示剩余24条评论
32个回答

406

 1. 如何完全避免反向工程 Android APK?这个可能吗?

据我所知,没有任何技巧可以完全避免反向工程。

正如 @inazaruk 所说的那样:无论你对代码做什么,潜在的攻击者都可以以任何可行的方式修改它。你基本上不能保护你的应用程序不被修改。而且你放置的任何保护措施都可以被禁用或删除。

 2. 如何保护应用程序的所有资源、资产和源代码,使黑客无法以任何方式破解APK文件?

你可以使用各种技巧来增加破解难度。例如,使用混淆(如果是Java代码)。这通常会显著减缓反向工程的速度。

 3. 有没有办法让破解变得更加困难甚至不可能?我还能做什么来保护APK文件中的源代码?

正如每个人所说的,而且你可能已经知道的那样,没有100%的安全性。但是,对于Android而言,Google内置的起点是ProGuard。如果你有包含共享库的选项,你可以在C++中包含所需的代码以验证文件大小、集成等。如果需要在每次构建时向APK的库文件夹添加外部本机库,则可以使用以下建议。

将库放入本地库路径中,默认情况下位于项目文件夹中的“libs”中。如果为'armeabi'目标构建了本机代码,则将其放在libs/armeabi下。如果是使用armeabi-v7a构建的,则将其放在libs/armeabi-v7a下。

<project>/libs/armeabi/libstuff.so

1
对于支付交易,我使用了ISO 8585标准,现在该标准的模式是使用Java的HashMap集合的键值对。当我对apk进行反向工程时,我会得到所有的模式。有没有可能避免模式通过反向工程暴露出来?您上次提到的共享库建议在这种情况下有用吗?您有任何链接可以让我了解Android中的共享库吗? - sachin003
4
你觉得在代码中加密字符串,然后在运行时解密怎么样?如果你像其他人建议的那样在远程服务器上进行解密,就不会出现解密密钥在源代码中的问题。 - kutschkem
是的,加密确实是一种方法,但不能保证不会被破解。如果我要加密字符串以便解密它们,我需要在代码中使用一个唯一的ID。如果有人能够反编译它,那么获取唯一ID将非常容易。 - Bhavesh Patadiya
为什么你添加了编辑的东西?这都是常规内容。 - Mohammed Azharuddin Shaikh
@hotveryspicy:是的,我现在已经从答案中删除了“编辑”标记。我之所以编辑了我的答案,是因为他想知道共享库在这种情况下如何有用。 - Bhavesh Patadiya
显示剩余4条评论

140
据我所知,您无法保护/res目录中的文件,不比它们现在受到的保护更多。
但是,您可以采取措施来保护源代码,或者至少保护其功能而非全部内容。
  1. 使用ProGuard等工具。这些工具会混淆您的代码,使得反编译变得更加困难或甚至不可能。
  2. 将服务的最关键部分移出应用程序,并移到一个Web服务中,使用PHP等服务器端语言隐藏。例如,如果您有一个算法,已经花费了您一百万美元的开发成本。您显然不希望人们从您的应用程序中窃取它。移动该算法并让其在远程服务器上处理数据,使用应用程序只提供数据。或者使用NDK将它们原生地写入.so文件,这比.apk文件更不容易被反编译。我认为目前还不存在.so文件的反编译器(即使存在也不如Java反编译器好)。此外,正如@nikolay在评论中提到的那样,在服务器和设备之间进行交互时,您应该使用SSL。
  3. 在设备上存储值时,不要以原始格式存储。例如,如果您有一个游戏,并且您正在SharedPreferences中存储用户拥有的游戏货币数量。假设是10000个硬币。不要直接保存10000,而是使用像((currency*2)+1)/13这样的算法进行保存。因此,您将1538.53846154保存到SharedPreferences中,而不是10000。但是,上述示例并不完美,您需要努力找出一个不会因舍入错误等失去货币的方程。
  • 你可以对服务器端的任务进行类似的操作。举个例子,让我们看一下支付处理应用程序。假设用户必须支付 $200。不要直接向服务器发送原始的 $200 值,而是发送一系列较小、预定义的值,这些值相加等于 $200。例如,在服务器上拥有一个文件或表格,将单词与数值相对应。因此,假设 Charlie 对应于 $47John 对应于 $3。那么,你可以发送 Charlie 四次和 John 四次,而不是发送 $200。在服务器上解释它们的含义并将它们加起来。这可以防止黑客向您的服务器发送任意值,因为他们不知道哪个单词对应哪个值。作为额外的安全措施,你也可以像第三点一样设置一个公式,并每 n 天更改关键词。
  • 最后,你可以将随机无用的源代码插入到你的应用程序中,使黑客寻找一根针在干草堆中。插入包含从互联网中获取的片段的随机类,或者只包含计算类似于斐波那契数列的随机函数。确保这些类可以编译,但不被实际功能所使用。增加足够多的这些虚假类,黑客将很难找到你真正的代码。
  • 总之,没有办法百分之百地保护你的应用程序。你只能让它更难攻击,但不是不可能。你的Web服务器可能会受到攻击,黑客可能通过监视多个交易金额和你发送的关键词来找出你的关键词,黑客可能会费力地浏览源代码并找出哪些代码是虚假的。

    你只能进行反击,但永远无法获胜。


    153
    不要通过对发送到服务器的值进行技巧性处理来实现安全,而是使用SSL并正确验证服务器证书。安全靠模糊不清通常是一个坏主意。 - Nikolay Elenkov
    62
    你可以往你的应用中插入一些无用的随机源代码,但这并没有什么帮助。这只会使你的应用程序变得臃肿,并使其更难维护。 - Anirudh Ramanathan
    7
    “更难?” 是的。但是这些代码除了给你一种虚假的安全感之外,什么也没有带来。过滤掉从未执行的代码并不难,那为什么还要费心去做呢? - Anirudh Ramanathan
    20
    如果您的算法价值一百万美元,那么仅仅因为没有 .so 文件的反编译器并不意味着我无法阅读汇编代码 :) 大部分这样的方法都会陷入同一个陷阱,只是混淆而不是正确保护自己。混淆只有在攻击者不愿付出时间去跟踪时才有效,所以如果您在这些技术上构建某些东西,最好希望它们不会流行起来,否则您将陷入困境,因为突然间您的代码库将变得难以维护并需要进行重大更改。 - Phoshi
    29
    我不明白为什么这个答案得分那么高。其中第三和第四点纯粹是愚蠢的,根本不会产生任何安全作用。 - Matti Virkkunen
    显示剩余13条评论

    135
    在计算机历史上,当你给攻击者一个工作副本时,防止软件逆向工程从来没有成为可能。而且,在大多数情况下,它永远不会成为可能。因此,有一个明显的解决方案:不要把你的机密信息提供给攻击者。虽然你不能保护你的APK内容,但你可以保护任何你不分发的东西。通常这是用于激活、支付、规则执行和其他有价值的代码的服务器端软件。你可以通过不在你的APK中分发它们来保护有价值的资产。相反,建立一个服务器,响应来自你的应用程序的请求,"使用"资产(不管那意味着什么),然后将结果发送回应用程序。如果这种模式对你考虑的资产不起作用,则您可能需要重新思考你的策略。
    此外,如果你的主要目标是防止应用程序盗版,那么就别费力了。你已经在解决这个问题上花费了比任何反盗版措施节省下来的时间和金钱更多的代价。解决这个问题的回报率如此之低,以至于甚至想都不值得想一想。

    23
    如果攻击者控制硬件,他们总能以某种方式击败你的软件。任何真正需要受到保护的东西必须留在你控制的硬件上,就是这么简单。最后一段关于投资回报率的观点也很恰当。 - Daniel Pryden

    103
    第一条应用程序安全规则:任何攻击者获得不受限制的物理或电子访问权限的机器现在都属于攻击者,无论它实际上在哪里或你为它支付了多少费用。
    第二条应用程序安全规则:任何离开攻击者无法穿透的物理边界的软件现在都属于攻击者,无论你花费多少时间编写它。
    第三条规则:任何离开那些攻击者无法穿透的物理边界的信息现在都属于攻击者,无论它对你有多么有价值。
    信息技术安全的基础是建立在这三个基本原则之上的;只有真正安全的计算机才是锁在保险箱、法拉第笼和钢笼子里的那台。有些计算机大部分服务时间都处于这种状态下;每年(或更少),它们会在众多见证人面前(镜头记录房间的每一寸空间)生成可信根认证机构的私钥。
    现在,大多数计算机并非在这些环境下使用;它们通常是放置在开放区域内,通过无线电信道连接到互联网。简而言之,它们容易受到攻击,它们的软件也容易受到攻击。因此,这些计算机是不可信任的。计算机和它们的软件必须知道或做一些特定的事情才能够有用,但必须注意确保它们永远不会知道或做足以造成损害的事情(至少不会超出那台单独的机器范围之外)。
    你已经知道了这些;这就是为什么你试图保护你应用程序的代码。但问题是:混淆工具可以让代码看起来很杂乱,人类难以解读,但程序仍然需要运行;这意味着应用程序的实际逻辑流和使用的数据不受混淆的影响。只需要一点毅力,攻击者就可以简单地反混淆代码,在某些情况下,甚至不需要反混淆,因为他看到的东西除了他要找的东西之外别无选择。
    相反,你应该努力确保即使攻击者可以轻松获取明文的代码,他也无法对你的代码进行任何操作。这意味着不能在代码中硬编码秘密信息,因为一旦代码离开你开发它的建筑物,这些秘密信息就不再是秘密了。
    应该从应用程序的源代码中完全删除这些硬编码的键-值。而是放置在三个位置之一:设备上的易失性存储器,攻击者更难(但仍然不是不可能)获得离线副本;永久存储在服务器集群上,你对其访问进行严格控制;或存储在与设备或服务器无关的第二个数据存储区域中,例如物理卡或用户记忆中(这意味着它最终会转移到易失性存储器中,但不必保留太长时间)。考虑以下方案。用户从记忆中输入应用程序的凭据到设备中。不幸的是,您必须相信用户的设备没有被键盘记录器或特洛伊木马程序入侵; 在这方面,您能做的最好的事情就是实现多因素安全性,通过记住用户使用的设备的难以伪造的身份信息(MAC / IP,IMEI等),并提供至少一个额外的渠道来验证对于陌生设备的登录尝试。
    凭据一旦输入,客户端软件会进行混淆处理(使用安全哈希),并丢弃明文凭据; 它们已经发挥了作用。混淆的凭证通过安全通道发送到经过认证的证书服务器,该服务器再次哈希它们以产生用于验证登录有效性的数据。这样,客户端永远不知道与数据库值实际比较的内容,应用程序服务器永远不知道用于验证的接收到的东西背后的明文凭据,数据服务器永远不知道如何生成其存储的验证数据,并且即使安全通道被破坏,中间人只会看到无意义的东西。
    一旦验证通过,服务器会通过通道传回一个令牌。该令牌仅在安全会话中有用,由随机噪声或加密(因此可验证)的会话标识符副本组成,客户端应用程序必须作为执行任何操作的一部分将此令牌发送到同一通道上的服务器。客户端应用程序将经常这样做,因为它不能执行涉及货币、敏感数据或其他可能是有害的事情; 它必须代替向服务器请求执行此任务。客户端应用程序不会在设备本身的持久内存中编写任何敏感信息,至少不是以明文方式; 客户端可以通过安全通道向服务器请求对称密钥来加密任何本地数据,服务器将记住该密钥;在以后的会话中,客户端可以请求相同的密钥以解密用于易失性内存中的数据。该数据不会是唯一的副本; 客户端存储的所有内容也应以某种形式传输到服务器。
    显然,这使您的应用程序严重依赖Internet访问;客户端设备没有适当连接和身份验证就无法执行其基本功能。与Facebook没有区别。
    现在,攻击者想要的计算机是您的服务器,因为它而不是客户端应用/设备可以为他赚钱或导致其他人蒙受痛苦。那没关系; 您花费金钱和精力来保护服务器比试图保护所有客户端效果更好。服务器可以位于各种防火墙和其他电子安全措施后面,并且还可以在钢,混凝土,钥匙/ PIN访问以及24小时视频监视后物理上得到保护。您的攻击者需要非常复杂才能直接访问服务器,并且您应该立即(应该)知道这一点。攻击者最多只能窃取用户的手机和凭据,并使用客户端的有限权限登录服务器。如果出现这种情况,就像丢失信用卡一样,应该告知合法用户拨打一个 800 号码(最好易于记忆,不要放在他们携带手机的钱包或公文包上以免被窃),从任何可以连接到客服的电话上说明他们的手机被盗,并提供一些基本的唯一标识,账户将被锁定,攻击者可能进行的任何交易都会被回滚,攻击者又回到原点。

    1
    完美的答案!!我非常喜欢你从服务器获取数据的方式,使用一些加密令牌,我认为这几乎不可能在此之后解码。 - dharmendra
    我知道有点晚了,但是关于访问服务器部分怎么样呢?像Microsoft Azure这样的服务提供商为您提供了类似以下代码来访问他们的服务器:MobileServiceClient mClient = new MobileServiceClient("MobileServiceUrl", // 用上面的网站URL替换 "AppKey", // 用应用程序密钥替换 this)。几乎任何有权访问此代码的人都可以访问他们的服务器并进行编辑。 - edwinj
    @edwinj - 计算机科学中没有不能通过增加另一层间接性来解决的问题。您的片段提供了访问Azure移动客户端服务的基本思路;它为防止“驱动”微软前门提供了基本的安全级别。您可以进一步添加其他层,例如在任何服务调用上要求会话密钥(基本上是加密令牌),并且要获得该密钥,他们必须首先使用凭据和加密方案的组合进行身份验证。 - KeithS
    1
    最佳答案之一。 - debo.stackoverflow

    69

     1. 如何完全避免对Android APK进行逆向工程?这可能吗?

    这是不可能的。

     2.如何保护所有应用程序的资源、资产和源代码,以便黑客无法以任何方式破解APK文件?

    当有人将.apk扩展名更改为.zip时,然后解压缩,某人可以轻松获取所有资源(除了Manifest.xml) ,但使用APKtool可以获取manifest文件的真实内容。同样,不能做到。

     3.有没有办法让黑客更难甚至不可能破解?我能做什么来保护我的APK文件中的源代码?

    再次强调,这是不可能的,但你可以在一定程度上防止,也就是:

    • 从Web上下载资源并执行加密过程
    • 使用预编译的本地库(C、C++、JNI、NDK)
    • 始终执行某些哈希(MD5/SHA键或任何其他逻辑)

    即使使用Smali,人们仍然可以玩弄你的代码。总之,这是不可能的。


    9
    当操作系统是开源且可取得 root 权限时,加密并不能提供太多帮助。系统需要一个密钥才能解密 APK 并运行应用程序。如果系统有一个密钥,而我可以无限制地访问这个系统,那么我知道在哪里可以找到这个密钥并获取它。这意味着现在我也拥有了这个密钥。 - cHao
    4
    @TrevorBoydSmith:然而,正是“如何执行”部分毁了整个想法。没有直接执行加密代码的方法;在某个时刻,必须可用解密后的代码。这意味着(1)必须有一个密钥(作为管理员,我可能可以访问),并且(2)我甚至可以在RAM中找到明文副本,而不必担心加密。 - cHao
    3
    问题在于,在这种情况下,你根本无法通过提高成本来使其不值得。我们不是在谈论暴力破解密钥,而是在讨论 已经拥有 密钥 - 操作系统必须拥有密钥,而我们也拥有操作系统。唯一修复这个问题的方式就是使操作系统无法获取 root 权限。祝你好运,即便是苹果也无法做到这一点。 :) - cHao
    6
    @TrevorBoydSmith:我并没有坚持任何这样的事情。我坚持的是静态、变化、移动等等都不重要。在开源操作系统中,仅靠加密无法保护代码免受可能会反向工程它的任何人的侵害。因为我可以阅读解密的代码,无论密钥如何获取、使用和/或存储,我都能看到你是如何做到的并且复制它 -- 甚至比我反向工程一些超级秘密的应用程序代码更容易。 - cHao
    3
    @TrevorBoydSmith,既然你显然没有理解这个问题,让我们简化一下:CPU完全受我控制。CPU需要能够看到解密后的代码才能运行它。因此,我可以看到解密后的代码。Q.E.D. - Jonathon Reinhart
    显示剩余7条评论

    44

    虽然不可能100%避免Android APK的反向工程,但您可以使用以下方式来避免提取更多数据(如源代码、APK中的资产和资源):

    1. 使用 ProGuard 对应用程序代码进行混淆

    2. 使用 NDK 以 C 和 C++ 的方式将应用程序核心和安全代码放入 .so 文件中

    3. 为了保护资源,请不要将所有重要资源都包含在 APK 的 assets 文件夹中。在应用程序首次启动时下载这些资源。


    14
    第三个问题确实方便攻击者的工作。嗅探网络通信比反向工程更容易。 - totten
    为了解决第三个问题,可以对下载的内容进行加密,并/或者使用加密连接(例如SSL/TLS)。 - user925861
    2
    加密连接可防止嗅探或修改流量的人。如果用户本身是恶意的(即他拥有您的apk并尝试黑客攻击),他仍然可以通过使用您的应用程序、提取资源作为root用户来获取内容;但确实有助于防止简单的嗅探攻击。 - Kevin Lee
    此外,还有以下几点建议: 4)使用DexGuard进行更高级的混淆,但需要付费; 5)在下载应用程序时使用OBB文件来下载资源,这可以帮助减小应用程序的大小。 - Ashok Kumar

    31
    以下是您可以尝试的几种方法:
    1. 使用混淆技术和类似ProGuard的工具。
    2. 加密部分源代码和数据。
    3. 在应用程序中使用专有的内置校验和来检测篡改。
    4. 引入代码以避免在调试器中加载,即让应用程序具备检测调试器并退出/终止调试器的能力。
    5. 将身份验证作为在线服务单独处理。
    6. 使用应用多样性技术
    7. 在对设备进行身份验证之前,使用指纹识别技术,例如来自不同子系统的设备硬件签名。

    26

     1. 如何完全避免反向工程 Android APK?这可能吗?

    不可能

     2.如何保护所有应用程序的资源、资产和源代码,使黑客无法以任何方式破解APK文件?

    不可能

     3. 有什么方法可以使破解更加困难甚至不可能?我还能做什么来保护我的APK文件中的源代码?

    让破解更加困难是可能的,但实际上这将主要对那些只是在谷歌寻找破解指南的普通用户更加困难。如果有人真的想要破解你的应用程序- sooner or later,它都将被破解。


    23

     1. 如何完全避免 Android APK 的反向工程?这可能吗?

    这是不可能的。

     2. 如何保护应用程序的所有资源、资产和源代码,以使黑客无法以任何方式破解 APK 文件?

    开发人员可以采取措施,如使用诸如ProGuard之类的工具来混淆他们的代码,但直到现在,完全防止别人反编译应用程序仍然相当困难。

    这是一个非常好的工具,可以增加“反向”您的代码的难度,同时缩小代码的占用空间。

    集成ProGuard支持:ProGuard现在与SDK工具一起打包。开发人员现在可以将混淆后的代码作为发布构建的一部分进行整合。

     3. 是否有办法使黑客更难或甚至不可能入侵?我还能做些什么来保护我的APK文件中的源代码?

    在研究过程中,我了解到HoseDex2Jar。这个工具将保护您的代码免受反编译,但似乎不可能完全保护您的代码。

    以下是一些有用的链接,您可以参考它们。

  • Stack Overflow问题 如何防止反向工程Android APK文件以保护代码?

  • 21

    这里的主要问题是dex文件是否可以反编译,答案是“有点”。有像dedexersmali这样的反汇编器。

    ProGuard经过正确配置后,可以混淆您的代码。DexGuard是ProGuard的商业扩展版本,可能会更有帮助。但是,您的代码仍然可以转换为smali,并且具有反向工程经验的开发人员将能够从smali中找出您正在做什么。

    也许选择一个好的许可证,并以最好的方式通过法律执行它。


    4
    补充一下(免责声明:我不是律师)——许可证并不能在所有司法管辖区和情况下都保护应用程序的权益(例如,在欧洲的某些国家,允许反汇编以提高兼容性)。 - Maciej Piechotka
    第二个链接有一半是坏的。 - Peter Mortensen

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接