Membalik-balik aplikasi teknik setelah kebingungan (Bagian 2)

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 pertama
Jika 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:

 /** The class for read file. */ 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

 /** A data reader DLL exports. */ 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

 /** The class for read file. */ 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)
 List<string> e = A_0.e; //....... List<string> list = A_0.e; int num3 = 0; while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (list[num3] == "__declspec") { if (num3 + 1 < list.Count) { num = list[num3 + 1].IndexOf(')'); if (num >= 0) { list[num3 + 1] = list[num3 + 1].Substring(num + 1).Trim(); } } list.RemoveAt(num3); num3--; } num3++; } if (list.Count > 0 && (list[0] == "struct" || list[0] == "union")) { if (list.Count == 1) { //...... 


Kami menyimpulkan ini atas dasar bahwa setelah verifikasi
list [num3] == "__declspec"
metode delete disebut
list.RemoveAt (num3);
Penalaran (berpikir keras):

  1. Metode CmdDocComment :: a memiliki variabel lokal yang berisi larik string

     List<string> list = A_0.e; 
  2. Elemen pertama array ini menyimpan awal dari deskripsi fungsi, struktur, kelas, dll. Yaitu, kata kunci "class", "struct", "union"
     list[0] == "struct" 
  3. Setiap elemen array berisi kata yang terpisah. Dalam kasus kami, itu akan menjadi {"class", "DATA_READER_DLL_EXPORTS", "ClassForReadFile"}
  4. Ada loop yang mengelilingi semua elemen array "e", mencari elemen "__declspec", dan menghapusnya dan semua yang ada di dalam tanda kurung
  5. 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

 //     ,          num2 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { // ,      .     if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } //  if (list[num3] == "__declspec"),  if (num3 != 0 && num3 < (list.Count - 1) && list[num3 + 1] != ":" && list[num3 + 1] != "where") { e.RemoveAt(index); --index; } num3++; } 

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.

Temukan metode kami
gambar

pilih saat dan ubah tampilan menjadi IL
Siklus kami
gambar

Saya juga akan memberikan daftar, meskipun agak besar

