Process Hollowing 是一种攻击技术,攻击者可以在目标机器上运行恶意代码而不被检测到. 其创建原理就是修改进程的内存数据, 向内存中写入 ShellCode 代码,并修改该进程的执行流程,使其转而执行 ShellCode 代码. 这样进程还是原来的进程, 但执行的操作却被替换了.
如何实现本文不做实现, 这里主要说明一种简单有效的实现方法来检测是否被攻击.
// 定义NtQueryInformationProcess函数
[DllImport("ntdll.dll")]
public static extern int NtQueryInformationProcess(
IntPtr processHandle,
int processInformationClass,
ref PROCESS_BASIC_INFORMATION processInformation,
int processInformationLength,
out int returnLength);
// 定义PROCESS_BASIC_INFORMATION结构体
public struct PROCESS_BASIC_INFORMATION
{
public IntPtr Reserved1;
public IntPtr PebBaseAddress;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public IntPtr[] Reserved2;
public IntPtr UniqueProcessId;
public IntPtr Reserved3;
}
static void Main(string[] args)
{
bool isHollowed = CheckProcessHollowing();
if (isHollowed)
{
Console.WriteLine("Process Hollowing Detected!");
}
else
{
Console.WriteLine("Process Hollowing Not Detected");
}
Console.Read();
}
static bool CheckProcessHollowing()
{
// 获取当前进程的句柄
Process currentProcess = Process.GetCurrentProcess();
IntPtr currentProcessHandle = currentProcess.Handle;
// 获取当前进程的PEB地址
PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
int returnLength;
int status = NtQueryInformationProcess(currentProcessHandle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
if (status != 0)
{
Console.WriteLine("获取进程信息失败!");
return false;
}
// 获取当前进程的映像大小
IntPtr currentProcessImageBase = currentProcess.MainModule.BaseAddress;
long currentProcessImageSize = currentProcess.MainModule.ModuleMemorySize;
// 获取父进程的PEB地址
IntPtr parentProcessId = pbi.UniqueProcessId;
Process parentProcess = Process.GetProcessById(parentProcessId.ToInt32());
IntPtr parentProcessHandle = parentProcess.Handle;
PROCESS_BASIC_INFORMATION parentPbi = new PROCESS_BASIC_INFORMATION();
status = NtQueryInformationProcess(parentProcessHandle, 0, ref parentPbi, Marshal.SizeOf(parentPbi), out returnLength);
if (status != 0)
{
Console.WriteLine("获取进程信息失败!");
return false;
}
// 获取父进程的映像大小
IntPtr parentProcessImageBase = parentProcess.MainModule.BaseAddress;
long parentProcessImageSize = parentProcess.MainModule.ModuleMemorySize;
// 如果当前进程的映像大小小于父进程的映像大小,则当前进程可能被 Process Hollowing 注入了
if (currentProcessImageSize < parentProcessImageSize)
{
return true;
}
return false;
}
首先,代码使用 DllImport 声明了 NtQueryInformationProcess 函数,这是一个用于获取进程信息的函数。然后定义了 PROCESS_BASIC_INFORMATION 结构体,用于保存进程信息。在 CheckProcessHollowing 函数中,首先获取当前进程的句柄,并使用 NtQueryInformationProcess 函数获取当前进程的 PEB 地址和进程信息,然后,获取当前进程的映像大小,并获取父进程的 PEB 地址和进程信息。最后,比较当前进程和父进程的映像大小,如果当前进程的映像大小小于父进程的映像大小,则当前进程可能被 Process Hollowing 注入了。如果是这种情况,函数返回 true,否则返回 false。
需要注意的是,这种检测方法并不能保证 100% 可靠地检测出 Process Hollowing,因为攻击者可能会采取措施来欺骗这种检测方法。但是,这种方法仍然是一种比较简单且有效的检测方式。
我们还可以采用以下方案:
- 使用代码混淆技术使反汇编变得困难,使攻击者难以分析和修改代码。
- 使用内存完整性保护技术检查模块的完整性,确保模块没有被修改或替换。
- 获取父进程的大小并与当前进程的大小进行比较,以检查当前进程是否被 Process Hollowing 攻击。
- 使用 Windows API 来获取父进程的信息,而不是使用 .NET Framework 提供的 Process 类,因为 .NET Framework 提供的 Process 类易受攻击者的欺骗。
- 使用SetThreadContext 来监控线程上下文的修改, 进而拦截傀儡进程的启动