[英]Mono.Cecil Replace argument in method
任務:
查找所有通話功能
public static void WriteString(int index0, string s, int index1)
{
Console.WriteLine(s);
}
在SomeCnsl.exe中並在函數ChangeString中包裝參數's'
public static string ChangeText(string text)
{
return text + "new";
}
例:
original: WriteString(0,"hello",1);
wrap: WriteString(0,ChangeText("hello"),1);
為了解決此任務,我使用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
所以,我的問題是:
我可以更精確地從函數WriteText中的第二個參數定義所有IL命令嗎? 如果可以的話,怎么辦?
它不是您的問題的答案,但可以通過另一種方式幫助您。 您可以根據需要使用Roslyn重寫代碼。
您可以通過繼承CSharpSyntaxRewriting
來做到這CSharpSyntaxRewriting
,然后重寫相關方法。 之后,您將收到帶有已修改代碼的新語法樹,然后可以對其進行編譯並將其保存到磁盤。 以這里為例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.