
Tahun lalu, kami melakukan pencarian online untuk pengembang seluler - Misi Droid. Dalam sebulan, para peserta harus menyelesaikan sebanyak mungkin masalah dalam tiga arah: perbaiki! (pencarian bug dan penelitian kode), retas itu! (rekayasa balik) dan gali! (mempelajari fitur-fitur Android). Secara total, pencarian memiliki 23 tugas - mereka sangat mirip dengan yang dihadapi spesialis Android dalam pekerjaan nyata. Dalam posting kami akan menunjukkan semua kondisi dan solusi yang tepat.
Cari kesalahan dan penelitian kode
perbaiki itu! # 1
Penulis: Anastasia LaushkinaPada Jumat malam yang tenang, dua agen begadang dan memimpin perselisihan yang tak terpecahkan. Mereka diperintahkan untuk menyimpan sejumlah besar data rahasia dalam database Sqlite dengan kunci integer.
Agen A membuat tabel dengan kueri bentuk: CREATE TABLE t (key INT PRIMARY KEY, secret_value_1, secret_value_2), dan agent B dengan query dari form CREATE TABLE t (key INTEGER PRIMARY KEY ASC, secret_value_1, secret_value_2).
Agen A benar-benar yakin bahwa tidak ada kesalahan dalam permintaannya, namun, Agen B memiliki argumen terhadap ini: ia mengklaim bahwa integritas data selama pendekatan Agen A dapat dilanggar.
Daftar dengan koma: kolom atau kolom yang bertanggung jawab untuk integritas Agen B, tetapi tidak bertanggung jawab untuk Agen A; serta struktur data, dalam bahasa Rusia, karena itu permintaan agen B akan lebih cepat.
CatatanFormat jawaban: [nama kolom 1], [nama kolom N], [nama struktur] (tanpa tanda kurung, dengan huruf kecil)
Contoh respons: x, y, array
SolusiIni tentang rowId dan hubungannya dengan kunci utama.
Dalam kasus "CREATE TABLE t (kunci INTEGER PRIMARY KEY ASC, secret_value_1, secret_value_2)" adalah alias untuk rowId. Dalam kasus "CREATE TABLE t (key INT PRIMARY KEY, secret_value_1, secret_value_2)" - no. Alasannya dapat ditemukan
dalam dokumentasi .
Oleh karena itu, dengan tidak adanya hubungan kunci dengan rowId, permintaan formulir "Sisipkan NILAI KE NILAI (" beberapa "," y "," z ")" akan berhasil dieksekusi, berpotensi melanggar integritas data. Jika ada koneksi seperti itu, kesalahan datatype ketidakcocokan akan terjadi.
Jawabannya diterima sebagai pasangan kunci, rowId, dan key / rowId secara terpisah (karena jika ada koneksi, mereka merujuk ke kolom yang sama).
Kecepatan dicapai melalui penyimpanan dalam bentuk pohon pencarian.
perbaiki itu! # 2
Penulis: Anastasia LaushkinaFile binerUntuk mendeteksi musuh dengan cepat, Agen Kew sedang mengembangkan aplikasi dengan daftar penjahat yang dikenal. Dia dengan mudah berhasil menampilkan daftar ini di aplikasi, tetapi dia memutuskan bahwa informasi penting seperti itu harus ada di layar utama.
Untuk melakukan ini, ia membutuhkan widget. Satu masalah: karena alasan tertentu, widget tidak menampilkan daftar. Insting memberi tahu agen bahwa masalahnya hanya pada satu baris.
Beri nama dengan koma, serta baris tempat Anda harus menggantinya, untuk mendapatkan solusi yang paling optimal.
CatatanFormat jawaban: [baris 1], [baris 2] (tanpa tanda kurung)
Contoh respons: FrameLayout, ScrollView
Solusi1. Kami mencari file tata letak widget, kami melihat elemen ConstraintLayout yang tidak valid di dalamnya (lihat
developer.android.com/guide/topics/appwidgets#CreatingLayout ).
2. Ubah ke yang dapat diterima dan optimal - LinearLayout / FrameLayout.
Terlepas dari kenyataan bahwa menggunakan FrameLayout memecah tata letak elemen dalam widget, itu tetap diterima sebagai jawaban.
perbaiki itu! # 3
Penulis: Artyom ViterFile binerPenguji memperhatikan peningkatan konsumsi RAM yang tidak normal. Ada kecurigaan bahwa ada kebocoran di suatu tempat.
Anda harus menyelidiki dan melokalisasi masalah. Dalam respons, tunjukkan nama metode kelas aplikasi dan tempat pemanggilannya, yang mengarah ke kebocoran memori. Jika Anda menemukan beberapa masalah, daftarkan semuanya.
Format jawaban: [Nama kelas tempat metode dipanggil]: [nomor baris dalam file ini]: [nama metode] (tanpa tanda kurung)
Tunjukkan beberapa jawaban dalam urutan leksikografis, dipisahkan dengan tanda #.
Contoh:
Jika Anda berpikir ada kebocoran kehabisan memori dalam file ExampleFileName.java,
class ExampleFileName { public problemMethodName(Sting arg) {} public test(Sting arg) { problemMethodName(arg); // 8 ExampleFileName.java } }
Contoh respons: ExampleFileName: 8: problemMethodName
SolusiCara pertama adalah analisis kode.
- Kami mempelajari kode aplikasi.
- Kebocoran pertama jelas. Ini adalah pendaftaran BroadCastReceiver di kelas SecondActivity. Pesan tentang masalah ini dapat dilihat di logcat jika Anda menjalankan aplikasi.
- Jika Anda memperhatikan metode startTimer (), Anda akan melihat bahwa kelas CountDownTimer menambahkan penutup ke penangan utas utama dengan tautan ke textView. Dan sebelum aktivitas dihancurkan, kelas instance (CountDownTimer) tidak memanggil metode cancel (). Ini adalah kebocoran memori kedua.
Cara kedua adalah alat. Anda dapat menerapkan perpustakaan
leakcanary dalam aplikasi, mendapatkan heap dump melalui adb atau Android Profiler dan menganalisis laporan yang diterima. Eclipse Memory Analyzer (MAT) dapat membantu dengan analisis ini. Untuk membuka heapdump di MAT, Anda perlu mengonversinya ke format yang dapat dimengerti untuk MAT menggunakan utilitas hprof-convd.
perbaiki itu! # 4
Dikirim oleh: Vali IbragimovFile binerIvan menjalani hari pertamanya di bioskop online. Karena dia adalah pengembang yang keren, dia langsung diminta untuk memperbaiki crash saat menonton film. Dia memutuskan pada saat yang sama untuk melakukan refactoring. Kode telah menjadi lebih ringkas dan bersih, kesalahan telah berhenti direproduksi.
Namun, belakangan ternyata posisi film saat ini tidak dikirim ke backend. Bantu Ivan memahami kelas mana yang ia campur aduk.
Responsnya adalah nama kelas yang harus digunakan.
SolusiUntuk mengirim posisi menonton setiap lima detik dan pergi ke jaringan, ExecutorService digunakan. Membuat ScheduledThreadPoolExecutor untuk tugas berkala, pikir Ivan - mengapa tidak menggunakannya untuk pergi ke jaringan? Bagaimanapun, ScheduledThreadPoolExecutor adalah keturunan dari ThreadPoolExecutor. Ivan bahkan tidak curiga bahwa ScheduledThreadPoolExecutor hanya menggunakan satu utas, dan ketika pergi ke jaringan, Anda harus menunggu penyelesaian tugas periodik.
scheduleFuture = scheduledExecutorService.scheduleAtFixedRate(() -> { try { timingsApi.sendTiming(filmId, player.getContentPosition()).get(); } catch (Exception e) { e.printStackTrace(); } }, 0, PERIOD_SECONDS, TimeUnit.SECONDS);
Solusi untuk masalah ini semakin diperumit oleh fakta bahwa kami tidak mengakses ExoPlayer di utas UI - dan di versi baru 2.9. * ExoPlayer mulai menulis peringatan tentang ini di log. Tapi sekarang ini hanya peringatan, dan bukan kesalahan yang dilakukan oleh Ivan.
perbaiki itu! # 5
Penulis: Alexander TsybinFile binerSatu perusahaan sedang mengembangkan game Farmer Simulator.
Dalam game ini, hanya pemain yang bisa bergerak bebas. Semua objek lain tidak bergerak atau memiliki lintasan pergerakan yang jelas. Pada titik tertentu, mereka memutuskan untuk menambahkan musuh ke permainan yang akan menjadi penghambat bagi pemain.
Pengembang Vasya diperintahkan untuk mewujudkan musuh. Selama implementasi, Vasya mengalami masalah kinerja. Dia memutuskan untuk memperbaikinya karena multithreading. Karena Vasya tidak terbiasa dengan multithreading, ia mengambil keuntungan dari solusi dari stackoverflow. Jadi dia menyingkirkan masalah kinerja.
Namun, pada tahap pengujian, ditemukan bahwa kadang-kadang permainan membeku. Bantu Vasya menemukan alasannya.
Sebagai tanggapan, masukkan nomor dari baris kode yang bermasalah - dipisahkan dengan koma, dalam urutan menaik dan tanpa spasi. Seperti ini: 1,17,42
SolusiTinju tipe primitif (mis. Int dalam Integer) adalah operasi yang mahal. Untuk tujuan optimasi, mesin Java terkadang menggunakan kembali objek tersebut. Garis tidak terkecuali.
Baris ENEMY_LOCK dan DECISIONS_LOCK digunakan dalam kode untuk sinkronisasi. Jadi, jika ada tempat dalam proyek dengan sinkronisasi pada baris yang sama, maka kebuntuan dapat terjadi.
Secara umum, Anda tidak dapat menggunakan String / Integer, dll., Untuk sinkronisasi.
Jawab: 7.8
perbaiki itu! # 6
Penulis: Ivan PukhovFile binerTim pengolah gambar telah merilis pustaka asli baru, Anda harus mencobanya dan menunjukkan masalahnya, jika ada.
SolusiIni adalah tugas yang mudah untuk pemanasan. Di sumber daya terletak perpustakaan itu sendiri dan file header untuk itu. Di header, satu-satunya fungsi mengembalikan sebuah jobject.
Tidak ada yang tersisa untuk dilakukan selain membuat proyek uji (dengan menginstal NDK) dan menyebutnya! Panggilan fungsi pada utas utama akan mengeluarkan pengecualian yang meminta untuk tidak memanggilnya dengan cara ini. Kami mentransfer eksekusi ke utas lain dengan cara apa pun - dan semuanya berfungsi.
Sekarang layak memeriksa objek yang dikembalikan - apakah ini benar-benar bitmap? Cara termudah adalah dengan segera menampilkannya di ImageView. Kami akan melihat gambar dengan string teks, dan tugas tersebut membutuhkan jawaban juga dalam bentuk string. Ini mungkin bukan hanya kebetulan. :)
perbaiki itu! # 7
Penulis: Dmitry ZaitsevFile binerAda aplikasi dengan kode di mana kode yang diinginkan dibingkai. Sayangnya, tata letak rusak dan perlu dipulihkan untuk menemukan kode.
CatatanSetelah mengoreksi kode, frame akan melewati 22 karakter yang harus terdaftar berlawanan arah jarum jam, mulai dari sudut kiri atas frame.
Contoh respons: ScQG1jxbazmjbcARefbRMo
SolusiSekarang Dagger telah menjadi standar de facto dalam pengembangan Android. Ketika DI sudah dikonfigurasi, sangat mudah untuk menulis Suntikkan di depan konstruktor dan memberikan semua sisa pekerjaan ke Belati. Sangat mudah bahwa Anda nanti bisa berhenti memikirkan bagaimana dependensi disediakan. Juga di Android, Anda sering harus bekerja dengan kelas Konteks dan turunannya: Aplikasi dan Aktivitas. Aplikasi terikat dengan siklus hidup aplikasi, dan Aktivitas terikat pada siklus hidup layar. Karena itu, dalam bekerja dengan kelas-kelas ini, penting untuk mengamati banyak fitur. Tugasnya justru didasarkan pada penggunaan konteks yang salah, dan semua ini disamarkan menggunakan DI.
Untuk menyelesaikannya, Anda dapat melakukan langkah-langkah berikut:
- Periksa file proyek dan perhatikan bahwa ItemStyle diterapkan ke semua TextViews.
- Pastikan gaya tersebut tidak benar-benar diterapkan. Ini harus mengarah pada gagasan bahwa masalahnya terkait dengan topik, dan oleh karena itu dengan konteks Kegiatan.
- Pahami grafik ketergantungan dan pahami bahwa konteks Aplikasi disediakan di adaptor.
- Di ContestAdapter, ubah tipe konteks menjadi Activity.
perbaiki itu! # 8
Dikirim oleh: Vali IbragimovFile binerIvan diberi tugas untuk memperbaiki aplikasi untuk mendapatkan daftar tugas melalui ContentProvider menggunakan DI. Dia refactored aplikasi menggunakan Belati, dan ketika mulai, aplikasi mulai macet.
Bantu Ivan memahami alasannya:
- Tentukan nomor baris dengan nama kelas yang ingin Anda transfer ke kelas lain.
- Berikan tautan ke dokumentasi dengan jangkar ke tempat yang akan membantu Anda memahami mengapa aplikasi macet.
CatatanFormat respons: [Nama kelas]: [Nomor baris] -> [Nama kelas]: [Nomor baris], [Tautan]
Contoh jawaban:
FirstCLass: 12-> SecondClass: 45, https: //developer.android.com/reference/android/app/Activity#activity-lifecycle
SolusiTugas itu terdiri dari dua bagian:
1. Memahami dan memberikan solusi kerusakan di ContentProvider saat mengakses komponen DI. Solusi yang benar adalah mentransfer inisialisasi komponen DI ke ContentProvider.

Metode onCreate pada ContentProvider disebut lebih awal dari pada Aplikasi. Ini mudah untuk diverifikasi jika Anda memeriksa kode mulai aplikasi di kelas ActivityThread dan metode handleBindApplication.

2. Berikan tautan yang menjelaskan bahwa onCreate untuk ContentProvider dipanggil lebih awal dari pada Aplikasi. Google tidak menyebutkan ini di halaman dokumentasi ContentProvider. Tapi di sini adalah kutipan dari deskripsi metode Aplikasi onCreate:
Disebut ketika aplikasi mulai, sebelum aktivitas, layanan, atau objek penerima (tidak termasuk penyedia konten) telah dibuat.
perbaiki itu! # 9
Penulis: Alexander TsybinFile binerPengembang diberi tugas menerapkan Tampilan untuk beberapa daftar item. Di TK ada persyaratan penting - untuk menggulir daftar ke elemen pertama dan terakhir sehingga elemen-elemen ini muncul di tengah layar. Pengembang mengimplementasikan fungsionalitasnya, tetapi ketika mengujinya ternyata terkadang pemusatan tidak bekerja. Bantu pengembang menemukan garis bermasalah dalam kode.
CatatanFormat jawaban:
<Nama file tanpa ekstensi>: <Nomor baris dipisahkan dengan koma>
<Nama file tanpa ekstensi>: <Nomor baris dipisahkan dengan koma>
Garis harus diurutkan dalam urutan leksikografis. Nomor baris harus disortir dalam urutan menaik.
Contoh jawaban:
BarFoo: 1
FooBar: 12,13,14,99
SolusiAda dua masalah dalam kode:
1. Dilihat dari kode, diasumsikan bahwa panggilan ke onViewDetachedFromWindow akan selalu terjadi setelah diBindViewHolder, tetapi tidak demikian halnya. Metode kembar untuk onViewDetachedFromWindow adalah onViewAttachedToWindow. Akibatnya, holder.view.reset () dipanggil dan pemusatan menghilang jika gulir lambat.
2. Pada Android sebelum versi 7, addView (rootView, params) tidak berfungsi seperti yang diharapkan.
Saat Anda menambahkan Tampilan, LayoutParam dihasilkan, yang terlihat seperti ini:
Marshmallow -
github.com/aosp-mirror/platform_frameworks_base/blob/marshmallow-release/core/java/android/widget/FrameLayout.java#L403Nougat -
github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/widget/FrameLayout.java#L384FrameLayout.LayoutParams memiliki dua konstruktor, satu di antaranya menerima ViewGroup.MarginLayoutParams dan menyalin margin ke dalam dirinya sendiri:
github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/view/ViewGroup.java#L14Konstruktor kedua menerima ViewGroup.LayoutParams dan mengabaikan margin:
github.com/aosp-mirror/platform_frameworks_base/blob/nougat-release/core/java/android/view/ViewGroup.java#L7328Akibatnya, pemusatan tidak bekerja di Android hingga versi 7.
Jawabannya adalah:
ItemView: 35
MainActivity: 53
Rekayasa terbalik
hack itu! # 1
Penulis: Valentin BaryshevFile binerSalah satu pengembang bergegas dan bahkan tidak menguji .apk final sebelum mengirimkan ke kontes.
Ternyata dia mencampur beberapa viewgroup di activity_main.xml. Selain itu, ia menempatkan setiap huruf kunci dalam tampilan teks terpisah. Akibatnya, semua tampilan tata letak terbuka dan ada kemarahan di layar.
Cobalah untuk menentukan viewgroup mana yang menggantikan yang ada saat ini.
Jawaban untuk masalah ini adalah kode yang muncul di layar setelah mengganti viewgroup yang benar.
SolusiKita perlu mendekompilasi .apk dengan cara apa pun yang tersedia (misalnya, dengan dekompiler online) dan membuat ulang proyek dengan kode.
Jika kita menganalisis main_layout, maka kita dapat menebak dari atribut bahwa root viewgroup harus ConstraintLayout.
Jawab: tniartsnoc
hack itu! # 2
Penulis: Alexander TsybinFile binerSeorang pengguna berpengalaman menghentikan aplikasi. Dia tidak terkejut, membuka log, menemukan garis yang menyebabkan kesalahan: N72XbphDx5NnFl6CKMNl8w == dan mengirimkannya ke dukungan teknis.
Ternyata dia menggunakan versi aplikasi yang sudah ketinggalan zaman, yang telah dikembangkan sebelum menggunakan repositori kode, sehingga kode dekripsi hilang. Namun, itu mungkin untuk menemukan .apk dari aplikasi versi lama.
Bantu mendekripsi pesan.
SolusiKita perlu mendekompilasi .apk dengan cara apa pun yang tersedia (misalnya, dengan dekompiler online) dan membuat ulang proyek dengan kode.
Kode itu sendiri berisi fungsi enkripsi dan fungsi dekripsi, yang dapat Anda berikan string terenkripsi dan mendapatkan jawaban.
Jawabannya adalah:
TF: Kerja bagus!
hack itu! # 3
Penulis: Valentin BaryshevFile binerSeberapa baik Anda mengetahui siklus hidup aplikasi Android?
Tahukah Anda bahwa dalam beberapa kasus sistem dapat mematikan proses aplikasi?
Simulasikan situasi ini pada aplikasi.
Anda akan melihat jawabannya setelah mengembalikan aplikasi - dalam bentuk kode.
SolusiItu sudah cukup untuk menginstal .apk di telepon, menjalankan dan mendapatkan kasing ketika sistem membongkar aplikasi, tetapi pada saat yang sama tetap di tirai.
Salah satu opsi adalah menginstal aplikasi, meluncurkannya dan menguranginya (atau hanya beralih ke aplikasi lain). Kemudian kami terhubung melalui adb ke komputer. Di Android Studio, pilih tab Logcat, pilih proses aplikasi ini dan klik ikon berhenti merah. Kemudian buka aplikasi yang sebelumnya diminimalkan dari tirai. Layar akan menampilkan jawabannya: sss2384gxcxxX.
hack itu! # 4
Penulis: Ivan PukhovFile binerSeorang kolega front-end memposting demo dari modul baru, tetapi lupa untuk mengirim spesifikasi. Sekarang dia tidur di zona waktu yang berbeda, dan demo harus dilakukan dalam beberapa menit. Dari percakapan dengan seorang kolega, Anda ingat frasa "Ya, mudah di sana, Anda hanya perlu menulis bertopik dan itu akan berhasil."
Unduh arsip dan coba atasi tanpa kolega.
SolusiIni adalah tugas yang sederhana, tetapi membutuhkan rasa ingin tahu dan ketekunan.
Unduh arsip, unggah isinya ke server uji lokal, buat proyek dengan WebView dan jalankan. Setelah batas waktu singkat, kami melihat pesan "Orientasi kalibrasi". Dan itu dia. Tetapi pesan mengisyaratkan bahwa mungkin Anda harus memutar telepon di tangan Anda? Setelah itu, pesan yang mencurigakan tentang pengecualian yang dilemparkan akan ditampilkan - dengan panggilan untuk memeriksa output konsol.
Kami membuka konsol dan melihat panggilan ke metode yang tidak ada pada objek API. Kami menambahkan objek ke WebView untuk mengimplementasikan JS API dan sekali lagi mencoba menguji aplikasi.
Rupanya, ada baiknya menambahkan nilai kembali. Kami memilih, menambahkan - itu berfungsi. Tetapi tidak ada garis dengan jawaban di mana pun. Hidupkan telepon lagi - metode baru dipanggil. Tambahkan juga. Setelah beberapa iterasi, kami mendapatkan panggilan finalisasi Kalibrasi dan kemudian storeResult, di mana saluran kami akan tiba.
Saat mengunduh sumber, Anda mungkin tidak ingin menyelesaikan apa pun, tetapi buka dan keluarkan jawabannya. Metode ini berhasil, tetapi beberapa garu kecil menanti Anda di sepanjang jalan.
hack itu! # 5
Penulis: Ivan PukhovPerakitan perangkat Android versi baru bocor ke jaringan, tempat mekanisme penandatanganan aplikasi yang baru di-debug langsung ke perangkat. Dalam majelis ini, kunci pribadi untuk penandatanganan dijahit ke dalam kode sumber sistem, dan pengembang meninggalkan bagian UI untuk menerimanya. Temukan kuncinya.
Solusi1. Ketika Anda menjalankan emulator, Anda akan melihat aplikasi SecretActivity. Jika Anda membukanya, bersulang muncul, menawarkan untuk melihat log. Karena ini adalah unit debugging, ada banyak log - mereka harus disaring berdasarkan nama aplikasi.
adb logcat | grep 'SecretActivity'
1898 1898 E SecretActivity: Try broadcast ZggwxXsaQ9SapPKyHpnwnYmALHazVFWX
2. Kami mencoba membuang siaran dengan aksi dari log:
adb shell am broadcast -a ZggwxXsaQ9SapPKyHpnwnYmALHazVFWX
3. Bersulang akan muncul di layar dengan proposal untuk melihat log oleh tag SecretReceiver.
adb logcat | grep 'SecretReceiver'
1898 1898 E SecretReceiver: ISecretService.aidl
1898 1898 E SecretReceiver: package android.app;
1898 1898 E SecretReceiver: interface ISecretService {
1898 1898 E SecretReceiver: String getSecret();
1898 1898 E SecretReceiver: }
1898 1898 E SecretReceiver: See global settings to bind SecretService
4. Kami melihat pengaturan global untuk menemukan tindakan untuk terhubung ke SecretService.
adb shell settings list global
...
bind_secret_service_action=g8mNyGQZR8aHLTXNWcjdwJJYZ85Ewx83
...
5. Kami sedang menulis klien untuk layanan ISecretService menggunakan antarmuka bantuan ini.
public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = new Intent(); intent.setAction("g8mNyGQZR8aHLTXNWcjdwJJYZ85Ewx83"); PackageManager pm = getPackageManager(); List<ResolveInfo> resolveInfoList = pm.queryIntentServices(intent, 0); if (resolveInfoList == null || resolveInfoList.size() != 1) { return; } ResolveInfo serviceInfo = resolveInfoList.get(0); ComponentName component = new ComponentName(serviceInfo.serviceInfo.packageName, serviceInfo.serviceInfo.name); Intent explicitIntent = new Intent(intent); explicitIntent.setComponent(component); bindService(explicitIntent, new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { ISecretService secretService = ISecretService.Stub.asInterface(service); try { Log.d("MainActivity", secretService.getSecret()); } catch (Exception e) { Log.e("MainActivity", "RemoteException", e); } } @Override public void onServiceDisconnected(ComponentName name) { } }, Context.BIND_AUTO_CREATE); } }
6. Jalankan, di log kita mendapatkan jawabannya:
adb logcat | grep 'MainActivity'
1898 1898 E MainActivity: VENHz=qWr7y!t3ZhP!8Skw!!kcTkt7V%
hack itu! # 6
Penulis: Pavel VorobkalovFile binerTn. Tupper menulis aplikasi Android yang mengirim pesan.
Dekripsi pesan dan lanjutkan.
Format jawaban: string sebagai tautan.
SolusiInstal dan jalankan aplikasi pada perangkat. Kami melihat dua tombol: Kirim pesan dan Kirim formula. Ketika Anda mengkliknya, tidak ada yang terlihat terjadi. Anda perlu mengetahui bagaimana dan di mana aplikasi mengirim sesuatu.

