Removing MSCRT

Introduction

The Microsoft C Run-Time Library is a set of low level functions and macros that provide support for C & C++ programs. It includes functions for memory management, string manipulations, and I/O functions.

The reason why we'd want to do this is to reduce entropy in the binary.

PE size w/ Microsoft CRT
PE size w/ out Microsoft CRT

Depending on the compiler you use, the CRT DLL library names will differ. Below are screenshots of the

Microsoft Visual Studio Compiler

Visual Studio is as follows: vcruntimeXXX.dll where XXX is the version number of the CRT library used. There's also api-ms-win-crt-stdio-l1-1-0.dll, api-ms-win-crt-runtime-l1-1-0.dll and api-ms-win-crt-locale-l1-1-0.dll that are also related to the CRT library. Each exporting it's own functions.

Mingw2 Compiler

MinGW stands for "minimalist GNU". Mingw uses msvcrt.dll to export all functions.

Removing CRT Mingw2

To remove CRT in mingw2, we can add these additional flags to our Mafefile.

-nostartfiles , -nostdlib, -static-libgcc, and -static

Note:** The only one we really need is -nostartfiles**

Example Makefile:

run:
    x86_64-w64-mingw32-gcc main.c -o Playground.exe -municode -static -nostdlib -nostartfiles

Replacing CRT Library Functions w/ our own.

Now that our our code is running independent from the C Standard Library, writing one's own version of functions such as printf, strlen, strcat, memcpy is necessary.

Libraries like VX-API may be used for this purpose. For example, StringCompare.cpp replaces the strcmp function for string comparison.

Replacing printf

#define PRINTA( STR, ... )                                                                  \
    if (1) {                                                                                \
        LPSTR buf = (LPSTR)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 1024 );           \
        if ( buf != NULL ) {                                                                \
            int len = wsprintfA( buf, STR, __VA_ARGS__ );                                   \
            WriteConsoleA( GetStdHandle( STD_OUTPUT_HANDLE ), buf, len, NULL, NULL );       \
            HeapFree( GetProcessHeap(), 0, buf );                                           \
        }                                                                                   \
    }  

Import Table after Custom printf

We are now only importing KERNEL32.dll & USER32.dll (wsprintf).

An important thing to remember

Building CRT independant malware

When building CRT independent malware it's important to keep in mind that some functions and macros use CRT to perform tasks.

ZeroMemory: Uses CRT memset to populate it's buffer with zeros. We need to find an alternative method then. The CopyMemoryEx.cpp function can be used as a replacement.

We can manually set custom versions of CRT-based functions like memset. Forcing the compiler to deal with this custom function instead of using the CRT exported version. Macros like ZeroMemory will also use this custom function.

Replacing Memset

Our custom version of the memset function can be specified to the compiler in the following manner, using the intrinsic keyword. The intrinsic function is a function that the compiler implements directly when possible.

Intrinsic functions are provided by the compiler, and do not require a #include like inline functions.

#include <Windows.h>

// The `extern` keyword sets the `memset` function as an external function.
extern void* __cdecl memset(void*, int, size_t);

// The `#pragma intrinsic(memset)` and #pragma function(memset) macros are Microsoft-specific compiler instructions.
// They force the compiler to generate code for the memset function using a built-in intrinsic function.
#pragma intrinsic(memset)
#pragma function(memset)

void* __cdecl memset(void* Destination, int Value, size_t Size) {
	// logic similar to memset's one
	unsigned char* p = (unsigned char*)Destination;
	while (Size > 0) {
		*p = (unsigned char)Value;
		p++;
		Size--;
	}
	return Destination;
}


int main() {
	
	PVOID pBuff = HeapAlloc(GetProcessHeap(), 0, 0x100);
	if (pBuff == NULL)
		return -1;

    // this will use our version of 'memset' instead of CRT's Library version 
	ZeroMemory(pBuff, 0x100);

	HeapFree(GetProcessHeap(), 0, pBuff);

	return 0;
}

Last updated