APK가 서명되었는지 또는 '디버그 빌드'인지 확인하는 방법은 무엇입니까?


121

내가 아는 한, 안드로이드에서 "release build"는 서명 된 APK입니다. 코드에서 확인하는 방법 또는 Eclipse에 일종의 비밀 정의가 있습니까?

웹 서비스 데이터에서 ListView 항목 채우기를 디버그하려면 이것이 필요합니다 (아니요, logcat은 옵션이 아님).

내 생각:

  • 응용 프로그램은 android:debuggable이지만 어떤 이유로 신뢰할 수없는 것 같습니다.
  • 서명 된 APK를 테스트하는 데 동일한 기기를 사용하고 있으므로 하드 코딩 기기 ID는 좋은 생각이 아닙니다.
  • 코드 어딘가에서 수동 플래그를 사용합니까? 그럴듯하지만 언젠가는 변경하는 것을 확실히 잊을 것이고 모든 프로그래머는 게으르다.

롤백 된 Phil의 편집. 이것은 프로그램이 시장에 합법적으로 배포되는지 여부에 대한 질문이 아닙니다. 에 대한 질문은 여전히 ​​"디버그 모드"에있는 프로그램입니다.
Im0rtality

이렇게하는 것이 가장 쉬운 방법입니다 : stackoverflow.com/a/23844716/2296787
MBH

답변:


80

응용 프로그램이 디버그 또는 릴리스 인증서를 사용하여 빌드되었는지 확인하는 다른 방법이 있지만 다음 방법이 가장 좋습니다.

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;
}

6
여러 가져 오기 일치를 해결하는 데 도움이되도록 여기에 사용 된 클래스는 java.security.cert.X509Certificate, java.security.cert.CertificateExceptionandroid.content.pm.Signature입니다. 다른 모든 수업은 나에게 여러 경기를 제공하지 않습니다
Christian García

1
이러한 가져 오기로 수정 된 답변입니다. 감사!
Cory Petosky 2013

애플리케이션 클래스의 onCreate 메소드에서 실행하기에 충분히 효율적입니까?
안드로이드 개발자

실행 시간을 기록하지 않았지만 내 앱에서 사용하고 있으며 효율성에 문제가 없습니다.
Omar Rehman

효율성을 위해 결과를 캐시 할 수 있습니다.
ftvs

138

디버깅 가능한 플래그를 확인하려면 다음 코드를 사용할 수 있습니다.

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

Kotlin :

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

자세한 내용은 Android LVL 애플리케이션 보안을 참조하십시오 .

또는 Gradle을 올바르게 사용하는 경우 BuildConfig.DEBUGtrue인지 false 인지 확인할 수 있습니다 .


이것은 여전히 ​​매니페스트의 android : debuggable을 확인하는 것 같습니다.
xster

2
첫 번째는 Manifest 디버깅 가능 여부를 테스트하며 더 이상 사용되지 않습니다. 두 번째는 라이브러리에 사용할 수 없으며 lib에는 자체 BuildConfig가 있습니다. Lib를 사용하는 앱의 BuildConfig를 가져올 수 없습니다. 표시된 답 이에 대한 "확인"입니다
크리스토프

이 답변은 도서관 프로젝트 또는 응용 프로그램 프로젝트에 관계없이 모든 경우에서 작동합니다.
Lavekush 아그라 왈

131

의해 답변을 Mark Murphy

가장 간단하고 장기적인 솔루션은를 사용하는 것 BuildConfig.DEBUG입니다. 이것은 디버그 빌드를위한 boolean값입니다 true. false그렇지 않으면 :

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

8
이 접근 방식의 유일한 단점은 라이브러리 프로젝트 (aar 's)에서 작동하지 않는다는 것입니다. 라이브러리가 빌드되면 false가되므로 라이브러리를 사용하는 애플리케이션이 디버그 모드에 있더라도이 검사는 라이브러리 코드 내에서 false가됩니다.
Vito Andolini 2015 년

24

APK정적으로 확인하려면 다음을 사용할 수 있습니다.

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

디버깅 할 수 없는 0경우 출력 됩니다 .APK1


3
이것은 최종 APK를 확인하는 유일한 솔루션입니다. 다른 응답은 소스가 있다고 가정합니다.
Guillermo Tobar

1
aapt여기에 산다/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt
Casey

21

늦었을지도 모르지만 사용 BuildConfig.DEBUG


이제 사용하기에 안전합니까? 몇 가지 문제가 있다는 기사가 있습니다. digipom.com/be-careful-with-buildconfig-debug
android 개발자

이것이 최고의 답변입니다!
Peter Fortuin 2014 년

타사 라이브러리를 작성하고 컴파일 타임에 BuildConfig의 패키지를 모르는 경우에는 그렇지 않습니다.
Sam Dozor 2014-07-11

샘, 자세히 설명해 주 시겠어요?
Agamemnus

10

먼저 이것을 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;
    }
}

