简体   繁体   English

方法中的Mono.Cecil替换参数

[英]Mono.Cecil Replace argument in method

Task: 任务:

Find all calls function 查找所有通话功能

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

in SomeCnsl.exe and wrap argument 's' in function ChangeString 在SomeCnsl.exe中并在函数ChangeString中包装参数's'

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

Example: 例:

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

To solve this task I use Mono.Cecil. 为了解决此任务,我使用Mono.Cecil。 And my solution look like this: 我的解决方案如下所示:

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

To find string argument I create recursive method GetStringArgument: 为了找到字符串参数,我创建了递归方法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);
    }
}

And it's work. 这是工作。 Until in arguments of WriteString put some strings, like this: 直到在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          

So, my question is: 所以,我的问题是:

Can I define all IL commands from second argument in function WriteText more precisely? 我可以更精确地从函数WriteText中的第二个参数定义所有IL命令吗? And if I can, then how? 如果可以的话,怎么办?

It's not answer on your question but it can help you to do it in another way. 它不是您的问题的答案,但可以通过另一种方式帮助您。 You can use Roslyn to rewrite your code as you want. 您可以根据需要使用Roslyn重写代码。

You can do it by inheritance CSharpSyntaxRewriting and then override the relevant methods. 您可以通过继承CSharpSyntaxRewriting来做到这CSharpSyntaxRewriting ,然后重写相关方法。 After that you will receive a new syntax tree with the modified code then you can compile it and save it to disk. 之后,您将收到带有已修改代码的新语法树,然后可以对其进行编译并将其保存到磁盘。 Take a look here for example. 这里为例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM