2016-03-22 15 views
2

Aufgabe:Mono.Cecil ersetzen Argument Methode

alle Anrufe funktionieren Finden

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

in SomeCnsl.exe und wickeln Argument 's' in Funktion ChangeString

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

Beispiel:

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

Um t zu lösen seine Aufgabe verwende ich Mono.Cecil. Und meine Lösung so aussehen:

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(); 
} 

String-Argument, um finde ich rekursive Methode GetStringArgument erstellen:

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); 
    } 
} 

Und es ist Arbeit. Bis in Argumente von Write einige Saiten setzen, wie folgt aus:

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   

Also, meine Frage ist:

Kann ich definieren alle IL aus dem zweiten Argument Befehle genauer in Funktion Writetext? Und wenn ich kann, wie?

Antwort

0

Es ist keine Antwort auf Ihre Frage, aber es kann Ihnen helfen, es auf eine andere Weise zu tun. Sie können Roslyn verwenden, um Ihren Code neu zu schreiben, wie Sie möchten.

Sie können es durch Vererbung tun und dann die relevanten Methoden überschreiben. Danach erhalten Sie einen neuen Syntaxbaum mit dem geänderten Code, den Sie dann kompilieren und auf Diskette speichern können. Schauen Sie sich zum Beispiel here an.