Betapa aku hampir ketahuan virus mencoba menjual sepatu bot



Saya adalah salah satu dari orang-orang yang, dengan awal musim gugur, berusaha menghabiskan lebih sedikit waktu di jalan. Di Moskow, ini tidak sulit: Anda terbatas pada rute dari rumah ke kantor dan kembali. Namun, cuaca lembab dapat menyebabkan ketidaknyamanan di ruangan, terutama jika tempat kerja Anda, seperti milik saya, ada di dekat jendela, dan setiap kolega kedua, yang mengeluh sesak, meminta ventilasi kantor. Agar tidak jatuh ke dalam limpa, musim gugur ini saya memperbarui lemari pakaian.

Berpikir tentang nasib hal-hal yang tidak perlu, saya bertanya-tanya apa yang harus dilakukan dengan mereka: membuangnya, memotongnya menjadi kain, memberikannya kepada adik lelaki saya untuk membawanya? Tetapi untuk satu hal tidak ada metode ini yang cocok: mereka adalah sepatu bot kulit dari ukuran yang layak, tetapi mereka mengganggu saya dalam rangka. Saya memutuskan untuk menjualnya di Avito. Saya mengunggah foto, menunjukkan nama palsu (keamanan informasi sama), mengeluarkan sepatu bot saya, beberapa hal lain dan pergi tidur. Bagaimana saya tahu bahwa ini akan menghasilkan analisis panjang dari aplikasi untuk ancaman tersembunyi?



Kejutan yang menyenangkan


Sehari setelah beberapa panggilan yang meragukan, saya menerima pesan SMS yang menarik dengan konten berikut:



Setelah beberapa hari, saya menerima pesan serupa lainnya:



Terkejut bahwa seseorang entah bagaimana dapat mentransfer uang kepada saya di Internet (rupanya, saya satu-satunya yang lama - saya masih menggunakan buku tabungan kertas), saya mengklik tautan dalam SMS.

Setelah itu, saya ditawari mengunduh aplikasi Android (file apk). Dengan senang hati mengunduh file tersebut, saya melihat yang berikut:



Itu kredibel! Saya ingin segera menginstal semuanya dan mengakhirinya.
Tapi di sini, seperti biasa, sistem operasi Android yang mengganggu karena suatu alasan tidak memungkinkan saya untuk menjalankan file. "Sudah berikan aku uangnya!" Saya marah. Saya harus pergi ke pengaturan dan menyalakan beberapa opsi "Sumber tidak dikenal", apakah ponsel ini benar-benar bodoh pada tahun 2018? Ngomong-ngomong, ponsel saya adalah Xiaomi Remdi dengan Andoid 6.0.1 (catatan untuk teknisi).



Ini diikuti oleh serangkaian peristiwa aneh. Telepon terus melaporkan sumber yang tidak dapat diandalkan. Tapi Avito adalah sumber yang dapat diandalkan ! Saya harus mencari di google, mencari tahu bagaimana menyiasatinya, dan kemudian mematikannya di pengaturan. Segera muncul antivirus yang tidak saya instal.



Akhirnya, saya melihat jendela instalasi standar yang didambakan - tidak masuk akal untuk melihat izin saat ini , sekarang bahkan notebook tidak akan mulai sampai Anda memberikan akses penuh ke telepon. Momen menyenangkan penyelesaian instalasi aplikasi dan MULAI! Saya menantikan uang yang dijanjikan. Selain itu, aplikasi meminta hak administrator, yang dengan senang hati saya setujui. Sayangnya, aplikasi tersebut bertingkah aneh dan tidak mau membayar, dan segera itu benar-benar menghilang dari daftar aplikasi di layar umum.





Spoiler
Kemudian saya memeriksa telepon lain - Lenovo dengan Android 4.4.2. Daftar izin selama instalasi ternyata jauh lebih besar. Dan tidak ada Play Protection dan Anti-Virus tidak mengganggu, Anda hanya perlu mengizinkan instalasi dari sumber yang tidak dapat diandalkan.





Altruisme



Jadi, apa yang kita ketahui:

  • Butuh 20 menit untuk menginstal.
  • Saya belum menerima uangnya.

Saya pikir masalahnya adalah kesalahan kode, seperti yang sering terjadi pada programmer. Saya memutuskan untuk mengidentifikasi kesalahan apa yang terjadi ketika aplikasi berjalan di ponsel saya, dan mengirimkan laporan tentang hal itu kepada pengembang.

Spoiler
Ada begitu sedikit orang yang tidak tertarik sekarang, salah satunya adalah saya.

Jelas bahwa jenis aplikasi ini harus berfungsi ketika Internet terhubung, jadi untuk awalnya saya mencoba menganalisis lalu lintas antara ponsel dan server aplikasi.

Panduan teknis konfigurasi proxy
Anda dapat mendengarkan lalu lintas Internet di titik mana pun di sepanjang jalur dari telepon ke server, baik itu router rumah, penyedia Internet lokal, tulang punggung Internet atau server aplikasi.

Masalahnya:

  • Diperlukan akses ke peralatan ini.
  • Diperlukan untuk memisahkan lalu lintas aplikasi yang diinginkan dari yang lain.
  • Dalam hal enkripsi (dan pada tahun 2018 semuanya sudah dienkripsi) - pengetahuan tentang kunci diperlukan.

