Hey folks! Today I am going to explain the working of cobalt strike executes assembly, and how it executes managed code through unmanaged Windows API. So, let’s begin!
What is CLR
CLR stands for Common Language Runtime and it is provided by .NET framework as one of its components, which is responsible for loading and executing the various Assemblies written in .NET framework (C#, VB.NET, etc.). The CLR provides many services to .NET applications, including memory management, type safety, security, and exception handling.
source: Youtube
The source code written in .NET framework , gets compiled by the compiler and converted to IL code (intermediate Language), which not machine-specific, and can be run on any machine wherever the CLR is installed. When the IL code gets executed, the CLR compiled it to machine-specific code using JIT (Just in Time Compiler). Refer to the below workflow.
Overall, CLR is a crucial component of .NET framework which is responsible for doing things like Memory management, Garbage Collector, Type Safety, etc. This is all about CLR.
NOTE:
Code that executes under the control of the runtime is called managed code. Conversely, code that runs outside the runtime is called unmanaged code. Refer here.
Interface in C
We all know that there is no concept of Interface in C, and if that is the case, then how we can call those interfaces in C , which is defined by Microsoft for executing the managed code in CLR.
Basically, You can think of an Interface as a structure, that has a member called lpVtbl (VTables), which again is a structure and contains pointers to all the functions present in that structure, and the first argument to those functions is just the structure itself.For this reason, if you are accessing any windows defined interface, you first have to access the lpVtbl member, and from that you can access the methods defined in that particular structure.
Behind the Scenes of Execute Assembly
Now you have understood what is CLR. Now we can go ahead into the coding part where I will explain which Unmanaged APIs to call for executing the managed code. So’ let’s begin.
First Include the header files which is necessary. Keep in mind, the version of .NET framework from which you have created the managed code-based executable, you have to include the same version of mscoree.h and metahost.h. For ex: I have created a simple Hello world in C#, .NET version 4.8 , so you have to include the same version header files in the C code.
Now, you have to define the CLRCreateInstance Function, where you can obtain to pointer to ICLRMetaHost Interface, which will help you to enumerate the installed CLR in the system.
First Parameter is CLSID , which uniquely identifies a COM object and the second parameter is RIID , which is “Reference to Interface Identifier”. We don’t have to worry about the background of these data types, as Microsoft have already defined the values we will be needing for ICLRMetaHost Interface. Have a look at the below screenshot
Third parameter is basically a LPVOID which a typedef for “void *”. This parameter will contain the pointer to the ICLRMetaHost Interface , but the argument wants the parameter of type LPVOID , that’s why there is a typecasting. Now we have obtained the pointer to ICLRMetaHost interface, let’s see how we can use it to enumerate the already installed CLR versions.
If you see above, ppInterface (which is pointer to ICLRMetahost Interface) first accessing a member lpVtbl (which is a structure containing pointer to all funtions) and then from there it accesses the function itself, and the first parameter of the function is the structure itself.
Second parameter to the EnumerateInstalledRuntimes function is of IEnumUnknown Type, which helps to enumerate through the objects in a component containing multiple objects.
“Next” is a method in IEnumUnknow Interface which will help to receive the objects contained in a enumeration. The first parameter is the structure itself, second parameter is number of items to retrieved at a time , third parameter is pointer to a pointer to ICLRRuntimeInfo interface which contains the information about the installed CLR in a system. Fourth parameter will be zero or lesser than the second parameter, it is basically a parameter which will tell you the number of items retrieved from the enumeration.
We are running a loop on top of it, which means for every CLR information objects retrieved , try to get me its version, which is what done by the ICLRRuntimeInfo Interface pointer, runtimesArray. Here, I have break out of the the loop , in the first iteration itself, which means the first CLR version which is enumerated is going to be used to load the managed code executable. If you are using .NET 4.0 and above , I would suggest to use CLR Version v4.0.30319.
Now, we have the pointer to ICLRRuntimeInfo, runtimeArray, which will helps to load (not Initializes) the CLR into current process using GetInterface Method. The First parameter is the structure name itself, second and third parameter is already defined by microsoft for specific type of CLR. Fourth parameter is a return value , which is pointer to interface ICLRRuntimeHost , which is basically pointer to the CLR itself.
Before this method, we are checking if the particular CLR version is loadable or not .And once we got pointer to ICLRRuntimeHost , then we can Initialize the CLR, using the start method. Once the CLR got initialize, we can now run our Managed executable using ExecuteInDefaultAppDomain method, see the screenshot below.
In the above method, the First parameter is the structure/interface name itself, the second parameter is the path to the executable, the third parameter is the FQDN of the Class, and the fourth parameter is the method name you want to execute in the exe, The fourth parameter is the argument you want to pass in the method, and fifth parameter will contain the return value of the function.
Here is the simple HelloWorld Program written in csharp , which is getting executed. Also, Microsoft has provided the signature of the method that is going to be executed, if your method is not matching with that signature, then your exe is not going to be executed.
Summary
Execute-assembly follows fork & run policy, which means it will spawn a new process, do its work, and kill the process. In the new process that it spawned, it will call the CLR with the appropriate method and signature and execute the assembly. That’s all about the CLR and Execute-assembly. All of the above code can be found in my GitHub repository, here
References
https://github.com/SecTheBit/MalDevelopment/
https://www.codeproject.com/Articles/13601/COM-in-plain-C
https://www.youtube.com/watch?v=t5SN7ulOxs8