Setelah menguasai alat-alat yang disebutkan di atas, Anda dapat mulai mengembangkan plug-in. Pertama, Anda harus memikirkan kebutuhan yang perlu diterapkan oleh plugin dan menemukan tampilan dan pengontrol yang relevan. Langkah pertama adalah menggunakan class-dump untuk menganalisis APP yang di-shell. Class-dump dapat menganalisis semua nama kelas dan deklarasi metode berdasarkan tabel simbol di file Mach-O.
~ » ./class-dump -H -o /header/path WeChat
Setelah mengekspor file header, tarik file header ini ke dalam proyek XCode untuk memfasilitasi pencarian selanjutnya.
Setelah Anda memiliki file header, Anda perlu menguncinya ke kelas pengontrol antarmuka yang relevan. Gunakan OpenSSH untuk terhubung ke iPhone yang sudah di-jailbreak dengan Mac, gunakan Cycript untuk menyuntikkan prosesnya, panggil metode [[UIApp keyWindow] recursiveDescription]
untuk mendapatkan hierarki tampilan saat ini, dapatkan alamat tampilan dan panggil [#0x2b2b2b00 nextResponder]
berulang kali metode [#0x2b2b2b00 nextResponder]
, Anda akhirnya akan mendapatkan kelas pengontrol antarmuka.
Pada titik ini, umumnya terdapat dua titik masuk untuk melanjutkan analisis sebaliknya.
Jika kita ingin operasi Hook dipicu oleh peristiwa sentuh pada tampilan di antarmuka, biasanya ada dua cara untuk menemukan tindakan tersebut:
[button allTargets]
untuk mendapatkan alamat Target, [button actionsForTarget: Targets forControlEvent: [button allControlEvents]]
untuk mendapatkan metode tindakan yang sesuai.Sumber data pengontrol dapat digunakan selama penulisan plug-in. Sumber data dapat diperoleh dengan menganalisis daftar variabel anggota kelas pengontrol, atau dengan menganalisis metode proksi sumber data dari beberapa tampilan (seperti TableView). Dengan sumber data, informasi yang lebih kaya tentang pengontrol dapat diperoleh.
Sebuah plug-in sederhana mungkin dapat mengunci target dan mengaitkannya hanya dengan menggunakan Cycript, tetapi dalam banyak kasus fungsi target memiliki banyak parameter dan logikanya relatif rumit. Jika Anda ingin mengetahui detail implementasi internalnya, Anda harus gunakan artefak analisis statis IDA ---- Ini dapat mendekompilasi kode yang ditulis dalam Objective-C menjadi kode perakitan, dan detail implementasi fungsi akan terlihat jelas dalam sekejap. Saat menganalisis hasil pembongkaran, karena karakteristik penyampaian pesan dari Objective-C, pengiriman pesan umum sebenarnya memanggil fungsi objc_msgSend
. Ingat saja arti setiap parameter objc_msgSend
dan konvensi pemanggilan arsitektur ARM, dan Anda berhasil memulihkan pemanggilan fungsi dari kode rakitan. Konvensi pemanggilannya adalah bahwa empat parameter pertama dari fungsi dilewatkan menggunakan register umum R0-R3, lebih banyak parameter yang dimasukkan ke dalam tumpukan, dan nilai yang dikembalikan disimpan dalam register R0. Kemudian [aObject aMessage: arg1];
sesuai dengan objc_msgSend(aObject, aMessage, arg1);
R0 menyimpan alamat penerima pesan, R1 menyimpan pemilih, dan R2 menyimpan alamat parameter pertama. Format pesan untuk parameter lainnya adalah sebagai berikut:
objc_msgSend(R0, R1, R2, R3, *SP, *(SP + sizeOfLastArg), …)
Melalui format di atas, logika dalam suatu fungsi tertentu dapat diurai langkah demi langkah. Jika ini dilengkapi dengan debugging satu langkah dinamis LLDB dan debugging pelacakan breakpoint di alamat pernyataan Majelis tertentu, ini akan membantu untuk memahami detail implementasi fungsi.
Ada banyak solusi untuk fungsi target Hook, tetapi pada prinsipnya semuanya didasarkan pada karakteristik dinamis Objective-C dan menggunakan Metode Swizzling untuk menggantikan implementasi aslinya. Kali ini kami akan memperkenalkan penggunaan perpustakaan CaptainHook secara detail. Pustaka ini diimplementasikan berdasarkan MSHookMessageEx()
di Substrat Cydia. Deklarasi fungsi ini adalah:
void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old);
Setelah iOSOpenDev diinstal, Anda dapat menggunakan template CaptainHook Tweak untuk membuat proyek. Pustaka CaptainHook memperkenalkan sintaks baru untuk menulis fungsi Hook. Pertama, muat kelas tempat fungsi yang akan di-Hooked berada di CHConstructor()
, seperti CHLoadLateClass(UIView)
. Kemudian daftarkan fungsi CHHook(argNumber, className, arg1, arg2)
agar menjadi Hooked. Definisi makro CHConstructor
adalah sebagai berikut:
#define CHConcat(a, b) CHConcat_(a, b)
#define CHConstructor static __attribute__((constructor)) void CHConcat(CHConstructor, __LINE__)()
Konten setelah __attribute__((constructor))
dijamin berjalan saat dylib dimuat, biasanya saat program dimulai. Demikian pula, simbol-simbol lain diperkenalkan melalui definisi makro.
Izinkan saya memperkenalkan cara menggunakan CaptainHook untuk mendeklarasikan fungsi Hook dan mengimplementasikannya, langsung ke dalam kode.
CHDeclareClass ( BXViewController );
CHOptimizedMethod ( 0 , self , void , BXViewController , viewDidLoad ) {
CHSuper ( 0 , BXViewController , viewDidLoad );
/* HERE TO WRITE YOUR CODE */
}
CHDeclareMethod0 ( void , BXViewController , addFriends ) {
/* HERE TO WRITE YOUR CODE */
}
Setelah penulisan selesai, sambungkan ke iPhone yang ada untuk kompilasi guna memastikan bahwa perpustakaan dinamis yang sesuai dengan arsitektur telah dihasilkan.
Gunakan alat yololib untuk memasukkan file dylib yang telah dikompilasi ke dalam daftar Perintah Muat dari file yang dapat dieksekusi Mach-O.
~ » ./yololib [binary] [dylib file]
Struktur file Mach-O terutama terdiri dari tiga bagian. Bagian front-end adalah struktur Header, yang menyimpan tipe platform Mach-O, tipe file, jumlah LoadCommands dan informasi lainnya; diikuti oleh Header adalah bagian Load Commands. Dengan mengurai bagian ini, struktur logis dari file dan lokasinya di file virtual dapat ditentukan. Alat yololib mengubah informasi di bagian Load Commands untuk memuat dylib. Proses implementasi spesifiknya adalah sebagai berikut:
Karena informasi Perintah Muat berubah, baik ncmds maupun sizeofcmds pada struktur Header terkait akan berubah, sehingga Header harus diubah terlebih dahulu:
// 取出 Header
fseek(newFile, top, SEEK_SET);
struct mach_header mach;
fread(&mach, sizeof(struct mach_header), 1, newFile);
NSData* data = [DYLIB_PATH dataUsingEncoding:NSUTF8StringEncoding];
// 计算 dylib 的大小
uint32_t dylib_size = (uint32_t)[data length] + sizeof(struct dylib_command);
dylib_size += sizeof(long) - (dylib_size % sizeof(long));
// 修改 cmds 和 sizeofcmds
mach.ncmds += 1;
uint32_t sizeofcmds = mach.sizeofcmds;
mach.sizeofcmds += dylib_size;
// 写回修改后的 Header
fseek(newFile, -sizeof(struct mach_header), SEEK_CUR);
fwrite(&mach, sizeof(struct mach_header), 1, newFile);
Kemudian ubah bagian Load Commands dan tambahkan informasi pemuatan dylib:
fseek(newFile, sizeofcmds, SEEK_CUR);
// 创建一个 dylib 类型的 command
struct dylib_command dyld;
fread(&dyld, sizeof(struct dylib_command), 1, newFile);
// 修改 dyld 结构体数据
dyld.cmd = LC_LOAD_DYLIB;
dyld.cmdsize = dylib_size;
dyld.dylib.compatibility_version = DYLIB_COMPATIBILITY_VERSION;
dyld.dylib.current_version = DYLIB_CURRENT_VER;
dyld.dylib.timestamp = 2;
dyld.dylib.name.offset = sizeof(struct dylib_command);
// 写回修改
fseek(newFile, -sizeof(struct dylib_command), SEEK_CUR);
fwrite(&dyld, sizeof(struct dylib_command), 1, newFile);
Data terakhir ditulis ke dylib.
fwrite([data bytes], [data length], 1, newFile);
Gunakan perintah codesign
untuk menandatangani ulang perpustakaan dinamis yang dihasilkan dan semua file yang dapat dieksekusi di APP (termasuk Ekstensi APP di folder Plugin). Gunakan perintah xcrun -sdk iphoneos PackageApplication -v
untuk mengemas perpustakaan dinamis dan semua file bersama-sama. Anda akan memahami seluruh proses. Jika terdapat sertifikat perusahaan, aplikasi yang ditandatangani dan dikemas dapat diinstal pada iPhone yang tidak di-jailbreak dan memercayai sertifikat tersebut. Bintang dayung!