Bruno Cabral “Reflection, Code Generation and Instrumentation in the.NET platform” University of Coimbra
21 Jan /33 Summary PE File Metadata Introspection Assembly and Code generation Using RAIL
21 Jan /33 PE File PE = Portable Executable Marked as Code and Execute Read for the OS loader and the CLR _CorExeMain (mscoree.dll)
21 Jan /33 PE File PE = Portable Executable Marked as Code and Execute Read for the OS loader and the CLR _CorExeMain (mscoree.dll)
21 Jan /33 PE File Multiple Modules Module = file
21 Jan /33 PE File - Metadata "data about data" Describes types Member fields, methods, properties, events Describes signatures Fields, properties, methods, delegates, local vars Describes types and references Describes miscellaneous entities Files, modules, assemblies
21 Jan /33 PE File - Metadata CIL instructions reference metadata via TOKENS First byte of token indicates type Last three bytes of token are either row # (tables) or offsets (heaps) Tokens are stored compressed in signatures (binary file viewers not very useful)
21 Jan /33 PE File - Metadata Tokens (and types) in listing MethodDef : (0x ) User string: (0x ) AssemblyRef : (0x ) TypeRef : (0x ) MemberRef : (0x0A000002) Only two are directly referenced by CIL in this example: example: String (by ldstr instruction) MemberRef (by call instruction)
21 Jan /33 PE File - Metadata
21 Jan /33 PE File - Metadata
21 Jan /33 Introspection Two major metadata APIs available Managed: System.Reflection Unmanaged: IMetaDataImport Both are limited in different ways System.Reflection does not permit access to CIL System.Reflection does not reveal token values IMetaDataImport cannot resolve *Ref tokens
21 Jan /33 Introspection What can we do with System.Reflection? See loaded assemblies See referenced assemblies See the types defined in the assembly See the methods defined in a type See attributes …
21 Jan /33 Introspection See loaded assemblies Assembly [] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach( Assembly assembly in assemblies ) Console.WriteLine( assembly.FullName );
21 Jan /33 Introspection See referenced assemblies AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies(); foreach( AssemblyName assemblyName in referencedAssemblies ) Console.WriteLine( "--> {0}", assemblyName.FullName );
21 Jan /33 Introspection See the types defined in the assembly Assembly a = Assembly.LoadFrom("Teste.exe"); Module[] m = a.GetModules( ); Console.WriteLine("\n" + a.FullName ); Type[] types = m[0].GetTypes( ); foreach( Type type in types ) Console.WriteLine( "==> {0}", type.FullName );
21 Jan /33 Introspection See the methods defined in a type Assembly a = Assembly.LoadFrom("HelloWorld.exe"); Module[] m = a.GetModules( ); Type[] types = m[0].GetTypes( ); Type type = types[0]; MethodInfo[] mInfo = type.GetMethods( ); foreach ( MethodInfo mi in mInfo ) Console.WriteLine(" {0}", mi);
21 Jan /33 Assembly and Code Generation How to use System.Reflection.Emit to generate assemblies in run-time? AssemblyBuilder, ModuleBuilder, TypeBuilder, MethodBuilder, … ILGenerator
21 Jan /33 Assembly and Code Generation AssemblyName myAssemblyName = new AssemblyName(); myAssemblyName.Name = "MyAssembly"; myAssemblyName.Version = new Version(" ");
21 Jan /33 Assembly and Code Generation AssemblyBuilder myAssemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(myAssemblyName, AssemblyBuilderAccess.RunAndSave); ModuleBuilder myModuleBuilder = myAssemblyBuilder.DefineDynamicModule("MyModule", fileName); TypeBuilder myTypeBuilder = myModuleBuilder.DefineType("MyType");
21 Jan /33 Assembly and Code Generation MethodBuilder myMethodBuilder = myTypeBuilder.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.HideBySig |MethodAttributes.Static, typeof(void), null); ILGenerator myILGenerator = myMethodBuilder.GetILGenerator(); myILGenerator.EmitWriteLine("Hello World!"); myILGenerator.Emit(OpCodes.Ret);
21 Jan /33 Assembly and Code Generation myTypeBuilder.CreateType(); myAssemblyBuilder.SetEntryPoint(myMethodBuilder); myAssemblyBuilder.Save(fileName);
21 Jan /33 What is missing? Reflection Generation …
21 Jan /33 What is missing? Reflection Generation INTRUMENTATION… Structural Reflection Behavioural Reflection HOW TO DO IT?
21 Jan /33 …use RAIL
21 Jan /33 Vision Statement “Create an API that allows CLR assemblies to be manipulated and instrumented before they are loaded and executed“ The reflection capabilities of the CLR are extremely powerful. Query the existing types at runtime Define new assemblies in memory and use Reflection.Emit to generate MSIL on-the-fly. Our plan is to fill the gap between these two concepts.
21 Jan /33 Process and Methodology Operating System program program.exe/dll PE Header MetadataIL ILx86 Source Code Compile Assembly JIT-compile RAIL
21 Jan /33 Process and Methodology
21 Jan /33 Key features ‘Rapid’ assembly instrumentation library High level of abstraction, no need for handling all the internal details Convenient object model for representation of all assembly modules Flexible MSIL instruction handling Use of design patterns
21 Jan /33 Application scenarios Runtime analysis tools Security verification MSIL optimization Application invisible proxy substitution Software fault injection Aspect Oriented Programming Others!
21 Jan /33 What can we really do with RAIL? Replace References Add epilogues and prologues to methods Redirect methods access/call Redirect field access Redirect field access to property Redirect field read and write access to methods Redirect property access Replace create new instructions with a static method Create, Run and Save assemblies
21 Jan /33 What can we really do with RAIL? private void Replace(MSIL.Code code, int pos) { MSIL.ILMethod ilm = new MSIL.ILMethod(OpCodes.Call, this.newMethod); code.Remove(pos); code.Insert(pos,ilm); } Play with MSIL code!!
21 Jan /33 Assembly Loading Interception ResolveEventHandler currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler); Load the assembly ourselves Modify the CLR (SSCLI)??? Others??
21 Jan /33 Conclusion Code instrumentation is not a simple task With RAIL it’s easy to develop high level functionalities Assembly creation mechanisms can be very slow The design patterns are very useful