Execute Unmanaged Code via C# PInvoke
An unmanaged code is one that is written outside the DotNet framework which is allowed to be executed at runtime. In this, you will learn how to use C# and DotNet to execute a shellcode crafted from Metasploit.
There is no doubt C# is a high-level language and it generates a bytecode on the compilation. Additionally, it also provides features to run unsafe code like pointers but that too is on a high level if you would compile the code with C# Compiler (csc.exe
). Such type of code in windows is called managed code, where all the memory management is handled by the Common Language Runtime.
Also, there is no doubt that C# is a very flexible language and it allows you to call the function defined in the DLLs of the source code which is not written in C#. Such type of code is known as unmanaged code. Unlike managed code, the developer is responsible for cleaning the resources.
This is post is dedicated to running a shellcode crafted from Metasploit directly into the C# code. Since it will be marked malicious by windows defender, it is advised to disable the AVs before proceeding further.
The source code of this post can be found here – https://github.com/tbhaxor/csharp-and-infosec/tree/main/PInvoke%20MSF%20Payload
Creating Shellcode
There are two ways to create shellcode with the Metasploit framework – MSFVenom and Metasploit CLI. I will be using Metasploit CLI in this case as I feel it is more efficient than the other due to autocompletion and one-time loading. The former one takes the same time when you regenerate the payload with it.
For demonstration, I will use message box payload and show Hello T3r@byt3
text with the title set to Hacked!
. This payload can be found in payload/windows/x64/messagebox
x64 architecture and payload/windows/messagebox
. We are using the C# programming language, so you need to set the format to csharp using the -f csharp
flag.
Now create the payload as shown below
Copy the payload from the above Metasploit output and paste it in the C# project under Program class. You need to make it static
to use the buf
array in the Main method. This is method is called by the CLR and is considered as the entry point of every C# program
Declaring the Unmanaged Function
To run the unmanaged code, you need to first copy the shellcode in the memory and then get the pointer to function so that you can execute it like a function. For this, you need to first allocate the memory in the current process's address space and copy the content from the buffer into that memory location.
Memory allocation can be done by calling the VirtualAlloc function from kernel32.dll
. You can get this signature from PInvoke.com – https://www.pinvoke.net/default.aspx/kernel32.VirtualAlloc#. The DllImport
attribute lets CLR call the function defined in the DLL specified in the first argument. The function should be marked extern
this tells C# that the function is defined in external assembly and DllImport tells CLR where to find that assembly. The name of the symbol should be the same as defined in the DLL otherwise lookup will fail
To get the function pointer, you need to first have a delegate with an UnmanagedFunctionPointer
attribute which will tell the CLR that this delegate is special and will be used to get the function pointer of unmanaged code instead. We will be requiring it during Marshaling. Marshalling is required to convert the memory representation of the shellcode into a format suitable for transmitting data.
Copying Shellcode in Memory
Now it's time to call the VirtualAlloc function and store the address in another IntPtr. Let's count on the system to allocate the memory for us. To do this, pass the IntPtr.Zero
value in the first argument. How many sizes you want to allocate, pass it in the second argument, here it is buf.Length
. Since we need to allocate the memory, the allocation type for such action is 0x1000
which is enumerated as MEM_COMMIT
. As a security feature, you need to provide the mode of the allocated page. The shellcode means to be "executed" + "read/write", you need to set it to PAGE_EXECUTE_READWRITE
whose hex value is 0x40
Once you have allocated the space, this will provide you with a starting address to that chunk. Let's call it to handle for now. So now you need to copy shellcode bytes from managed memory to this handle in an unmanaged location. It's one of the simplest things you will see in this post. Call the Marshal.Copy
method
Executing the Shellcode From Memory
Once you have copied the shellcode, you need to get the function pointer to execute the code. For C/C++ or C# language, when you add ()
after any address, it means to "execute it".
To get a function pointer, you need to call Mashal.GetDelegateForFunctionPointer
the method which will give you the function pointer from unmanaged code to the managed code as a delegate. A delegate is the same as a function pointer but in managed code. All you need to do to execute your shellcode is to call the delegate function as you would call a normal function