Pendahuluan
Publikasi ini bertujuan mempelajari beberapa teknik rekayasa terbalik. Semua materi disajikan hanya untuk tujuan informasi dan tidak dimaksudkan untuk digunakan untuk keuntungan pribadi apa pun.Disarankan membaca setelah bagian
pertamaJika seorang ahli bedah diajarkan bagaimana seseorang bekerja dan memberinya pisau bedah, itu tidak berarti bahwa ia akan menggunakan pengetahuan ini untuk merugikan seseorang, dan seorang ahli yang berpengetahuan luas tidak bermimpi menulis virus super.
Jadi, dalam pelajaran ini Anda tidak harus mencari petunjuk retakan dan retasan.
Subjek penelitian
Kami terus mempelajari kode plug-in untuk Dokumentasi Visual Studio Atomineer Pro (selanjutnya disebut APD). Mari berkenalan dengan alat dan kemampuannya.
Jadi, anggaplah kita memiliki kelas di C ++.
class ClassForReadFile { public: ClassForReadFile(); };
mengkonfigurasi APD sehingga komentar dalam gaya Doxygen. Kami mendapatkan kursor di
kelas dan tekan
CTRL + SHIFT + D. Kami mendapatkan yang berikut ini:
class ClassForReadFile { public: ClassForReadFile(); };
Plugin menambahkan deskripsi yang bagus tentang kelas. Semuanya hebat! Kami melanjutkan. Misalkan kelas milik perpustakaan dan kita harus mengekspornya. Tambahkan makro dan ubah definisi kelas
#ifdef DLL_EXPORTS #define DATA_READER_DLL_EXPORTS __declspec(dllexport) #else #define DATA_READER_DLL_EXPORTS __declspec(dllimport) #endif class DATA_READER_DLL_EXPORTS ClassForReadFile { public: ClassForReadFile(); };
Untuk C ++ (OS Windows), situasinya standar. Lihatlah plugin kami. Tekan
CTRL + SHIFT + D dan tidak mendapatkan apa yang kami harapkan
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
nama
definisi DATA_READER_DLL_EXPORTS didefinisikan sebagai nama kelas, bukan
ClassForReadFile , dan deskripsi kelas dihasilkan dari nama ini. Artinya, dalam kode plugin situasi ini, yaitu ekspor kelas, tidak diproses atau sedang diproses dengan kesalahan. Inilah yang akan kami coba untuk perbaiki.
Langkah 1
Kami akan mencari petunjuk. Pertama, karena ekspor fungsi dan kelas dari C / C ++ adalah situasi standar, kami masih mencoba "memaksa" plugin dengan benar. Di tempat DATA_READER_DLL_EXPORTS
mendefinisikan, masukkan instruksi
__declspec itu sendiri dan hasilkan dokumentasi
class __declspec(dllexport) ClassForReadFile { };
Dan, lihat, mereka mendapat deskripsi kelas yang benar! Dengan demikian, kami menyimpulkan bahwa dalam APD ada beberapa kode yang memeriksa keberadaan string "__declspec" dalam deskripsi kelas dan mengabaikan algoritma lebih lanjut untuk membangun dokumentasi.
Kami mendekompilasi pustaka dengan ildasm.exe standar dari Microsoft SDKs. Temukan baris "__declspec". Ini ditemukan dalam 2 metode CmdDocComment :: a dan CmdDocComment :: b. Kelas satu. Kami akan membawanya untuk studi lebih lanjut.
Langkah 2
Saya harus segera mengatakan bahwa apa yang kita cari ada dalam metode CmdDocComment :: a
Di sinilah
__declspec bertemu. Hanya baris yang paling menarik yang ditampilkan.
string a (CmdDocComment.GeneratorInfo A_0) Kami menyimpulkan ini atas dasar bahwa setelah verifikasi
list [num3] == "__declspec"
metode delete disebut
list.RemoveAt (num3);
Penalaran (berpikir keras):
- Metode CmdDocComment :: a memiliki variabel lokal yang berisi larik string
List<string> list = A_0.e;
- Elemen pertama array ini menyimpan awal dari deskripsi fungsi, struktur, kelas, dll. Yaitu, kata kunci "class", "struct", "union"
list[0] == "struct"
- Setiap elemen array berisi kata yang terpisah. Dalam kasus kami, itu akan menjadi {"class", "DATA_READER_DLL_EXPORTS", "ClassForReadFile"}
- Ada loop yang mengelilingi semua elemen array "e", mencari elemen "__declspec", dan menghapusnya dan semua yang ada di dalam tanda kurung
- Ada kondisi tambahan untuk keluar dari siklus. Ini adalah lokasi dari kata "where" atau ":". Kata resmi "di mana" adalah, jujur, tidak akrab dengan, tetapi ":" digunakan ketika mewarisi kelas
Tetapkan algoritma baru dan tujuan perubahan:
1. perubahan seharusnya tidak mempengaruhi fungsi lainnya
2. kami akan menghapus elemen-elemen dari array "daftar" sesuai dengan algoritma
- lewati elemen pertama;
- jika elemen berikutnya bukan ":" dan bukan "di mana" dan bukan akhir dari array, maka hapus.
Tulis siklus yang diinginkan
Masih memprogramnya.
Langkah 3
Program dengan keras. Pemrograman dalam kasus umum adalah penulisan kode sumber, kompilasi, penautan. Tetapi obfuscator merampas kesempatan seperti itu dari kami. Kami akan menggunakan
alat dnSpy yang direkomendasikan. Kami akan mengubah kode HEX dari perintah CIL langsung di perpustakaan, yang ternyata sangat menarik dan informatif! Mari kita mulai. Buka dnSpy, muat pustaka.
pilih saat dan ubah tampilan menjadi IL
Saya juga akan memberikan daftar, meskipun agak besar
Sekarang kita memiliki jendela dalam perintah CIL, representasi HEX mereka, offset file dan deskripsi. Semua ada di satu tempat. Sangat nyaman (terima kasih
CrazyAlex25 ).
Mari kita perhatikan blok yang menyebutkan "__declspec". Blokir offset 0x0001675A. Ini akan menjadi awal dari suntingan kami. Selanjutnya, temukan metode RemoveAt. Ini akan bermanfaat bagi kita tidak berubah. Byte terakhir dari blok adalah 0x000167BF. Buka editor HEX
Ctrl + X dan tulis 0x00 di kisaran ini. Kami akan menyimpan dan memeriksa apa yang menyebabkan perubahan.
lingkaran kosong while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } list.RemoveAt(num3); num3--; num3++; }
Sekarang kita akan menerapkan logika baru. Pertama, tambahkan kondisinya
if (num3 != 0 && num3 < list.Count - 1)
Tabel menunjukkan perintah baru dan deskripsinya.
1119 | ldloc.s | Memuat variabel lokal dengan indeks yang ditentukan ke tumpukan perhitungan (formulir pendek). |
---|
2C61 | brfalse.s | Melewati kontrol ke pernyataan akhir jika nilainya salah, referensi nol, atau nol. Catatan : Jika num3 == 0, maka lanjutkan ke langkah meningkatkan iterator loop. Nilai 0x64 adalah alamat yang diimbangi dengan instruksi 0x000167BF (lihat daftar) |
---|
1119 | ldloc.s | Memuat variabel lokal dengan indeks yang ditentukan ke tumpukan perhitungan (formulir pendek) |
---|
07 | ldloc.1 | Memuat variabel lokal dengan indeks 1 ke tumpukan perhitungan |
---|
6FF700000A | callvirt | get_Count () - Memanggil metode objek yang terikat terakhir dan mendorong nilai kembali ke tumpukan perhitungan |
---|
17 | ldc.i4.1 | Menekan nilai integer 1 pada tumpukan komputasi sebagai int32 |
---|
59 | sub | Kurangi satu nilai dari yang lain dan dorong hasilnya ke tumpukan perhitungan. |
---|
2F55 | bge.s | Ini mentransfer kontrol ke instruksi akhir (bentuk pendek) jika nilai pertama lebih besar atau sama dengan yang kedua. Catatan : Jika num3> list.Count - 1, maka lanjutkan ke langkah meningkatkan iterator loop. Nilai 0x55 adalah alamat offset sebelum instruksi 0x000167BF |
---|
Kami menulis byte ini mulai dari offset 0x0001675A. Simpan dan dekompilasi lagi
Kondisi pertama while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; }
Sekarang kita tambahkan cek string "where" dan ":". Saya mengutip kode HEX berikut tanpa komentar tambahan:
07 11 19 17 58 6F F9 00 00 0A 72 A3 1D 00 70 28 70 00 00 0A 2D 3F 07 11 19 17 58 6F F9 00 00 0A 72 92 5E 00 70 28 70 00 00 0A 2D 29
dekompilasi dan dapatkan apa yang Anda rencanakan
Siklus baru while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (num3 != 0 && num3 < list.Count - 1 && !(list[num3 + 1] == ":") && !(list[num3 + 1] == "where")) { list.RemoveAt(num3); num3--; } num3++; }
Dengan perubahan ini, plugin akan menghasilkan dokumentasi kode berikut:
class DATA_READER_DLL_EXPORTS ClassForReadFile { };
Kesimpulan
Dalam pelajaran ini kami belajar bagaimana menerapkan pengetahuan kami untuk memperbaiki bug. Tentu saja, contoh ini tidak mencerminkan seluruh variasi kesalahan dan perawatannya, tetapi ini bukan "celah dangkal". Kami memperbaiki bug yang jelas tanpa memiliki kode sumber, dan tanpa membangun kembali aplikasi.