We found that confuserex, an open-source. Net encryption program, is also used by many people, especially foreigners. This is a headache for many times. It is mainly because there is no ready-made shelling program and manual shelling is required. Although it is not difficult, it is updated fast and constantly changing, it always gives people the feeling that they can catch up with each other. In other words, I don't know if there are really no big cows in China, or if there are big cows hidden and hidden. The shelling programs are all foreign works, and tianchao cannot access GG, so I can only do it myself. In fact, the purpose of this article is to rebuild a new Assembly based on the source set in the memory. Instead of dumping, it is necessary to restore the names and string information that have been obfuscated. Obviously, dump cannot meet this requirement.
This task has been dragging on for a long time, mainly because there are too many content to be prepared in the early stage. The structure of the PE and net headers is good to say that the 45 yuan data table is miserable. First, the document cannot be found, second, I can't understand the English level of this primary school. For various reasons, I have been stranded until one day ...... It's boring to download the source code of confuserex. I found that it uses the same dnlib library as de4dot, and I found that this library is encapsulated. net assembly, the physical structure and basic logic of all metadata, isn't that my long-sought true love? Now, let's give up on my old unfinished project and start a new journey.
The following process is the topic.
Using the dnlib library, you can obtain metadata information in the entire assembly, including assembly, resources, modules, types, fields, attributes, events, methods, parameters, and generics. You can use this library to dynamically create an assembly. net System. reflection. there are two obvious differences between the Assemblies created in the emit namespace. First, the assemblies created by the former cannot be directly run in the memory and can only be run after being saved and loaded, the latter is directly created in the CLR and can be saved to the disk. Another difference is that signaure is emphasized in dblib, while. NET programming is difficult to come into contact with the "signature", so figuring out the relationship between the data in the meta-data table and the signature becomes the focus of whether to dump an assembly.
Another focus of assemlyb in the memory of dump is to sort out the ing between metadata and metadata, and add them to the new assembly in sequence according to the content in the source set. This process is a bit complicated, the main reason for the complexity is that I do not know much about the physical structure of metadata, so that I have taken a lot of detours. The following is my implementation process. The code is not pasted, and the person guides the way, but the person does not walk.
0. Create a cache dictionary, including the type, field, attribute, event, method, method parameter, generic parameter, and method body parameter. The method body parameters do not have a one-to-one correspondence with the method parameters. In most obfuscation software, the method parameters are removed because they are not mandatory, the unique purpose is to record the parameter name. Generic parameters include generic method parameters and generic type parameters. However, it takes some time to figure out the generic signature.
1. Create an assembly and a module, including the Assembly name, assembly version, assembly public key, module version, module CLR version, and module target platform.
2. Creation type. Create an empty type with the same namespace and name and cache the corresponding relationship. Note that the nested type should also be created cyclically.
3. loop through all the types created in the previous step, add base classes, interface types used for adding types, add fields, add properties, add events, and add empty methods. The attribute and event have two methods, set, get, add, and remove, respectively. The names must be restored. The more important thing is the signature. The signatures of these class members need to be re-generated. I wrote a function named clonesig in advance, corresponding to corhdr. in H, each basic corelementtype is generated according to the signature constructor in the dnlib library.
4. In the previous section, when creating a method, note that the four methods with fixed names of attributes and events should not be created. Pay attention to the generic parameters and signature of the method, in most obfuscated programming sets, both generic signature and generic parameters and method body parameters do not have a one-to-one relationship. here we need to recreate and restore the name. In addition, the constant values of method parameters and fields cannot be forgotten.
5. Create a correspondence between methods. Corresponding to new, virtual, and override. The key point is that after all methods are created, find the corresponding relationship from the cache and reshape the relationship between methods. Here, I have a function named clonemethod, which mainly distinguishes between the methods defined in the module and the Methods referenced from other modules. If it is a referenced method, it calls another function clonemember (described below)
6. The most important thing is the reconstruction of the method body. Here, you don't need to worry about tiny or big. dnlib will help us solve the problem.
6.1 re-create and cache the local variables according to the signature of the local variables in the method body.
6.2. Traverse each il command, generate a new il command, and cache the ing relationship with the dictionary. An il command can be divided into opcode and operand. I have to say that dnlib is very powerful. It classifies both of them and records the running and stack methods. Sort by operandtype:
6.2.1 inlinefield, inlinemethd, inlinetype, and inlinetok correspond to four types of operand: fielddef, itypedeforref, memberref, and methodspec. You can replace the first two types of objects in the cache, methodspec is actually a reference to a generic method. It needs to regenerate a new object, while memberref is a little more complicated. Using a function named clonemember serves not only to regenerate references, you also need to add a method or field to distinguish this memberref, and add a reference to its parent class. Therefore, the assemblyref and typeref tables in the metadata table are available.
6.2.2 inlinevar and shortinlinevar. operand is the read/write operation of local variables and method parameters, replace the cached method body parameters with the cached local variables and the method body parameters when creating the method.
6.2.3, inlinesig type, in fact, it is a command to directly reference the method signature to jump to the pointer, operand is a method signature, re-build it.
6.2.4, inlinebrtarget, shortinlinebrtarget, and inlineswitch types. The first two are the Il commands for direct jump, while the switch is a jump table. In dnlib, the jump command operand is not the offset number, but the command to jump directly to, that is, the destination instruction, so we need to cache it, after all the commands are added, the jump relationships will be rebuilt.
6.2.5, the other operandtypes are left with the Il commands for direct data operations. Copy the operand commands directly, because they are all system types.
6.3, calculate the offset and re-establish the jump relationship. At the beginning, I also manually calculated the offset of each Il. Later I found a method named updateinstructionoffsets IN THE cilbody class. Rebuild the correspondence between the preceding jump command cache and all the above cached il commands, note that the switch jump table is of the instruction [] type, while other Long and Short jump commands are of the normal instruction type.
6.4. Rebuild the exception handling table. In fact, I didn't quite understand it here, but it doesn't matter. just create a new execptionhandler one by one based on all the cached il commands and add it to the table.
6.5. At this time, the reconstruction of the entire method body has been completed, but so on, we seem to have missed something. Of course, the obfuscated method body needs to be restored, the encrypted strings and data also need to be restored, so I marked a comment here.
7. Create their features based on the cached assembly, module, type, field, attribute, event, method, method parameter, and generic parameter.
8. Add resources for the module, including. NET Resources and Win32 resources.
9. I am sorry to add the module entry point. I still don't know how to handle the native code entry point assembly, mainly because the native code method has not been rebuilt.
10. Save, test, error, test, and error. After a period of time, it can be regarded as successful.
Dump assembly in memory to disk