+++ title = “Maldev Academy notes” description = “Notes on my progress with Maldev Academy” weight = 1

updated = 2024-10-03

created = 2024-10-03 +++

Here I share notes on what I’m learning form Maldev Academy.

Module 3 - Required Tools

x64dbg

Some notes on the x64dbg interface.

The main tab (‘CPU’) has 4 screens:

  1. Disassembly (top-left): displays assembly instructions being executed
  2. Dump (bottom-left): displays memory contents
  3. Registers (top-right): displays values of CPU registers
  4. Stack (bottom-right): displays stack contents

Module 4 - Coding Basics

Structures/structs are declared using typedef to give aliases. F.e.:

typedef struct _STRUCTURE_NAME {
 
  // structure elements
 
} STRUCTURE_NAME, *PSTRUCTURE_NAME;
  • STRUCTURE_NAME alias: refers to structure name
  • PSTRUCTURE_NAME alias: represents pointer to structure (P is a Microsoft convention)

Passing By Reference

Passing by reference is a method of passing arguments to a function where the argument is a pointer to the object, rather than a copy of the object’s value.

Module 5 - Windows Architecture

diagram of user and kernel mode

  1. User Processes - A program/application executed by the user such as Notepad, Google Chrome or Microsoft Word.
  2. Subsystem DLLs - DLLs that contain API functions that are called by user processes. An example of this would be kernel32.dll exporting the CreateFile Windows API (WinAPI) function, other common subsystem DLLs are ntldll.dll, advapi32.dll and user32.dll.
  3. Ntdll.dll - A system-wide DLL which is the lowest layer available in user mode. This is a special DLL that creates the transition from user mode to kernel mode. This is often referred to as the Native API or NTAPI.
  4. Executive Kernel - This is what is known as the Windows Kernel and it calls other drivers and modules available within kernel mode to complete tasks. The Windows kernel is partially stored in a file called ntoskrnl.exe under “C:\Windows\System32”.

Function Call Flow

The image below shows an example of an application that creates a file. It begins with the user application calling the CreateFile WinAPI function which is available in kernel32.dll. Kernel32.dll is a critical DLL that exposes applications to the WinAPI and is therefore can be seen loaded by most applications. Next, CreateFile calls its equivalent NTAPI function, NtCreateFile, which is provided through ntdll.dll. Ntdll.dll then executes an assembly sysenter (x86) or syscall (x64) instruction, which transfers execution to kernel mode. The kernel NtCreateFile function is then used which calls kernel drivers and modules to perform the requested task.

image

Module 7 - Introduction to the Windows API

Windows Data Types

Data typefunction
DWORDrepresents values up to 2^32-1 (32-bit uint)
size_trepresents size of an object (32- or 64-bit uint)
VOIDindicates absence of specific data type
PVOIDpointer of any data type (32-bit or 64-bit pointer)
HANDLEspecifies a particular object that the operating system is managing (file, process, thread)
HMODULEhandle to a module (DLL, EXE). base address of module in memory
LPCSTR/PCSTRpointer to a constant null-terminated string of 8-bit Windows characters (ANSI). equivalent to const char*
PWSTR/LPWSTRsame as LPCSTR/PCSTR but does not point to a constant variable but to a readable and writable string (equivalent to wchar*)
wchar_tsame as wchar which is used to represent wide characters
ULONG_PTRrepresents uint that is same size as pointer (32-bit or 64-bit). Used with arithmetic expressions containing pointers.
Data typeexample
DWORDDWORD dwVariable = 42;
size_tSIZE_T = sVariable = sizeof(int);
VOIDvoid* pVariable = NULL; // This is the same as PVOID
PVOIDPVOID pVariable = &SomeData;
HANDLEHANDLE hFile = CreateFile(…);
HMODULEHMODULE hModule = GetModuleHandle(…);
LPCSTR/PCSTRLCPSTR lpcString = ‘Hello, world!’
PCSTR pcwString = L’Hello, world!’
PWSTR/LPWSTRLPWSTR lpwString = L’Hello, world!’
PWSTR pwString = L’Hello, world!‘
wchar_twchar_t wChar = L’A’;
ULONG_PTRPVOID Pointer = malloc(100);
//Pointer = pointer +10; //not allowed
Pointer = (ULONG_PTR)Pointer + 10;
// allowed

Ansi & Unicode Functions

  • CreateFileA ‘A’ indicates ‘ANSI’
    • first parameter for CreateFileA is a LPCSTR
  • CreateFileW ‘W’ indicates ‘Wide’ and represents Unicode
    • first parameter for CreateFileW is LPCWSTR

Module 8

PE Structure

structure of a Portable Executable

DOS header data structure

typedef struct _IMAGE_DOS_HEADER {
	WORD e_magic;    // Magic number
	WORD e_cblp;     // Bytes on last page of file
	WORD e_cp;       // Pages in file
	WORD e_crcl;     // Relocations
	WORD e_cparhdr;  //Size of header in paragraphs
	WORD e_minalloc; // Minimum extra paragraphs needed
	WORD e_maxalloc; // Maximum extra paragraphs needed
	WORD e_ss;       // Initial (relative) SS value 
	WORD e_sp;       // Initial SP value
	WORD e_csum;     // Checksum
	WORD e_ip;       // Initial IP value
	WORD e_cs;       // Initial (relative) CS value
	WORD e_lfarlc;   // File address of relocation table
	WORD e_ovno;     // Overlay number
	WORD e_res[4];   // Reserved words
	WORD e_oemid;    // OEM identifier (for e_oeminfo)
	WORD e_oeminfo;  // OEM information; e_oemid specific
	WORD e_res2[10]; // Reserved words
	LONG e_lfanew;   // Offset to the NT header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

The most important members of the struct are e_magic and e_lfanew.

e_magic is 2 bytes with a fixed value of 0x5A4D or MZ.

e_lfanew is a 4-byte value that holds an offset to the start of the NT Header. Note that e_lfanew is always located at an offset of 0x3C.

NT Header (IMAGE_NT_HEADERS)

NT Header contains FileHeader and OptionalHeader. NT Header can be reached via the e_lfanew member inside the DOS Header.

typedef struct _IMAGE_NT_HEADERS {
	DWORD                      Signature;
	IMAGE_FILE_HEADER          FileHeader;
	IMAGE_OPTIONAL_HEADER32/64 OptionalHeader;
} IMAGE_NT_HEADERS32/64, *PIMAGE_NT_HEADERS32/64;

File Header (IMAGE_FILE_HEADER)

typedef struct _IMAGE_FILE_HEADER {
	WORD Machine;
	WORD NumberofSections;
	DWORD TimeDateStamp;
	DWORD PointerToSymbolTable;
	DWORD NumberOfSymbols;
	WORD SizeOfOptionalHeader;
	WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_OF_FILE_HEADER;

NumberOfSections = number of sections in the PE file. Characteristics = Flags that specify certain attributes about the executable file, such as whether it’s a dynamic-link library (DLL) or a console application. SizeOfOptionalHeader = The size of the following optional header.

Optional header (IMAGE_OPTIONAL_HEADER)

32-bit-version:

typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD                 Magic;
  BYTE                 MajorLinkerVersion;
  BYTE                 MinorLinkerVersion;
  DWORD                SizeOfCode;
  DWORD                SizeOfInitializedData;
  DWORD                SizeOfUninitializedData;
  DWORD                AddressOfEntryPoint;
  DWORD                BaseOfCode;
  DWORD                BaseOfData;
  DWORD                ImageBase;
  DWORD                SectionAlignment;
  DWORD                FileAlignment;
  WORD                 MajorOperatingSystemVersion;
  WORD                 MinorOperatingSystemVersion;
  WORD                 MajorImageVersion;
  WORD                 MinorImageVersion;
  WORD                 MajorSubsystemVersion;
  WORD                 MinorSubsystemVersion;
  DWORD                Win32VersionValue;
  DWORD                SizeOfImage;
  DWORD                SizeOfHeaders;
  DWORD                CheckSum;
  WORD                 Subsystem;
  WORD                 DllCharacteristics;
  DWORD                SizeOfStackReserve;
  DWORD                SizeOfStackCommit;
  DWORD                SizeOfHeapReserve;
  DWORD                SizeOfHeapCommit;
  DWORD                LoaderFlags;
  DWORD                NumberOfRvaAndSizes;
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

64-bit version

typedef struct _IMAGE_OPTIONAL_HEADER64 {
  WORD                 Magic;
  BYTE                 MajorLinkerVersion;
  BYTE                 MinorLinkerVersion;
  DWORD                SizeOfCode;
  DWORD                SizeOfInitializedData;
  DWORD                SizeOfUninitializedData;
  DWORD                AddressOfEntryPoint;
  DWORD                BaseOfCode;
  ULONGLONG            ImageBase;
  DWORD                SectionAlignment;
  DWORD                FileAlignment;
  WORD                 MajorOperatingSystemVersion;
  WORD                 MinorOperatingSystemVersion;
  WORD                 MajorImageVersion;
  WORD                 MinorImageVersion;
  WORD                 MajorSubsystemVersion;
  WORD                 MinorSubsystemVersion;
  DWORD                Win32VersionValue;
  DWORD                SizeOfImage;
  DWORD                SizeOfHeaders;
  DWORD                CheckSum;
  WORD                 Subsystem;
  WORD                 DllCharacteristics;
  ULONGLONG            SizeOfStackReserve;
  ULONGLONG            SizeOfStackCommit;
  ULONGLONG            SizeOfHeapReserve;
  ULONGLONG            SizeOfHeapCommit;
  DWORD                LoaderFlags;
  DWORD                NumberOfRvaAndSizes;
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

Important struct members of _IMAGE_OPTIONAL_HEADER

Magic - Describes the state of the image file (32- or 64-bit image) MajorOperatingSystemVersion - The major version number of the required operating system MinorOperatingSystemVersion - The minor version number of the required operating system SizeOfCode - The size of the .text section AddressOfEntryPoint - Offset to the entry point of the file (Typically the main function) BaseOfCode - Offset to the start of the .text section SizeOfImage - The size of the image file in bytes ImageBase - It specifies the preferred address at which the application is to be loaded into memory when it is executed. DataDirectory - Array of IMAGE_DATA_DIRECTORY which contains the directories in a PE files

Data Directory

Array of data type IMAGE_DATA_DIRECTORY with the following data structure (below). Data Directory array is of size IMAGE_NUMBEROF_DIRECTORY_ENTRIES, constant value of 16.

typedef struct _IMAGE_DATA_DIRECTORY {
	DWORD VirtualAddress;
	DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

Each element of the array represents a data directory containing data about a PE section or Data Table. SPecific data directories can be indexed:

#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

Export Directory

Contains addresses of exported functions and variables which can be used by other executable files to access the functions and data. Generally found in DLLs that export functions (like kernel32.dll exporting CreateFileA).

Import Address Table

Contains information about the addresses of functions imported from other executable files. Addresses are used to access the functions and data in other executables (like Application.exe importing CreateFileA from kernel32.dll.)

PE Sections

contains code and data used to create an executable program. PE secions have a unique name and contains (1) executable code, (2) data, or (3) resource information. Sections can be added later manually, the IMAGE_FILE_HEADER.NumberOfSections determine that number.

Important PE sections:

.text - executable code which is the written code .data - initialized data which are variables initialized in the code .rdata - read-only data which are constant variables prefixed with const .idata - contains import tables which relate to functions called using the code .reloc - information how to fix up memory addresses so the program can be loaded .rsrc - store resources like icons and bitmaps

Each PE section has an IMAGE_SECTION_HEADER data structure, saved under the NT headers and are stacked above each other, representing a section. IMAGE_SECTION_HEADERcontains:

typedef STRUCT _IMAGE_SECTION_HEADER {
  BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // name of the section (.text, .data, .rdata)
  union {
    DWORD PhysicalAddress;            // size of the section in memory
    DWORD VirtualSize;
  } Misc;
  DWORD VirtualAddress;               // offset of start of section in memory
  DWORD SizeOfRawData;
  DWORD PointerToRawData;
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD NumberOfRelocations;
  WORD NumberOfLinenumbers;
  DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Module 9

Unlike EXE files, DLL files cannot execute code on their own: DLL libraries need to be invoked by other programs to execute the code.

  • There are 4 possibilities for a DLL entry point to be called:

    • DLL_PROCESS_ATTACH - A process is loading the DLL.
    • DLL_THREAD_ATTACH - A process is creating a new thread.
    • DLL_THREAD_DETACH - A thread exits normally.
    • DLL_PROCESS_DETACH - A process unloads the DLL.
  • Try to load a DLL dynamically (link to tutorial) maldevacademy socc studie

Module 11

Virtual memory allows the operating system to use more memory than what is physically available by creating a virtual address space that can be accessed by the applications. These virtual address spaces are divided into ‘pages’ which are then allocated to processes.

pages

Type of memoryDefinition
Private memorydedicated to a single process, stores specific data
Mapped memorycan be shared, stores shared libraries, memory segments, shared files
Image memorycontains code and data of executable, often related to DLL files loaded into process address space

Process Environment Block (PEB)

PEB is a Windows data structure containing process information like:

  • parameters
  • startup information
  • allocated head information
  • loaded DLLs
  • process ID (PID) and path to executable

PEB struct in C reserved members (can be ignored)):

typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged;
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
  PVOID                         Reserved4[3];
  PVOID                         AtlThunkSListPtr;
  PVOID                         Reserved5;
  ULONG                         Reserved6;
  PVOID                         Reserved7;
  ULONG                         Reserved8;
  ULONG                         AtlThunkSListPtr32;
  PVOID                         Reserved9[45];
  BYTE                          Reserved10[96];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoute;
  BYTE                          Reserved11[128];
  PVOID                         Reserved12[1];
  ULONG                         SessionId;
} PEB, *PPEB;

PEB struct reserved members:

  • BeingDebugged

    • indicates whether the process is being debugged, used by the Windows loader
  • Ldr

    • pointer to PEB_LDR_DATA struct containing information about process’s loaded DLLs
    • can be leveraged to find base address of particular DLL and functions
    • used for a custom version of GetModuleHandleA/W for added stealth
typedef struct _PEB_LDR_DATA {
  BYTE Reserved1[8];
  PVOID Reserved2[3];
  LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
  • ProcessParameters
    • data structure containing command line parameters passed to process
    • used for command line spoofing
typedef struct _RTL_USER_PROCESS_PARAMETERS {
  BYTE Reserved1[16];
  PVOID Reserved2[10];
  UNICODE_STRING ImagePathName;
  UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
  • AtlTHunkSListPtr and AtlThunkSListPtr32

    • used by the ATL (Active Template Library) to store a pointer to linked list of thunking functions
      • thunking functions used to call functions implemented in different address space
      • often represent functions exported from a DLL
  • PostProcessInitRoutine

    • stores pointer to function called by OS after TLS (Thread Local Storage) initialization
  • SessionId

    • unique identifier assigned to single session

Thread Environment Block (TEB)

typedef struct _TEB {
  PVOID Reserved1[12];
  PPEB  ProcessEnvironmentBlock;
  PVOID Reserved2[399];
  BYTE  Reserved3[1952];
  PVOID TlsSlots[64];
  BYTE  Reserved4[8];
  PVOID Reserved5[26];
  PVOID ReservedForOle;
  PVOID Reserved6[4];
  PVOID TlsExpansionSlots;
} TEB, *PTEB;

ProcessEnvironmentBlock (PEB) is a pointer to the PEB structure, located inside the Thread Environment Block (TEB) and used to store information about the currently running process.

TlsSlots (Thread Local Storage) Slots are locations in the TEB used to store thread-specific data (thread-specific variables, handles, states).

TlsExpansionSlots are a set of pointers to store thead-local storage data for a thread, reserved for use by system DLLs.

Module 12 - Undocumented Structures

reserved members within stuctures are presented as arrays of BYTES or PVOID data types and prevent users from understanding the structure to avoid modifications to these reserved members.

One way to determine PEB’s reserved members is through the !peb command in WinDbg.

Choosing a structure definition

  • Some structure definitions only work for a specific architecture (x86 or x64 f.e.)
  • It can be necessary to define multiple structures due to nested structures (PEB contains a member that acts as a pointer to another structure)
  • Select only one definition of a structure to avoid redefinition errors thrown by a compiler (f.e. Visual Studio’s)

Module 13 - Payload placement - .data & .rdata sections

The .data and .rdata sections can be merged / merged into the .text section.

.data section

benefit of string payload in #.data section: readable and writable, so you can have an encrypted payload that is decrypted at runtime

code snippet example of payload stored in .data section:

#include <Windows.h>
#include <stdio.h>
 
// msfvenomn calc shellcode generated with:
// msfvenomn -p windows/x64/exec CMD=calc.exe -f c
// .data saved payload
 
unsigned char Data_RawData[] = {
  // hex characters
};
 
int main() {
  printf("[i] Data_RawData var : 0x%p \n", Data_RawData);
  printf("[#] Press <Enter> To Quit ...");
  getchar();
  return 0;
}

Compiling and executing this code gives an antivirus warning

.rdata section

The ‘r’ in .rdata indicates read-only, which can be done using the const (constant) qualifier for variables.

An example of this using the previous code:

#include <Windows.h>
#include <stdio.h>
 
// msfvenom calc shellcode
// msfvenom -p windows/x64/exec CMD=calc.exe -f c
// .rdata saved payload
const unsigned char Rdata_RawData[] = {
  // hex characters
};
 
int main() {
  printf("[i] Rdata_RawData var: 0x%p\n", Rdata_RawData);
  printf("[#] Press <Enter> To Quit...");
  getchar();
  return 0;
}

.text section

The #.text section stores variables within executable memory permissions so they can be executed directly without editing the memory regions permissions. Mostly useful for small payloads, less than 10 bytes. Saving a variable in the .text section requires you to instruct the compiler to do so. An example:

#include <Windows.h.>
#include <stdio.h>
 
// mfsvenom calc shellcode
// msfvenom -p windows/x64/exec CMD=calc.exe -f c
// .text saved payload
#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Text_RawData[] = {
  // hex characters
};
 
int main() {
  printf("[i] Text_RawData var: 0x%p\n", Text_RawData);
  printf("[#] Press <Enter> To Quit...");
  getchar();
  return 0;
}

.rsrc section

Larger payloads cannot be stored in the .data or .rdata sections due to size limits. The #.rsrc section is cleaner (no errors during compilation).

One way to store a payload in the .rsrc section with Visual Studio:

  1. right-click ‘Resource files’ ‘Add’ ‘New Item…’
  2. ‘Resource File’
  3. Right-click on the ‘Resource.rc’ default file select ‘Add Resource’
  4. ‘Import’
  5. Select calc.ico which is raw payload
  6. In prompt, enter ‘RCDATA’ under ‘Resource Type:’
  7. resource.h should be visible and named according to the previous .rc file

Payloads stored in .rsrc section cannot be accessed directly but through these WINAPIs:

  • FindResourceW - get location of specified data stored in resource section of a special ID passed in (this is defined in the header file)
  • LoadResource - retrieves a HGLOBAL handle of resource data, which can be used to obtain the base address of the specified resource in memory
  • LockResource - obtains pointer to specified data in resource section from its handle
  • SizeofResource - gets size specified data in resource section
#include <Windows.h>
#include <stdio.h>
#include "resource.h"
 
int main() {
 
  HRSRC hRsrc           = NULL;
  HGLOBAL hGlobal       = NULL;
  PVOID pPayloadAddress = NULL;
  SIZE_T sPayloadSize   = NULL;
 
  // Get location to data stored in .rsrc by its id *IDR_RCDATA1*
  hRsrc = FindResourceW(NULL, MAKEINTRESOURCEw(IDR_RCDATA1), RT_RCDATA);
  if (hRsrc == NULL) {
    // in case of function failure
    printf("[!] FindResourceW failed with error: %d \n", GetLastError());
    - [ ] return -1;
  }
 
  // GetHGLOBAL or the handle of the specified resource data since its required to call LockResource later on
  hGlobal = LoadResource(NULL, hRsrc);
  if (hGlobal == NULL) {
    // in case of function failure
    printf("[!] LoadResource failed with errror: %d \n", GetLastError());
    return -1;
  }
 
  // Get the address of our payload in .rsrc section
  pPayloadAddress = LockResource(hGlobal);
  if (pPayloadAddress == NULL) {
    // in case of function failure
    printf("[!] LockResource failed with Error: %d \n", GetLastError());
    return -1;
  }
 
  // Get the size of our payload in .rsrc section
  sPayloadSize = SizeofResource(NULL, hRsrc);
  if (sPayloadSize == NULL) {
    // in case of function failure
    printf("[!] SizeofResource failed with error: %d \n", GetLastError());
    return -1;
  }
 
  // Printing pointer and size to the screen
  printf("[i] pPayloadAddress var: 0x%p \n", pPayloadAddress);
  printf("[i] sPayloadSize var: %ld \n", sPayloadSize);
  printf("[#] Press <Enter> to quit...");
  getchar();
  return 0;
}

Important to note: the payload address that this code prints to the screen, is read-only memory. To edit a payload, you must allocate a buffer with the same size as the payload and copy it over. The new buffer is where changes (like decrypting a payload!) can be made. This can be done with memcpy like this:

// Allocating memory using a HeapAlloc call.
PVOID pTmpBuffer = HeapAlloc(GetProcessHeap(), 0, sPayloadSize);
if (pTmpBuffer != NULL){
  // copying the payload from the resource section to the new buffer.
  memcpy(pTmpBuffer, pPayloadAddress, sPayloadSize);
}
 
// Printing the base address of our buffer (pTmpBuffer)
printf("[i] pTmpBuffer var: 0x%p \n", pTmpBuffer);

Module 16 - Introduction to Payload Encryption

Encrypting (parts of) the malware is almost always necessary against modern security solutions. Encryption may not be effective against f.e. runtime-analysis and heuristic-analysis. The more data that’s encrypted within a file, the higher the entropy of the file. The most widely used encryption algorithms in malware development are:

  • XOR
  • AES
  • RC4

Payload Encryption - XOR

Exclusive or or XOR encryption is faster than AEC and RC4 and does not require additional libraries or the Windows API. It is also a bidirectional encryption algorithm.

Example of an XOR encryption function:

/*
 
  - pShellcode: Base address of the payload to encrypt.
  - sShellcodeSize: Size of the payload.
  - bKey: A single arbitrary byte representing the key for encrypting the payload.
*/
VOID XorByOneKey(IN PBYTE pShellcode, IN SIZE_T sShellcodeSize, IN BYTE bKey) {
  for (size_t i = 0; i < sShellcodeSize; i++){
    pShellcode[i] = pShellcode[i] ^ bKey;
  }
}