
Invoca dinámicamente código no administrado arbitrario desde código administrado sin PInvoke.
Reemplazo dinámico de PInvoke en Windows. DInvoke contiene primitivas potentes que pueden combinarse de forma inteligente para invocar dinámicamente código no administrado desde el disco o desde la memoria con precisión cuidada. Esto puede utilizarse para muchos propósitos, como el análisis de PE, la resolución dinámica e inteligente de API, la carga dinámica de plugins PE en tiempo de ejecución, la inyección de procesos y la evasión de ganchos de API.
Características:
Charla de conferencia (Staying # & Bringing Covert Injection Tradecraft to .NET): https://www.youtube.com/watch?v=FuxpMXTgV9s
Entradas de blog:
Este proyecto fue creado originalmente para SharpSploit (https://github.com/cobbr/SharpSploit). Con permiso de su(s) autor(es), ahora se aloja aquí como biblioteca independiente y paquete NuGet.
NuGet: https://www.nuget.org/packages/DInvoke/
El siguiente ejemplo demuestra cómo usar DInvoke para encontrar y llamar dinámicamente exportaciones de una DLL.
///Author: b33f (@FuzzySec, Ruben Boonen)
using System;
using DynamicInvoke = DInvoke.DynamicInvoke;
namespace SpTestcase
{
class Program
{
static void Main(string[] args)
{
// Details
String testDetail = @"
#=================>
# Hello there!
# I find things dynamically; base
# addresses and function pointers.
#=================>
";
Console.WriteLine(testDetail);
// Get NTDLL base from the PEB
Console.WriteLine("[?] Resolve Ntdll base from the PEB..");
IntPtr hNtdll = DynamicInvoke.Generic.GetPebLdrModuleEntry("ntdll.dll");
Console.WriteLine("[>] Ntdll base address : " + string.Format("{0:X}", hNtdll.ToInt64()) + "\n");
// Search function by name
Console.WriteLine("[?] Specifying the name of a DLL (\"ntdll.dll\"), resolve a function by walking the export table in-memory..");
Console.WriteLine("[+] Search by name --> NtCommitComplete");
IntPtr pNtCommitComplete = DynamicInvoke.Generic.GetLibraryAddress("ntdll.dll", "NtCommitComplete", true);
Console.WriteLine("[>] pNtCommitComplete : " + string.Format("{0:X}", pNtCommitComplete.ToInt64()) + "\n");
Console.WriteLine("[+] Search by ordinal --> 0x260 (NtSetSystemTime)");
IntPtr pNtSetSystemTime = DynamicInvoke.Generic.GetLibraryAddress("ntdll.dll", 0x260, true);
Console.WriteLine("[>] pNtSetSystemTime : " + string.Format("{0:X}", pNtSetSystemTime.ToInt64()) + "\n");
Console.WriteLine("[+] Search by keyed hash --> 138F2374EC295F225BD918F7D8058316 (RtlAdjustPrivilege)");
Console.WriteLine("[>] Hash : HMACMD5(Key).ComputeHash(FunctionName)");
String fHash = DynamicInvoke.Generic.GetAPIHash("RtlAdjustPrivilege", 0xaabb1122);
IntPtr pRtlAdjustPrivilege = DynamicInvoke.Generic.GetLibraryAddress("ntdll.dll", fHash, 0xaabb1122);
Console.WriteLine("[>] pRtlAdjustPrivilege : " + string.Format("{0:X}", pRtlAdjustPrivilege.ToInt64()) + "\n");
// Search for function from base address of DLL
Console.WriteLine("[?] Specifying the base address of DLL in memory ({0:X}), resolve function by walking its export table...", hNtdll.ToInt64());
Console.WriteLine("[+] Search by name --> NtCommitComplete");
IntPtr pNtCommitComplete2 = DynamicInvoke.Generic.GetExportAddress(hNtdll, "NtCommitComplete");
Console.WriteLine("[>] pNtCommitComplete : " + string.Format("{0:X}", pNtCommitComplete2.ToInt64()) + "\n");
// Pause execution
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
}
}
}
En el ejemplo siguiente, primero llamamos a OpenProcess normalmente usando PInvoke. Luego, lo llamaremos de varias formas usando DInvoke para demostrar que cada mecanismo ejecuta con éxito el código no administrado y evita los ganchos de API.
///Author: TheWover
using System;
using System.Runtime.InteropServices;
using Data = DInvoke.Data;
using DynamicInvoke = DInvoke.DynamicInvoke;
using ManualMap = DInvoke.ManualMap;
namespace SpTestcase
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
Data.Win32.Kernel32.ProcessAccessFlags processAccess,
bool bInheritHandle,
uint processId
);
static void Main(string[] args)
{
// Details
String testDetail = @"
#=================>
# Hello there!
# I demonstrate API Hooking bypasses
# by calling OpenProcess via
# PInvoke then DInvoke.
# All handles are requested with
# PROCESS_ALL_ACCESS permissions.
#=================>
";
Console.WriteLine(testDetail);
//PID of current process.
uint id = Convert.ToUInt32(System.Diagnostics.Process.GetCurrentProcess().Id);
//Process handle
IntPtr hProc;
// Create the array for the parameters for OpenProcess
object[] paramaters =
{
Data.Win32.Kernel32.ProcessAccessFlags.PROCESS_ALL_ACCESS,
false,
id
};
// Pause execution
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Call OpenProcess using PInvoke
Console.WriteLine("[?] Call OpenProcess via PInvoke ...");
hProc = OpenProcess(Data.Win32.Kernel32.ProcessAccessFlags.PROCESS_ALL_ACCESS, false, id);
Console.WriteLine("[>] Process handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");
// Pause execution
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Call OpenProcess using GetLibraryAddress (underneath the hood)
Console.WriteLine("[?] Call OpenProcess from the loaded module list using System.Diagnostics.Process.GetCurrentProcess().Modules ...");
hProc = DynamicInvoke.Win32.OpenProcess(Data.Win32.Kernel32.ProcessAccessFlags.PROCESS_ALL_ACCESS, false, id);
Console.WriteLine("[>] Process handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");
// Pause execution
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Search function by name from module in PEB
Console.WriteLine("[?] Specifying the name of a DLL (\"kernel32.dll\"), search the PEB for the loaded module and resolve a function by walking the export table in-memory...");
Console.WriteLine("[+] Search by name --> OpenProcess");
IntPtr pkernel32 = DynamicInvoke.Generic.GetPebLdrModuleEntry("kernel32.dll");
IntPtr pOpenProcess = DynamicInvoke.Generic.GetExportAddress(pkernel32, "OpenProcess");
//Call OpenProcess
hProc = (IntPtr)DynamicInvoke.Generic.DynamicFunctionInvoke(pOpenProcess, typeof(DynamicInvoke.Win32.Delegates.OpenProcess), ref paramaters);
Console.WriteLine("[>] Process Handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");
// Pause execution
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Manually map kernel32.dll
// Search function by name from module in PEB
Console.WriteLine("[?] Manually map a fresh copy of a DLL (\"kernel32.dll\"), and resolve a function by walking the export table in-memory...");
Console.WriteLine("[+] Search by name --> OpenProcess");
Data.PE.PE_MANUAL_MAP moduleDetails = ManualMap.Map.MapModuleToMemory("C:\\Windows\\System32\\kernel32.dll");
Console.WriteLine("[>] Module Base : " + string.Format("{0:X}", moduleDetails.ModuleBase.ToInt64()) + "\n");
//Call OpenProcess
hProc = (IntPtr)DynamicInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "OpenProcess", typeof(DynamicInvoke.Win32.Delegates.OpenProcess), paramaters);
Console.WriteLine("[>] Process Handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");
// Pause execution
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Map kernel32.dll using Module Overloading
// Search function by name from module in PEB
Console.WriteLine("[?] Use Module Overloading to map a fresh copy of a DLL (\"kernel32.dll\") into memory backed by another file on disk. Resolve a function by walking the export table in-memory...");
Console.WriteLine("[+] Search by name --> OpenProcess");
moduleDetails = ManualMap.Overload.OverloadModule("C:\\Windows\\System32\\kernel32.dll");
Console.WriteLine("[>] Module Base : " + string.Format("{0:X}", moduleDetails.ModuleBase.ToInt64()) + "\n");
//Call OpenProcess
hProc = (IntPtr)DynamicInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "OpenProcess", typeof(DynamicInvoke.Win32.Delegates.OpenProcess), paramaters);
Console.WriteLine("[>] Process Handle : " + string.Format("{0:X}", hProc.ToInt64()) + "\n");
// Pause execution
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
//////////////////////////////////////////////////////////////////////////////////////////////////////////
Console.WriteLine("[!] Test complete!");
// Pause execution
Console.WriteLine("[*] Pausing execution..");
Console.ReadLine();
}
}
}
Para probar que esto evadió los ganchos, usaremos la herramienta API Monitor v2 para enganchar kernel32.dll!OpenProcess. Luego ejecutaremos la demo a través de API Monitor. Puedes observar cuáles de nuestras llamadas a OpenProcess fueron capturadas por los ganchos fijándote en las que se llaman con el indicador PROCESS_ALL_ACCESS. Como verás, API Monitor captura con éxito la llamada a la API cuando se realiza con PInvoke. Sin embargo, NO tiene éxito cuando usamos DInvoke o el mapeo manual. Puedes ver el video en Vimeo para verlo en acción.
SharpSploit: Evadiendo ganchos de API mediante DInvoke y el mapeo manual