1
신뢰할 수 있기 때문에이 답변을 선호합니다. 그래도 애플리케이션 ID가 다르기 때문에 새 "허용 된 Android 애플리케이션"항목을 내 Google Maps API 키에 추가해야했습니다.
Baz

5

디버그 빌드도 다른 키로 서명됩니다. Eclipse에 의해 자동으로 생성되며 인증서는 1 년 동안 만 유효합니다. 무엇이 문제 android:debuggable입니까? 을 사용하여 코드에서이 값을 가져올 수 있습니다 PackageManager.


3

언급 할 가치가있는 또 다른 옵션입니다. 디버거가 연결된 경우에만 일부 코드를 실행해야하는 경우 다음 코드를 사용하십시오.

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

0

로 해결 android:debuggable. 항목에 대한 디버그 플래그가 if (m.debug && !App.isDebuggable(getContext()))항상 평가 되는 레코드에 저장되지 않는 경우 항목을 읽을 때 버그였습니다 false. 내 잘못이야.


13
나는 이것이 1 년이 넘었다는 것을 알고 있지만 @Omar Rehman의 대답이 아니라 수락해야합니다. 당신이 게시 한 것은 결국 당신이 한 일이지만, 당신이 요청한 질문에 실제로 대답하지 않는 반면, Omar의 해결책은 그렇게하는 것처럼 보입니다. 즉, 그는 인정받을만한 가치가 있음을 의미합니다.
mah

7
@Mah- 문제를 스스로 해결 한 지 거의 1 년 후에 게시 된 답변을 받아들이지 않는다는 이유로 누군가를 괴롭히는 것은 완전히 부적절 합니다! 그리고 그것은 당신이 지명하려는 대답이 그들이 함께 갔던 것보다 얼마나 더 복잡한 지 무시하는 것입니다. 사실은 질문에 대답합니다. 왜냐하면 질문은 버그에 의해 유발되어 깃발이 신뢰할 수 없다는 잘못된 인상을 받았기 때문입니다. .
Chris Stratton

4
@ChrisStratton 내 반응이 괴롭힘이라고 생각한다면 인터넷을 많이 읽지 않는 것 같습니다. 나는 당신이했던 것처럼 당신의 반대 견해를 코멘트에 게시 할 수있는 당신의 권리를지지하며, 그 결과 나는이 질문에 대한 나의 코멘트와 다른 포스트들을 검토했고, 나는 나의 원래 코멘트를지지합니다 : 포스터는 합법적 인 질문을했습니다. 누군가가 정확하게 대답했습니다. 자신의 '답변'에 따르면 원래 질문은 처음에 묻고 싶은 것이 아닙니다. APK의 서명 (출시 또는 디버그)은 매니페스트와 정확히 관련이 없습니다.
mah

3
@mah- 귀하가 제기하는 구별이 중요한지 여부를 포함하여 실제 응용 프로그램 요구를 충족시키는 것이 무엇인지 결정하는 것은 질문자 가 아닙니다.이 경우에는 분명히 그렇지 않습니다. 더 중요한 것은 귀하가 지명하려는 답변이 실제 요구 사항이 충족 된 지 거의 1 년 후에 게시되었다는 사실을 완전히 간과하고 있다는 것 입니다. 그것은 괴롭힘을 구성하는 대부분의 년 후 그들의 수락을 변경하기 위해 돌아 오지 않은 누군가를 처벌하는 것입니다.
Chris Stratton 2013

3
@ChrisStratton 실제 질문을 물어 보는 것은 질문자에게 달려 있습니다 ./ 그는 대답을 원합니다.이 경우에는 수행되지 않은 일입니다. 게시 된 내용에 실제로 답변하는 답변을했을 때 간과 한 것이 맞습니다 만, 벌칙은 전혀없고 내 의견의 합리적 표현 밖에 없습니다. 당신은 내가 깡패 여기에 행동하고 생각한다면, 나는 강력하게 불량 내 댓글 플래그를 요청합니다. 그러나 그렇게하기 전에 여기에서 자신의 게시물을 검토 할 수 있습니다.
mah

0

현재 사용중인 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 프로세스의 경우).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.