String Hashing

Hashing is a technique that is used to create a fixed-size representation of a piece of data, called a hash value or hash code. Hashing algorithms are designed to be one-way functions, meaning that it is computationally infeasible to determine the original input data using the hash value.

String hashing is a useful approach for hiding strings used in an implementation, as strings can be used as signatures to help security vendors detect malicious binaries.

Normal String Hashing

The output of these algorithms is a number expressed in hexadecimal format, as it is neater and more compact.

- Djb2

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

// reference: https://github.com/vxunderground/VX-API/blob/main/VX-API/HashStringDjb2.cpp

#define INITIAL_HASH	3731		// added to randomize 
#define INITIAL_SEED	7			// recommended to be 0 < INITIAL_SEED < 10

// generate Djb2 hashes from Ascii input string
DWORD HashStringDjb2A(_In_ PCHAR String)
{
	ULONG Hash = INITIAL_HASH;
	INT c;

	while (c = *String++)
		Hash = ((Hash << INITIAL_SEED) + Hash) + c;

	return Hash;
}

// generate Djb2 hashes from wide-character input string
DWORD HashStringDjb2W(_In_ PWCHAR String)
{
	ULONG Hash = INITIAL_HASH;
	INT c;

	while (c = *String++)
		Hash = ((Hash << INITIAL_SEED) + Hash) + c;

	return Hash;
}

int main() {

	CHAR*	cTest = "Hola";
	WCHAR*	wTest = L"Hola";
	
	printf("[+] Hash Of \"%s\" Is : 0x%0.8X \n", cTest, HashStringDjb2A(cTest));
	wprintf(L"[+] Hash Of \"%s\" Is : 0x%0.8X \n", wTest, HashStringDjb2W(wTest));


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

	return 0;
}

- JenkinsOneAtATime32Bit

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

// reference: https://github.com/vxunderground/VX-API/blob/main/VX-API/HashStringJenkinsOneAtATime32Bit.cpp

#define INITIAL_SEED	7	// recommended to be 0 < INITIAL_SEED < 10

// generate JenkinsOneAtATime32Bit hashes from Ascii input string
UINT32 HashStringJenkinsOneAtATime32BitA(_In_ PCHAR String)
{
	SIZE_T Index = 0;
	UINT32 Hash = 0;
	SIZE_T Length = lstrlenA(String);

	while (Index != Length)
	{
		Hash += String[Index++];
		Hash += Hash << INITIAL_SEED;
		Hash ^= Hash >> 6;
	}

	Hash += Hash << 3;
	Hash ^= Hash >> 11;
	Hash += Hash << 15;

	return Hash;
}

// generate JenkinsOneAtATime32Bit hashes from wide-character input string
UINT32 HashStringJenkinsOneAtATime32BitW(_In_ PWCHAR String)
{
	SIZE_T Index = 0;
	UINT32 Hash = 0;
	SIZE_T Length = lstrlenW(String);

	while (Index != Length)
	{
		Hash += String[Index++];
		Hash += Hash << INITIAL_SEED;
		Hash ^= Hash >> 6;
	}

	Hash += Hash << 3;
	Hash ^= Hash >> 11;
	Hash += Hash << 15;

	return Hash;
}

int main() {

	CHAR*	cTest	= "Hola";
	WCHAR*	wTest	= L"Hola";

	printf("[+] Hash Of \"%s\" Is : 0x%0.8X \n", cTest, HashStringJenkinsOneAtATime32BitA(cTest));
	wprintf(L"[+] Hash Of \"%s\" Is : 0x%0.8X \n", wTest, HashStringJenkinsOneAtATime32BitW(wTest));

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

	return 0;
}

- LoseLose

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

// reference: https://github.com/vxunderground/VX-API/blob/main/VX-API/HashStringLoseLose.cpp

#define INITIAL_SEED	2	// recommended to be 0 < INITIAL_SEED < 5

// generate LoseLose hashes from Ascii input string
DWORD HashStringLoseLoseA(_In_ PCHAR String)
{
	ULONG Hash = 0;
	INT c;

	while (c = *String++) {
		Hash += c;
		Hash *= c + INITIAL_SEED;	// update
	}
	return Hash;
}

// generate LoseLose hashes from wide-character input string
DWORD HashStringLoseLoseW(_In_ PWCHAR String)
{
	ULONG Hash = 0;
	INT c;

	while (c = *String++) {
		Hash += c;
		Hash *= c + INITIAL_SEED;	// update
	}

	return Hash;
}

