مقدمه
در درس قبل، با زبان میانی مشترک (CIL) به عنوان زبان اسمبلی دنیای .NET آشنا
شدیم. دیدیم که کد C# ما به این زبان میانی ترجمه میشود. یکی از ویژگیهای جالب و
قدرتمند اکوسیستم .NET، قابلیت انجام مهندسی رفت و برگشت (Round-trip
Engineering) است. این مفهوم به این معناست که ما میتوانیم یک اسمبلی کامپایلشده
(.dll یا .exe) را به کد منبع
CIL آن "دیساسمبل" (disassemble) کنیم، در صورت تمایل آن کد CIL را تغییر
دهیم، و سپس آن را مجدداً به یک اسمبلی کاملاً کاربردی "اسمبل" (assemble) کنیم.
این قابلیت نه تنها یک تمرین آکادمیک جالب است، بلکه یک ابزار قدرتمند برای درک عمیقتر نحوهی کار
کامپایلر و CLR، و همچنین برای سناریوهای پیشرفته مانند بهینهسازی سطح پایین و تولید کد دینامیک
محسوب میشود. در این درس، این فرآیند رفت و برگشتی را به صورت گام به گام بررسی خواهیم کرد.
ابزارهای مورد نیاز: ildasm و ilasm
برای انجام مهندسی رفت و برگشت، ما از دو ابزار خط فرمان استاندارد که به همراه .NET SDK
نصب میشوند، استفاده میکنیم:
- ildasm.exe (Intermediate Language Disassembler): این ابزار یک
فایل اسمبلی را به عنوان ورودی گرفته و کد CIL و فرادادهی آن را در قالب یک فایل
متنی با پسوند .il استخراج میکند.
- ilasm.exe (Intermediate Language Assembler): این ابزار یک فایل
.il را به عنوان ورودی گرفته و آن را به یک فایل اسمبلی .dll یا .exe کامپایل میکند.
این دو ابزار به ما اجازه میدهند تا چرخهی کامل C# → CIL → Assembly را مشاهده و
دستکاری کنیم.
یک مثال گام به گام
بیایید این فرآیند را با یک مثال ساده از ابتدا تا انتها انجام دهیم.
قدم اول: نوشتن و کامپایل کد C#
ابتدا یک کلاس ساده در یک فایل به نام MyLibrary.cs مینویسیم.
MyLibrary.cs
namespace MyCode
{
public class SimpleCalc
{
public int Add(int x, int y)
{
return x + y;
}
}
}
سپس، با استفاده از کامپایلر C# (csc.exe) از طریق خط
فرمان، این کد را به یک کتابخانهی کلاس (.dll) کامپایل میکنیم.
Command Prompt
csc /target:library MyLibrary.cs
پس از اجرای این دستور، یک فایل به نام MyLibrary.dll در پوشهی فعلی
ایجاد میشود.
قدم دوم: دیساسمبل کردن به CIL
حالا از ابزار ildasm برای استخراج کد CIL از اسمبلی خود استفاده میکنیم. دستور زیر،
محتوای اسمبلی را در یک فایل متنی به نام MyLibrary.il میریزد.
Command Prompt
ildasm /out=MyLibrary.il MyLibrary.dll
اگر فایل MyLibrary.il را باز کنیم، کدی شبیه به زیر را خواهیم دید که
شامل تعریف اسمبلی، فضای نام، کلاس، و کد CIL برای متد Add است.
MyLibrary.il
.namespace MyCode
{
.class public auto ansi beforefieldinit SimpleCalc
extends [System.Runtime]System.Object
{
.method public hidebysig instance int32
Add(int32 x, int32 y) cil managed
{
// Code size 7 (0x7)
.maxstack 2
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0006: ret
} // end of method SimpleCalc::Add
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// ... constructor CIL ...
} // end of method SimpleCalc::.ctor
} // end of class SimpleCalc
} // end of namespace MyCode
قدم سوم: اسمبل کردن مجدد CIL
در این مرحله، ما میتوانیم فایل .il را مستقیماً ویرایش کنیم (هرچند
این کار نیازمند تسلط کامل بر CIL است). برای مثال، میتوانیم یک دستور
Console.WriteLine را به متد Add اضافه کنیم. اما برای این درس، ما فایل را بدون تغییر رها کرده
و آن را مستقیماً به یک اسمبلی جدید کامپایل میکنیم.
Command Prompt
ilasm /dll /output=NewMyLibrary.dll MyLibrary.il
دستور بالا، فایل MyLibrary.il را خوانده و یک اسمبلی جدید به نام
NewMyLibrary.dll ایجاد میکند. این اسمبلی جدید از نظر عملکردی
کاملاً با MyLibrary.dll اصلی یکسان است و میتواند در هر پروژهی دیگری مورد ارجاع قرار گیرد. ما
با موفقیت یک چرخهی کامل رفت و برگشت را انجام دادیم!