Siklus kami
 /* 0x00016710 07 */ IL_018C: ldloc.1 //         1. /* 0x00016711 1119 */ IL_018D: ldloc.s V_25 //          ( ). /* 0x00016713 6FF900000A */ IL_018F: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016718 72925E0070 */ IL_0194: ldstr "where" //       ,   ,   . /* 0x0001671D 287000000A */ IL_0199: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016722 3AAB000000 */ IL_019E: brtrue IL_024E //    ,   value  true,    null   . /* 0x00016727 07 */ IL_01A3: ldloc.1 //         1. /* 0x00016728 1119 */ IL_01A4: ldloc.s V_25 //          ( ). /* 0x0001672A 6FF900000A */ IL_01A6: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x0001672F 72A31D0070 */ IL_01AB: ldstr ":" //       ,   ,   . /* 0x00016734 287000000A */ IL_01B0: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016739 3A94000000 */ IL_01B5: brtrue IL_024E //    ,   value  true,    null   . /* 0x0001673E 07 */ IL_01BA: ldloc.1 //         1. /* 0x0001673F 1119 */ IL_01BB: ldloc.s V_25 //          ( ). /* 0x00016741 6FF900000A */ IL_01BD: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016746 03 */ IL_01C2: ldarg.1 //     1   . /* 0x00016747 7B12010004 */ IL_01C3: ldfld string Atomineer.Utils.CmdDocComment/GeneratorInfo::b //      ,       . /* 0x0001674C 287000000A */ IL_01C8: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016751 2C07 */ IL_01CD: brfalse.s IL_01D6 //    ,   value  false,    . /* 0x00016753 09 */ IL_01CF: ldloc.3 //         3. /* 0x00016754 16 */ IL_01D0: ldc.i4.0 //    0     int32. /* 0x00016755 2F03 */ IL_01D1: bge.s IL_01D6 //     ( ),        . /* 0x00016757 1119 */ IL_01D3: ldloc.s V_25 //          ( ). /* 0x00016759 0D */ IL_01D5: stloc.3 //                3. /* 0x0001675A 07 */ IL_01D6: ldloc.1 //         1. /* 0x0001675B 1119 */ IL_01D7: ldloc.s V_25 //          ( ). /* 0x0001675D 6FF900000A */ IL_01D9: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016762 729E5E0070 */ IL_01DE: ldstr "__declspec" //       ,   ,   . /* 0x00016767 287000000A */ IL_01E3: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x0001676C 2C51 */ IL_01E8: brfalse.s IL_023B //    ,   value  false,    . /* 0x0001676E 1119 */ IL_01EA: ldloc.s V_25 //          ( ). /* 0x00016770 17 */ IL_01EC: ldc.i4.1 //    1     int32. /* 0x00016771 58 */ IL_01ED: add //         . /* 0x00016772 07 */ IL_01EE: ldloc.1 //         1. /* 0x00016773 6FF700000A */ IL_01EF: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<string>::get_Count() //             . /* 0x00016778 2F37 */ IL_01F4: bge.s IL_022D //     ( ),        . /* 0x0001677A 07 */ IL_01F6: ldloc.1 //         1. /* 0x0001677B 1119 */ IL_01F7: ldloc.s V_25 //          ( ). /* 0x0001677D 17 */ IL_01F9: ldc.i4.1 //    1     int32. /* 0x0001677E 58 */ IL_01FA: add //         . /* 0x0001677F 6FF900000A */ IL_01FB: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016784 1F29 */ IL_0200: ldc.i4.s 41 //      int8     int32 ( ). /* 0x00016786 6FC800000A */ IL_0202: callvirt instance int32 [mscorlib]System.String::IndexOf(char) //             . /* 0x0001678B 0C */ IL_0207: stloc.2 //                2. /* 0x0001678C 08 */ IL_0208: ldloc.2 //         2. /* 0x0001678D 16 */ IL_0209: ldc.i4.0 //    0     int32. /* 0x0001678E 3221 */ IL_020A: blt.s IL_022D //     ( ),      . /* 0x00016790 07 */ IL_020C: ldloc.1 //         1. /* 0x00016791 1119 */ IL_020D: ldloc.s V_25 //          ( ). /* 0x00016793 17 */ IL_020F: ldc.i4.1 //    1     int32. /* 0x00016794 58 */ IL_0210: add //         . /* 0x00016795 07 */ IL_0211: ldloc.1 //         1. /* 0x00016796 1119 */ IL_0212: ldloc.s V_25 //          ( ). /* 0x00016798 17 */ IL_0214: ldc.i4.1 //    1     int32. /* 0x00016799 58 */ IL_0215: add //         . /* 0x0001679A 6FF900000A */ IL_0216: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x0001679F 08 */ IL_021B: ldloc.2 //         2. /* 0x000167A0 17 */ IL_021C: ldc.i4.1 //    1     int32. /* 0x000167A1 58 */ IL_021D: add //         . /* 0x000167A2 6FCB00000A */ IL_021E: callvirt instance string [mscorlib]System.String::Substring(int32) //             . /* 0x000167A7 6F8600000A */ IL_0223: callvirt instance string [mscorlib]System.String::Trim() //             . /* 0x000167AC 6FFF00000A */ IL_0228: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::set_Item(int32, !0) //             . /* 0x000167B1 07 */ IL_022D: ldloc.1 //         1. /* 0x000167B2 1119 */ IL_022E: ldloc.s V_25 //          ( ). /* 0x000167B4 6F6701000A */ IL_0230: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::RemoveAt(int32) //             . /* 0x000167B9 1119 */ IL_0235: ldloc.s V_25 //          ( ). /* 0x000167BB 17 */ IL_0237: ldc.i4.1 //    1     int32. /* 0x000167BC 59 */ IL_0238: sub //           . /* 0x000167BD 1319 */ IL_0239: stloc.s V_25 //                index ( ). /* 0x000167BF 1119 */ IL_023B: ldloc.s V_25 //          ( ). /* 0x000167C1 17 */ IL_023D: ldc.i4.1 //    1     int32. /* 0x000167C2 58 */ IL_023E: add //         . /* 0x000167C3 1319 */ IL_023F: stloc.s V_25 //                index ( ). /* 0x000167C5 1119 */ IL_0241: ldloc.s V_25 //          ( ). /* 0x000167C7 07 */ IL_0243: ldloc.1 //         1. /* 0x000167C8 6FF700000A */ IL_0244: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<string>::get_Count() //             . /* 0x000167CD 3F3EFFFFFF */ IL_0249: blt IL_018C //    ,     . 


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.
1119ldloc.sMemuat variabel lokal dengan indeks yang ditentukan ke tumpukan perhitungan (formulir pendek).
2C61brfalse.sMelewati 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)
1119ldloc.sMemuat variabel lokal dengan indeks yang ditentukan ke tumpukan perhitungan (formulir pendek)
07ldloc.1Memuat variabel lokal dengan indeks 1 ke tumpukan perhitungan
6FF700000Acallvirtget_Count () - Memanggil metode objek yang terikat terakhir dan mendorong nilai kembali ke tumpukan perhitungan
17ldc.i4.1Menekan nilai integer 1 pada tumpukan komputasi sebagai int32
59subKurangi satu nilai dari yang lain dan dorong hasilnya ke tumpukan perhitungan.
2F55bge.sIni 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; } //     if (num3 != 0 && num3 < list.Count - 1) { list.RemoveAt(num3); num3--; } 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:

 /** The class for read file. */ 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.

Source: https://habr.com/ru/post/id445126/


All Articles