Skip to content

Instantly share code, notes, and snippets.

@kumpera
Created September 2, 2010 18:37
Show Gist options
  • Save kumpera/562697 to your computer and use it in GitHub Desktop.
Save kumpera/562697 to your computer and use it in GitHub Desktop.
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
public class Driver {
public static void Main () {
new Weaver ("sample.exe").Weave ();
}
}
public class Weaver {
AssemblyDefinition ad;
ModuleDefinition module;
public Weaver (string str) {
this.ad = AssemblyDefinition.ReadAssembly ("sample.exe");
this.module = ad.MainModule;
}
public void Weave () {
foreach (var type in module.Types)
ProcessType (type);
ad.Write ("res.exe");
Console.WriteLine ("done");
}
void ProcessType (TypeDefinition type) {
Console.WriteLine ("\n\n------ {0}--------", type);
foreach (var method in type.Methods)
ProcessMethod (method);
}
void ProcessMethod (MethodDefinition method) {
Console.WriteLine ("\n**{0}", method);
if (!method.HasBody)
return;
var processor = method.Body.GetILProcessor();
var ins = method.Body.Instructions [0];
for (; ins != null; ins = ins.Next) {
//Console.WriteLine("\t{0}", ins.GetType ());
switch (ins.OpCode.FlowControl) {
case FlowControl.Call:
MethodReference mr = (MethodReference)ins.Operand;
Console.WriteLine ("ref: {0}", mr);
if (IsInteresting (mr))
RecordOp (method, processor, ins);
break;
}
}
}
void RecordOp (MethodDefinition method, ILProcessor processor, Instruction ins) {
Console.WriteLine ("recording {0}", ins);
MethodReference mr = (MethodReference)ins.Operand;
var mparams = mr.Parameters;
//1 save args
VariableDefinition[] vars = new VariableDefinition [mparams.Count];
for (int i = mparams.Count - 1; i >= 0; --i) {
var param = mparams [i];
VariableDefinition local = vars [i] = new VariableDefinition (param.ParameterType);
method.Body.Variables.Add (local);
processor.InsertBefore (ins, processor.Create (OpCodes.Stloc, local));
}
var paramArr = new VariableDefinition (module.Import (typeof (object[])));
method.Body.Variables.Add (paramArr);
processor.InsertBefore (ins, processor.Create (OpCodes.Ldc_I4, mparams.Count));
processor.InsertBefore (ins, processor.Create (OpCodes.Newarr, module.Import (typeof (object[]))));
processor.InsertBefore (ins, processor.Create (OpCodes.Stloc, paramArr));
for (int i = 0; i < mparams.Count; ++i) {
processor.InsertBefore (ins, processor.Create (OpCodes.Ldloc, paramArr));
processor.InsertBefore (ins, processor.Create (OpCodes.Ldc_I4, i));
processor.InsertBefore (ins, processor.Create (OpCodes.Ldloc, vars [i]));
processor.InsertBefore (ins, processor.Create (OpCodes.Box, vars [i].VariableType));
processor.InsertBefore (ins, processor.Create (OpCodes.Stelem_Ref));
}
VariableDefinition thisVal = null;
if (ins.OpCode != OpCodes.Newobj && mr.HasThis) {
thisVal = new VariableDefinition (mr.DeclaringType);
method.Body.Variables.Add (thisVal);
processor.InsertBefore (ins, processor.Create (OpCodes.Stloc, thisVal));
processor.InsertBefore (ins, processor.Create (OpCodes.Ldloc, thisVal));
}
for (int i = 0; i < mparams.Count; ++i) {
processor.InsertBefore (ins, processor.Create (OpCodes.Ldloc, vars [i]));
}
//2 - save result
Instruction lastIns = ins;
VariableDefinition retVal = null;
if (!IsVoid (mr.ReturnType) || ins.OpCode == OpCodes.Newobj) {
retVal = new VariableDefinition (ins.OpCode == OpCodes.Newobj ? mr.DeclaringType : mr.ReturnType);
method.Body.Variables.Add (retVal);
processor.InsertAfter (ins, lastIns = processor.Create (OpCodes.Stloc, retVal));
}
//3 log
if (ins.OpCode == OpCodes.Newobj) {
var trace = typeof (Tracer).GetMethod ("TraceNewObject");
var m = module.Import (trace);
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Ldloc, retVal));
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Ldloc, paramArr));
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Call, m));
} else {
//public static void TraceCall (object me, string name, object ret, object[] args) {
var trace = typeof (Tracer).GetMethod ("TraceCall");
var m = module.Import (trace);
if (thisVal != null)
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Ldloc, thisVal));
else
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Ldnull));
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Ldstr, mr.Name));
if (retVal != null)
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Ldloc, retVal));
else
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Ldnull));
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Ldloc, paramArr));
processor.InsertAfter (lastIns, lastIns = processor.Create (OpCodes.Call, m));
}
//4 - restore result
if (!IsVoid (mr.ReturnType) || ins.OpCode == OpCodes.Newobj) {
processor.InsertAfter (lastIns, processor.Create (OpCodes.Ldloc, retVal));
}
}
bool IsVoid (TypeReference type) {
return type.Namespace == "System" && type.Name == "Void";
}
bool IsInteresting (MethodReference method) {
if (method.DeclaringType.Scope.Name != "mscorlib")
return false;
/*if (IsInteresting (method.DeclaringType))
Console.WriteLine ("\ttarget");
if (IsInteresting (method.ReturnType))
Console.WriteLine ("\treturn");
foreach (var p in method.Parameters) {
if (IsInteresting (p.ParameterType))
Console.WriteLine ("\tparam {0}", p.Index);
}*/
if (IsInteresting (method.DeclaringType))
return true;
if (IsInteresting (method.ReturnType))
return true;
foreach (var p in method.Parameters) {
if (IsInteresting (p.ParameterType))
return true;
}
return false;
}
bool IsInteresting (TypeReference type) {
if (type.Namespace == "System.Reflection" || type.Namespace == "System.Reflection.Emit")
return true;
if (type.Namespace == "System" && type.Name == "Type")
return true;
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment