Bagaimana cara memeriksa apakah APK ditandatangani atau "debug build"?

121

Sejauh yang saya tahu, di android "rilis build" adalah APK bertanda tangan. Bagaimana cara memeriksanya dari kode atau apakah Eclipse memiliki semacam definisi rahasia?

Saya memerlukan ini untuk men-debug mengisi item ListView dari data layanan web (tidak, logcat bukan opsi).

Pikiran saya:

  • Aplikasi android:debuggable, tetapi untuk beberapa alasan itu tidak terlihat dapat diandalkan.
  • ID perangkat pengkodean keras bukanlah ide yang baik, karena saya menggunakan perangkat yang sama untuk menguji APK yang ditandatangani.
  • Menggunakan tanda manual di suatu tempat dalam kode? Masuk akal, tetapi suatu saat pasti akan lupa untuk mengubahnya, ditambah lagi semua pemrogram malas.
Im0rtality
sumber
Hasil edit Phil yang didukung gulungan. Ini bukan pertanyaan tentang program yang didistribusikan secara legal di pasar atau tidak. Ini pertanyaan tentang apakah program masih dalam "mode debug".
Im0rtality
Cara ini adalah cara termudah untuk melakukannya: stackoverflow.com/a/23844716/2296787
MBH

Jawaban:

80

Ada cara berbeda untuk memeriksa apakah aplikasi dibuat menggunakan debug atau sertifikat rilis, tetapi cara berikut menurut saya paling baik.

Menurut info dalam dokumentasi Android Menandatangani Aplikasi Anda , kunci debug berisi nama yang dibedakan subjek berikut: " CN = Android Debug, O = Android, C = US ". Kita dapat menggunakan informasi ini untuk menguji apakah paket ditandatangani dengan kunci debug tanpa tanda tangan kunci debug hardcode ke dalam kode kita.

Diberikan:

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

Anda dapat mengimplementasikan metode isDebuggable dengan cara ini:

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;
}
Omar Rehman
sumber
6
Untuk membantu menyelesaikan beberapa pencocokan impor, kelas yang digunakan di sini adalah java.security.cert.X509Certificate, java.security.cert.CertificateExceptiondan android.content.pm.Signature. Semua kelas lain tidak menyajikan beberapa pertandingan untuk saya
Christian García
1
Jawaban yang diedit dengan impor ini. Terima kasih!
Cory Petosky
apakah cukup efisien untuk dijalankan pada metode onCreate pada kelas aplikasi?
Pengembang android
Saya belum mencatat waktu eksekusi, tetapi saya telah menggunakannya di aplikasi saya dan tidak memiliki masalah dengan efisiensi.
Omar Rehman
Hasilnya dapat disimpan dalam cache untuk efisiensi.
ftvs
138

Untuk memeriksa flag debuggable, Anda dapat menggunakan kode ini:

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

Kotlin:

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

Untuk informasi lebih lanjut, silakan lihat Mengamankan Aplikasi LVL Android .

Atau, jika Anda menggunakan Gradle dengan benar, Anda dapat memeriksa apakah BuildConfig.DEBUGbenar atau salah.

Blundell
sumber
sepertinya ini masih memeriksa android manifes:
debuggable
2
Pengujian pertama untuk Manifest debuggable, ini sudah tidak digunakan lagi. Yang kedua tidak dimungkinkan untuk pustaka, lib akan memiliki BuildConfignya sendiri - tidak dapat mengimpor BuildConfig dari Aplikasi, yang menggunakan Lib. Oleh karena itu jawaban yang ditandai adalah "ok"
Christoph
Jawaban ini akan berfungsi di semua kasus terlepas dari proyek perpustakaan atau proyek aplikasi.
Lavekush Agrawal
131

Dijawab oleh Mark Murphy

Solusi jangka panjang yang paling sederhana dan terbaik adalah dengan menggunakan BuildConfig.DEBUG. Ini adalah booleannilai yang akan digunakan trueuntuk debug build, falsejika tidak:

