如何检查 APK 是不是已签名或“调试构建”?

Posted

技术标签:

【中文标题】如何检查 APK 是不是已签名或“调试构建”?【英文标题】:How to check if APK is signed or "debug build"?如何检查 APK 是否已签名或“调试构建”? 【发布时间】:2011-10-28 11:26:32 【问题描述】:

据我所知,在 android 中,“release build”是签名的 APK。如何从代码中检查它 或者 Eclipse 是否有某种秘密定义?

我需要它来调试从 Web 服务数据填充 ListView 项目(不,logcat 不是一个选项)。

我的想法:

应用程序的android:debuggable,但由于某种原因,这看起来不可靠。 硬编码设备 ID 不是一个好主意,因为我使用相同的设备来测试签名的 APK。 在代码中的某处使用手动标志?有道理,但肯定会在某个时候忘记更改,而且所有程序员都很懒惰。

【问题讨论】:

滚动支持 Phil 的编辑。这不是关于程序是否在市场上合法分发的问题。问题是程序是否仍处于“调试模式”。 这种方式是最简单的方法:***.com/a/23844716/2296787 【参考方案1】:

要检查可调试标志,您可以使用以下代码:

boolean isDebuggable =  ( 0 != ( getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE ) );

科特林:

val isDebuggable = 0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE

更多信息请见Securing Android LVL Applications。

或者,如果您正确使用 Gradle,您可以检查 BuildConfig.DEBUG 是真还是假。

【讨论】:

这似乎仍然检查清单的 android:debuggable 第一个测试 Manifest debuggable,已弃用。第二个对于库是不可能的,lib 将有它自己的 BuildConfig - 无法导入正在使用 Lib 的 App 的 BuildConfig。因此标记的答案是“好的” 无论库项目还是应用程序项目,此答案都适用于所有情况。【参考方案2】:

马克·墨菲回答

最简单、最好的长期解决方案是使用BuildConfig.DEBUG。这是一个 boolean 值,对于调试版本将是 true,否则为 false

if (BuildConfig.DEBUG) 
  // do something for a debug build

【讨论】:

这种方法的唯一缺点是它不适用于库项目(aar's)。构建库时,这将导致 false,因此即使使用该库的应用程序处于调试模式,此检查也会导致库代码中的 false。【参考方案3】:

有不同的方法可以检查应用程序是使用调试证书还是发布证书构建,但以下方法对我来说似乎是最好的。

根据 Android 文档 Signing Your Application 中的信息,调试密钥包含以下主题可分辨名称:“CN=Android Debug,O=Android,C=US”。我们可以使用此信息来测试包是否使用调试密钥进行签名,而无需将调试密钥签名硬编码到我们的代码中。

给定:

import android.content.pm.Signature;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

您可以通过这种方式实现 isDebuggable 方法:

private static final X500Principal DEBUG_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
private boolean isDebuggable(Context ctx)

    boolean debuggable = false;

    try
    
        PackageInfo pinfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(),PackageManager.GET_SIGNATURES);
        Signature signatures[] = pinfo.signatures;

        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        for ( int i = 0; i < signatures.length;i++)
           
            ByteArrayInputStream stream = new ByteArrayInputStream(signatures[i].toByteArray());
            X509Certificate cert = (X509Certificate) cf.generateCertificate(stream);       
            debuggable = cert.getSubjectX500Principal().equals(DEBUG_DN);
            if (debuggable)
                break;
        
    
    catch (NameNotFoundException e)
    
        //debuggable variable will remain false
    
    catch (CertificateException e)
    
        //debuggable variable will remain false
    
    return debuggable;

【讨论】:

为了帮助解决多个导入匹配,这里使用的类是java.security.cert.X509Certificatejava.security.cert.CertificateExceptionandroid.content.pm.Signature。所有其他课程都不会为我呈现多个匹配项 使用这些导入编辑了答案。谢谢! 在应用类的onCreate方法上运行是否足够高效? 我没有记下执行时间,但我一直在我的应用程序中使用它并且没有任何效率问题。 可以缓存结果以提高效率。【参考方案4】:

如果你想静态检查APK,你可以使用

aapt dump badging /path/to/apk | grep -c application-debuggable

如果APK 不可调试,则输出0,如果是,则输出1