Saya memutuskan untuk masuk dengan cara yang lebih klasik dan mengkonfigurasi proxy di telepon dengan server proxy di laptop saya sendiri. Untuk mengurangi masalah enkripsi, saya memutuskan untuk mengimpor sertifikat saya, dan tidak memulai aplikasi lain untuk memisahkan lalu lintas aplikasi.
Saya memilih bersendawa suite sebagai program server proxy. Saya mengatur server proxy dan mengekspor sertifikat ke telepon saya.





Setelah mengatur perangkat lunak khusus, saya dapat melihat permintaan apa yang dikirim oleh aplikasi ke server:



Seperti yang Anda lihat, data tidak dikirim (kolom yang tidak diketahui ditunjukkan dalam kolom IP), apalagi, itu tidak dapat dianalisis karena adanya enkripsi tambahan di tingkat aplikasi. Dilihat oleh kesalahan, telepon tidak dapat menentukan alamat IP server dan subdomainnya https: //*.sky-sync.pw dengan nama domainnya.

Ini hanya bisa berarti opsi berikut:

  • Nama domain sudah tidak ada lagi
    • Itu diblokir oleh pemiliknya sendiri.
    • Dia diblokir oleh pendaftar pada pengaduan.
  • Masalah dengan server DNS
    • Server DNS tidak tahu alamatnya, karena pengembang dalam produksi meluncurkan alamat DNS lokal.
    • Server DNS secara khusus memblokir permintaan ini, yang tidak mengejutkan di era sensor internet.

Untuk memeriksa asumsi masalah dengan server DNS, saya mencoba membuat permintaan dari berbagai server DNS besar: Google, Yandex, OpenDNS (biasanya DNS lokal disensor):



Dapat dilihat bahwa tidak satupun dari mereka yang tahu tentang nama ini. Selanjutnya, saya melihat informasi whois tentang pendaftaran domain:



Penasaran: domain terdaftar, artinya, kemungkinan besar itu bukan lokal, tetapi karena domain tidak menyelesaikannya, domain itu mungkin diblokir oleh pencatat karena penyalahgunaan. Tapi untuk apa? Apa yang salah yang dia lakukan?

Untuk mengetahui apa aplikasi ini semua sama dan bagaimana saya dapat mengambil uang saya, saya memutuskan untuk menggunakan keajaiban teknik reverse.

Jurang maut


Jika Anda seorang humanis dan telah membaca sampai di tempat ini, maka ini bagus - untuk pengembangan selanjutnya Anda layak mendapatkan penghargaan anumerta .

Toolkit


Untuk mencari tahu apa yang "di balik tudung" aplikasi, kita perlu mengunduh alat khusus. Anda dapat mengunduhnya secara individual:

  • Buka kemasan apk
    • Klasik - ApkTool .
    • Anda dapat mengekstraknya dengan pengarsipan konvensional, tetapi kemudian semua sumber daya biner, termasuk aplikasi dan file Manifest, tidak dapat dibaca.
  • Pengurai kode smali
    • Standarnya adalah Dex2Jar , tetapi pelajari bahwa program ini sering bekerja dengan bengkok.
    • Anda perlu mendekati masalah ini dengan sangat hati-hati, karena dekompiler ke dekompiler berbeda, kami akan mempertimbangkannya nanti.
  • Suatu program untuk melihat kode yang didekompilasi , saya akan merekomendasikan jd-gui

Atau Anda bisa menggunakan produk, biasanya berbayar, di mana ada semuanya sekaligus. Saya lebih suka JebDecompiler : dia bisa mengirimkan aplikasi apk ke inputnya, dan dia akan mengatur semuanya dengan rapi di tab, plus lebih mudah untuk beralih antara kode Java smali dan yang telah di-decompile di dalamnya.

Secara terpisah, saya ingin mencatat:

  • Jika ada lib asli (folder / libs dalam struktur aplikasi), disassembler akan diperlukan.
  • Anda mungkin memerlukan seperangkat utilitas untuk bekerja dengan kode smali (dex-to-smali, smali-to-dex).
  • Menyoroti sintaks di Notepad ++ agar tidak mematahkan mata.

Hanya orang tua yang pergi berperang


Ulasan


Ketika Anda membuka kode yang didekompilasi, segera menjadi jelas bahwa itu dikaburkan.



Bagaimana saya mengerti itu?

  • Nama kelas yang dapat dibaca manusia
    isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq.wukovicq;
  • Kode tidak dapat dijangkau
     if(0 != 0) {</li> String v1 = "flnwznvh";</li> if(v1.length() != 661 && v1.charAt(0) == 104) {</li> v1.length();</li> } 
  • Enkripsi string
     vcgrnfjx.execSQL(nvhdzjfo.xipswfqb(new String[]{"f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6","378f40211b6e32a5406cd97e85bcf9ad","6378a459b1c20edf", "gexnfwok", "meazfhdp", "bsmotaxn"}) 

Tidak mungkin bahwa programmer sangat gila untuk awalnya mengembangkan kode. Kemungkinan besar, dia menggunakan salah satu obfuscator yang tersedia untuk umum. Langkah ini cukup umum untuk menyulitkan analisis kode untuk melindungi kekayaan intelektual, misalnya, tetapi seperti apa "twist" bagi peneliti.

Mari kita perhatikan fungsi enkripsi utama:



Fungsi enkripsi itu sendiri menerima 3 jalur input (jika lebih, sisanya tidak masuk akal):

  1. ciphertext
  2. kuncinya
  3. vektor inisialisasi untuk CBC - AES

Fungsi ini direferensikan dalam program setidaknya 213 kali:



Saya perhatikan bahwa ini adalah kunci penting untuk analisis kode normal. Selanjutnya Anda perlu berpikir kami memiliki cara-cara berikut untuk menganalisis program:

  1. Kembalikan logika fungsi, kumpulkan semua panggilan dalam analisis statis, dekripsi baris. Mungkin sulit dan panjang, tetapi akan memberikan hasil 100%.
  2. Buat perubahan pada kode-smali aplikasi, kompilasi lagi, jalankan aplikasi dan tangkap baris yang didekripsi di log. Ini mudah dilakukan, tetapi bagaimana aplikasi akan berperilaku dalam peluncuran tertentu tidak diketahui, dan Anda mungkin tidak melihat keseluruhan gambar (tidak mendapatkan panggilan ke semua fungsi). Selain itu, mungkin ada masalah dengan pemeriksaan sendiri oleh aplikasi sertifikat dan (atau) integritas.
  3. Jika sulit untuk mengembalikan logika fungsi, maka Anda dapat mengumpulkan semua panggilan fungsi dan menarik fungsi-fungsi ini sendiri dengan parameter yang diperlukan secara langsung dalam dinamika (menggunakan, misalnya, perangkat lunak Frida .

Kami akan memilih metode nomor 1 sebagai yang paling andal paling parah .

Deobfusiasi


Segera melakukan reservasi, deobfusi seringkali merupakan proses yang panjang dan melelahkan, jadi Anda perlu mengevaluasi kerangka waktu Anda dengan benar. Untuk analisis kami, cukup mendekripsi semua baris setidaknya dalam beberapa cara dan melakukannya dalam waktu minimum, bahkan dengan cara penopang, daripada mengacaukan selama sebulan demi pilihan ideal yang samar-samar.

Deobfuscasi kualitatif masuk akal dalam kasus rekayasa balik yang lengkap, misalnya, pencuri properti intelektual harus melakukan ini ketika mereka mencoba menyalin solusi pesaing, atau jika Anda sering harus menganalisis program yang diproses oleh satu obfuscator, tetapi ini bukan kasus kami.

Kode sumber setelah JEB Decompiler v.1.4 decompiler

Spoiler
 public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; String v7 = args[1]; String v0 = args[2]; if(v10 == null) { goto label_9; } if(v10.length() != 0) { goto label_11; } goto label_9; label_11: IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); goto label_15; } catch(NoSuchPaddingException v3) { } catch(NoSuchAlgorithmException v3_1) { } String v11 = ""; goto label_10; label_15: SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); if(v2.length <= 0) { goto label_48; } v4 = 0; v6 = v2.length - 1; label_29: if(v6 < 0) { goto label_38; } if(v2[v6] != 0) { goto label_33; } } catch(Exception v3_2) { goto label_51; } ++v4; label_33: --v6; goto label_29; label_38: if(v4 <= 0) { goto label_48; } try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch(Exception v3_2) { label_51: v11 = ""; goto label_10; } label_48: v11 = new String(v2); goto label_10; label_9: v11 = ""; label_10: return v11; } } 

Catatan Dekompiler
Ngomong-ngomong, dex2jar sering crash. Jadi, pada gambar di bawah ini dapat dilihat bahwa dex2jar versi 2.0 tidak dapat mengatasi dan hanya mengeluarkan kode smali.



Versi terbarunya, dikompilasi dari sumber, menghasilkan kode yang didekompilasi untuk fungsi ini, tetapi tidak dapat mendekompilasi banyak lainnya (itulah triknya).





Intinya: hati-hati mempertimbangkan pilihan dekompiler - ini akan menghemat banyak waktu dan akan lebih mudah daripada menganalisis kode smali.


Jadi, jika kita hanya menempelkan kode ini ke IDE sekarang, maka itu tidak akan berfungsi karena kesalahan.

Penting untuk diingat: Dekompiler tidak diharuskan untuk menghasilkan kode yang valid yang ditulis oleh pengembang. Dia hanya penyelamat dalam analisis dan membuat asumsi tentang bagaimana kode dapat ditulis. Dalam kebanyakan kasus, setelah optimisasi oleh kompiler, tugas mengembalikan kode asli benar-benar berhenti menjadi sepele.

Contoh dekompilasi yang buruk:

 if(v10 == null) { goto label_9; } if(v10.length() != 0) { goto label_11; } goto label_9; … label_9: v11 = ""; return v11; 

Kami melihat bahwa itu ternyata buruk dan tidak beroperasi. Kami menulis ulang:

 if ((v10 == null) || (v10.length() == 0)) { return ""; } 

Sekarang jauh lebih jelas, inilah cek input yang biasa. Dalam hal ini, kita perlu:

  • Ganti semua "goto" dengan konstruksi bahasa lain, seperti "Goto" telah lama menjadi operator yang tidak valid.
  • Ganti panggilan pustaka Android dengan panggilan pustaka Java (jika kami mencoba menjalankan kode dalam IDE Java).
  • Masukkan kelas dependen yang dirujuk oleh kode kami.
  • Pikirkan sendiri apa yang salah.