Kami membuka aplikasi .apk menggunakan beberapa cara untuk merekayasa balik dan melihat Kegiatan utama. Di sana Anda dapat melihat metode onSendMessageClick dan onSendFormulaClick. Mereka membuat Intent dengan action "com.yandex.tupper.action.NEW_MESSAGE" dan tambahan dengan kunci "com.yandex.tupper.message" dan mengirimkannya ke aplikasi dari daftar (menggunakan metode umum). Mereka mendapatkan daftar dengan memanggil PackageManager.queryBroadcastReceivers.
Anda dapat terus menggunakan alat rekayasa terbalik untuk mempelajari pesan dengan mengekstraknya dari kode aplikasi. Tetapi jalur ini sengaja (walaupun sedikit) rumit oleh enkripsi RSA tambahan.
Ada cara lain yang lebih cocok dengan pemrograman Android. Anda perlu menulis aplikasi yang menangani Intent siaran ini. Kami membuat aplikasi kami, di penerima register manifes:
<receiver android:name=".TupperMessageReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.yandex.tupper.action.NEW_MESSAGE" /> </intent-filter> </receiver>
Di handler, kami mendapatkan pesan dari ekstra dan menjalankan Aktivitas kami sendiri, di mana kami akan menunjukkan hasil pemrosesan:
public class TupperMessageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE); if (message == null) { return; } Intent activityIntent = new Intent(context, MainActivity.class); activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); activityIntent.putExtra(MainActivity.EXTRA_MESSAGE, message); context.startActivity(activityIntent); } }
Sekarang mari kita berurusan dengan dekripsi pesan itu sendiri. Jika Anda mencari di Yandex untuk "pesan Tupper", Anda dapat menemukan
artikel di rumus Tupper Wupper. Ini memiliki contoh rumus yang disandikan dalam bentuk angka:
48584 ...4858450636189713423582095962494202044581400587983244549483093085061934704708809928450644769865524364849997247024915119110411605739177407856919754326571855442057210445735883681829823754139634338225199452191651284348332905131193199953502413758765239264874613394906870130562295813219481113685339535565290850023875092856892694555974281546386510730049106723058933586052544096664351265349363643957125565695936815184334857605266940161251266951421550539554519153785457525756590740540157929001765967965480064427829131488548259914721248506352686630476300
Nomor ini dikirim sebagai pesan dengan mengklik tombol Kirim rumus. Hasil pengodean angka tersebut juga ada di artikel Wikipedia. Ini dapat digunakan untuk men-debug implementasi algoritma decoding.
Kami menerapkan algoritma decoding di Jawa dan, demi kecantikan, mengonversi angka biner yang dihasilkan ke Bitmap monokrom:
public class TupperCodec { static final int TUPPER_R = 17; private TupperCodec() {} public static BigInteger decodeFromDecString(String decString) throws NumberFormatException { BigInteger data = new BigInteger(decString, 10); return data.divide(BigInteger.valueOf(TUPPER_R)); } public static String getNumberAsBinImage(BigInteger number) { String binString = number.toString(2); StringBuffer result = new StringBuffer(binString.length()); int i; for (i = 0; i < binString.length(); i++) { if ((i % TUPPER_R) == 0 && i > 0) { result.append('\n'); } result.append(binString.charAt(binString.length() - 1 - i)); } int numberOfRows = (binString.length() + TUPPER_R - 1) / TUPPER_R; for (; i < numberOfRows * TUPPER_R; i++) { result.append(~_~quot�quot~_~); } return result.toString(); } public static Bitmap getBinImageAsBitmap(String binImage) { String[] lines = binImage.split("\n"); int[] colors = new int[lines.length * TUPPER_R]; for (int i = 0; i < lines.length; i++) { String line = lines[i]; assert line.length() == TUPPER_R; for (int j = 0; j < line.length(); j++) { colors[i * TUPPER_R + j] = line.charAt(j) == '0' ? Color.WHITE : Color.BLACK; } } return Bitmap.createBitmap(colors, TUPPER_R, lines.length, Bitmap.Config.ARGB_8888); } }
Di Activity, panggil decoding dan tampilkan hasilnya sebagai Bitmap di ImageView:
public class MainActivity extends AppCompatActivity { static final String EXTRA_MESSAGE = "com.yandex.tupper.message"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = getIntent(); if (intent != null && intent.hasExtra(MainActivity.EXTRA_MESSAGE)) { String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE); try { BigInteger data = TupperCodec.decodeFromDecString(message); String binImage = TupperCodec.getNumberAsBinImage(data); Bitmap bitmap = TupperCodec.getBinImageAsBitmap(binImage); ImageView imageView = findViewById(R.id.message_image); imageView.setImageBitmap(bitmap); CharSequence toastText = TextUtils.ellipsize(message, new TextPaint(), 400, TextUtils.TruncateAt.END); Toast.makeText(this, toastText, Toast.LENGTH_SHORT).show(); } catch (NumberFormatException ex) { Toast.makeText(this, getString(R.string.invalid_message), Toast.LENGTH_LONG).show(); } } } }

