void ICustomCodeGenerator.GenerateAssembly(MethodHook methodHook, string path) { if (methodHook == null) { throw new ArgumentNullException("methodHook"); } if (!methodHook.RunCustomCode) { throw new ArgumentException("RunCustomCode should be set in order to generate custom code"); } StringBuilder customCode = new StringBuilder(); foreach (string ns in m_referencedNamespaces) { customCode.AppendLine(String.Format("using {0};", ns)); } customCode.AppendLine(); customCode.AppendLine(String.Format("namespace {0}", CustomNamespace)); customCode.AppendLine("{"); string name = GetName(methodHook); string className = String.Format("{0}_{1}", name, "Class"); customCode.AppendLine(String.Format("public class {0}", className)); customCode.AppendLine("{"); string returnType = GetReturnType(methodHook); string methodName = String.Format("{0}_{1}", name, "Hook"); var parameters = methodHook.Method.Parameters.Cast<ParameterDefinition>().Select( p => "ref " + p.ParameterType.Name + " " + p.Name ); string parametersString = String.Join(", ", parameters); customCode.AppendLine(String.Format("public {0} {1}({2})", returnType, methodName, parametersString)); customCode.AppendLine("{"); customCode.AppendLine(methodHook.Code); // method customCode.AppendLine("}"); // class customCode.AppendLine("}"); // namespace customCode.AppendLine("}"); GenerateAssembly(methodHook, customCode.ToString(), path); }
public frmEditHook(MethodHook methodHook) : this() { m_methodHook = methodHook; LoadProperties(); }
private void PatchMethod(MethodDefinition methodDefinition, TypeDefinition typeDefinition, MethodHook methodHook) { #if DEBUG DumpMethod("dump.txt", methodDefinition); #endif if (!methodDefinition.HasBody || methodDefinition.IsGetter || methodDefinition.IsSetter) { return; } if (methodHook != null && methodHook.RunCustomCode) { InjectCustomCode(methodDefinition, methodHook); } if (m_hookProvider.LogMethodNames || (methodHook != null && methodHook.LogMethodName)) { Instruction targetInstruction = LogMethodName(methodDefinition, typeDefinition); if (m_hookProvider.LogParameterValues || (methodHook != null && methodHook.LogParameterValues)) { LogMethodParameters(methodDefinition, targetInstruction); } if (m_hookProvider.LogReturnValues || (methodHook != null && methodHook.LogReturnValues)) { LogReturnValue(methodDefinition); } } #if DEBUG DumpMethod("dump_changed.txt", methodDefinition); #endif }
private void InjectCustomCode(MethodDefinition method, MethodHook methodHook) { // generate assembly with custom code m_codeGenerator.GenerateAssembly(methodHook, Path.GetDirectoryName(m_assemblyPath)); // load generated assembly string assemblyName = methodHook.GetSafeName(); AssemblyDefinition generatedAssembly = LoadGeneratedAssembly(assemblyName); // get method reference with custom code string generatedClassName = String.Format("{0}_{1}", assemblyName, "Class"); var generatedType = generatedAssembly.MainModule.Types.Cast<TypeDefinition>().First(t => t.Name == generatedClassName); string generatedMethodName = String.Format("{0}_{1}", assemblyName, "Hook"); // TODO: take parameter types into account, to resolve methods with the same names var generatedMethod = generatedType.Methods.Cast<MethodDefinition>().First(m => m.Name == generatedMethodName); MethodReference generatedTypeCtor = null; for (int i = 0; i <= generatedType.Constructors.Count; i++) { if (!generatedType.Constructors[i].HasParameters) { generatedTypeCtor = generatedType.Constructors[i]; break; } } if (generatedTypeCtor == null) { throw new InvalidOperationException("Default constructor was not found in generated assembly"); } // inject call MethodReference generatedTypeCtorRef = m_assemblyDefinition.MainModule.Import(generatedTypeCtor); MethodReference generatedMethodRef = m_assemblyDefinition.MainModule.Import(generatedMethod); method.Body.InitLocals = true; CilWorker cilWorker = method.Body.CilWorker; if (methodHook.HookType == HookType.ReplaceMethod) { method.Body.Instructions.Clear(); method.Body.Variables.Clear(); // return value Instruction returnInstruction = cilWorker.Create(OpCodes.Ret); cilWorker.Append(returnInstruction); InsertCustomCodeCall(method, generatedTypeCtorRef, generatedMethodRef, cilWorker, returnInstruction, true); } else if ((methodHook.HookType & HookType.OnMethodEnter) == HookType.OnMethodEnter) { Instruction firstInstruction = method.Body.Instructions[0]; InsertCustomCodeCall(method, generatedTypeCtorRef, generatedMethodRef, cilWorker, firstInstruction, false); } else if ((methodHook.HookType & HookType.OnMethodExit) == HookType.OnMethodExit) { Instruction returnInstruction = method.Body.Instructions[method.Body.Instructions.Count - 1]; if (returnInstruction.OpCode != OpCodes.Ret) { throw new InvalidOperationException(String.Format("Method '{0}' has no valid ret instruction", method.Name)); } InsertCustomCodeCall(method, generatedTypeCtorRef, generatedMethodRef, cilWorker, returnInstruction, false); } }
private void GenerateAssembly(MethodHook methodHook, string code, string path) { var compilerOptions = new Dictionary<string, string>(); compilerOptions.Add("CompilerVersion", "v4.0"); CSharpCodeProvider provider = new CSharpCodeProvider(compilerOptions); CompilerParameters cp = new CompilerParameters(); cp.ReferencedAssemblies.Clear(); cp.ReferencedAssemblies.AddRange(GetReferencedAssemblies()); cp.GenerateExecutable = false; cp.GenerateInMemory = false; // do not include standard mscorlib.dll cp.CompilerOptions = "/noconfig /nostdlib"; string dllName = String.Format("{0}.dll", methodHook.GetSafeName()); cp.OutputAssembly = Path.Combine(path, dllName); CompilerResults results = provider.CompileAssemblyFromSource(cp, code); if (results.Errors.Count > 0) { string errors = String.Join(Environment.NewLine, results.Errors.Cast<CompilerError>().Select(e => e.ErrorText)); throw new InvalidOperationException("Unable to compile custom code: " + Environment.NewLine + errors); } }
private string GetReturnType(MethodHook methodHook) { string returnTypeName = methodHook.Method.ReturnType.ReturnType.FullName; return (returnTypeName != "System.Void") ? returnTypeName : "void"; }
private string GetName(MethodHook methodHook) { return methodHook.GetSafeName(); }
void IMainView.AddMethod(MethodHook methodHook) { lbMethods.Items.Add(methodHook); lbMethods.SelectedItem = methodHook; }
void IMainView.ShowEditHookForm(MethodHook methodHook) { using (frmEditHook form = new frmEditHook(methodHook)) { form.ShowDialog(); } }
private void FillAssemblyMethods(XAPAssembly xapAssembly) { foreach (var method in xapAssembly.GetMethods()) { var hook = new MethodHook(method) { LogMethodName = true, LogParameterValues = true, LogReturnValues = true }; m_hooks.Add(hook); } }