I have been following this article to generate a dynamic assembly as follows:
var directory = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
var file = new FileInfo(Path.Combine(directory.FullName, #"MyDynamicAssembly.exe"));
var domain = AppDomain.CurrentDomain;
var name = new AssemblyName("Namespace.With.Dots");
var builderAssembly = domain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Save, directory.FullName);
var builderModule = builderAssembly.DefineDynamicModule("Namespace.With.Dots.Temp.exe");
var builderType = builderModule.DefineType("Program", TypeAttributes.Class | TypeAttributes.Public, typeof(object));
var builderMethod = builderType.DefineMethod("Main", MethodAttributes.Private | MethodAttributes.Static, typeof(int), new Type [] { typeof(string []) });
var generator = builderMethod.GetILGenerator();
generator.Emit(OpCodes.Ldstr, "Hello, World!");
generator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type [] { typeof(string) }));
generator.EmitWriteLine("Hello again.");
generator.Emit(OpCodes.Ldc_I4, 0);
generator.Emit(OpCodes.Ret);
builderAssembly.SetEntryPoint(builderMethod, PEFileKinds.ConsoleApplication);
builderAssembly.Save(file.Name, PortableExecutableKinds.ILOnly, ImageFileMachine.I386);
var process = Process.Start(file.FullName); // Crashes with image below.
var result = process.WaitForExit();
var exitCode = process.ExitCode; // -532462766.
Here is what I know about the code above:
It is creating a dynamic assembly as Save only.
The assembly name, module name and output PE name are all different (I'm assuming this is not a problem).
It creates a public static class called Program.
It creates a single method in this class with signature private static int Main (string []).
It sets this method as an entry point and configures the assembly to be a console app.
It writes the assembly as ILOnly which is processor architecture agnostic.
It configures the assembly image as i386 which is what I'm running on (Win7 32 bit with an Intel processor).
METHOD:
Pushes a string literal reference to the stack.
Calls Console.WriteLine with the arguments taken from the stack.
Calls Console.WriteLine again using EmitWriteLine.
Pushes 0 as an Int32 to the stack as a return value.
Returns.
CRASH:
Ignore the filename on the image. It would be MyDynamicAssembly.exe per the code above.
Any pointers on what's going wrong here would be appreciated.
You'll have a lot more tough debugging jobs ahead of you beyond this one. You basically only ever get two exit codes. -532462766 or 0xe0434352 is the infamous "CCR" exception. The CLR died trying to load the assembly and can't perform the normal exception handling logic. You of course want to make sure that your generated IL is correct by testing it in-process before you try to run it stand-alone in a separate process. You'll at least have a debugger available that way.
The other one is -532459699 or 0xe0434f4d, the normal "COM" exception. Produced when the code threw a plain .NET exception and it wasn't handled because of a lack of try/catch and no AppDomain.UnhandledException event handler. You'll have to make-do without a stack trace and can only reverse-engineer the location where the exception was thrown with the hints in this answer.
Very punishing trouble-shooting of course, you basically do not ever want to do this. At least consider loading the code in another AppDomain so you stand a chance to produce a diagnostic and recover. It can still be in another process by writing a small "host" program that creates the appdomain and loads the assembly and generates a diagnostic. Also provides you with a way to use the debugger.
Finally got it to work by changing the module builder cell to overload:
var builderModule = builderAssembly.DefineDynamicModule("MyDynamicAssembly", "MyDynamicAssembly.exe", false);
Related
Recently I found an architect-often-used .net program has implemented a function wrongly. So I successfully patched it using ILSpy and Reflexil in a static way of modifying binaries. However, it is annoying that you need to patch it and remove StrongNameCheck again when new minor version releases. (btw, the author believes it is a feature instead of a bug)
Hopefully, the program fully supports assemblies as a plugin. And my target is a public non-static member function in a public class which can be directly called by plugins. Is there a way to patch the function on the fly?
I usually use some APIHook tricks in unmanaged C++ but dotnet is really a different thing. In this case, I want the modification still valid after my assembly unloads (so more similar to a patch, not a hook).
Yes you can do it with code injection. But you need to know some MSIL. There is also a library for that which is called Mono.Cecil.
Here is a example code
Console.WriteLine("> INJECTING INTO 12345.EXE..." + Environment.NewLine);
AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(#"C:\dummy.exe");
var writeLineMethod = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
var writeLineRef = asm.MainModule.Import(writeLineMethod);
var pStartMethod = typeof(Process).GetMethod("Start", new Type[] { typeof(string) });
var pStartRef = asm.MainModule.Import(pStartMethod);
foreach (var typeDef in asm.MainModule.Types)
{
foreach (var method in typeDef.Methods)
{
//Let's push a string using the Ldstr Opcode to the stack
method.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Ldstr, "INJECTED!"));
//We add the call to the Console.WriteLine() method. It will read from the stack
method.Body.Instructions.Insert(1, Instruction.Create(OpCodes.Call, writeLineRef));
//We push the path of the executable you want to run to the stack
method.Body.Instructions.Insert(2, Instruction.Create(OpCodes.Ldstr, #"calc.exe"));
//Adding the call to the Process.Start() method, It will read from the stack
method.Body.Instructions.Insert(3, Instruction.Create(OpCodes.Call, pStartRef));
//Removing the value from stack with pop
method.Body.Instructions.Insert(4, Instruction.Create(OpCodes.Pop));
}
}
asm.Write("12345.exe"); //Now we just save the new assembly
Don't monkey patch code. Add the functionality to your code base and call that function. Or write an adapter class that wraps the underlying assembly, which is much neater.
If the author of the code thinks it's not a bug then it may be in there for reasons you don't understand and could be part of any number of bug fixes.
For my log4net solution, I have an API wrapper that uses the CallerInfo attributes, e.g.
public void Write(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0)
However, I am also using Unity Interception so that I can perform trace logging of the before/after responses, e.g. using ICallHandler like below in the Invoke method.
public class TraceCallHandler : ICallHandler
{
...
public IMethodReturn Invoke(IMethodInvocation input,
GetNextHandlerDelegate getNext)
{
//---- Trace method inputs
this.LogInfoBeforeInvoke(input);
//---- invoking the target method
InvokeHandlerDelegate next = getNext();
IMethodReturn methodReturn = next(input, getNext);
//---- invoking the target method
this.LogInfoAfterInvoke(methodReturn.ReturnValue);
}
}
Note: The above code is in no way complete/correct... but just wanted to show you what I was doing for Unity Interception.
My question / challenge is this:
when I eventually call log.Write(...), I want the target's caller info, not my TraceCallHandler info.
e.g. for method name, I can do this:
string methodName = input.MethodBase.Name;
How do I get the Caller's File Path and Caller's Line Number? Is it even possible to do via reflection?
Thanks!
Yes, you can get these using reflection:
var sf = new System.Diagnostics.StackTrace(1).GetFrame(0);
Console.WriteLine(" File: {0}", sf.GetFileName());
Console.WriteLine(" Line Number: {0}", sf.GetFileLineNumber());
// Note that the column number defaults to zero
// when not initialized.
Console.WriteLine(" Column Number: {0}", sf.GetFileColumnNumber());
However as it says clearly in the documentation:
StackFrame information will be most informative with Debug build
configurations. By default, Debug builds include debug symbols, while
Release builds do not. The debug symbols contain most of the file,
method name, line number, and column information used in constructing
StackFrame objects.
So if all you want this for is debugging, then enable it in debug builds and log away. In Release builds though it will be at best unhelpful and at worst downright misleading as apart from the symbol considerations above the compiler will aggressively inline methods and reorder things and generally mess with your stuff.
I just ran across this issue and thought I would share what I learned. First, when you include [CallerFilePath] in a method argument a side effect is that the full path of the file, including any user identifiable data, will be included in your .exe. I created a simple program with one method. I created an exe. I then added a [CallerFilePath] attribute to the test function. When I compared the results of strings.exe (from sysinternals), the one with the attribute differed in that it included the full path of my source file.
c:\users\<my name>\documents\visual studio 2015\Projects\TestCallerAttribute\TestCallerAttribute\Program.cs
The answer above by stuartd is correct in that you will not be able to get the data you want from the stack trace in a release build.
There is a solution to getting strong data however: Event Tracing for Windows. From msdn: "Event Tracing for Windows (ETW) is an efficient kernel-level tracing facility that lets you log kernel or application-defined events to a log file. You can consume the events in real time or from a log file and use them to debug an application or to determine where performance issues are occurring in the application."
This is not a quick solution. There is work in setting up the events and the listeners to get the provenance you need. The long term payoff is strong.
I am working on some software that will dynamically build menu items for certain dlls so that we can load components in dynamically based on what dlls are available on the users machine. Any dlls that I want to load have been flagged with an Assembly Attribute in the AssemblyInfo.cs file and how I determine whether or not I want to build a menu item for that dll. Here is my method so far:
private void GetReportModules() {
foreach (string fileName in Directory.GetFiles(Directory.GetCurrentDirectory())) {
if (Path.GetExtension(fileName) == ".dll" || Path.GetExtension(fileName) == ".exe") {
System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(fileName);
object[] attributes = assembly.GetCustomAttributes(typeof(ReportNameAttribute), false);
if (attributes.Count() > 0) {
ReportNameAttribute reportNameAttribute = attributes[0] as ReportNameAttribute;
Type type = assembly.GetType(reportNameAttribute.BaseType);
MenuItem customReportsMenuItem = new MenuItem();
customReportsMenuItem.Header = reportNameAttribute.ReportName;
ReportsMenuItem.Items.Add(customReportsMenuItem);
customReportsMenuItem.Click += (s, ev) => {
var obj = Activator.CreateInstance(type);
type.InvokeMember("Show", System.Reflection.BindingFlags.Default | System.Reflection.BindingFlags.InvokeMethod, null, obj, null);
};
}
}
}
}
For the most part its working fine, I am getting the dlls that I am expecting back out and am creating my menu items fine. The problem is that in order to check for the attribute I first need to load the assembly using Reflection. Some of the other local dlls are throwing errors when I try to load them about missing dependencies or he module was expected to contain an assembly manifest. Is there a way I can safely check to see if an assembly CAN be loaded before I actually do so? (sounds stupid as I write it out). Any thoughts on the problem I'm running into or better suggestions for how to accomplish what I'm trying here? Feeling a little bit in over my head.
You can create a separate AppDomain, try to load the assemblies there, send the results back, and unload the AppDomain. This way you do not change your current AppDomain with 'garbage' of any loaded assemblies.
One way would be to make use of a try catch block. If it throw's an exception, you're not interested...
EDIT:
MSDN explains clearly the type of exceptions LoadFrom can throw. FileLoadException looks likely in your case.
I'm sure there is code out there that carried on after a catch. For example a logging framework. I would not want my framework to catch an exception and make my executable stop etc, i'd want it to smother the exception. My application should not fail just because a line of log miss fired.
You can try the Unmanaged Metadata API (http://msdn.microsoft.com/en-us/library/ms404384.aspx) or the Common Compiler Infrastructure Metadata API (http://ccimetadata.codeplex.com/) as alternatives to plain reflection.
I have tried the obvious:
var appDomain = AppDomain.CreateDomain("New Domain");
var engine = IronPython.Hosting.Python.CreateEngine(appDomain); // boom!
But I am getting the following error message: Type is not resolved for member 'Microsoft.Scripting.Hosting.ScriptRuntimeSetup,Microsoft.Scripting, Version=0.9.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
Googling for this error has not proved fruitful sofar...
EDIT #1:
I tried to create a minimal reproducing project by copying the relevant stuff to a new Console Application:
using System;
using Microsoft.Scripting;
namespace PythonHostSamle
{
class Program
{
static void Main(string[] args)
{
AppDomain sandbox = AppDomain.CreateDomain("sandbox");
var engine = IronPython.Hosting.Python.CreateEngine(sandbox);
var searchPaths = engine.GetSearchPaths();
searchPaths.Add(#"C:\Python25\Lib");
searchPaths.Add(#"C:\RevitPythonShell");
engine.SetSearchPaths(searchPaths);
var scope = engine.CreateScope();
//scope.SetVariable("revit", _application);
//engine.Runtime.IO.SetOutput(new ScriptOutputStream(_instance), Encoding.UTF8);
//engine.Runtime.IO.SetErrorOutput(new ScriptOutputStream(_instance), Encoding.UTF8);
var script = engine.CreateScriptSourceFromString("print 'hello, world!'", SourceCodeKind.Statements);
script.Execute(scope);
Console.ReadKey();
}
}
}
This works as expected!
I am thus left to conclude that the error I am getting is related to one of the lines I commented out: The scope added to the engine contains an object I have little control over - a reference to a plugin host this software is intended to run in (Autodesk Revit Architecture 2010).
Maybe trying to pass that is what is creating the error?
Is there a way to pass a proxy instead? (will have to look up .NET remoting...)
EDIT #2:
I have whittled the problem down to passing an object via the scope that does cannot be proxied to the other AppDomain: All objects added to the scope of an IronPython interpreter running in a different AppDomain will have to be marshaled somehow and must thus either extend MarshalByRefObject or be Serializable.
Just create your own bootstrapping class that will run in a new AppDomain and will do the initialization of IronPyton there, will it solve the prob?
I am using the below code to execute the IronPython Script on separate "appDomain" from c#.
(I used this approach to resolve memory leakage issue)
The scripts which take a lesser time (less than 3 mins), executes fine.
But if the script which takes a longer time (more than 5mins) throws an exception saying
-> System.Runtime.Remoting.RemotingException: Object '/011b230e_2f28_4caa_8bbc_92fabb63b311/vhpajnwe48ogwedf6zwikqow_4.rem'
using System;
using Microsoft.Scripting;
namespace PythonHostSamle
{
class Program
{
static void Main(string[] args)
{
AppDomain sandbox = AppDomain.CreateDomain("sandbox");
var engine = IronPython.Hosting.Python.CreateEngine(sandbox);
var searchPaths = engine.GetSearchPaths();
searchPaths.Add(#"C:\Python25\Lib");
searchPaths.Add(#"C:\RevitPythonShell");
engine.SetSearchPaths(searchPaths);
ScriptScope scope = engine.ExecuteFile("C:\Python25\Test.py")
// Script takes morethan 5mins to execute(sleep in the script)
ObjectHandle oh = scope.GetVariableHandle("GlobalVariableName")
// System throws following exception
//System.Runtime.Remoting.RemotingException:
// Object '/011b230e_2f28_4caa_8bbc_92fabb63b311/vhpajnwe48ogwedf6zwikqow_4.rem'
Console.ReadKey();
}
}
}
Overriding InitializeLifetimeServices and returning null would be the normal approach. I doubt that's possible in your case. Including the <lifetime> element in the app.config file is another approach.