2016-03-22 45 views
2

任務:Mono.Cecil能做到替換參數在方法

找到所有的呼叫起作用

public static void WriteString(int index0, string s, int index1) 
{ 
    Console.WriteLine(s); 
} 
在SomeCnsl.exe

,敷參數 's' 的函數ChangeString

public static string ChangeText(string text) 
{ 
    return text + "new"; 
} 

示例:

original: WriteString(0,"hello",1); 
wrap:  WriteString(0,ChangeText("hello"),1); 

要解決t他的任務是使用Mono.Cecil。 我的解決辦法是這樣的:

private static AssemblyDefinition MainAssembly; 
static void Main(string[] args) 
{ 
    MainAssembly = AssemblyDefinition.ReadAssembly("SomeCnsl.exe"); 

    var changeTextMethod = typeof(SomeCnsl.Program).GetMethod("ChangeText"); 
    var changeTextMethodRef = MainAssembly.MainModule.Import(changeTextMethod); 

    var mainMethod = MainAssembly.Modules.SelectMany(mod => ModuleDefinitionRocks.GetAllTypes(mod)) 
     .SelectMany(t => t.Methods) 
     .Where(method => null != method.Body); 

    foreach (var body in mainMethod.Select(m => m.Body)) 
    { 
     var processor = body.GetILProcessor(); 
     var instructions = body.Instructions.Where(instr => instr.OpCode == OpCodes.Call && instr.ToString().Contains("WriteString")).ToList(); 
     foreach (var instr in instructions) 
     { 
      var stringEndArg = GetStringArgument(instr); 
      var writeInstruction = processor.Create(OpCodes.Call, changeTextMethodRef); 
      processor.InsertAfter(stringEndArg, writeInstruction); 
     } 
    } 
    SavePatchedAssembly(); 
} 

要查找的字符串參數創建遞歸方法GetStringArgument:

public static Instruction GetStringArgument(Instruction callDrawString) 
{  
    if (callDrawString.Previous.OpCode == OpCodes.Ldstr || callDrawString.Previous.OpCode == OpCodes.Ldarg_1 || 
     (callDrawString.Previous.OpCode == OpCodes.Call && callDrawString.Previous.ToString().Contains("System.String::")) || 
     (callDrawString.Previous.OpCode == OpCodes.Callvirt && callDrawString.Previous.ToString().Contains("System.String::")) || 
     (callDrawString.Previous.OpCode == OpCodes.Callvirt && callDrawString.Previous.ToString().Contains("Generic.List`1<System.String>::get_Item")) || 
     (callDrawString.Previous.OpCode == OpCodes.Callvirt && callDrawString.Previous.ToString().Contains("Generic.Dictionary`2<") && callDrawString.Previous.ToString().Contains("System.String>::get_Item")) || 
     ((callDrawString.Previous.Operand as ParameterReference) != null && (callDrawString.Previous.Operand as ParameterReference).ParameterType.FullName == typeof(string).FullName) || 
     ((callDrawString.Previous.Operand as FieldReference) != null && (callDrawString.Previous.Operand as FieldReference).FieldType.FullName == typeof(string).FullName) || 
     ((callDrawString.Previous.Operand as PropertyReference) != null && (callDrawString.Previous.Operand as PropertyReference).PropertyType.FullName == typeof(string).FullName)) 
    { 
     return callDrawString.Previous; 
    } 
    else 
    { 
     return GetStringArgument(callDrawString.Previous); 
    } 
} 

而且它的工作。直到WriteString的論據一些字符串,像這樣:

static Dictionary<string, int> listParam = new Dictionary<string, int> { { "first", 1 }, { "second", 2 }, { "third", 3 } }; 
static int index = 2; 
static string indexString = "second"; 

static void Main(string[] args) 
{ 
    while(true) 
    { 
     Thread.Sleep(1000); 
     WriteString(index, indexString, listParam[indexString]); 
    }   
} 

ILCode WriteString call: 
    IL_0022: ldsfld  int32 SomeCnsl.Program::index 
    IL_0027: ldsfld  string SomeCnsl.Program::indexString 
    IL_002c: ldsfld  class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> SomeCnsl.Program::listParam 
    IL_0031: ldsfld  string SomeCnsl.Program::indexString 
    IL_0036: callvirt  instance !1/*int32*/ class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::get_Item(!0/*string*/) 
    IL_003b: call   void SomeCnsl.Program::WriteString(string, int32, int32) 
    IL_0040: nop   

所以,我的問題是:

我可以定義所有IL從第二個參數的函數WRITETEXT更精確地命令了嗎?如果我能,那麼怎麼樣?

回答

0

這不是你的問題的答案,但它可以幫助你以另一種方式做到這一點。 您可以使用Roslyn根據需要重寫代碼。

你可以通過繼承CSharpSyntaxRewriting然後覆蓋相關的方法。之後,您將收到一個帶修改代碼的新語法樹,然後您可以編譯它並將其保存到磁盤。 例如,看看here