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
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. 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:
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:
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? 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.
You can do it by inheritance CSharpSyntaxRewriting
and then override the relevant methods. 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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.