Sebagai hasilnya, kita mendapatkan:

 package com.company; //package isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq; import java.util.Base64; //import android.util.Base64; //import bnxvhlyg.nkhoirul.zfxogwqi.mdpqejcw.srnepbly.pcbvwxrs.vixdqclm.wnuqvrhp.bnvceayd.bwdoclkr; //   import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public abstract class Main { public Main() { super(); } //hex to ascii public static byte[] xkvasepi(String str) { byte[] v0 = null; if(str != null && str.length() >= 2) { int v2 = str.length() / 2; v0 = new byte[v2]; int v1; for(v1 = 0; v1 < v2; ++v1) { v0[v1] = ((byte)Integer.parseInt(str.substring(v1 * 2, v1 * 2 + 2), 16)); } } return v0; } public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; //text String v7 = args[1]; //key String v0 = args[2]; //IV //check if ((v10 == null) || (v10.length() == 0)) { return ""; } IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); } catch(NoSuchPaddingException v3) { return ""; } catch(NoSuchAlgorithmException v3_1) { return ""; } SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); //v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); v2=v1.doFinal(xkvasepi(v10)); //check if(v2.length <= 0) { return new String(v2); } } catch(Exception v3_2) { return ""; } v4=0; for (v6=v2.length-1;v6>=0;v6--){ if (v2[v6]==0) ++v4; } if(v4 > 0) { try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch (Exception v3_2) { return ""; } } v2 = Base64.getDecoder().decode(v2); return new String(v2); } public static void main(String[] args) { // write your code here System.out.println(podxiwkt(new String[] { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc", "637904cd08aeb2d3f6a21b5c7e84f519", "8f4c796d5a3120eb", "zcmwgvdn", "mkngbsyr", "rwcdaieu" })); } } { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc", "637904cd08aeb2d3f6a21b5c7e84f519", "8f4c796d5a3120eb", "zcmwgvdn", "mkngbsyr", "rwcdaieu"})); package com.company; //package isqpwcmx.isfdztgb.adscjobz.nxscomkr.jypbdxnt.utagwpym.wprtdznb.swldgrhm.yrbjpktq; import java.util.Base64; //import android.util.Base64; //import bnxvhlyg.nkhoirul.zfxogwqi.mdpqejcw.srnepbly.pcbvwxrs.vixdqclm.wnuqvrhp.bnvceayd.bwdoclkr; //   import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public abstract class Main { public Main() { super(); } //hex to ascii public static byte[] xkvasepi(String str) { byte[] v0 = null; if(str != null && str.length() >= 2) { int v2 = str.length() / 2; v0 = new byte[v2]; int v1; for(v1 = 0; v1 < v2; ++v1) { v0[v1] = ((byte)Integer.parseInt(str.substring(v1 * 2, v1 * 2 + 2), 16)); } } return v0; } public static String podxiwkt(String[] args) { int v6; int v4; byte[] v2; Cipher v1; String v10 = args[0]; //text String v7 = args[1]; //key String v0 = args[2]; //IV //check if ((v10 == null) || (v10.length() == 0)) { return ""; } IvParameterSpec v5 = new IvParameterSpec(v0.getBytes()); try { v1 = Cipher.getInstance("AES/CBC/NoPadding"); } catch(NoSuchPaddingException v3) { return ""; } catch(NoSuchAlgorithmException v3_1) { return ""; } SecretKeySpec v9 = new SecretKeySpec(v7.getBytes(), "AES"); int v11_1 = 2; try { v1.init(v11_1, ((Key)v9), ((AlgorithmParameterSpec)v5)); //v2 = Base64.decode(v1.doFinal(bwdoclkr.xkvasepi(v10)), 0); v2=v1.doFinal(xkvasepi(v10)); //check if(v2.length <= 0) { return new String(v2); } } catch(Exception v3_2) { return ""; } v4=0; for (v6=v2.length-1;v6>=0;v6--){ if (v2[v6]==0) ++v4; } if(v4 > 0) { try { byte[] v8 = new byte[v2.length - v4]; System.arraycopy(v2, 0, v8, 0, v2.length - v4); v2 = v8; } catch (Exception v3_2) { return ""; } } v2 = Base64.getDecoder().decode(v2); return new String(v2); } public static void main(String[] args) { // write your code here System.out.println(podxiwkt(new String[] { "b1acd584a6eae4ca6321b1f7cdf9ba9617112b4fb39e76c8def876346e3032fbd32b2d188a09715f27124c1bf9facfdc", "637904cd08aeb2d3f6a21b5c7e84f519", "8f4c796d5a3120eb", "zcmwgvdn", "mkngbsyr", "rwcdaieu" })); } } 

Kode ini berhasil dipenuhi. Setelah karyanya menjadi jelas, dapat disederhanakan dan disederhanakan, mengarah ke jenis yang seharusnya singkat yang ditulis oleh programmer (kecuali, tentu saja, tangannya pada awalnya tidak bengkok).

Catatan
Omong-omong, dalam hal ini, dekripsi string dapat ditunjukkan menggunakan banyak sumber daya online. Contoh memanggil string terenkripsi di dalam suatu program:



Di sini, vektor inisialisasi pertama-tama harus dikonversi ke format Hex:



Ganti semua nilai:



Dan pada akhirnya, decode dari base64:



