Làm cách nào để kiểm tra xem APK đã được ký hay "bản dựng gỡ lỗi"?


121

Theo như tôi biết, trong android "bản dựng phát hành" là APK đã ký. Làm thế nào để kiểm tra nó từ mã hoặc Eclipse có một số loại định nghĩa bí mật?

Tôi cần điều này để gỡ lỗi việc điền các mục ListView từ dữ liệu dịch vụ web (không, logcat không phải là một tùy chọn).

Suy nghĩ của tôi:

  • Ứng dụng của android:debuggable, nhưng vì một số lý do mà trông không đáng tin cậy.
  • ID thiết bị mã hóa cứng không phải là ý kiến ​​hay vì tôi đang sử dụng cùng một thiết bị để thử nghiệm các APK đã ký.
  • Sử dụng cờ thủ công ở đâu đó trong mã? Hợp lý, nhưng chắc chắn sẽ quên thay đổi vào một lúc nào đó, cộng với tất cả các lập trình viên đều lười biếng.

Bản chỉnh sửa của Phil được hỗ trợ cuộn. Đây không phải là câu hỏi về việc chương trình có được phân phối hợp pháp trên thị trường hay không. Câu hỏi đặt ra là chương trình vẫn ở "chế độ gỡ lỗi".
Im0rtality

Bằng cách này là cách dễ nhất để làm như vậy: stackoverflow.com/a/23844716/2296787
mbH

Câu trả lời:


80

Có nhiều cách khác nhau để kiểm tra xem ứng dụng đang được xây dựng bằng cách sử dụng gỡ lỗi hoặc chứng chỉ phát hành, nhưng cách sau có vẻ tốt nhất đối với tôi.

Theo thông tin trong tài liệu Android Ký ứng dụng của bạn , khóa gỡ lỗi chứa tên phân biệt chủ đề sau: " CN = Android Debug, O = Android, C = US ". Chúng tôi có thể sử dụng thông tin này để kiểm tra xem gói được ký bằng khóa gỡ lỗi mà không cần mã hóa cứng chữ ký khóa gỡ lỗi vào mã của chúng tôi hay không.

Được:

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

Bạn có thể triển khai phương thức isDebuggable theo cách này:

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
Để giúp giải quyết nhiều kết hợp nhập, các lớp được sử dụng ở đây là java.security.cert.X509Certificate, java.security.cert.CertificateExceptionandroid.content.pm.Signature. Tất cả các lớp khác làm nhiều trận đấu không có mặt đối với tôi
Christian García

1
Đã chỉnh sửa câu trả lời với những lần nhập này. Cảm ơn!
Cory Petosky

nó có đủ hiệu quả để chạy trên phương thức onCreate của lớp ứng dụng không?
nhà phát triển android

Tôi không ghi nhận thời gian thực hiện, nhưng tôi đã sử dụng nó trong ứng dụng của mình và không gặp bất kỳ vấn đề nào về hiệu quả.
Omar Rehman

Kết quả có thể được lưu vào bộ nhớ cache để tăng hiệu quả.
ftvs

138

Để kiểm tra cờ có thể gỡ lỗi, bạn có thể sử dụng mã sau:

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

Kotlin:

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

Để biết thêm thông tin, vui lòng xem Bảo mật ứng dụng LVL của Android .

Ngoài ra, nếu bạn đang sử dụng Gradle đúng cách, bạn có thể kiểm tra xem BuildConfig.DEBUGlà đúng hay sai.


nó có vẻ như điều này vẫn kiểm tra của manifest Android: debuggable
xster

2
Thử nghiệm đầu tiên cho Tệp kê khai có thể gỡ lỗi, điều này không được dùng nữa. Cái thứ hai là không thể cho các thư viện, lib sẽ có BuildConfig của riêng nó - không thể nhập BuildConfig của Ứng dụng đang sử dụng Lib. Vì câu trả lời được đánh dấu là "ok"
Christoph

Câu trả lời này sẽ hoạt động trong mọi trường hợp bất kể dự án thư viện hay dự án ứng dụng.
Lavekush Agrawal

131

Trả lời bởi Mark Murphy

Giải pháp lâu dài đơn giản và tốt nhất là sử dụng BuildConfig.DEBUG. Đây là một booleangiá trị sẽ truedành cho bản dựng gỡ lỗi, falsenếu không thì:

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

