Cara memasukkan parameter ke dalam metode tanpa parameter dalam kode aman

Halo Kali ini kami terus menertawakan panggilan metode biasa. Saya mengusulkan untuk berkenalan dengan pemanggilan metode dengan parameter tanpa melewati parameter. Kami juga akan mencoba mengubah tipe referensi ke nomor - alamatnya, tanpa menggunakan pointer dan kode yang tidak aman .


Penafian


Sebelum melanjutkan cerita, saya sangat menyarankan Anda membaca posting sebelumnya tentang StructLayout . Di sini saya akan menggunakan beberapa fitur, yang dijelaskan di sana.

Saya juga ingin memperingatkan bahwa artikel ini tidak mengandung bahan yang harus digunakan dalam proyek nyata.

Beberapa informasi awal


Sebelum kita mulai berlatih, mari kita ingat bagaimana kode C # dikonversi menjadi kode assembler.
Mari kita periksa contoh sederhana.

public class Helper { public virtual void Foo(int param) { } } public class Program { public void Main() { Helper helper = new Helper(); var param = 5; helper.Foo(param); } } 

Kode ini tidak mengandung sesuatu yang sulit, tetapi instruksi yang dihasilkan oleh JiT berisi beberapa poin kunci. Saya mengusulkan untuk melihat hanya sebagian kecil dari kode yang dihasilkan. dalam contoh saya, saya akan menggunakan kode assembler untuk mesin 32 bit.

 1: mov dword [ebp-0x8], 0x5 2: mov ecx, [ebp-0xc] 3: mov edx, [ebp-0x8] 4: mov eax, [ecx] 5: mov eax, [eax+0x28] 6: call dword [eax+0x10] 

Dalam contoh kecil ini, Anda dapat mengamati konvensi pemanggilan cepat yang menggunakan register untuk melewatkan parameter (dua parameter pertama dari kiri ke kanan di register ecx dan edx), dan parameter yang tersisa dilewatkan melalui tumpukan dari kanan ke kiri. Parameter pertama (implisit) adalah alamat instance dari kelas di mana metode ini dipanggil (untuk metode non-statis).

Dalam kasus kami, parameter pertama adalah alamat instance, yang kedua adalah nilai int kami.

Jadi int baris pertama kita melihat variabel lokal 5, tidak ada yang menarik di sini.
Di baris kedua , kami menyalin alamat instance Helper ke register ecx. Ini adalah alamat penunjuk ke tabel metode.
Pada baris ketiga ada penyalinan variabel lokal 5 ke dalam register edx
Pada baris keempat kita bisa melihat menyalin alamat tabel metode ke dalam register eax
Baris kelima berisi memuat nilai dari memori pada alamat 40 byte lebih besar dari alamat tabel metode: awal alamat metode dalam tabel metode. (Tabel metode berisi berbagai informasi yang disimpan sebelumnya. Misalnya alamat tabel metode kelas dasar, alamat EEClass, berbagai bendera, termasuk bendera pengumpul sampah, dan sebagainya). Dengan demikian, alamat metode pertama dari tabel metode sekarang disimpan dalam register eax.
Catatan: Di .NET Core, tata letak tabel metode diubah. Sekarang ada bidang (masing-masing pada 32/64 bit offset untuk sistem 32 dan 64 bit) yang berisi alamat awal daftar metode.
Pada baris keenam , metode ini disebut pada offset 16 dari awal, yaitu yang kelima pada tabel metode. Mengapa hanya metode kami yang kelima? Saya mengingatkan Anda bahwa objek memiliki 4 metode virtual ( ToString (), Equals (), GetHashCode () dan Finalize () ), yang dimiliki oleh semua kelas.

Praktek Goto;


Praktif:
Sudah waktunya untuk memulai demonstrasi kecil. Saya sarankan kosong kecil seperti itu (sangat mirip dengan kosong dari artikel sebelumnya).

  [StructLayout(LayoutKind.Explicit)] public class CustomStructWithLayout { [FieldOffset(0)] public Test1 Test1; [FieldOffset(0)] public Test2 Test2; } public class Test1 { public virtual int Useless(int param) { Console.WriteLine(param); return param; } } public class Test2 { public virtual int Useless() { return 888; } } public class Stub { public void Foo(int stub) { } } 

Dan mari kita gunakan hal-hal seperti itu:

  class Program { static void Main(string[] args) { Test2 fake = new CustomStructWithLayout { Test2 = new Test2(), Test1 = new Test1() }.Test2; Stub bar = new Stub(); int param = 55555; bar.Foo(param); fake.Useless(); Console.Read(); } } 

Seperti yang Anda duga, dari pengalaman artikel sebelumnya, metode U1ess (int j) tipe Test1 akan dipanggil.

Tapi apa yang akan ditampilkan? Pembaca yang penuh perhatian, saya percaya, telah menjawab pertanyaan ini. "55555" ditampilkan di konsol.

Tapi mari kita lihat fragmen kode yang dihasilkan.

  mov ecx, [ebp-0x20] mov edx, [ebp-0x10] cmp [ecx], ecx call Stub.Foo(Int32) mov ecx, [ebp-0x1c] mov eax, [ecx] mov eax, [eax+0x28] call dword [eax+0x10] 

Saya pikir Anda mengenali pola panggilan metode virtual, itu dimulai setelah panggilan Stub.Foo (Int32) . Seperti yang bisa kita lihat, seperti yang diharapkan, ecx diisi dengan alamat instance di mana metode dipanggil. Tetapi karena kompiler berpikir bahwa kita memanggil metode tipe Test2, yang tidak memiliki parameter, tidak ada yang ditulis ke edx. Namun, kami memiliki panggilan metode lain sebelumnya. Dan di sana kami telah menggunakan edx untuk melewatkan parameter. Dan tentu saja kami tidak memiliki instruksi, itu jelas edx. Jadi, seperti yang Anda lihat di output konsol, nilai edx sebelumnya digunakan.

Ada nuansa menarik lainnya. Saya secara khusus menggunakan tipe yang bermakna. Saya sarankan mencoba mengganti tipe parameter dari metode Foo tipe Stub dengan tipe referensi apa pun, misalnya string. Tetapi tipe parameter dari metode Useless () tidak berubah. Di bawah ini Anda dapat melihat hasilnya di komputer saya dengan beberapa informasi klarifikasi: WinDBG dan Kalkulator :)


Gambar yang dapat diklik

Jendela output menampilkan alamat tipe referensi dalam notasi desimal.

Total


Kami menyegarkan pengetahuan tentang metode panggilan menggunakan konvensi panggilan cepat dan segera menggunakan register edx yang bagus untuk melewatkan parameter dalam 2 metode sekaligus. Kami juga meludahi semua jenis dan dengan pengetahuan bahwa semuanya hanya byte dengan mudah memperoleh alamat objek tanpa menggunakan pointer dan kode tidak aman. Lebih lanjut saya berencana untuk menggunakan alamat yang diterima untuk tujuan yang bahkan lebih tidak berlaku!

Terima kasih atas perhatiannya!

Kode PS C # dapat ditemukan di sini

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


All Articles