首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

使用 Mono.Cecil 修改 .NET 組件:從 IL 重寫到組件修補實戰

     編輯:關於.NET

Mono.Cecil 是 Mono 專案旗下的組件操作庫,支援讀取、修改、寫入 .NET 組件 (DLL/EXE),不需載入至 AppDomain,效能優於 System.Reflection.Emit,且能直接修改現有組件。Reflector 等工具亦以此為核心庫。

基本物件模型

  • ModuleDefinition:組件模組入口,ReadModule(path) 載入。
  • TypeDefinition:型別定義,含 Methods, Fields, Properties 等集合。
  • MethodDefinition:方法定義,Body.Instructions 為 IL 指令串列。
  • ILProcessor:指令編輯器,InsertBefore/After, Replace, Create(OpCode, operand)。

極簡範例:在 Main 方法前後插入呼叫

目標程式 TestApp.exe

namespace TestApp
{
    class Program
    {
        static void Main() { Console.WriteLine("Main"); }
        private static void Before() { Console.WriteLine("Before"); }
        private static void After() { Console.WriteLine("After"); }
    }
}

修補工具 CecilTest.exe

using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Linq;

namespace CecilTest
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 1) { Console.WriteLine("Usage: CecilTest TestApp.exe"); return; }

            var module = ModuleDefinition.ReadModule(args[0]);
            var progType = module.Types.First(t => t.Name == "Program");
            var main = progType.Methods.First(m => m.Name == "Main");
            var before = progType.Methods.First(m => m.Name == "Before");
            var after = progType.Methods.First(m => m.Name == "After");

            var il = main.Body.GetILProcessor();
            // 在第一條指令前插入 call Before
            il.InsertBefore(main.Body.Instructions[0], il.Create(OpCodes.Call, before));
            // 在最後一條指令 (ret) 前插入 call After
            il.InsertBefore(main.Body.Instructions.Last(), il.Create(OpCodes.Call, after));

            module.Write(args[0] + ".exe");
            Console.WriteLine("Done");
        }
    }
}

執行結果

  • TestApp.exe → 輸出 Main
  • TestApp.exe.exe (修補後) → 輸出 Before, Main, After

Cecil vs Emit 關鍵差異

特性CecilReflection.Emit
修改既有組件✅ 直接讀寫❌ 需重新產生動態組件
需載入組件執行❌ 純元資料操作✅ 需載入
區域變數定義method.Body.Variables.Add()ILGenerator.DeclareLocal()
分支標籤直接參考 Instruction 物件DeclareLabel() / MarkLabel()
泛型處理⚠️ 极繁瑣 (需手動構造 GenericInstanceType/Method)✅ 較直覺
相依組件解析可僅載入目標組件 (不解析相依)需完整解析相依鏈

實務心得與坑點

  • 優點: API 直覺、不污染 AppDomain、支援混合模式組件讀取 (不修改非受控部分)、適合 AOP 注入、日誌植入、授權檢查等編譯後處理。
  • 泛型地獄: 處理開放泛型、封閉泛型、方法實體化需大量樣板碼,DbEntry 專案最終採用 Reflection + Cecil 混合模式繞過。
  • 社群回應: 官方倉庫 Issue 回應「不視為問題」,建議自行封裝 Helper 簡化泛型操作。
  • 建議: 使用 Git 最新版 (含最新修復);搭配 peverify 驗證輸出組件合法性。

結語

Cecil 是 .NET 組件修補的利器,對於「編譯後修改 IL」場景 (如 Profiling、AOP、熱修補、移除強名稱) 極具價值。掌握其指令編輯模型與泛型限制,可構建強大的自動化建置後處理管線。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved