Quick introduction to P/Invoke and D/Invoke for red team professionals
Learn what is P/Invoke and D/Invoke, their differences and how they enable red team tradecraft.
When I started learning about advanced red team tactics, I often came across the terms, P/Invoke and D/Invoke. Despite reading about them multiple times, I often get confused between the two. Therefore, I decided to write about them here just to understand them better and etch their differences in my mind.
Before I delve into it, let’s go through some terminology. This will help in understanding the further content better.
API Hashing - It is a technique in which unique hashes are created for Windows APIs and these hashes are then used to search for the required Windows APIs from the respective library instead of their names.
Managed Code - Managed code is computer program code that runs under the control of a runtime, such as the Common Language Runtime (CLR) or Java Virtual Machine (JVM). Managed code is written in high-level programming languages like C#, J#, and Visual Basic .NET. The code is compiled into an Intermediate Language (IL). The runtime compiles the IL into native executable code.
Unmanaged Code - Unmanaged code is code that runs directly on a computer's hardware, outside of the runtime. It's also known as unsafe code. Unmanaged code is written in languages, such as C or C++, which are compiled into native machine code and executed by the CPU itself.
Marshalling - Marshalling is the process of moving an object between memory spaces when they need to cross the boundary between managed and unmanaged code. Marshalling is needed because the types in the managed and unmanaged code are different. In managed code, for instance, you have a
string
, while unmanaged strings can be .NETstring
encoding (UTF-16), ANSI Code Page encoding, UTF-8, null-terminated, ASCII, etc.C# Namespaces - Think of namespaces as buckets designed to group related code elements such as classes, structures, enums or interfaces within a logical boundary.
Dynamic Resolution - Resolving and loading a DLL or a Windows API during runtime.
Static Resolution - Resolving and loading a DLL or a Windows API during compile time. Windows API loaded via this method will appear in the executable’s Import Address Table (IAT).
Reflective Programming - Reflective programming or reflection refers to the ability of a program or process to introspect itself during runtime, and modify its structure and behavior. Conceptually, this is similar to a human’s ability to introspect (or reflect) on themselves and modify their thought, behavior or beliefs.
Method / Function Signature - A function signature is a collection of information that defines a function's input and output. Think of it like a prototype that specifies the general information about a function like the name, scope and parameters.
Delegate - Think of a delegate as a placeholder for a method or a function. They can be linked to a function by declaring a delegate type that specifies the method's signature. The linked method can then be called using the delegate. It enables passing functions as pointers and allows code reuse.
Function Hooking - Function hooking means to write certain instructions at the beginning of the function to redirect the execution to the some other piece of code (e.g. EDR functions). So whenever that function will be called, the other code will be executed first.
Phew! that was a lot of terminology. Maybe, give it another read to fully grasp the underlying concepts.
Now that we have our terminology in place, lets get to the crux of the matter.
P/Invoke
P/Invoke stands for Platform Invoke. It is a mechanism that allows access to unmanaged code elements such as structures and functions from the managed code. In the context of red team, it allows operators to access unmanaged Windows APIs from high-level languages such as C#. For example, the code below uses P/Invoke to access the MessageBox Windows API from user32.dll.
using System;
using System.Runtime.InteropServices;
public partial class Program
{
// Import user32.dll (containing the function we need) and define
// the method corresponding to the native function.
[LibraryImport("user32.dll", StringMarshalling = StringMarshalling.Utf16, SetLastError = true)]
private static partial int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
public static void Main(string[] args)
{
// Invoke the function as a regular managed method.
MessageBox(IntPtr.Zero, "Command-line message box", "Attention!", 0);
}
}
Offensive tool developers have utilized this capability to create .NET Assemblies (EXEs/DLLs) that leverage both managed and unmanaged Windows APIs for post-exploitation activities. Because .NET Assemblies can be easily loaded and executed directly from memory, offensive operators can carry out advanced post-exploitation techniques without leaving files on disk, minimizing the chances of detection by endpoint security tools.
The endpoint detection solution vendors caught on fast though. This was primarily because of following drawbacks of P/Invoke:
Any Windows APIs referenced via P/Invoke are included in the Import Address Table (IAT) of the executable. This poses the risk of malware analysts or automated tools being able to understand the executable’s functionality by looking at the IAT.
If an endpoint detection solution has hooked Windows APIs for detecting malicious behavior, then any APIs being referenced via P/Invoke will end up feeding directly to the endpoint detection solution. Leading to the detection of the payload or executable.
This is where D/Invoke comes into the picture.
D/Invoke
D/Invoke, developed by Ruben Boonen and The Wover, stands for Dynamic Invoke. As opposed to P/Invoke, D/Invoke leverages delegates to reference Windows APIs during the runtime. This involves loading the DLL manually into the memory, searching the DLL for the required pointer to the API, creating a delegate linked to the API / function pointer and using the delegate to call the API. Refer to Using D/Invoke section of this article for a code example.
D/Invoke allows for stealthy operations as any APIs referenced via this technique do not appear in the executable’s IAT. Also, since it manually maps the umanaged DLLs into memory, it helps to evade any hooks placed by the endpoint detection solution.
D/Invoke allows to dynamically call Windows APIs via three methods:
Standard - The search for the Windows API is performed within an already loaded DLL in the memory.
Manual Mapping - The required DLL is first manually mapped into the memory and then the search for the required Windows API is performed within the manually loaded DLL. This helps in evading any hooks placed by the endpoint detection solution.
Module Overloading - This is same as manual mapping. The only difference is that the mapped DLL is backed by the valid module on disk. This helps in evading detection as EDRs or malware analysts usually get suspicious about code being part of the process but not backed by a valid file on the disk.
Red Team Notes
- P/Invoke or Platform Invoke uses static resolution to call APIs from unmanaged code. This results in the API being part of the Import Address Table and hooked APIs being used.
- D/Invoke or Dynamic Invoke uses dynamic resolution to call APIs from unmanaged code. This provides a stealthy approach to use Windows APIs without them being showing up in the Import Address Table. Additionally, it can help in bypassing endpoint detection solution hooks by manually mapping the required library in memory.
Follow my journey of 100 Days of Red Team on WhatsApp or Discord.
If you want to dive deep into the technical details of how D/Invoke works and how to use it in your application, please refer to following resources:
Emulating Covert Operations - Dynamic Invocation (Avoiding PInvoke & API Hooks)
Below is a recording of the presentation, Staying # & Bringing Covert Injection Tradecraft to .NET, by Ruben Boonen and The Wover, presented at BlueHat IL, 2020.
Additional Resources
p-invoke.net - This website contains most of the P/Invoke definitions from previous version of pinvoke.net (don’t visit the current version, its full of advertisements), adding the link to the Microsoft documentation for each one.
Stripped down version of D/Invoke by rasta-mosue - The aim of this project is to provide D/Invoke in a more minimalist form. It only contains the core DynamicInvoke and ManualMap functionality, without all the additional helper methods and delegates. This help keeps the packages small and lowers the detection surface for AV. You can learn more about changes in this fork here.
Process Injection using DInvoke - Provides an example to compare both P/Invoke and D/Invoke.
Follow my journey of 100 Days of Red Team on WhatsApp or Discord.