Loader/Agent Memory Allocation

The what

Both the agent and loader use an internal contract for memory allocations in the form of a function definition called allocate_memory:

LPVOID allocate_memory ( size_t size, DWORD protection );

This is a simple contract where the caller is asking for a region of memory of the specified size and protection. It doesn't care how it's allocated (or freed). The default implementation just calls VirtualAlloc:

DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc ( LPVOID, SIZE_T, DWORD, DWORD );

LPVOID allocate_memory ( size_t size, DWORD protection ) {
    return KERNEL32$VirtualAlloc ( NULL, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, protection );
}

There's a corresponding contract for freeing memory, called free_memory, where the default implementation naturally calls VirtualFree.

DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree ( LPVOID, SIZE_T, DWORD );

void free_memory ( LPVOID address ) {
    KERNEL32$VirtualFree ( address, 0, MEM_RELEASE );
}
circle-info

You obviously need to treat these as related pairs. The freeing function has to be compatible with the allocation function.

The why

This approach allows users to bring their own custom implementation for these two functions, overriding the defaults. This gives much more flexibility than the framework providing pre-made options for you to choose from.

The how

When you extract the client releasearrow-up-right, you'll see a resources directory that contains the default agent and loader COFF files.

To implement custom memory allocation, just write your custom functions in C:

And build as a COFF file.

Copy that COFF into the resources directory and modify agent.spec. You need to load the target COFF, merge it, and then redirect the old call sites to the new ones.

The same can be applied to the loader if you want your custom memory code to be used in both places. You can, of course, have different COFFs implementing different techniques; merge one into the loader and a different one into the agent. The flexibility is yours.

circle-info

The +optimize option will ensure that the old [allocate/free]_memory functions will get stripped out of the final PIC. So you don't have to worry about dead code being left behind.

Last updated