Serangkaian program yang semakin kompleks menunjukkan fungsi hooking pada Windows 64 bit.
Saya menulis program ini sambil belajar sendiri bagaimana fungsi hooking bekerja. Mereka mungkin berguna bagi orang lain yang mencoba melakukan hal yang sama (atau masa depan saya setelah saya lupa cara kerjanya lagi). Mereka dimaksudkan untuk dilihat secara berurutan. Saat pertama kali suatu fungsi digunakan dalam suatu program, fungsi tersebut akan disertakan dalam file .cpp untuk contoh program spesifik tersebut, dengan namanya diawali dengan garis bawah. Contoh berikutnya yang menggunakan fungsi yang sama akan menggunakan salinan fungsi tersebut yang disertakan dalam hooking_common.h, untuk meminimalkan duplikasi kode dan menjaga contoh program selanjutnya cukup kecil agar tetap mudah dibaca.
Saya telah melakukan sedikit pekerjaan untuk membersihkannya, namun contoh selanjutnya masih agak berantakan. Penting untuk dicatat bahwa hasil akhir dalam repo ini tidak cukup untuk menulis pustaka hooking berfitur lengkap, namun cukup untuk memulai jalur tersebut.
Pada saat runtime, beberapa proyek mungkin bergantung pada proyek lain yang sedang dibangun (menginjeksi dll mengharuskan dll telah dibuat), atau berjalan pada saat yang sama (mengaitkan program target mengharuskannya sudah berjalan).
Semua contoh dibuat menggunakan Visual Studio 2019 (v142) dengan Windows SDK 10.0.17763.0. Saya rasa tidak ada apa pun di sini yang bergantung pada versi VS atau SDK, tapi saya mencantumkannya di sini untuk berjaga-jaga. Hampir pasti ada beberapa hal yang spesifik untuk MSVC.
Terakhir, contoh trampolin terakhir memasang pengait di mspaint. Saya berasumsi suatu saat nanti, pembaruan pada mspaint akan menyebabkan contoh ini rusak. Pada saat penulisan, versi mspaint saat ini adalah tahun 1909 (OS Build 18363.1016).
Contohnya dibagi menjadi dua kategori: contoh yang menggunakan trampolin, dan contoh yang tidak. Contoh non-trampolin ada semata-mata untuk mendemonstrasikan pengalihan aliran program dari satu fungsi ke fungsi lainnya dalam situasi berbeda. Membuat trampolin itu rumit, dan ketika saya mencoba mencari tahu cara kerja pengait fungsi, akan sangat membantu jika saya memulai dengan membuat contoh non-trampolin terlebih dahulu. Selain itu, ada 4 "program target" yang digunakan oleh contoh yang ingin mendemonstrasikan cara memasang hook di berbagai proses (yang sudah berjalan).
Sebagian besar contoh ini membocorkan memori yang terkait dengan kait. Saya tidak terlalu peduli, karena contoh-contoh ini hanya untuk mendemonstrasikan konsep yang menarik, dan karena alokasi yang "bocor" ini harus tetap ada hingga program dihentikan.
Meskipun tampaknya tidak banyak terminologi standar untuk teknik pengait fungsi, kode (dan readmes) dalam repositori ini menggunakan istilah berikut:
Karena contoh ini tidak membuat trampolin saat memasang pengaitnya, saya menganggap fungsi ini menunjukkan pengait yang "merusak", karena fungsi aslinya sama sekali tidak dapat digunakan setelah dikaitkan.
Contoh kecil menimpa byte awal suatu fungsi dengan instruksi lompat yang mengalihkan aliran program ke fungsi lain dalam program yang sama. Karena tidak ada trampolin yang sedang dibangun, operasi ini bersifat destruktif, dan fungsi aslinya tidak lagi dapat dipanggil. Ini adalah satu-satunya contoh 32 bit di repositori.
Versi 64 bit dari contoh sebelumnya. Dalam aplikasi 64 bit, fungsi dapat ditempatkan cukup jauh di memori sehingga tidak dapat dijangkau melalui instruksi lompatan relatif 32 bit. Karena tidak ada instruksi lompatan relatif 64 bit, program ini pertama-tama membuat fungsi "relai", yang berisi byte untuk instruksi jmp absolut yang dapat menjangkau mana saja di memori (dan melompat ke fungsi muatan). Lompatan 32 bit yang dipasang di fungsi target melompat ke fungsi relai ini, bukan langsung ke payload.
Memberikan contoh penggunaan teknik dari proyek sebelumnya untuk mengaitkan fungsi anggota, bukan fungsi bebas.
Sedikit berbeda dari contoh sebelumnya, program ini menunjukkan cara memasang hook ke dalam fungsi anggota virtual dengan mendapatkan alamat fungsi tersebut melalui vtable suatu objek. Tidak ada contoh lain yang berhubungan dengan fungsi virtual, tapi menurut saya cukup menarik untuk disertakan di sini.
Contoh paling sederhana memasang hook ke proses lain yang sedang berjalan. Contoh ini menggunakan perpustakaan DbgHelp untuk menemukan fungsi dalam proses target (A - Target Dengan Fungsi Gratis) berdasarkan nama string. Ini hanya mungkin karena program target dibuat dengan simbol debug yang diaktifkan. Meskipun sederhana, contoh ini sedikit lebih panjang dibandingkan program sebelumnya karena banyaknya fungsi baru yang diperkenalkan (untuk mencari dan memanipulasi proses jarak jauh).
Contoh ini menunjukkan cara mengaitkan fungsi yang telah diimpor oleh proses lain dari dll. Ada beberapa perbedaan dalam cara mendapatkan alamat fungsi dll dalam proses jarak jauh karena cara kerja ASLR, yang ditunjukkan di sini. Jika tidak, contoh ini hampir sama dengan contoh sebelumnya.
Contoh ini menunjukkan cara memasang hook dalam fungsi yang tidak diimpor oleh dll, dan yang tidak ada dalam tabel simbol (kemungkinan karena proses jarak jauh tidak memiliki simbol debug). Ini berarti tidak ada cara (mudah) untuk menemukan fungsi target berdasarkan nama string. Sebaliknya, contoh ini mengasumsikan bahwa Anda telah menggunakan disassembler seperti x64dbg untuk mendapatkan alamat virtual relatif (RVA) dari fungsi yang ingin Anda kaitkan. Program ini menggunakan RVA tersebut untuk memasang pengait.
Mirip dengan di atas, kecuali contoh ini menggunakan injeksi dll untuk menginstal fungsi payload daripada menulis byte kode mesin mentah. Ini jauh lebih mudah untuk dikerjakan, karena payload Anda dapat ditulis dalam C++ lagi. Payload untuk contoh ini terkandung dalam proyek 08B-DLL-Payload.
Contoh berikut memasang trampolin saat melakukan hook, artinya program masih dapat menjalankan logika pada fungsi target setelah hook dipasang. Karena memasang hook akan menimpa setidaknya 5 byte pertama dalam fungsi target, instruksi yang terkandung dalam 5 byte ini dipindahkan ke fungsi trampolin. Jadi, pemanggilan fungsi trampolin secara efektif mengeksekusi logika asli dari fungsi target.
Pemasangan trampolin yang setara dengan contoh #2. Contoh ini agak aneh karena saya ingin mendemonstrasikan pembuatan trampolin tanpa perlu menggunakan mesin pembongkaran. Dalam hal ini, fungsi target dibuat agar memiliki instruksi 5 byte yang diketahui di awal, jadi kita cukup menyalin lima byte pertama dari fungsi tersebut ke fungsi trampolin. Ini berarti membuat trampolin sangatlah mudah, karena kita mengetahui ukuran pastinya dan tidak menggunakan pengalamatan relatif apa pun yang perlu diperbaiki. Jika Anda menulis trampolin untuk kasus penggunaan yang sangat spesifik, Anda mungkin bisa melakukan variasi saja.
Contoh ini menunjukkan skenario serupa dengan yang sebelumnya, kecuali kali ini saya menggunakan disassembler (batu penjuru) untuk mendapatkan byte yang kita perlukan untuk mencuri dari fungsi target. Hal ini memungkinkan kode pengait digunakan pada fungsi apa pun, bukan hanya fungsi yang kita tahu akan menjadi kasus yang mudah. Sebenarnya ada banyak hal yang terjadi dalam contoh ini, karena contoh ini melompat dari hook yang ditargetkan (seperti yang sebelumnya) ke membangun fungsi hook yang umum. Trampolin harus mengubah panggilan/lompatan relatif menjadi instruksi yang menggunakan alamat absolut, yang semakin memperumit masalah. Ini juga bukan contoh hooking umum yang 100% bagus, ini akan gagal dengan instruksi loop, dan jika Anda mencoba menghubungkan fungsi dengan instruksi kurang dari 5 byte.
Pada dasarnya sama dengan yang di atas, hanya saja contoh ini menyertakan kode untuk menjeda semua thread yang sedang berjalan saat memasang hook. Hal ini tidak dijamin aman untuk thread dalam semua kasus, namun jelas jauh lebih aman daripada tidak melakukan apa pun.
Ini memperluas kode hooking/trampoline yang digunakan dalam dua contoh sebelumnya untuk mendukung pengalihan beberapa fungsi ke payload yang sama, dan untuk memungkinkan fungsi payload memanggil fungsi lain dengan hook terpasang di dalamnya.
Ini adalah contoh trampolin pertama yang memasang pengait dalam proses berbeda (dalam hal ini, aplikasi target B - Target dengan Fungsi Gratis Dari DLL). Semua logika pengait terkandung dalam muatan dll 13B - Payload DLL Fungsi Impor Trampolin. Tidak banyak yang baru di sini, contoh ini hanya menggabungkan hal-hal yang mengaitkan trampolin yang sudah dilakukan dengan teknik yang ditunjukkan sebelumnya untuk mengaitkan fungsi yang diimpor dari dll.
Permata mahkota repo. Contoh ini memasukkan payload dll (14B - Trampoline Hook MSPaint Payload) ke dalam instance mspaint yang sedang berjalan (Anda harus meluncurkan mspaint sendiri sebelum menjalankan ini). Pengait yang terpasang menyebabkan kuas menggambar dengan warna merah, tidak peduli warna apa yang sebenarnya Anda pilih di MSPaint. Sejujurnya tidak ada apa pun di sini yang tidak ditampilkan pada contoh sebelumnya, sangat menyenangkan melihat ini bekerja pada program yang tidak dibuat-buat.
Aplikasi target sederhana yang memanggil fungsi gratis dalam satu lingkaran. Dikompilasi dengan informasi debug disertakan.
Aplikasi target yang memanggil fungsi gratis yang telah diimpor dari dll (B2 - GetNum-DLL) dalam satu lingkaran.
Aplikasi target yang memanggil fungsi anggota non virtual dalam satu lingkaran.
Aplikasi target yang memanggil fungsi anggota virtual dalam satu lingkaran.