—
clck.ru/FevR5 .
hack it! #7
:.
. , , .
: - .
.apk zip- classes.dex — Java- . , , dex2jar Android Studio. dex2jar Java-, Android Studio class-. , .
, proguard ( android.support.* androidx.*, ), — com.yandex.contest.keygenme.MainActivity, Activity . , : MainActivity.a#doInBackground bbaab b , .

c d. N- π ( --), . , π
hexed [c[i] + key[i]] = d[i], i = 0..11. , . 50 hex- π. : 537306089144. key_2056BE33E064.
, , — . doInBackground:

, , if (var12[0] == var8) {… }. var7 — i- . , var7 0 9 . . , .apk ( JVM) .
hack it! #8
:N , . : Android 4.4, 1200 × 600, FM-. , , FM- . FM- , .
, FM-. .
, :
1. 106.00
2. (seek)
3. ,
, .
: . .
: , a(106.00) 106.00 , b(true) , c() , a(106.00);b(true);c()
: , FM- . .apk dex-. Java. , , — jadx.
.apk, . MainActivity. : fragment_holder RadioFullFragment . . RadioFullPresenter, ChangeRadioFrequencyUsecase, RadioRepo. .
«implements RadioRepo». : RadioRepoImpl. , b. ServiceConnection — . , b lambda$new$0$RadioRepoImpl. , $$Lambda$RadioRepoImpl$FYTR9gAgZEGvrLjkbkpAIup7OKw, serviceConnection IRadioService radioService. , RadioService.
: onServiceConnected «b radioManager = Stub.asInterface(service)». , . «bindService». bindService «com.some_company.help_service». , com.some_company.carradio.FmUtils. , , . , : j , : 8880 88.00 . 8800. a. , .
. : k z. RadioRepoImpl, enableRadio() disableRadio() k. z(boolean isForwardDirection), « (seek) ».
: a(10600);z(true);j().
hack it! #9
:flatbuffers . GPX . . , .
FlatBuffers :
namespace ru.yandex.android.task; struct GeoPoint { latitude:double; longitude:double; } table Points { items:[GeoPoint]; } root_type Points;
: , sha256. .
, FlatBuffers.
1. fbs- .
2. Java-. JVM- , Gradle flatbuffers.
3. fbs-.
4. fbs-, - — . , FlatBuffers.
5. , . structs , struct table. .
6. Kotlin. FlatBuffers.
fbs , . FlatBufferBuilder input. input :
val pointsList = Points.getRootAsPoints(buffer) val restoredPoints = (0 until pointsList.itemsLength()) .mapNotNull(pointsList::items)
c , (lat, long). as-is GPX-.
GPX- ( ) . GPX XML. , XML- . XML- GPX-track-. XML- MAP- . «yandroid».
, . CLI-:
echo "yandroid" | shasum -a 256
.
Android
dig it! #1
:QA- .
:
— ( 'Screen 1')
— GO NEXT ( 'Screen 2')
— GO BACK
— GO NEXT,
— 5–10
: 'Screen 2'.
: , « ».
.apk .
C stack trace, .
Contohstack trace …
at android.os.MessageQueue.nativePollOnce(Native method) at android.os.MessageQueue.next(MessageQueue.java:326) at android.os.Looper.loop(Looper.java:160) at com.android.server.SystemServer.run(SystemServer.java:454) at com.android.server.SystemServer.main(SystemServer.java:294) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:838)
… , RuntimeInit.java, 493, :
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
, , « ».
/data/anr/traces.txt /data/anr/anr_* (https://developer.android.com/topic/performance/vitals/anr#pull_a_traces_file). adb-:
adb root adb shell ls /data/anr adb pull /data/anr/<filename>
( adb root , Google API.)
stack trace . — stack trace, :
com.droid.mission.anrapk.MainActivity$2.onClick(MainActivity.java:43)

dig it! #2
:Android Studio, , MultiDex. -, apk- .dex- (65536). / apk Andorid Studio .dex-.
— dex- , dex- ( , ) . dex- .
: multidex , android gradle plugin 3.0.0 – 3.1.4
.
?
, dex-. Gradle — , , .apk.
Java- dex- ART/Dalvik: transformClassesWithDexBuilderForDebug. dex-, . Android Gradle plugin (AGP) Gradle . , — , Gradle . , . AGP, — DSL- Gradle build.gradle.
3.1.* minimal-main-dex. , .
dig it! #3
:— - .
?
: — .
, . Android WindowManager.
. ,
WindowManager.LayoutParams . , ,
TYPE_APPLICATION .
, , . , , - .
, Android SDK, , . ,
WindowManager AOSP. , , , .
, ,
TYPE_BOOT_PROGRESS . getWindowLayerFromTypeLw WindowManagerPolicy. Javadoc :
Returns the layer assignment for the window type. Allows you to control how different kinds of windows are ordered on-screen
, , . switch-case , layer — TYPE_POINTER, 2018. .
dig it! #4
:, - , .
. , , , .
Android , ?
, , Android SDK, .
startActivity Context . Context Activity — ,
startActivity . startActivityForResult. , Activity
mInstrumentation.execStartActivity .
? startActivity ActivityManagerService, ServiceManager.getService(Context.ACTIVITY_SERVICE).
ActivityManagerService . startActivityAsUser, startActivity, ActivityStarter execute. ,
.
C
startActivity . ,
abort , true START_ABORTED Activity.
, , mService.mIntentFirewall.checkStartActivity. ,
. :
This is called from ActivityManager to check if a start activity intent should be allowed.
, IntentFirewall .
RULES_DIR . : ifw.
dig it! #5
:. , (, ) — 8800. , , (+8800). , 8800 +8800 , / , .
. . , +8800 8800 .
, (java/c/c++). , .
:
source.android.com— :
androidxref.comDialer , , URI
authorities = "content://com.android.contacts"
.
ContentProvider. URI ContactsPoviider. : , SQL- . :
sb.append("PHONE_NUMBERS_EQUAL(" + Tables.DATA + "." + Phone.NUMBER + ", ");
PHONE_NUMBERS_EQUAL — SQL-. sqlite .
sqlite, , . ContactsProvider , config_use_strict_phone_number_comparation, . :
<bool name="config_use_strict_phone_number_comparation">true</bool>