Malware Development: Crafting Digital Chaos 0x0: Basics

10 minute read

What programming language should be used to develop Malware?

There isn’t such a standard called “Best language for Malware”, Malware are essentialy a software that does malicious actions, but it’s a software!, every programming language that you can use to develop a software (regardless of how the software works) can be used to write harmful malware, what makes the difference and can be a key part of deciding which language to use is the question: What are you targeting?

Your understanding of your target will help you decide what language to use, for example if we are targeting Windows, it won’t be the best choice to use JavaScript, C and C++ are more suitable here, and so on..

This series of posts will focus on Windows Malware, and I’ll be using C and C++ throughout the lessons, so pack your bags and tools and let’s start!.

Note: if you are not familiar with C it’s not the best time to read this article now, learn C first and comeback when you at-least know the basics.

Tools required to start

In order to start developing Malware you need to install Visual Studio and install C++ on it, also it is good to have some disassemblers and debuggers like IDA Pro, XDBG, Process Hacker (Process Monitoring tool) and so on..

Apparently all this should be on a VIRTUAL MACHINE, you weren’t thinking that you’d work on you host machine’s VS were you?

Coding basics and WINAPI

WINAPI or “Windows API,” which is a set of functions and data structures provided by Microsoft for use in writing applications for the Windows operating system. It provides developers with access to the underlying features and functionality of Windows, allowing them to create software that interacts with the operating system.

Developers use WINAPI functions in programming languages like C and C++ to create Windows applications.

We will be utilizing WINAPI features to write our Malware.

WINAPI header file

All the WINAPI functions, data types, macros, constants are embeded in a header file, to start using WINAPI inside your project you need to include its headers file.

#include <windows.h> 

WINAPI Data Types

Here is a list of some popular WINAPI data types:

BOOL: A Boolean variable (should be TRUE or FALSE).
BYTE: A byte (8 bits).
DWORD: A 32-bit unsigned integer.
CHAR: An 8-bit Windows (ANSI) character.
DWORD: A 32-bit unsigned integer.
FLOAT: A floating-point variable.
HANDLE: A handle to a module. This is the base address of the module in memory.
HKEY: A handle to a registry key.
INT: A 32-bit signed integer.
LPCSTR: A pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters.
LPWSTR: A pointer to a constant null-terminated string of 16-bit Unicode characters.
LPTSTR: An LPWSTR if UNICODE is defined, an LPSTR otherwise.
LPVOID: A pointer to any type.
PDWORD: A pointer to a DWORD.

The full list can be found here, I recommend taking a look.

WINAPI functions

WINAPI provides functions for a wide range of tasks, including:

  1. User input handling:
GetMessage()
MessageBox()
TranslateMessage()
  1. File I/O:
    WriteFile()
    ReadFile()
    
  2. Memory management:
    VirtualAlloc()
    VirtualProtect()
    VirtualAllocEx()
    HeapAlloc()
    
  3. Process and thread management:
     CreateProcess()
     WriteProcessMemory()
     OpenProcess()
     CreateRemoteThread()
    

When we want to use any function we need first to read it’s MSDN documentation, Microsoft has done a great work documenting each detail of the inner workings, parameters, return value of the API calls, however we will be exploring some undocumented structures and functions in the upcoming posts!.

Portable Executable format

Portable executable or PE files are a file format used in 32-bit and 64-bit versions of Windows operating systems for executable programs, DLLs (Dynamic Link Libraries), device drivers, and other types of executable files. PE files are the standard binary format for Windows executables and are used by the Windows operating system loader to load and execute programs.

Learning PE file format is important, knowing the inner components and headers will give us more control on our malware.

PE file structure

P1

Header: PE files begin with a header that contains information about the file, including the number and placement of sections within the file, machine type (e.g., x86, x64), and signature.

Optional Header: This header, which comes after the main header, contains other details about the executable, like the image base address, the size of the code and data sections, and the entry point address.

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;

Data Directories: A data directory pointing to different PE file data structures, including the import table, export table, and debug information, is also included in the optional header.

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

Section Table: PE files are organized into sections, each of which holds a particular kind of data, such as resources, code, data, or import/export details. Text or code (.text), data (.data), resource (.rsrc), and import/export (.idata/.edata) sections are examples of common sections.

Import Table: The import table contains information about the external functions and libraries that the executable depends on. It includes the names of the imported functions and the addresses of the corresponding functions in the loaded libraries.

Export Table: If the PE file is a DLL, it may contain an export table that lists the functions and variables that are exported by the DLL for use by other programs.

For more information about the PE file structure check this documentation

Function call process

The kernel in Windows is responsible for completing all the tasks in Windows (writing - opening files, network connections, etc.), so every application call needs to go through a process in order to invoke certain functionality from the kernel.

P1