8
Hạn chế duy nhất của phương pháp này là nó sẽ không hoạt động trong các dự án thư viện (aar's). Khi các thư viện được xây dựng, điều này sẽ dẫn đến false, vì vậy ngay cả khi một ứng dụng sử dụng thư viện đang ở chế độ gỡ lỗi, việc kiểm tra này sẽ dẫn đến false trong mã thư viện.
Vito Andolini

24

Nếu bạn muốn kiểm tra APKtĩnh, bạn có thể sử dụng

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

Kết quả này xuất ra 0nếu APKkhông thể gỡ lỗi và 1nếu có.


3
đây là giải pháp duy nhất để xác minh qua apk cuối cùng. Các phản hồi khác cho rằng bạn có nguồn.
Guillermo Tobar

1
aaptsống ở đây/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt
Casey

21

Có thể muộn, nhưng sử dụng iosched BuildConfig.DEBUG


bây giờ nó là an toàn để sử dụng? có một bài báo nói rằng nó có một số vấn đề: digipom.com/be-careful-with-buildconfig-debug
android phát triển

Đây là câu trả lời tốt nhất!
Peter Fortuin

không nếu bạn đang viết thư viện của bên thứ 3 và không biết gói BuildConfig tại thời điểm biên dịch.
Sam Dozor

Sam, bạn có thể nói rõ hơn về điều này?
Agamemnus

10

Trước tiên, hãy thêm tệp này vào tệp build.gradle của bạn, điều này cũng sẽ cho phép chạy song song các bản dựng gỡ lỗi và phát hành:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Thêm phương pháp này:

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

1
Tôi thích câu trả lời này hơn, vì nó đáng tin cậy. Tuy nhiên, tôi cần phải thêm mục nhập "ứng dụng Android được phép" mới vào khóa API Google Maps của mình (vì id ứng dụng khác nhau).
Baz

5

Một bản dựng gỡ lỗi cũng được ký, chỉ với một khóa khác. Nó được tạo tự động bởi Eclipse và chứng chỉ của nó chỉ có giá trị trong một năm. Có vấn đề gì với android:debuggable? Bạn có thể nhận được giá trị này từ mã bằng cách sử dụng PackageManager.


3

Một lựa chọn khác, đáng nói. Nếu bạn chỉ cần thực thi một số mã khi trình gỡ lỗi được đính kèm, hãy sử dụng mã này:

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

0

Đã giải quyết với android:debuggable. Đó là lỗi khi đọc mục mà trong một số trường hợp, cờ gỡ lỗi trên mục không được lưu trữ trong hồ sơ if (m.debug && !App.isDebuggable(getContext()))luôn được đánh giá false. Lỗi của tôi.


13
Tôi nhận ra rằng điều này đã hơn một năm, tuy nhiên bạn nên chấp nhận câu trả lời của @Omar Rehman, không phải câu trả lời này. Mặc dù những gì bạn đã đăng là những gì cuối cùng bạn đã làm, nhưng nó không thực sự trả lời câu hỏi bạn đã hỏi, trong khi giải pháp của Omar dường như làm được điều đó, có nghĩa là anh ấy xứng đáng được công nhận.
mah

7
@Mah - bắt nạt ai đó vì không chấp nhận câu trả lời được đăng gần một năm sau khi họ tự giải quyết vấn đề của mình là hoàn toàn không phù hợp ! Và điều đó thậm chí còn bỏ qua câu trả lời bạn chỉ định phức tạp hơn câu trả lời mà họ đã chọn - thực tế là câu trả lời cho câu hỏi được hỏi, vì câu hỏi được nhắc bởi một lỗi dẫn đến ấn tượng nhầm lẫn rằng lá cờ không đáng tin cậy .
Chris Stratton

4
@ChrisStratton nếu bạn cho rằng phản hồi của tôi là bắt nạt, tôi nghĩ bạn không đọc internet nhiều. Tôi ủng hộ quyền của bạn để đăng quan điểm phản đối của bạn trong một bình luận như bạn đã làm, và kết quả là tôi đã xem xét nhận xét của mình và các bài đăng khác trong câu hỏi này, và tôi giữ nguyên nhận xét ban đầu của mình: người đăng đã đặt một câu hỏi hợp pháp và được ai đó trả lời chính xác. Dựa trên "câu trả lời" của chính anh ấy, câu hỏi ban đầu của anh ấy không phải là điều anh ấy muốn hỏi ngay từ đầu ... chữ ký của APK (phát hành hoặc gỡ lỗi) hoàn toàn không có trên tệp kê khai.
mah

3
@mah - tùy thuộc vào người hỏi, không phải bạn quyết định điều gì đáp ứng nhu cầu ứng dụng thực tế của họ, bao gồm nếu sự khác biệt mà bạn nêu ra là một trong những tầm quan trọng - hoặc, dường như trong trường hợp này, không. Quan trọng hơn, bạn hoàn toàn bỏ qua rằng câu trả lời bạn đề cử đã được đăng gần một năm sau khi nhu cầu thực tế của họ được thỏa mãn . Sẽ trừng phạt ai đó nếu không quay lại để thay đổi chấp nhận của họ trong vòng một năm sau đó, điều này cấu thành hành vi bắt nạt.
Chris Stratton

3
@ChrisStratton cũng tùy thuộc vào người hỏi để hỏi câu hỏi thực tế mà anh ta / anh ta muốn có câu trả lời - điều mà trong trường hợp này chưa được thực hiện. Bạn đúng là tôi đã bỏ qua khi câu trả lời thực sự trả lời những gì đã được đăng được đưa ra, tuy nhiên không có hình phạt nào cả, chỉ có một sự bày tỏ ý kiến ​​hợp lý của tôi. Nếu bạn nghĩ rằng tôi đang đóng vai trò kẻ bắt nạt ở đây, tôi mạnh mẽ yêu cầu bạn cờ nhận xét của tôi đối với sự lạm dụng. Tuy nhiên, trước khi làm điều đó, bạn có thể muốn kiểm tra các bài đăng của chính mình tại đây.
mah

0

Giải pháp trong Kotlin mà tôi đang sử dụng hiện tại:

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

bằng cách đó, tôi vẫn có thể ĐĂNG KÝ trong gỡ lỗi và những điều đó sẽ được báo cáo cho Crashlytics (ví dụ: đối với quy trình QA)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.