.NET Core analysis assembly better way, beyond ReflectionOnlyLoad

Time:2022-11-24

As we all know, using the Assembly.LoadFile() method to analyze an assembly file has certain limitations. If you only want to analyze the assembly, but do not need to execute the assembly, what should you do? Today, I will teach you through a simple experiment.

When writing a .NET program, if we need to analyze an assembly file, we can use Assembly.LoadFile() to load the assembly, and then further analyze the Assembly object returned by the LoadFile() method. But the Assembly.LoadFile() method will load the assembly into the program for the purpose of execution, so it has strict requirements on the loaded assembly file. For example, if the assembly that the assembly depends on does not exist, then LoadFile () will throw an exception. For another example, when loading the assembly of .NET Framework in .NET Core, LoadFile() will also throw an exception. If we only want to analyze the assembly, but don’t need to execute the assembly, then we need a way to purely analyze the assembly file.

.NET Framework provides Assembly.ReflectionOnlyLoad() to achieve similar effects, but this method is not supported in .NET Core because it depends on AppDomain. Microsoft once proposed a System.Reflection.TypeLoader that implements this function in .NET Core in a laboratory project, but for some reason, this class is not provided in the official version of .NET Core.

We know that .NET assembly is a file in PE format. .NET provides a class PEReader for analyzing PE files (located in the NuGet package System.Reflection.Metadata), so we can use PEReader to analyze assembly files.

In PEReader, we can get all the classes in the assembly through TypeDefinitions, and we can use GetMethods() to get all the methods defined in a class. In order to improve efficiency, the objects obtained by members such as TypeDefinitions and GetMethods() are handle types such as TypeDefinitionHandle and MethodDefinitionHandle. These objects only contain address information, and do not contain detailed information such as type names, method names, and method parameters. To obtain this information, we need to call the methods such as GetTypeDefinition() and GetMethodDefinition() of MetadataReader. The following code is used to load an assembly and output all the type information in the assembly and the methods defined in the type:


//Install-PackageSystem.Reflection.Metadata
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

string file [email protected]"E:\Microsoft.AspNetCore.Components.Web.dll";
using FileStream fileStream =File.OpenRead(file);
using PEReader peReader = newPEReader(fileStream);
if(!peReader.HasMetadata)
{
   Console.WriteLine($"{file} doesn't contain CLI metadata.");
   return;
}
var mdReader =peReader.GetMetadataReader();
if (!mdReader.IsAssembly)
{
   Console.WriteLine($"{file} is not an assembly.");
   return;
}
foreach (var typeHandler inmdReader.TypeDefinitions)
{
   var typeDef = mdReader.GetTypeDefinition(typeHandler);
   string name = mdReader.GetString(typeDef.Name);
   string nameSpace = mdReader.GetString(typeDef.Namespace);
   Console.WriteLine($"***********{nameSpace}.{name}***********");
   foreach (var methodHandler in typeDef.GetMethods())
    {
       var methodDef = mdReader.GetMethodDefinition(methodHandler);
       Console.WriteLine(mdReader.GetString(methodDef.Name));
    }
}

When using PEReader, we need to obtain XXXHandler first, and then call MetadataReader to obtain the detailed information of the handle. Although the performance is relatively high, the code is cumbersome, and it is troublesome to implement some advanced operations. For example, if we want to obtain the CustomAttribute information of an assembly, PEReader does not provide a relatively simple method, and we need to be very proficient in the PE format to write the corresponding code.

We can use AsmResolver.DotNet, a third-party Nuget package, to simplify the reading analysis of assembly files, which is an advanced package of PEReader. The following code is used to load an assembly, output the company information of the assembly, and output all the type information in the assembly and the methods defined in the type:

string file [email protected]"E:\Microsoft.AspNetCore.Components.Web.dll";
var moduleDef =AsmResolver.DotNet.ModuleDefinition.FromFile(file);//Do not use the ModuleDefinition class under the System.Reflection.Metadata namespace
var asmCompanyAttr =moduleDef.Assembly.CustomAttributes.FirstOrDefault(c =>c.Constructor.DeclaringType.FullName =="System.Reflection.AssemblyCompanyAttribute");
var utf8Value =(Utf8String?)asmCompanyAttr.Signature.FixedArguments[0].Element;
var strValue = (string?)utf8Value;
Console.WriteLine($"companyname:{strValue}");
foreach(var typeDef inmoduleDef.GetAllTypes())
{
   string name = typeDef.Name;
   string nameSpace = typeDef.Namespace;
   Console.WriteLine($"***********{nameSpace}.{name}***********");
   foreach (var methodDef in typeDef.Methods)
    {
       Console.WriteLine(methodDef.Name);
    }
}

In short, if we need to analyze an assembly and run the code in it, we can use Assembly.LoadFile(); if we don’t need to run the assembly, but just want to analyze the assembly, then using PEReader is a better choice, of course we You can also choose the AsmResolver.DotNet NuGet package that encapsulates PEReader. The author of this article, Yang Zhongke, used AsmResolver.DotNet when implementing the function of “judging whether an assembly is developed by Microsoft” in the open source project Zack.Commons. You can check the GitHub code warehouse of this project to view the source code.

Recommended Today

“Oracle” client PL/SQL DEVELOPER installation and use

Author: threedayman source:Hang Seng LIGHT Cloud Community background Compared with the command line, the client has richer visual interface information. At the same time, in order to run large sql files more conveniently, choose to install the client. Download and install PL/SQL DEVELOPER download addresshttps://www.allroundautomations.com/registered-plsqldev/Choose the appropriate version to install. Download Instantclient download addresshttps://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html After […]