The picture above describes that process, first when an application requests to open a process using OpenProcess() API call from kernel32.dll it first goes to kernelbase.dll then ntdll.dll (The NATIVE API) which is the lowest layer in user mode and most of the APIs are implemented inside it, NTDLL is responsible for transferring the execution to kernel mode.

The last stage is done inside the kernel mode as the kernel uses its drivers and resources to complete that specific task and return back an output.

User Mode

  • User mode is the least privileged mode of operation.
  • Applications and most user-level software run in user mode.
  • In user mode, software has limited access to system resources and cannot directly access hardware or sensitive system data.
  • User mode processes rely on system calls to request services from the operating system kernel.
  • User mode processes are isolated from each other to prevent one process from interfering with another.

Kernel Mode

  • Kernel mode is the most privileged mode of operation.
  • The operating system kernel runs in kernel mode.
  • In kernel mode, software has full access to system resources, including hardware and sensitive system data.
  • Kernel mode components have unrestricted access to system memory and can execute privileged instructions.
  • Device drivers and critical system services run in kernel mode to perform low-level tasks and manage hardware resources.
  • Direct access to kernel mode is restricted to prevent unauthorized access and protect the stability and security of the system.

An important thing to remind is that applications can directly make Syscalls or invoke NTDLL functions but it is a bit more complicated to use, however being a Malware developer you need to explore this technique because it is more stealthy when it come to obfuscating or hiding the functionality of your malware.

Memory management in Windows

The address space in Windows differs based on the architecture, 32-bit processes can view up to 4 GB of memory and each process has 8-terabyte address space on 64-bit Windows.

X32

Virtual Address Space:

  • Each 32-bit process in Windows has a 4 GB virtual address space.
  • This address space is divided into two main regions: user mode and kernel mode.

User Mode:

  • The user mode portion of the virtual address space typically ranges from 0x00000000 to 0x7FFFFFFF (2 GB).
  • User mode contains the memory allocated for user-mode processes, including executable code, data, heap, and stack.

Kernel Mode:

  • The kernel mode portion of the virtual address space resides above the user mode portion, starting at 0x80000000.
  • Kernel mode contains memory allocated for the Windows kernel, device drivers, and other kernel-mode components

X64

Virtual Address Space:

  • Each 64-bit process in Windows has a vast 64-bit virtual address space, theoretically allowing access to up to 2^64 bytes of memory.
  • This address space enables the operating system to address much larger amounts of physical memory and provides more room for user-mode processes.

User Mode:

  • User mode typically ranges from 0x0000000000000000 to 0x7FFFFFFFFFFFFFFF.
  • User mode contains the memory allocated for user-mode processes, including executable code, data, heap, and stack.

Kernel Mode:

  • The kernel mode portion of the virtual address space resides above the user mode portion.
  • Kernel mode typically starts at 0x8000000000000000 and extends upwards.

Memory Allocation example


    // Method 1: Using GlobalAlloc / GlobalFree
    // Allocate memory
    HGLOBAL hGlobal = GlobalAlloc(GMEM_FIXED, 1024); // Allocating 1 KB of memory

    // Use the allocated memory
    // ...

    // Free the memory
    GlobalFree(hGlobal);

    // Method 2: Using VirtualAlloc / VirtualFree
    // Allocate memory
    LPVOID lpMemory = VirtualAlloc(NULL, 1024, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // Allocating 1 KB of memory

    // Use the allocated memory
    // ...

    // Free the memory
    VirtualFree(lpMemory, 0, MEM_RELEASE);

    // Method 3: Using HeapAlloc / HeapFree
    // Get the process heap handle
    HANDLE hHeap = GetProcessHeap();

    // Allocate memory
    LPVOID lpHeapMemory = HeapAlloc(hHeap, 0, 1024); // Allocating 1 KB of memory

    // Use the allocated memory
    // ...

    // Free the memory
    HeapFree(hHeap, 0, lpHeapMemory);

Memory pages

Memory pages refers to chunks or blocks of allocated memory with each page typically being 4 KB in size, although larger page sizes are also valid.

Memory pages protection constants:

  • PAGE_EXECUTE: Enables execute access to the committed region of pages. An attempt to write to the committed region results in an access violation.
  • PAGE_EXECUTE_READ: Enables execute or read-only access to the committed region of pages. An attempt to write to the committed region results in an access violation.
  • PAGE_EXECUTE_READWRITE: Enables execute, read-only, or read/write access to the committed region of pages.
  • PAGE_READONLY: Enables read-only access to the committed region of pages. An attempt to write to the committed region results in an access violation.
  • PAGE_READWRITE: Enables read-only or read/write access to the committed region of pages.

For more information check Microsoft’s documentation.

Memory protection

  1. Data Execution Prevention (DEP): Prevents execution of code in non-executable memory regions, mitigating buffer overflow attacks.
  2. Address Space Layout Randomization (ASLR): Randomizes memory addresses to prevent predictable memory layouts, making it harder for attackers to exploit vulnerabilities.