plugin ida_medigate C++ untuk IDA Pro
[Daftar Daftar]
Rekayasa balik kode C++ yang dikompilasi tidaklah menyenangkan. Rekayasa Pembalikan Statis dari kode C++ yang dikompilasi membuat frustrasi. Alasan utama yang membuatnya sulit adalah fungsi virtual. Berbeda dengan kode C yang dikompilasi, tidak ada alur kode yang jelas. Terlalu sering seseorang menghabiskan banyak waktu untuk mencoba memahami apa nama fungsi virtual berikutnya, daripada hanya melihat fungsi seperti dalam kode C yang dikompilasi.
Ketika seseorang menyelidiki suatu fungsi virtual, jumlah waktu yang dia perlukan untuk berusaha menemukan xref-nya, tidak masuk akal.
Setelah terlalu banyak proyek C++ RE, saya menyerah dan memutuskan bahwa saya memerlukan alat yang fleksibel (Python) dan stabil (yang dapat saya pelihara dengan mudah) untuk jenis penelitian ini. Sebagian besar plugin ini ditulis pada Januari 2018, dan baru-baru ini saya memutuskan untuk membersihkan debu dan menambahkan dukungan untuk dukungan kelas IDA (7.2) yang baru.
Plugin ini tidak dimaksudkan untuk selalu bekerja "di luar kotak", tetapi untuk menjadi alat lain untuk membalikkan keadaan.
Plugin ini terdiri dari dua bagian:
Bagian pertama ini tidak bergantung pada bagian kedua, sehingga plugin dapat digunakan untuk merekayasa balik biner yang tidak berisi RTTI, dengan mendefinisikan kelas-kelas tersebut secara manual berdasarkan API plugin.
Apa yang membuat plugin ini unik adalah fakta bahwa plugin ini menggunakan lingkungan yang sama dengan yang sudah familiar bagi peneliti, dan tidak menambahkan menu atau objek baru, dan berdasarkan pada blok penyusun IDA yang diketahui (struktur, gabungan, tipe anggota struktur, dll. ) - Ini memungkinkan plugin untuk mendukung abstraksi C++ untuk setiap arsitektur yang didukung IDA .
Catatan: Pengurai RTTI mem-parsing x86/x64 g++ RTTI, tetapi strukturnya memungkinkan untuk menambahkan dukungan untuk lebih banyak arsitektur dan kompiler dengan mudah.
plugins/
) Salin medigate_cpp_plugin
ke direktori plugins
dan tambahkan jalur kode sumber ke file idapythonrc.py
Anda
Salin plugins/ida-referee/referee.py
ke direktori yang sama.
Dengan asumsi kode sumber biner asli adalah sebagai berikut ( examples/a.cpp
):
using namespace std ;
class A {
public:
int x_a;
virtual int f_a ()=0;
};
class B : public A {
public:
int x_b;
int f_a (){x_a = 0 ;}
virtual int f_b (){ this -> f_a ();}
};
class Z {
public:
virtual int f_z1 (){cout << " f_z1 " ;}
virtual int f_z2 (){cout << " f_z2 " ;}
};
class C : public B , public Z {
public:
int f_a (){x_a = 5 ;}
int x_c;
int f_c (){x_c = 0 ;}
virtual int f_z1 (){cout << " f_z3 " ;}
};
int main ()
{
C *c = new C ();
c-> f_a ();
c-> f_b ();
c-> f_z1 ();
c-> f_z2 ();
return 0 ;
}
Binernya dihapus tetapi berisi RTTI.
Saat kita baru saja memuat biner, fungsi main
( sub_84D
dalam versi 32 bit) terlihat seperti:
Jalankan parser g++ RTTI dan jalankan, menggunakan:
from ida_medigate.rtti_parser import GccRTTIParser
GccRTTIParser.init_parser()
GccRTTIParser.build_all()
Sekarang segarkan struct C (lihat bagian Keterangan), masukkan v0
menjadi C *
, dekompilasi lagi:
Jika tidak ada RTTI, infrastruktur kami masih memungkinkan untuk mendefinisikan kelas c++ secara manual. Untuk contoh yang sama (examples/a32_stripped) Anda dapat membuat struct B secara manual, lalu pilih tabel virtualnya dan ketik
from ida_medigate import cpp_utils
cpp_utils.make_vtable("B")
make_vtable
juga bisa mendapatkan vtable_ea
dan vtable_ea_stop
alih-alih area yang dipilih.
Kemudian buat struct C, dan terapkan warisan:
cpp_utils.add_baseclass("C", "B")
Sekarang Anda dapat membangun kembali tabel kelas C dengan memilihnya dan mengetik:
cpp_utils.make_vtable("C")
Tambahkan struktur Z, bangun kembali vtable-nya juga, dan sekarang bagian kerennya:
cpp_utils.add_baseclass("C", "Z", 0x0c, to_update=True)
yang menerapkan pewarisan C dari Z pada offset 0x0c dan menyegarkan struct juga (lihat keterangan).
Hal terakhir yang tersisa adalah memperbarui vtable kedua dari C, yang mengimplementasikan antarmuka Z. Tandai vtable ini dan ketik:
cpp_utils.make_vtable("C", offset_in_class=0x0c)
ida_medigate mengetahui bahwa vtable ini adalah vtable kelas Z dan hasilnya adalah:
Hasil akhirnya sama seperti kasus RTTI:
Ketika struct vtable baru dibuat (oleh parser RTTI secara manual oleh pengguna) setiap fungsi yang belum berubah diganti namanya, didekompilasi, dan menyetel argumen pertamanya ke this
.
Klik dua kali pada anggota struktur yang sesuai dengan fungsi tersebut akan menavigasi ke fungsi tersebut, sehingga pengguna dapat membaca alur kode C++ dengan cara yang nyaman!
Setiap perubahan nama atau tipe suatu fungsi atau anggota penunjuk fungsi yang terkait di vtables dihubungkan dan disinkronkan di antara semuanya. Artinya, misalnya, pengguna dapat mengubah tipe anggota vtable melalui jendela dekompiler, dan tipe baru (prototipe) ini juga akan diterapkan pada fungsi target.
Pada baris 15 pada gambar sebelumnya, ada panggilan ke B::sub_9A8 (B::f_b di kode sumber). Argumen fungsi ini adalah B *
:
Namun, fungsi ini juga dapat dipanggil oleh instance C
(up-casting). kami ingin melihat fungsi virtual yang akan dipanggil oleh instance tersebut. Asumsikan ada banyak kelas turunan potensial sehingga this
ke C *
tidak selalu memungkinkan. Oleh karena itu, kami menerapkan gabungan untuk setiap kelas dasar yang memiliki anak yang memiliki tabel virtual berbeda. Seseorang dapat memilih untuk menampilkan tabel virtual turunan B
yang berbeda dengan mengklik alt+y (pintasan untuk memilih anggota gabungan yang berbeda):
jadi pada akhirnya kita hanya dapat "mentransmisikan" panggilan tertentu ke fungsi virtual yang berbeda:
Cawan suci para reverse engineer C++ yang frustrasi. Kami memelihara xrefs dari fungsi virtual hingga anggota struct vtable yang mewakilinya!
Menggabungkan ini dengan ida-referee
memungkinkan kami melacak semua xref panggilan fungsi virtual!
Batasan: kami hanya dapat melacak panggilan virtual yang telah didekompilasi. Untungnya, analisis otomatis mengetahui cara mengisi tipe argumen antar fungsi, jadi dengan proses berulang dalam memberikan lebih banyak argumen->mendekompilasi semua fungsi yang relevan -> membaca kode lagi dan memberikan lebih banyak argumen (...) kemampuan ini menjadi sangat kuat!
Cara kami menandai anggota struktur sebagai subkelas di IDAPython tidak langsung disinkronkan ke IDB. Peretasan yang kami lakukan adalah mengedit struktur sehingga sinkronisasi akan terpicu. Anda juga dapat menggunakan
utils.refresh_struct(struct_ptr)
yang menambahkan bidang dummy di akhir struct dan kemudian membatalkan definisinya.