Sebagai hasilnya, kami mendapatkan string yang biasa, dan panggilan tersebut mengambil tampilan yang bermakna.

Dengan demikian, Anda harus membaca seluruh kode dan mengumpulkan semua string terenkripsi, sekarang kita dapat mendekripsi secara independen. Poin penting di sini adalah bahwa pada tahap "memodifikasi dan mengomentari kode" kita dapat bekerja di tingkat smali dan di tingkat Java (decompiled smali).

Plus dari modifikasiCons Mods
SmaliAnda dapat membuat perubahan, mengkompilasi ulang dalam dex dan mendekompilasi dengan baris baruTidak selalu mudah untuk bekerja dengan kode smali. Jika perubahan tidak benar, aplikasi tidak akan dikompilasi
JawaSeringkali jauh lebih mudah untuk mengekstraksi data dari operasi tingkat yang lebih tinggi.Sebagian besar pemirsa kode Java tidak dapat mengedit.

Contoh garis lain

 vcgrnfjx.execSQL(nvhdzjfo.xipswfqb(new String[]{"f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6","378f40211b6e32a5406cd97e85bcf9ad","6378a459b1c20edf", "gexnfwok", "meazfhdp", "bsmotaxn"}) 

Dari sini, sangat mudah untuk mengambil parameter menggunakan ekspresi reguler, daripada menulis reguler pada kode berikut:

Contoh kode smali 1

 00000280 new-instance v13, Ljava/lang/StringBuilder; 00000284 invoke-direct {v13}, Ljava/lang/StringBuilder;-><init>()V 0000028A const/4 v14, 0x6 0000028C new-array v14, v14, [Ljava/lang/String; 00000290 const/4 v15, 0x0 00000292 const-string v16, "f741f04a4991fc2f0a0029f610bbd1c250dfe115fb7770b892f75d8718b822d273251013991b4407e224fa3f9d4e92f6" 00000296 aput-object v16, v14, v15 0000029A const/4 v15, 0x1 0000029C const-string v16, "378f40211b6e32a5406cd97e85bcf9ad" 000002A0 aput-object v16, v14, v15 000002A4 const/4 v15, 0x2 000002A6 const-string v16, "6378a459b1c20edf" 000002AA aput-object v16, v14, v15 000002AE const/4 v15, 0x3 000002B0 const-string v16, "gexnfwok" 000002B4 aput-object v16, v14, v15 000002B8 const/4 v15, 0x4 000002BA const-string v16, "meazfhdp" 000002BE aput-object v16, v14, v15 000002C2 const/4 v15, 0x5 000002C4 const-string v16, "bsmotaxn" 000002C8 aput-object v16, v14, v15 


Contoh kode smali 2

 0000008E new-array v0, v0, [Ljava/lang/String; 00000092 move-object/from16 v89, v0 00000096 const/16 v90, 0x0 0000009A const-string v91, "4500b5e2e2ad26b7545eb54d70ab360ae28c9d031e2afcc3f6a2b2ac488ea440" 0000009E aput-object v91, v89, v90 000000A2 const/16 v90, 0x1 000000A6 const-string v91, "da96f678922d4b07350b3a184ecc1f5e" 000000AA aput-object v91, v89, v90 000000AE const/16 v90, 0x2 000000B2 const-string v91, "0cf69e3d2745a1b8" 000000B6 aput-object v91, v89, v90 000000BA const/16 v90, 0x3 000000BE const-string v91, "jhiqsaoe" 000000C2 aput-object v91, v89, v90 000000C6 const/16 v90, 0x4 000000CA const-string v91, "khbqxurn" 000000CE aput-object v91, v89, v90 


Contoh kode smali 3

 00000D3E new-array v0, v0, [Ljava/lang/String; 00000D42 move-object/16 v298, v0 00000D48 const/4 v0, 0x0 00000D4A move/16 v299, v0 00000D50 const-string v0, "b286945744e085f4d5c19916fd261481" 00000D54 move-object/16 v300, v0 00000D5A move-object/from16 v0, v300 00000D5E move-object/from16 v1, v298 00000D62 move/from16 v2, v299 00000D66 aput-object v0, v1, v2 00000D6A const/4 v0, 0x1 00000D6C move/16 v299, v0 00000D72 const-string v0, "df6883742b2911ac5ac7b4dee065390f" 00000D76 move-object/16 v300, v0 00000D7C move-object/from16 v0, v300 00000D80 move-object/from16 v1, v298 00000D84 move/from16 v2, v299 00000D88 aput-object v0, v1, v2 00000D8C const/4 v0, 0x2 00000D8E move/16 v299, v0 00000D94 const-string v0, "90a463ce2df17b58" 00000D98 move-object/16 v300, v0 00000D9E move-object/from16 v0, v300 00000DA2 move-object/from16 v1, v298 00000DA6 move/from16 v2, v299 00000DAA aput-object v0, v1, v2 00000DAE const/4 v0, 0x3 00000DB0 move/16 v299, v0 00000DB6 const-string v0, "cupyzsgq" 00000DBA move-object/16 v300, v0 00000DC0 move-object/from16 v0, v300 00000DC4 move-object/from16 v1, v298 00000DC8 move/from16 v2, v299 00000DCC aput-object v0, v1, v2 


Seperti yang dapat kita lihat, variabel internal berubah, urutan perintah bervariasi, jumlah argumen juga bervariasi, di samping itu, dalam program, fungsi dekripsi disebut tidak secara langsung, tetapi melalui fungsi-fungsi layer. Cobalah untuk menulis aturan sendiri untuk mencari konstruk ini, menghindari kesalahan dalam menangkap string dari fungsi lain, dan melakukan semua ini dengan cepat ( semoga sukses ).

Rencana Perangkap:

  1. Kami akan mengekstrak semua nilai dari kode yang didekompilasi.
  2. Dekripsi.
  3. Ganti ciphertext dengan yang terbuka di kode smali. Kami mengganti, misalnya, bukan operator pertama. (Akan lebih profesional untuk memotong seluruh pemanggilan fungsi dan meninggalkan string yang didekripsi kembali, tetapi sekali lagi ada risiko besar melanggar program).
  4. Mari kita kumpulkan kode smali ke file dex.
  5. Akan lebih mudah untuk melihat lebih jauh ke dalam penganalisa kode, di mana kita mulai.

Jika Anda mengumpulkan semua kode yang didekompilasi ke dalam satu file, Anda mendapatkan sekitar 20.000 baris, yang untuk analisis manual memerlukan banyak waktu, yang harganya jelas lebih mahal daripada sepatu bot yang saya pasang untuk dijual. Pertama, kumpulkan semua garis dengan ekspresi reguler.



Kami melihat 593 pertandingan, ditambah selusin yang tidak termasuk dalam peraturan ini, keluarga memiliki domba hitamnya . Contoh:



Sortir, saring, total 422 garis unik:



Kami melewati fungsi dekripsi yang kami kembalikan sebelumnya. Hasil:



Ganti ciphertext dengan yang terbuka di smali-code menggunakan Python:

 import os words_replace=dict() words_replace["0018aacad3d146266317d8d8c51785fd"]="imei" words_replace["016d15e4d0a72667c61428e736a6f3b8"]="WakeLock" words_replace["032c534efb6c9990cd845a08c5a08b95"]="inbox" #…  .. # smali- #      def change(path): print("file="+path) file_handle = open(path, 'r') context_full = file_handle.read() file_handle.close() for i in words_replace: context_full=context_full.replace(i, words_replace[i]) #print (i+""+words_replace[i]) file_handle = open(path, 'w') context_full = file_handle.write(context_full) file_handle.close() #      smali- for top, dirs, files in os.walk('C:\\work\\test'): for nm in files: path=os.path.join(top, nm) print (path) change(path) 

Kami mengumpulkan file smali dalam dex:



Sekarang ini entah bagaimana dapat dianalisis (dengan membaca argumen pertama dari keseluruhan konstruksi):



Analisis


Jadi, kami memiliki 20.000 lebih atau kurang baris kode yang dapat dibaca, kami tidak perlu melakukan analisis penuh. Penting untuk memahami fungsi secara keseluruhan. Di sini, pada kenyataannya, hanya kemampuan untuk membaca kode sumber Java yang diperlukan. Telusuri kode, lihat referensi silang, ganti nama variabel dan fungsi.

Apa cara terbaik untuk menganalisis aplikasi Android, terutama yang besar?

Opsi 1: Anda dapat pindah dari file Manifest

Misalnya, secara berurutan dari LAUNCHER cobalah untuk melepas seluruh rantai panggilan. Omong-omong, jangan lupa bahwa masih ada "Penerima" dan "Layanan" yang dapat mengubah eksekusi linear dari program.



File Manifes Lengkap
 <?xml version="1.0" encoding="utf-8" standalone="no"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="internalOnly" package="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft"> <uses-permission android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.permission.C2D_MESSAGE"/> <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/> <uses-permission android:name="android.permission.SEND_SMS"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> <uses-permission android:name="android.permission.RECEIVE_SMS"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.QUICKBOOT_POWERON"/> <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <permission android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.permission.C2D_MESSAGE" android:protectionLevel="signature"/> <application android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/tgiwmpqy" android:noHistory="true"> <activity android:configChanges="orientation" android:excludeFromRecents="true" android:label="@string/tgiwmpqy" android:launchMode="singleTop" android:name="zemquyog.csrtmnak.xrkfygen.wkahrnjd.acnfunjh.rgipxbuf.lruiwxeg.blqndche.dcjihbou" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:configChanges="orientation" android:launchMode="singleTop" android:name="xbfrscou.hxrvwnoi.djvpcqri.enlnrfio.aoegxbiu.heywzmnb.znfnxcht.nazcxobq" android:screenOrientation="portrait"/> <activity android:configChanges="orientation" android:launchMode="singleTop" android:name="hcfkagds.timkagsd.oetvghzr.fcioynvl.psynofdj.slcghdjz.tapnwsdk.gzvwnban.htenafdb.qwebhzgy" android:noHistory="true" android:screenOrientation="portrait"/> <activity android:configChanges="orientation" android:excludeFromRecents="true" android:launchMode="singleTop" android:name="njfbwmre.voefarqx.ftuxvngl.wrmshxqj.zdenywgn.eiwyunlg.jysgkbam.yrijthab.vstqxpuo.iplamgxf" android:priority="2147483647" android:screenOrientation="portrait"/> <receiver android:name="gfbaznoc.asyoqtnm.kbetoqca.mqysobzu.gqwfibrv.dorxijuk.wgzkmiep.ywnnurzv.csfpqhrn" android:permission="android.permission.BIND_DEVICE_ADMIN"> <meta-data android:name="@string/pkzrlscm" android:resource="@xml/ynqukvnb"/> <intent-filter android:priority="2147483646"> <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> </intent-filter> </receiver> <receiver android:name="ykwbodxc.gymjhibn.kgmdfqor.hbasvmfz.yegkmaif.ortzknvm.quplincn.cuxytvhs.fqonzuts.cyuoxgqi.znumwyct" android:permission="com.google.android.c2dm.permission.SEND"> <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE"/> <action android:name="com.google.android.c2dm.intent.REGISTRATION"/> <action android:name="com.google.android.c2dm.intent.UNREGISTRATION"/> <category android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft"/> </intent-filter> </receiver> <receiver android:enabled="true" android:exported="true" android:name="kqwihjot.nvkqjloc.grjnyknm.owydvckh.mugknwdx.enhcyvja.mhvbpcue.ztbwjhfo"> <intent-filter android:priority="2147483646"> <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/> <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/> <action android:name="android.intent.action.QUICKBOOT_POWERON"/> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.USER_PRESENT"/> <action android:name="android.intent.action.BATTERY_OKAY"/> <action android:name="android.intent.action.BATTERY_LOW"/> <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/> <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/> <action android:name="android.intent.action.APP_ERROR"/> <action android:name="android.intent.action.HEADSET_PLUG"/> <action android:name="android.intent.action.PHONE_STATE"/> <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> <action android:name="android.intent.action.TIME_TICK"/> <action android:name="android.intent.action.SCREEN_ON"/> <action android:name="android.intent.action.SCREEN_OFF"/> <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> <action android:name="android.net.wifi.WIFI_STATE_CHANGED"/> <action android:name="android.intent.action.DREAMING_STOPPED"/> <category android:name="android.intent.category.HOME"/> </intent-filter> </receiver> <receiver android:name="btnsxnuz.wmjizbky.lynvjxqz.zinomjuv.yizlgcnf.qwoikgnc.wnrskjea.wfqgmeny.lcgvqrms.ocwkgblp"> <intent-filter android:priority="2147483646"> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> <service android:name="ltvsrezg.ehxndrat.twnnyxrf.nqynefws.dhbalcnr.ynjkuxod.nhoxmsbq.nackoyhn.voycgfek.znhwkqba.taxvnfyn"/> <service android:name="rbnakfzo.qsreiubk.pwvlnngs.twoxnhfv.mftarcnd.pfioxcub.xjlaftqr.nxrqvlwh"/> <service android:enabled="true" android:name="xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft.ugshpjvo"/> </application> </manifest> 


Opsi 2: Anda dapat berpindah dari garis yang menarik



Bagian dari string yang didekripsi
 system_update.apk () () , error = , unregistered = ,  .permission.C2D_MESSAGE //sky-sync.pw/ //sms/inbox /system_update.apk ALLCONTACTS ALLMSG AUTHENTICATION_FAILED Acquiring wakelock Application BLOCKER_BANKING_START BLOCKER_EXTORTIONIST_START BLOCKER_STOP BLOCKER_UPDATE_START Banking CHANGE_GCM_ID CONTACTS CONTACTS_PRO CREATE TABLE IF NOT EXISTS END Error|No process list|No access Extortionist Foreground GCM returned invalid number of GCMBaseIntentService GCMBroadcastReceiver GCMIntentService- GCMRegistrar GCM_LIB GET MESSAGE Mobile Network NEWMSG Not retrying failed operation ONLINE PAGE POST Process finished with exit code 0 RESTART Received deleted messages Registering receiver Releasing wakelock SERVICE_NOT_AVAILABLE SSL START STOP Saving regId on app version Scheduling registration retry, backoff = Setting registeredOnServer status as Stop System UNBLOCK UPDATE UPDATE_PATTERNS URL UTF-8 Update WakeLock Wakelock reference is null Wi-Fi WiMax _success add_msg_ok address android.intent.action.QUICKBOOT_POWERON answer_text answer_to api_url app appVersion application application/vnd.android.package-archive apps_list ask backoff_ms blocker blocker_banking blocker_banking_autolock blocker_banking_forced_access blocker_banking_success blocker_extortionist blocker_extortionist_autolock blocker_extortionist_forced_access blocker_extortionist_success blocker_update blocker_update_forced_access blocker_update_success body build callback cardSuccess check com.android.settings com.google.android.c2dm.intent.RECEIVE com.google.android.c2dm.intent.REGISTER com.google.android.c2dm.intent.REGISTRATION com.google.android.c2dm.intent.UNREGISTER com.google.android.gcm com.google.android.gcm.intent.RETRY com.google.android.gsf com.htc.intent.action.QUICKBOOT_POWERON command command_receive contactslist country data date delete deleted_messages device_block disableDataConnectivity enableDataConnectivity error failure file deleted. first_start force-locked gafzpjxb.cix gcm gcm_id gcm_register gcm_register_ok getITelephony get_message_list id integer primary key autoincrement, id=? imei immunity inbox init_bootable init_imei is_admin is_awake_display is_imunnity is_locked is_network_type is_top_activity job job_date job_id komgejif.hqr locked message message_delivered message_type method model msg msg_id msglist name not nypjtinq.nvp ok onServer onServerExpirationTime onServerLifeSpan operator org.android.sys.admin.disabled org.android.sys.admin.enabled org.android.sys.admin.request org.android.sys.command.receive org.android.sys.launch.first org.android.sys.sms.pro.sent org.android.sys.sms.push org.android.sys.sms.sent outbox page params pattern patterns personal phone phone_list privet process_list protocol qwertyuiopasdfghjklzxcvbnm receive regId regex register register_ok registrationId = registration_id repeat resetting backoff for ru save_contacts_list save_message_history sender sent sent_status sid ss status stop_blocker text text, text/html time token total_deleted type unknown unregistered until url useragent utf-8 value version xpls yes   !     ...                   !  ?                ! 

Opsi 3: Anda dapat beralih dari sumber daya yang menarik (aset, lib)

Dalam hal ini, opsi 3 lebih disukai. Ada tiga file html yang menarik di folder / assets (apk container). Inilah pandangan mereka di browser:





Tampaknya diragukan untuk program transfer pembayaran Avito resmi, bukankah begitu menurut Anda? Mari melacak apa yang terjadi ketika Anda menekan tombol untuk mengirim data perbankan pada halaman dengan logo Sberbank. JavaScript memanggil fungsi sendCardData() :



Dan kemudian ditransfer ke kode Java melalui panggilan ok.performClick() :



Dalam kode Java, pemrosesan dilakukan:



Selanjutnya, semua ini dienkripsi di kelas mcrypt :



Di dalam fungsi, data dienkripsi dengan cara yang sama seperti yang dipertimbangkan sebelumnya:



Tapi untuk yang lain, kuncinya terprogram:



Kami mencoba mendekripsi melalui sumber online:



Dan mengkonversi dari base64. Sukses! Kami dapat mendekripsi semua data aplikasi: diuji pada lalu lintas yang ditangkap sebelumnya.

Aplikasi melaporkan ke server tentang semua acara
 { "sid":15, "imei":"861117030537111", "phone":"System", "message":"     22.10.2018 23:30:47", "time":"1540240247", "msg_id":1, "status":"unknown", "type":"inbox", "method":"message" } 

Ini juga secara berkala mentransfer semua aplikasi yang berjalan
 { "sid": 15, "imei": "861117030537111", "country": "ru", "operator": "MTS RUS", "phone": "", "model": "Xiaomi Redmi 3X", "version": "6.0.1", "application": "", "build": "30.0.2", "process_list": [ "Background|com.android.bluetooth|com.android.bluetooth.hid.HidService", "Background|com.android.settings:remote|com.android.settings.wifi.MiuiWifiService", "Background|com.android.phone|org.codeaurora.ims.ImsService", "Background|system|com.qualcomm.location.LocationService", ..., "Background|xfmpuwon.mtnbupnc.ihqdgjal.ndgmqawx.bjunzerq.cznfpnoq.fzevcuym.jmpdiqft|ltvsrezg.ehxndrat.twnnyxrf.nqynefws.dhbalcnr.ynjkuxod.nhoxmsbq.nackoyhn.voycgfek.znhwkqba.taxvnfyn" ], "apps_list": [ "com.introspy.config", "com.google.android.youtube", "com.google.android.googlequicksearchbox", "org.telegram.messenger", ..., "com.google.android.inputmethod.latin", "jakhar.aseem.diva" ], "method": "register" } 

Jika saya memiliki jendela entri data bank dalam dinamika, maka data akan menjadi lalu lintas. Dengan demikian, kita dapat menyimpulkan bahwa ini adalah aplikasi "phishing".

Mereka yang berhati-hati memperhatikan bahwa ada beberapa izin dalam file Manifest, dan aplikasi memiliki fungsi yang lebih kaya. Kami akan melakukan analisis mendalam tentang fungsi di artikel lain. Sementara itu, sukseslah!

Kesimpulan


Saya kecewa karena saya tidak menjual sepatu bot. Dan kesimpulannya adalah sebagai berikut:

  • Jangan menjual sepatu bot di Avito
  • Jangan mengklik tautan yang tidak jelas (bahkan jika dari teman dan bahkan jika "Anda perlu segera meminjam 100 rubel - masalah hidup dan mati")
  • Jangan mengunduh aplikasi selain dari Google Play atau AppStore
    • Putuskan sambungan instalasi dari "sumber tidak dapat diandalkan" jika Anda benar-benar tidak mengerti apa itu.
    • Jangan lepaskan "Play Protection".
    • Ingatlah bahwa mungkin ada malware di Google Play
  • Instal antivirus di telepon (ini benar-benar berfungsi).
  • Jika Anda seorang pengembang, jangan mengaburkan kode, biarkan orang memastikan niat baik Anda ( hanya bercanda )
  • Jika Anda seorang peneliti, jangan bekerja untuk makanan, menganalisis aplikasi di waktu luang Anda dan menerbitkan laporan. Bersama kita akan membuat dunia menjadi tempat yang lebih baik.

PS Saya mencoba untuk menulis artikel sedikit dalam format lucu dan mengirimkannya sesederhana mungkin, karena bahkan saya tidak ingin mungkin membaca pada hari Jumat sebuah longride yang serius dengan judul "Reverse Engineering Aplikasi Android Berbahaya Kumbang".

Source: https://habr.com/ru/post/id430074/


All Articles