
Invoquer dynamiquement du code non managé arbitraire à partir de code managé sans PInvoke.
Remplaçant dynamique de PInvoke sur Windows. DInvoke contient des primitives puissantes qui peuvent être combinées intelligemment pour invoquer dynamiquement du code non managé depuis le disque ou la mémoire avec une précision minutieuse. Cela peut être utilisé à de nombreuses fins, comme l'analyse de PE, la résolution intelligente d'API dynamiques, le chargement dynamique de plugins PE au moment de l'exécution, l'injection de processus et l'évitement des hooks d'API.
Fonctionnalités :
Conférence (Rester # et apporter les techniques d'injection furtive à .NET) : https://www.youtube.com/watch?v=FuxpMXTgV9s
Articles de blog :
Ce projet a été initialement créé pour SharpSploit (https://github.com/cobbr/SharpSploit). Avec la permission des auteurs, il est maintenant hébergé ici en tant que bibliothèque autonome et package NuGet.
NuGet : https://www.nuget.org/packages/DInvoke/
L'exemple ci-dessous montre comment utiliser DInvoke pour trouver et appeler dynamiquement des exports d'une 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();
}
}
}
Dans l'exemple ci-dessous, nous appelons d'abord OpenProcess normalement avec PInvoke. Ensuite, nous l'appelons de plusieurs manières en utilisant DInvoke pour démontrer que chaque mécanisme exécute avec succès le code non managé et contourne les hooks d'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();
}
}
}
Pour tester que cela a contourné les hooks, nous utiliserons l'outil API Monitor v2 pour hameçonner kernel32.dll!OpenProcess. Ensuite, nous exécuterons la démo via API Monitor. Vous pouvez observer lesquels de nos appels à OpenProcess ont été interceptés par les hooks en surveillant ceux qui sont appelés avec le flag PROCESS_ALL_ACCESS. Comme vous le verrez, API Monitor intercepte avec succès l'appel API lorsqu'il est effectué avec PInvoke. Cependant, il n'y parvient PAS lorsque nous utilisons DInvoke ou le mappage manuel. Vous pouvez regarder la vidéo sur Vimeo pour le voir en action.
SharpSploit : Contournement des hooks d'API via DInvoke et le mappage manuel