if (BuildConfig.DEBUG) {
  // do something for a debug build
}
Zar E Ahmer
sumber
8
Satu-satunya kelemahan dari pendekatan ini adalah ia tidak akan berfungsi dalam proyek perpustakaan (aar's). Saat library dibuat, ini akan menghasilkan false, jadi meskipun aplikasi yang menggunakan library berada dalam mode debug, pemeriksaan ini akan menghasilkan false dalam kode library.
Vito Andolini
24

Jika Anda ingin memeriksa secara APKstatis, Anda bisa menggunakan

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

Output ini 0jika APKtidak debuggable dan 1jika.

Heath Borders
sumber
3
ini adalah satu-satunya solusi, untuk memverifikasi apk final. Tanggapan lain menganggap Anda memiliki sumbernya.
Guillermo Tobar
1
aapttinggal di sini/Users/USER_NAME/library/Android/sdk/build-tools/28.0.3/aapt
Casey
21

Mungkin terlambat, tapi penggunaan yang terjatuh BuildConfig.DEBUG

urSus
sumber
apakah sekarang aman digunakan? ada artikel yang mengatakan ada beberapa masalah: digipom.com/be-careful-with-buildconfig-debug
pengembang android
Inilah jawaban terbaik!
Peter Fortuin
tidak jika Anda menulis pustaka pihak ketiga dan tidak mengetahui paket BuildConfig pada waktu kompilasi.
Sam Dozor
Sam, bisakah kamu menjelaskan ini?
Agamemnus
10

Pertama tambahkan ini ke file build.gradle Anda, ini juga akan memungkinkan berjalannya debug dan rilis build secara berdampingan:

buildTypes {
    debug {
        applicationIdSuffix ".debug"
    }
}

Tambahkan metode ini:

public static boolean isDebug(Context context) {
    String pName = context.getPackageName();
    if (pName != null && pName.endsWith(".debug")) {
        return true;
    } else {
        return false;
    }
}
Jahat
sumber
1
Saya lebih suka jawaban ini, karena dapat diandalkan. Saya memang perlu menambahkan entri baru "aplikasi Android yang diizinkan" ke kunci Google Maps API saya (karena id aplikasinya berbeda).
Baz
5

Sebuah build debug ditandatangani juga, hanya dengan kunci yang berbeda. Ini dibuat secara otomatis oleh Eclipse, dan sertifikatnya hanya berlaku untuk satu tahun. Apa masalahnya android:debuggable? Anda bisa mendapatkan nilai ini dari kode menggunakan PackageManager.

Nikolay Elenkov
sumber
3

Opsi lain, layak disebutkan. Jika Anda perlu menjalankan beberapa kode hanya saat debugger terpasang, gunakan kode ini:

if (Debug.isDebuggerConnected() || Debug.waitingForDebugger()) { 
    //code to be executed 
}
Igor Gorjanc
sumber
0

Diselesaikan dengan android:debuggable. Itu adalah bug dalam membaca item di mana dalam beberapa kasus tanda debug pada item tidak disimpan dalam catatan untuk if (m.debug && !App.isDebuggable(getContext()))selalu dievaluasi false. Salahku.

Im0rtality
sumber
13
Saya menyadari ini sudah lebih dari setahun, namun Anda seharusnya menerima jawaban @Omar Rehman, bukan yang ini. Meskipun apa yang Anda posting adalah apa yang akhirnya Anda lakukan, itu tidak benar-benar menjawab pertanyaan yang Anda ajukan, sementara solusi Omar tampaknya melakukan itu, yang berarti dia layak mendapatkan pujian yang diterima.
mah
7
@Mah - sangat tidak pantas untuk menindas seseorang karena tidak menerima jawaban yang diposting hampir setahun setelah mereka menyelesaikan masalahnya sendiri! Dan itu bahkan mengabaikan betapa rumitnya jawaban yang Anda nominasikan daripada yang mereka gunakan - yang sebenarnya menjawab pertanyaan yang diajukan, karena pertanyaan itu dipicu oleh bug yang mengarah pada kesan keliru bahwa bendera itu tidak dapat diandalkan .
Chris Stratton
4
@ChrisStratton jika menurut Anda tanggapan saya adalah penindasan, saya pikir Anda tidak banyak membaca internet. Saya mendukung hak Anda untuk memposting sudut pandang yang berlawanan dalam komentar seperti yang Anda lakukan, dan sebagai hasilnya saya telah meninjau komentar saya dan posting lain dalam pertanyaan ini, dan saya mendukung komentar asli saya: poster mengajukan pertanyaan yang sah dan dijawab dengan benar oleh seseorang. Berdasarkan "jawaban" -nya sendiri, pertanyaan aslinya bukanlah pertanyaan yang ingin dia tanyakan sejak awal ... tanda tangan APK (rilis atau debug) sama sekali tidak ada hubungannya dengan manifes.
mah
3
@mah - terserah penanya, bukan Anda yang memutuskan apa yang memenuhi kebutuhan aplikasi mereka yang sebenarnya, termasuk jika perbedaan yang Anda angkat adalah salah satu yang penting - atau, seperti yang tampaknya dalam kasus ini, tidak. Lebih penting lagi, Anda benar-benar mengabaikan bahwa jawaban yang Anda nominasikan telah diposting hampir setahun setelah kebutuhan mereka yang sebenarnya terpenuhi . Itu menghukum seseorang karena tidak kembali untuk mengubah penerimaan mereka hampir setahun kemudian, yang merupakan intimidasi.
Chris Stratton
3
@ChrisStratton juga tergantung pada penanya untuk mengajukan pertanyaan yang sebenarnya dia ingin jawab - sesuatu yang tidak dilakukan dalam kasus ini. Anda benar bahwa saya lupa ketika jawaban yang benar-benar menjawab apa yang diposting dibuat, namun tidak ada hukuman sama sekali, hanya ungkapan pendapat saya yang masuk akal. Jika menurut Anda saya bertindak sebagai penindas di sini, saya sangat meminta Anda menandai komentar saya karena melanggar. Namun sebelum melakukan itu, Anda mungkin ingin memeriksa posting Anda sendiri di sini.
mah
0

Solusi di Kotlin yang saya gunakan saat ini:

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

dengan cara itu saya masih bisa SIGN di debug dan itu akan dilaporkan ke Crashlytics (contoh, untuk proses QA)

Rafael Ruiz Muñoz
sumber