Scanning .NET Assemblies for References
Introduction
As part of my work on running code in isolation, or sandbox, I set out to investigate how can we restrict what the hosted code runs, or, at least, find out what it is trying to do.
I was interested in finding out what references does an assembly have, not just assembly references, but what types and methods it is actually accessing, from other assemblies. I ran into an old acquaintance, Mono.Cecil, which can helps us do just that, as I'll explain.
Introducing Mono.Cecil
From it's page on the Mono Project site: "Cecil is a library written by Jb Evain to generate and inspect programs and libraries in the ECMA CIL format. With Cecil, you can load existing managed assemblies, browse all the contained types, modify them on the fly and save back to the disk the modified assembly."
Cecil is part of Mono Project, a most interesting initiative, which you should be aware of, if not already. It started in the old days of .NET Framework, when it wasn't open-source and platform-agnostic and only ran on Windows. Of course, here we are not interested in code weaving, but in code inspection: we can use Cecil to inspect all of the instructions inside an assembly, and, for each, figure out if it's a method call. We will only be interested in method calls to outside libraries. For example: calls to Process.Start, TcpListener, SqlConnection, etc, might be considered problematic; we can use Cecil to check an assembly against our blacklist.
Introducing ReferencesScanner
ReferencedScanner is my new project for getting external references for an assembly. It consists of a type Reference that holds method references, and an IScanner contract:
public record struct Reference(System.Reflection.MethodBase Method);
public interface IScanner
{
IEnumerable<Reference> GetReferences(string assemblyName);
}
As you can see, the Reference record struct is, for now, just a holder of MethodBase references, which can either be constructor instantiation (ConstructorInfo) or method calls (MethodInfo). From it, you can see what type and assembly was called, as well as the method itself, of course.
There are a few extensions to IScanner that allow us to pass in Assembly or Type objects, but, the core GetReferences method takes as its only parameter the path of an assembly (.dll or .exe).
Sample usage:
Assembly asm = ...; IScanner scanner = new ReferencesScanner(); var references = scanner.GetReferences(asm);
ReferencesScanner is the only provider implementation, which uses Mono.Cecil to get IL information from the target assembly. For now, I kept it very simple: it will ignore references to method calls on strings, objects, or other primitive types.
The purpose of this project is solely getting all references from an assembly, so that we can get a glimpse of what it's supposed to do.
Future Work
I may want to try out other code inspectors other than Cecil and also a few more things, such as integrating with Isolator. This project will always be very simple, I suppose. Let me know if you'd be interested in having something else.
Conclusion
You can find the code and package on the following locations:
Comments
Post a Comment