int main() {

	CHAR*	cTest = "Hola";
	WCHAR*	wTest = L"Hola";

	printf("[+] Hash Of \"%s\" Is : 0x%0.8X \n", cTest, HashStringLoseLoseA(cTest));
	wprintf(L"[+] Hash Of \"%s\" Is : 0x%0.8X \n", wTest, HashStringLoseLoseW(wTest));

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

	return 0;
}

- Rotr32

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

// reference: https://github.com/vxunderground/VX-API/blob/main/VX-API/HashStringRotr32.cpp

#define INITIAL_SEED	5	// recommended to be 0 < INITIAL_SEED < 10

// helper function that apply the bitwise rotation
UINT32 HashStringRotr32Sub(UINT32 Value, UINT Count)
{
	DWORD Mask = (CHAR_BIT * sizeof(Value) - 1);
	Count &= Mask;
#pragma warning( push )
#pragma warning( disable : 4146)
	return (Value >> Count) | (Value << ((-Count) & Mask));
#pragma warning( pop ) 
}

// generate Rotr32 hashes from Ascii input string
INT HashStringRotr32A(_In_ PCHAR String)
{
	INT Value = 0;

	for (INT Index = 0; Index < lstrlenA(String); Index++)
		Value = String[Index] + HashStringRotr32Sub(Value, INITIAL_SEED);

	return Value;
}

// generate Rotr32 hashes from wide-character input string
INT HashStringRotr32W(_In_ PWCHAR String)
{
	INT Value = 0;

	for (INT Index = 0; Index < lstrlenW(String); Index++)
		Value = String[Index] + HashStringRotr32Sub(Value, INITIAL_SEED);

	return Value;
}

int main() {

	CHAR*	cTest = "Hola";
	WCHAR*	wTest = L"Hola";

	printf("[+] Hash Of \"%s\" Is : 0x%0.8X \n", cTest, HashStringRotr32A(cTest));
	wprintf(L"[+] Hash Of \"%s\" Is : 0x%0.8X \n", wTest, HashStringRotr32W(wTest));

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

	return 0;
}

Compile-Time String Hashing

This can be accomplished by creating a function that computes a compile-time hash, followed by performing a bitwise XOR operation with a compile-time generated XOR key. The deobfuscation process will then take place during runtime.

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

#define HASH_STR( x )  ( HashDeObf( ExprHashStringA( ( x ) ) ) )
#define H_MAGIC_KEY    5381

constexpr ULONG RandomCompileTimeSeed(
    void
) {
    return '0' * -40271 +
        __TIME__[7] * 1 +
        __TIME__[6] * 10 +
        __TIME__[4] * 60 +
        __TIME__[3] * 600 +
        __TIME__[1] * 3600 +
        __TIME__[0] * 36000;
};

// The compile time random seed
constexpr auto g_XorKey = RandomCompileTimeSeed() % 0xFFFF;

/*!
 * @brief
 *  Hashing ascii strings at compile time
 *
 * @param String
 *  Data/String to hash
 *
 * @param Length
 *  size of data/string to hash.
 *  if 0 then hash data til null terminator is found.
 *
 * @return
 *  hash of specified data/string
 */
constexpr ULONG ExprHashStringA(
    _In_ PCHAR String
) {
    ULONG Hash = 0;
    CHAR  Char = 0;

    Hash = H_MAGIC_KEY;

    if (!String) {
        return 0;
    }

    while ((Char = *String++)) {

        /* turn current character to uppercase */
        if (Char >= 'a') {
            Char -= 0x20;
        }

        Hash = ( ( Hash << 5 ) + Hash ) + Char;
    }

    return Hash ^ g_XorKey;
}

__declspec(noinline) ULONG HashDeObf(
    _In_ ULONG Hash
) {
    return Hash ^ g_XorKey;
}

int main() {
    printf( "[*] VirtualAllocEx Hash -> Obf:[%x] -> DeObf:[%x]\n", 
        ExprHashStringA( (PCHAR)"VirtualAllocEx" ), 
        HASH_STR( (PCHAR)"VirtualAllocEx" ) 
    );

    printf( "[*] NtAllocateVirtualMemory Hash -> Obf:[%x] -> DeObf:[%x]\n", 
        ExprHashStringA( (PCHAR)"NtAllocateVirtualMemory" ), 
        HASH_STR( (PCHAR)"NtAllocateVirtualMemory" ) 
    );
}

Last updated