In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces "what is the working principle of the .NET Core Clipper". In the daily operation, I believe that many people have doubts about the working principle of the .NET Core Clipper. The editor consulted all kinds of materials and sorted out a simple and easy-to-use method of operation. I hope it will be helpful to answer the question of "how does the .NET Core Clipper work?" Next, please follow the editor to study!
Technology 1. Detect the assemblies and classes loaded by the program
Microsoft provides a library Diagnostics for analyzing the runtime behavior of .NET Core, which can obtain rich runtime information, such as class instance creation, assembly loading, class loading, method calls, GC runs, file read and write operations, network connections, and so on. The tool in Visual Studio to evaluate the call time of each method is implemented using Diagnostics.
To use the Diagnostics library, we first need to install the Microsoft.Diagnostics.NETCore.Client and Microsoft.Diagnostics.Tracing.TraceEvent assemblies, and then use the DiagnosticsClient class to connect to the processes of the. NETCore program being parsed. The code is as follows:
Using Microsoft.Diagnostics.NETCore.Client;using Microsoft.Diagnostics.Tracing;using Microsoft.Diagnostics.Tracing.Parsers;using Microsoft.Diagnostics.Tracing.Parsers.Clr;using System.Diagnostics;using System.Diagnostics.Tracing;string filepath = @ "E:\ temp\ test6\ ConsoleApp1.exe"; / / the program path being analyzed is ProcessStartInfo psInfo = new ProcessStartInfo (filepath); psInfo.UseShellExecute = true;using Process? P = Process.Start (psInfo); / / Startup program var providers = new List () / / events to be listened for {new EventPipeProvider ("Microsoft-Windows-DotNETRuntime", EventLevel.Informational, (long) ClrTraceEventParser.Keywords.All)}; var client = new DiagnosticsClient (p.Id); / / set the process DiagnosticsClient listens to using EventPipeSession session = client.StartEventPipeSession (providers, false); / / start listening var source = new EventPipeEventSource (session.EventStream) Source.Clr.All + = (TraceEvent obj) = > {if (obj is ModuleLoadUnloadTraceData) / / Assembly loading event var data = (ModuleLoadUnloadTraceData) obj; string path = data.ModuleILPath;// gets the assembly path Console.WriteLine ($"Assembly Loaded: {path}");} else if (obj is TypeLoadStopTraceData) / / Class loading event var data = (TypeLoadStopTraceData) obj; string typeName = data.TypeName;// gets the class name Console.WriteLine ($"Type Loaded: {typeName}");}; source.Process ()
Different types of messages correspond to different types of objects in the source.Clr.All event, all of which inherit from TraceEvent. Here I analyze the assembly loading event ModuleLoadUnloadTraceData and the class loading event TypeLoadStopTraceData.
In this way, we can know the assembly and type information loaded while the program is running, so we know which assemblies and types are not loaded, so we know which assemblies and types to delete.
Technology 2. Delete classes that are not used in the assembly
Zack.DotNetTrimmer provides the ability to delete IL of classes that are not used in the assembly, which uses the dnlib library to edit assembly files. Dnlib is an open source project that reads, writes, and edits .NET assembly files.
In Dnlib, we use ModuleDefMD.Load to load an existing assembly, and the return value of the Load method is of type ModuleDefMD. ModuleDefMD represents assembly information, such as the Types attribute that represents all types in the assembly. We can modify the ModuleDefMD and the objects in it, and then call the Write method to save the modified assembly to disk.
For example, the following code is used to change all non-public types in an assembly to public types and to remove all Attribute decorated on the method:
Using dnlib.DotNet;string filename = @ "E:\ temp\ net6.0\ AppToBeTested1.dll"; ModuleDefMD module = ModuleDefMD.Load (filename); foreach (var typeDef in module.Types) {if (typeDef.IsPublic = = false) {typeDef.Attributes | = TypeAttributes.Public;// modified class access level} foreach (var methodDef in typeDef.Methods) methodDef.CustomAttributes.Clear (); / / Attribute} module.Write (@ "E:\ temp\ net6.0\ 1.dll") of the clear method; / / Save changes
Here is the source code for the assembly to be tested:
Internal class Class1 {[DisplayName ("AAA")] public void AA () {Console.WriteLine ("hello");}}
The following is the decompilation result of the modified assembly:
Public class Class1 {public void AA () {Console.WriteLine ("hello");}}
You can see that our changes to the assembly are working.
Having mastered the method of using Dnlib to modify the assembly, we can delete the types that are not used in the assembly, as long as we delete the corresponding types from the Types property of ModuleDefMD. However, in practice, this will encounter problems, because the class we want to delete may be referenced elsewhere, although those places only refer to the class we want to delete and are not actually called, in order to ensure the validity of the modified assembly, the Write method of ModuleDefMD will still do the validity check, otherwise the Write method will throw a ModuleWriterException exception, such as:
ModuleWriterException:'A method was removed that is still referenced by this module.'
Therefore, when we write code, we need to carefully examine the assembly to make sure that every place that references the class to be deleted is deleted. Because the class definition itself takes up very little file size, and the main code takes up all the space in the method body of the class, I found an alternative, which is not to delete the class, but to empty the method body of the class.
In Dnlib, the type corresponding to a method is the MethodDef type, and the Body property of the CilBody type of MethodDef represents the method body of the method. If a method has a method body (that is, not an abstract method, etc.), then the Instructions of the CilBody represents a collection of IL instructions for the method body code. So I immediately thought of the method body to clear the method body with the following code:
MethodDef.Body.Instructions.Clear ()
However, at run time, saving using the cleaned ModuleDefMD of the above code may cause illegal assembly structure. For example, some methods define the return value. If we empty the method body directly, it will cause the problem that the method does not return the return value. So I changed my way of thinking, that is, all the method bodies are changed to the corresponding IL code of throw null;, the C # code, because all method bodies can be changed to throw an exception form to ensure the correctness of the logic. So I wrote the following code to clean up the method body:
Method.Body.ExceptionHandlers.Clear (); method.Body.Instructions.Clear (); method.Body.Variables.Clear (); method.Body.Instructions.Add (new Instruction (OpCodes.Nop) {Offset = 0}); method.Body.Instructions.Add (new Instruction (OpCodes.Ldnull) {Offset = 1}); method.Body.Instructions.Add (new Instruction (OpCodes.Throw) {Offset = 2})
The IL code added in the last three lines corresponds to the throw null line of C # code.
Other issues used by Dnlib
In the process of using Dnlib, I have some other gains, which I record here to share with you.
Harvest 1. Problems encountered when Dnlib saves assemblies with native code
When cleaning up assemblies using the methods I mentioned above, most of them are fine for the custom assemblies we wrote and the assemblies for third-party NuGet packages. But there is a problem when using the same method to deal with PresentationCore.dll, System.Private.CoreLib.dll, etc. NET Core basic assemblies, that is, even if I just Load the assembly, after I don't make any changes, the assembly will become significantly smaller after Write. For example, I use the following code to deal with PresentationFramework.dll:
Using (var mod = ModuleDefMD.Load (@ "E:\ temp\ PresentationFramework.dll") {mod.Write (@ "E:\ temp\ PresentationFramework.New.dll");}
The original PresentationFramework.dll size is 15.9MB, while the saved new file size is only 5.7MB. After asking the Dnlib author to know that these assemblies contain native code (such as code written in C++/CLI or assemblies in ReadyToRun / NGEN / CrossGen format, etc.), these native code will be ignored when saved using the Write method, which is why the size of the saved assembly is significantly smaller. We can use the NativeWrite method instead of the Write method, because this method retains the native code.
However, according to Washi1337, author of AsmResolver, an open source project similar to DnLib, the NativeWrite approach preserves the structure of native code as much as possible so it cannot reduce the size of the assembly and may even increase the size of the assembly (see https://github.com/Washi1337/AsmResolver/issues/267 for details). And in the actual use, I found that after making changes to these assemblies, the program will fail to start. Looking at the Windows event log, I found that it was caused by the failure of CLR startup when the program was started. According to Washi1337, if only the ReadyToRun native code is in the assembly, just remove the ILLibrary flag in the assembly and let CLR skip the ReadyToRun native code and execute the IL code directly. after all, the original IL code is still saved for the ReadyToRun-optimized assembly. But after I did what Washi1337 said, the program still failed to start, and it's not clear why, because assemblies with native code can't be well tailored, so I didn't go any further. Friends who are proficient in CLR are welcome to share their experience.
Harvest II. Other applications of Dnlib
Because DnLib can modify assemblies, we can use it to do a lot of things, such as changing the default behavior of the program (you know). We can use DnLib to write our own code obfuscator or implement static weaving for facet programming (AOP).
What other DnLib application scenarios do you think of? Welcome to share.
At this point, the study on "how the .NET Core Clipper works" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.