【讨论】:

这是唯一的解决方案,用于验证最终的 apk。其他响应假定您有来源。 aapt 住在这里/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt【参考方案5】:

也许晚了,但 iosched 使用 BuildConfig.DEBUG

【讨论】:

现在可以安全使用吗?有一篇文章说它有一些问题:digipom.com/be-careful-with-buildconfig-debug 这是最好的答案! 如果你正在编写一个第三方库并且在编译时不知道 BuildConfig 的包。 Sam,你能详细说明一下吗?【参考方案6】:

首先将其添加到您的 build.gradle 文件中,这也将允许同时运行调试和发布版本:

buildTypes 
    debug 
        applicationIdSuffix ".debug"
    

添加这个方法:

public static boolean isDebug(Context context) 
    String pName = context.getPackageName();
    if (pName != null && pName.endsWith(".debug")) 
        return true;
     else 
        return false;
    

【讨论】:

我更喜欢这个答案,因为它可靠。我确实需要在我的 Google Maps API 密钥中添加一个新的“允许的 Android 应用程序”条目(因为应用程序 ID 不同)。 我也使用过这个,但我建议删除 if 并返回 pName != null && pName.endsWith(".debug")【参考方案7】:

调试版本也已签名,只是使用不同的密钥。它由 Eclipse 自动生成,证书有效期仅为一年。 android:debuggable 有什么问题?您可以使用 PackageManager 从代码中获取此值。

【讨论】:

【参考方案8】:

另一个选项,值得一提。如果您需要仅在附加调试器时执行某些代码,请使用此代码:

if (Debug.isDebuggerConnected() || Debug.waitingForDebugger())  
    //code to be executed 

【讨论】:

【参考方案9】:

已通过android:debuggable 解决。这是读取项目的错误,在某些情况下,项目上的调试标志没有存储在记录中,得到if (m.debug &amp;&amp; !App.isDebuggable(getContext())) 总是评估为false。我的错。

【讨论】:

我知道这已经有一年多了,但是你应该接受@Omar Rehman 的回答,而不是这个。虽然您发布的内容是您最终所做的,但它并没有真正回答您提出的问题,而 Omar 的解决方案似乎做到了这一点,这意味着他应该得到认可。 @ChrisStratton 如果你认为我的回应是欺凌,我认为你没有太多阅读互联网。我支持您像您一样在评论中发表反对观点的权利,因此我已经审查了我的评论和这个问题中的其他帖子,我坚持我原来的评论:发帖人提出了一个合法的问题并由某人正确回答。根据他自己的“答案”,他最初的问题并不是他想问的...... APK 的签名(发布或调试)与清单的关系完全为零。 @ChrisStratton 也由提问者提出他/她想要回答的实际问题——在这种情况下没有做的事情。您是正确的,我在做出实际回答所发布内容的答案时忽略了,但是根本没有惩罚,只有我的意见的合理表达。如果您认为我在这里扮演恶霸,我强烈要求您将我的评论标记为滥用。但是,在此之前,您可能希望在此处检查您自己的帖子。【参考方案10】:

我目前正在使用的 Kotlin 解决方案:

@SuppressLint("PackageManagerGetSignatures")
@Suppress("DEPRECATION")
fun isSigned(context: Context?): Boolean 
    return (context?.packageManager?.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES)?.signatures?.firstOrNull()?.toByteArray()
            ?.let 
                return@let CertificateFactory.getInstance("X.509").generateCertificate(ByteArrayInputStream(it))
             as? X509Certificate)
            ?.issuerDN
            ?.name
            ?.contains("O=Android", ignoreCase = false) ?: true

这样我仍然可以在调试中登录,并且这些将报告给 Crashlytics(例如,用于 QA 流程)

【讨论】:

以上是关于如何检查 APK 是不是已签名或“调试构建”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我无法将调试器附加到已签名的 apk 进程?

有没有办法检查应用签名是调试还是发布?

检测 qt 是不是在运行时运行调试构建

如何判断 Android 应用的 Apk 签名是不是一致?

使用flavor签名配置覆盖调试构建类型签名配置

已签名的 APK 网络问题 - 未签名的 apk 能够读取 API,但已签名的 apk 继续加载