P/Invoke y D/Invoke son dos técnicas utilizadas para llamar a funciones nativas (generalmente de bibliotecas DLL en C/C++) desde código administrado, como C# en .NET. Ambas tienen como objetivo realizar llamadas a APIs nativas, pero funcionan de manera diferente y se usan en contextos distintos.
Windows APIs en C#
La creación de malware requiere un profundo conocimiento del sistema operativo Windows, incluyendo el aprovechamiento de las funcionalidades de las APIs internas para maximizar el alcance y mantenerse oculto, evitando ser detectado o bloqueado por las herramientas de defensa. El abuso de estas APIs internas ofrece una ventaja considerable en términos de las capacidades que se pueden explotar.
Estas APIs internas, formalmente conocidas como Windows APIs, deben ser invocadas a través de DLLs, las cuales generalmente están programadas en C o C++. Sin embargo, gracias a la interoperabilidad que ofrece Windows, es posible realizar llamadas a las Windows APIs desde cualquier lenguaje de programación que utilice un motor de Microsoft, como C#, PowerShell o VBScript.
A continuación, se muestra un ejemplo de cómo obtener el nombre del equipo utilizando C# y Windows APIs.
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool GetComputerName([Out] StringBuilder lpBuffer, ref uint nSize);
static void Main()
{
uint size = 256; // Tamaño del buffer
StringBuilder buffer = new StringBuilder((int)size);
if (GetComputerName(buffer, ref size))
{
Console.WriteLine("Nombre del Computador: " + buffer.ToString());
}
else
{
Console.WriteLine("Error al obtener el nombre del computador.");
}
}
}
Para probar este codigo puedes usar Mono, esta utilidad compila el programa usando la API de .NET Framework desde cualquier *Nix.
brew install mono
csc -platform:x64 example.cs
1. P/Invoke (Platform Invocation Services):
P/Invoke, abreviatura de Platform Invocation, es un mecanismo en .NET que permite que el código administrado (C# u otro lenguaje .NET) invoque funciones no administradas (nativas C/C++) de una DLL. Es muy común para llamar funciones del sistema operativo como las APIs de Windows.
Características clave de P/Invoke:
- Estático y explícito: Al usar P/Invoke, debes declarar explícitamente la función que quieres invocar mediante un atributo
DllImport
en tu código. Ejemplo:
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string moduleName);
- Facilidad de uso: Es fácil de implementar y se usa mayormente en aplicaciones legítimas para interactuar con APIs nativas del sistema operativo.
- Comportamiento predecible: La firma de la función y el nombre de la DLL se declaran estáticamente en el código.
- Uso común: Se usa para integrar bibliotecas nativas existentes, como bibliotecas de Windows o librerías personalizadas escritas en C o C++.
Ejemplo con P/Invoke (Platform Invocation)
P/Invoke permite llamar a funciones no administradas (por ejemplo, de la API de Windows) desde código C#. Aquí llamamos a la función MessageBox
de la biblioteca de Windows user32.dll
.
using System;
using System.Runtime.InteropServices;
class Program
{
// Declaración de la función MessageBox desde user32.dll
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
static void Main(string[] args)
{
// Llamar a MessageBox usando P/Invoke
MessageBox(IntPtr.Zero, "Hello, P/Invoke!", "P/Invoke Example", 0);
}
}
En este ejemplo:
- Se utiliza el atributo
DllImport
para importar la funciónMessageBox
desde la bibliotecauser32.dll
. - La función
MessageBox
toma varios parámetros: el identificador de ventana (hWnd
), el texto, el título y el tipo de cuadro de mensaje. - Luego, se llama a la función dentro del
Main
, mostrando el cuadro de mensaje.
2. D/Invoke (Dynamic Invocation):
D/Invoke es una técnica más avanzada y compleja, que permite invocar funciones nativas de manera dinámica y, por lo general, se emplea en el desarrollo de herramientas de ciberseguridad y malware para evitar la detección por parte de herramientas de análisis o antivirus.
Características clave de D/Invoke:
- Dinámico y ofuscado: A diferencia de P/Invoke, en D/Invoke no se hace una declaración estática y explícita de las funciones que se llaman. En su lugar, las llamadas a funciones nativas se realizan dinámicamente en tiempo de ejecución, lo que hace más difícil para los antivirus o herramientas de seguridad identificar qué funciones se están invocando.
- Técnica avanzada: D/Invoke a menudo se usa para evitar detección y análisis estático en el binario, ya que no se dejan rastros claros en el binario qué APIs se están invocando.
- Desempeño en entornos de ciberseguridad: Se utiliza comúnmente en malware o herramientas de hacking, ya que permite evadir firmas de antivirus que podrían detectar fácilmente llamadas P/Invoke estáticas.
- Ofuscación: Los nombres de las funciones y las DLLs pueden estar ofuscados o cargarse de manera dinámica, lo que dificulta el análisis por herramientas de detección.
Ejemplo con D/Invoke (Dynamic Invocation)
D/Invoke se refiere al uso de invocación dinámica, donde se cargan las bibliotecas y las funciones en tiempo de ejecución, sin usar atributos como DllImport
. Aquí usamos funciones de reflexión para hacerlo de manera dinámica.
using System;
using System.Reflection;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
// Cargar la biblioteca user32.dll
IntPtr hModule = LoadLibrary("user32.dll");
if (hModule == IntPtr.Zero)
{
Console.WriteLine("No se pudo cargar la librería user32.dll");
return;
}
// Obtener la dirección de la función MessageBox
IntPtr pMessageBox = GetProcAddress(hModule, "MessageBoxW");
if (pMessageBox == IntPtr.Zero)
{
Console.WriteLine("No se pudo encontrar la función MessageBox");
return;
}
// Crear un delegado para la función MessageBox
var messageBoxDelegate = (MessageBoxDelegate)Marshal.GetDelegateForFunctionPointer(pMessageBox, typeof(MessageBoxDelegate));
// Invocar MessageBox dinámicamente
messageBoxDelegate(IntPtr.Zero, "Hello, D/Invoke!", "D/Invoke Example", 0);
// Liberar la biblioteca
FreeLibrary(hModule);
}
// Definir el delegado para la función MessageBox
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Auto)]
delegate int MessageBoxDelegate(IntPtr hWnd, string text, string caption, uint type);
// Declarar métodos para manipular las bibliotecas dinámicamente
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
}
En este ejemplo de D/Invoke:
- Usamos
LoadLibrary
para cargaruser32.dll
dinámicamente. GetProcAddress
se usa para obtener la dirección de la funciónMessageBox
.- Creamos un delegado (
MessageBoxDelegate
) que coincida con la firma deMessageBox
. - Luego, utilizamos
Marshal.GetDelegateForFunctionPointer
para crear una instancia del delegado que apunta a la funciónMessageBox
y la invocamos dinámicamente. - Finalmente, se libera la biblioteca cargada con
FreeLibrary
.
Conclusión
P/Invoke es la opción estándar para llamadas a funciones nativas en .NET cuando no se necesita evasión o protección, mientras que D/Invoke se usa en contextos donde la detección debe ser evadida, como en malware o técnicas ofensivas de seguridad informática.
Referencias
- https://learn.microsoft.com/es-es/dotnet/standard/native-interop/pinvoke
- https://learn.microsoft.com/es-es/windows/win32/apiindex/windows-api-list
- https://www.pinvoke.net/
- https://www.youtube.com/watch?v=baj9IpB_Z6Y
- https://github.com/rasta-mouse/DInvoke
- https://github.com/Dump-GUY/Get-PDInvokeImports
- https://vay3t.medium.com/malware-development-droppers-baits-281066fd9019
- https://vay3t.medium.com/malware-development-instalaci%C3%B3n-del-entorno-de-desarrollo-de-malware-en-macos-3c347084599e