Akaun Sidang Kemuncak Pengarang: windy_ll
Aplikasi ebpf dalam keselamatan Android: Digabungkan dengan pengikat untuk melengkapkan kotak pasir pemeriksaan tingkah laku (Bahagian 1) 1. Pengenalan ringkas kepada IPC
IPC ialah singkatan kepada Inter-Process Communication, yang bermaksud komunikasi antara proses atau komunikasi silang proses Ia merujuk kepada proses pertukaran data antara dua proses.
Bilakah Android memerlukan komunikasi silang proses? Apabila Android meminta perkhidmatan sistem, terdapat keperluan untuk komunikasi silang proses, seperti mengakses buku alamat telefon mudah alih, mendapatkan lokasi, dsb. Matlamat artikel ini adalah untuk melaksanakan kotak pasir mudah untuk menangkap tingkah laku ini
2. Pengenalan ringkas kepada binder
Binder ialah satu bentuk komunikasi silang proses dalam Android, yang boleh difahami sebagai kaedah pelaksanaan khusus IPC
3 Pengenalan ringkas kepada ServiceManager
ServiceManager ialah perkhidmatan sistem yang sangat penting dalam Android Seperti yang anda boleh ketahui daripada namanya, ia digunakan untuk mengurus perkhidmatan sistem
ServiceManager dimulakan oleh proses init
ServiceManager bertanggungjawab untuk fungsi berikut: pendaftaran dan carian perkhidmatan, komunikasi antara proses, permulaan dan rangsangan perkhidmatan sistem, dan menyediakan contoh senarai perkhidmatan sistem
Pemandu pengikat menentukan butiran komunikasi asas, jadi ServiceManager adalah setara dengan navigasi, memberitahu komunikasi khusus bagaimana hendak pergi, ke mana hendak ke sana dan sebagainya
4. Analisis Komunikasi 4.1 Analisis Lapisan JAVA Panggilan Pelanggan
Ambil fungsi getConnectInfo kelas WifiManager (fungsi ini mendapatkan maklumat wifi) sebagai contoh untuk analisis
Ctrl+klik kiri untuk melihat rujukan, anda boleh mendapati bahawa fungsi itu ditakrifkan dalam kelas .wifi.WifiManager, seperti yang ditunjukkan di sebelah kanan:
Seperti yang anda lihat dari gambar di atas, kod khusus fungsi getConnectInfo hanya mempunyai satu ayat: thrownewRuntimeException("Stub!");, yang memberitahu kita bahawa fungsi ini digantikan dan dilaksanakan oleh kelas yang sama dalam fungsi rom yang ditakrifkan di sini diperlukan untuk penyusunan (PS: Untuk rujukan), kita boleh mencari kelas ini dalam rangka kerja direktori/base/wifi/java/amdroid/net/wifi dalam kod sumber android, dan kemudian cari pelaksanaan khusus bagi fungsi tersebut, seperti yang ditunjukkan di sebelah kanan:
Anda boleh mendapati bahawa fungsi ini memanggil fungsi getConnectionInfo bagi IWifiManager Anda boleh mencari fail IWifiManager.aidl dalam direktori frameworks/base/wifi/java/amdroid/net/wifi Fungsi getConnectionInfo ditakrifkan dalam aidl di sebelah kanan:
Satu konsep perlu diperkenalkan di sini---AIDL ialah bahasa soket android, yang digunakan untuk mendedahkan soket perkhidmatan android untuk merealisasikan panggilan fungsi silang. AIDL akan menghasilkan dua kelas semasa menyusun, iaitu Kelas Stub dan Proksi ialah manifestasi lapisan perwakilan bahagian pelayan, dan Proksi ialah contoh yang diperoleh oleh pelanggan Android yang melaksanakan IPC melalui corak reka bentuk proksi
Tulis fail aidl di bawah dan kemudian jana kod java yang sepadan untuk melihat cara panggilan dilaksanakan Mula-mula, kami mencari projek rawak dalam Android Studio, dan kemudian buat fail aidl baharu, seperti yang ditunjukkan di sebelah kanan:
.Selepas itu, Build->MakeProbject boleh dijana laluan yang dijana terletak dalam build/generated/aidl_source_output_dir/debug/out/nama pakej, seperti yang ditunjukkan di sebelah kanan:
Memerhatikan fail java yang dihasilkan, kita dapati bahawa kelas Proksi telah pun dijana Dalam kelas Proksi kita boleh mencari fungsi yang kita takrifkan, seperti yang ditunjukkan dalam gambar di sebelah kanan:
Mari analisa fungsi ini secara terperinci Mula-mula, instance Parcel dijana melalui fungsi peroleh, dan kemudian fungsi siri tulis Parcel dipanggil untuk menulis Walaupun ia adalah proses bersiri, fungsi transaksi IBinder kemudiannya dipanggil, dan fungsi itu dikesan dan dianalisis. Fail java boleh didapati dalam rangka kerja direktori/base/core/java/android/os, seperti yang ditunjukkan di sebelah kanan:
Boleh didapati bahawa IBinder hanyalah perintah padam soket linux, yang mentakrifkan kaedah transaksi ini mempunyai 4 parameter Kod parameter pertama ialah nombor fungsi dalam panggilan jauh kami, ia akan Cari pembolehubah statik dalam kelas Stub, jadi anda boleh menghuraikan fungsi yang dipanggil _data dan _reply kedua dan ketiga ialah parameter yang dihantar dan nilai yang dikembalikan, yang kesemuanya adalah data bersirilapisan Kernel linux android , yang terakhir. bendera parameter menunjukkan sama ada perlu untuk menyekat dan menunggu keputusan, 0 bermakna menyekat dan menunggu, dan 1 bermakna kembali serta-merta.
Selepas carian global, anda boleh mendapati bahawa kelas BinderProxy dalam direktori yang sama melaksanakan soket ini (PS: Perlu diingat bahawa terdapat juga kelas Binder dalam direktori yang sama, yang juga melaksanakan soket ini, tetapi kelas Binder adalah pelaksanaan sebelah pelayan, bukan pelaksanaan Pelanggan), seperti yang ditunjukkan dalam gambar di sebelah kanan:
Selepas menganalisis fungsi ini, kita dapati bahawa ia akhirnya bergerak ke arah fungsi transactNative Pada ketika ini, analisis lapisan Java klien komunikasi IPC telah selesai
4.2 Analisis panggilan pelanggan Lapisan asli
Cari fungsi transactNative secara global dan anda boleh mendapati bahawa fungsi itu mendaftarkan maklumat dalam lapisan asli, seperti yang ditunjukkan di sebelah kanan:
Jejak fungsi android_os_BinderProxy_transact Anda boleh mendapati bahawa fungsi tersebut mula-mula memperoleh objek BpBinder melalui getBPNativeData(env,obj)->mObject.get(), dan kemudian memanggil fungsi transaksi BpBinder, seperti yang ditunjukkan di sebelah kanan:
Teruskan membuat susulan dan anda akan mendapati bahawa anda telah memasuki fungsi transaksi IPCThreadState, seperti yang ditunjukkan di sebelah kanan:
Menyusul, anda boleh mendapati bahawa fungsi writeTransactionData pertama kali dipanggil, yang digunakan untuk mengisi struktur binder_transaction_data dan bersedia untuk menghantarnya kepada pemacu pengikat Kemudian ia memanggil waitForResponse untuk menunggu pemulangan, seperti yang ditunjukkan di sebelah kanan:
Mengikuti fungsi waitForResponse, kita dapati bahawa perkara yang paling penting tentang fungsi ini ialah memanggil fungsi talkWithDriver Menganalisis fungsi talkWithDriver, kita boleh mendapati bahawa ioctl akhirnya dipanggil, seperti yang ditunjukkan di sebelah kanan:
.Setakat ini, analisis lapisan asli pelanggan selesai
4.3 Analisis lapisan kernel (analisis pemacu pengikat)
Pada ketika inilapisan kernel linux android, program ebpf kami boleh mula menangkap dan menghuraikan format data
Apabila lapisan pengguna memanggil ioctl, ia akan memasuki keadaan kernel dan memasuki fungsi kernel binder_ioctl (ps: deskriptor yang sepadan boleh didapati dalam binder.c dalam kod sumber peranti kernel Analyse the binder_ioctl function and you will find the fungsi utama fungsi ini. Untuk menghantar data antara dua proses, perintah ioctl data komunikasi kami ialah BINDER_WRITE_READ Apabila arahan ini ditemui, fungsi binder_ioctl_write_read akan dipanggil, seperti yang ditunjukkan di sebelah kanan:
.Berikutan fungsi binder_ioctl_write_read, kita dapati fungsi membaca nilai alamat yang ditunjuk oleh parameter arg jenis unsignedlong terlebih dahulu ke dalam struktur binder_write_read, menunjukkan bahawa apabila arahan ioctl ialah BINDER_WRITE_READ, parameter yang dihantar adalah penunjuk yang menunjuk ke struktur binder_write_read , seperti yang ditunjukkan di sebelah kanan:
Walaupun analisis keadaan kernel kami boleh diselesaikan di sini, kami telah memerhatikan data yang kami mahu, iaitu struktur binder_write_read Anda boleh melihat definisi struktur ini, seperti yang ditunjukkan di bawah:
<em style="cursor: pointer;font-size: 12px"> 复制代码</em><em style="cursor: pointer;font-size: 12px"> 隐藏代码<br></em><code><span>struct</span> <span>binder_write_read</span> {<br> <span>binder_size_t</span> write_size; <span>/* 写内容的数据总大小 */</span><br> <span>binder_size_t</span> write_consumed; <span>/* 记录了从缓冲区读取写内容的大小 */</span><br> <span>binder_uintptr_t</span> write_buffer; <span>/* 写内容的数据的虚拟地址 */</span><br> <span>binder_size_t</span> read_size; <span>/* 读内容的数据总大小 */</span><br> <span>binder_size_t</span> read_consumed; <span>/* 记录了从缓冲区读取读内容的大小 */</span><br> <span>binder_uintptr_t</span> read_buffer; <span>/* 读内容的数据的虚拟地址 */</span><br>};</code>
Struktur ini digunakan untuk menerangkan data yang dihantar semasa komunikasi antara proses Apabila kita membaca paket komunikasi yang dihantar dari klien ke pelayan, kita hanya perlu memberi perhatian kepada write_size, write_consumed, dan write_buffer antaranya senarai terpaut. Medan ini mengandungi struktur data_transaksi_pengikat.
Secara amnya, pemandu akan menghantar berbilang arahan dan kombinasi alamat pada satu masa, dan kita perlu mencari arahan yang sepadan dengan struktur binder_transaction_data Dalam fail pengepala binder.h, kita boleh mencari semua arahan yang ditakrifkan oleh pemacu (+ /refs /heads/android-mainline/include/uapi/linux/android/binder.h), seperti yang ditunjukkan di sebelah kanan:
可以发觉,BC_TRANSACTION和BC_REPLY指令都对应着binder_transaction_data结构体参数,但我们只须要顾客端发往驱动的数据包,所以我们只须要BC_TRANSACTION指令对应的参数即可
经过前面的剖析,我们找到了我们须要的核心数据---binder_transaction_data结构体,现今来看一下该结构体的定义,定义如下:
<em style="cursor: pointer;font-size: 12px"> 复制代码</em><em style="cursor: pointer;font-size: 12px"> 隐藏代码<br></em><code><span>struct</span> <span>binder_transaction_data</span> {<br> <span>union</span> {<br> __u32 handle;<br> <span>binder_uintptr_t</span> ptr;<br> } target; <span>/* 该事务的目标对象 */</span><br> <span>binder_uintptr_t</span> cookie; <span>/* 只有当事务是由Binder驱动传递给用户空间时,cookie才有意思,它的值是处理该事务的Server位于C++层的本地Binder对象 */</span><br> __u32 code; <span>/* 方法编号 */</span><br> __u32 flags;<br> <span>pid_t</span> sender_pid;<br> <span>uid_t</span> sender_euid;<br> <span>binder_size_t</span> data_size; <span>/* 数据长度 */</span><br> <span>binder_size_t</span> offsets_size; <span>/* 若包含对象,对象的数据大小 */</span><br> <span>union</span> {<br> <span>struct</span> {<br> <span>binder_uintptr_t</span> buffer; <span>/* 参数地址 */</span><br> <span>binder_uintptr_t</span> offsets; <span>/* 参数对象地址 */</span><br> } ptr;<br> __u8 buf[<span>8</span>];<br> } data; <span>/* 数据 */</span><br>};</code>
有了该数据结构linux安装,我们就可以晓得顾客端调用服务端的函数的函数、参数等数据了
五、实现疗效
PS:整套系统用于商业,就不做开源处理了,这儿只给出核心结构体复印的截图,就不再发后端的截图了
读取手机通信录(ps:这儿读取下来的数据采用了Toast复印的,所以将捕捉到的Toast相应的通讯包也一起复印了下来,下同):
获取地理位置:
获取wifi信息:
六、其他
里面当然我们只剖析到了发送部份,反过来,虽然我们也可以读取返回包甚至于更改返回包,就可用于对风控的对抗,比如在内核态中更改APP恳求的设备标示信息(其实前提是app走系统提供的驱动通讯),亦或则用于逆向的工作,比如过root检查等等。
这部份验证代码就暂不放下来了,感兴趣的可以自己实现一下
-官方峰会
公众号设置“星标”,您不会错过新的消息通知
如开放注册、精华文章和周边活动等公告
Atas ialah kandungan terperinci lapisan kernel linux android Komunikasi silang proses Android: pengenalan kepada IPC, Binder dan ServiceManager. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!