傀儡进程 (Process Hollowing) 检测

1,621 阅读3分钟

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,因为攻击者可能会采取措施来欺骗这种检测方法。但是,这种方法仍然是一种比较简单且有效的检测方式。

我们还可以采用以下方案:

  1. 使用代码混淆技术使反汇编变得困难,使攻击者难以分析和修改代码。
  2. 使用内存完整性保护技术检查模块的完整性,确保模块没有被修改或替换。
  3. 获取父进程的大小并与当前进程的大小进行比较,以检查当前进程是否被 Process Hollowing 攻击。
  4. 使用 Windows API 来获取父进程的信息,而不是使用 .NET Framework 提供的 Process 类,因为 .NET Framework 提供的 Process 类易受攻击者的欺骗。
  5. 使用SetThreadContext 来监控线程上下文的修改, 进而拦截傀儡进程的启动