Let us jump into learning the prerequisites required for Malware Development! Keep in mind that you will not be developing the next Stuxnet overnight but let us think of this blog as a stepping stone into eventually getting there (eventually here meaning a long time).
Malware development resembles software development but requires a much deeper understanding of the OS’s inner workings. Unlike typical software, malware aims to exploit system behavior, bypass defenses, and manipulate OS components in ways they weren’t intended to operate. This demands in-depth knowledge of memory management, process manipulation, kernel functions, and security mechanisms to achieve stealth and persistence within the target environment. THIS IS NOT RECOMMENDED FOR A BEGINNER (seriously start with something simpler to not get demotivated).
If you haven’t already I would recommend learning a programming language like C/C++, C#, Rust or Golang. For Maldev specifically you should lean more towards low level languages as they offer more control. Strictly in my opinion C# is goated and I would recommend this excellent resource for C#. This blog will be in C to get started but we will explore C# in the upcoming parts.
This blog will be a series that I will add to when I get free time as this is more of a hobby for me, the main focus will always be penetration testing.
Process #
Let us discuss about processes in the context of Windows. If you are a Computer Science student like me, then you would already know about processes already (as explained by your sweaty OS professor), if not I implore you to read more here. I will provide a basic explanation.
Process is a container that houses everything needed for a program to run such as:
- Program Code: The actual instructions that the CPU executes.
- Memory: Space for variables and data structures (divided into segments like the stack, heap, etc.).
- Resources: Access to file handles, network connections, and other OS resources.
- Registers and State: CPU state specific to the process, like register contents.
- Process Control Block (PCB): Metadata maintained by the OS for process management.
- Handles : Used to manage resources like files, network connections, and devices securely.
A program can have multiple processes, a browser for example can have a process for each tab you open.
A process can also create it’s own processes called child processes. A process is largely isolated and not aware of other processes.
Each process has it’s own virtual address space and it thinks all that memory is allocated to it and itself only (SO GREEDY!). The OS grants each process its own virtual address space, giving it the illusion of having exclusive access to a huge block of memory. This keeps processes isolated from each other, preventing them from meddling with each other’s data—a critical aspect for both security and stability.
Types of Processes #
There are 3 types:
- Application processes - These are processes launched by users to run specific applications, like browsers or text editors. They run in the foreground and interact directly with users.
- Background - These run without direct user interaction, often providing support services. Examples include update checkers, network services, or indexing services. They might be started by applications or the OS itself
- Windows process - These are essential system processes, automatically started with the OS. They handle critical functions like memory management (e.g.,
System
), security (lsass.exe
), and hardware interaction (e.g., driver processes). Their stability is crucial for the system’s overall health.
Priority #
Processes have priority levels in Windows similar to niceness in Linux, the priority level of a process influences the amount of CPU time it receives relative to other processes. There are 6:
- Realtime: Should be avoided as it can interrupt critical system threads responsible for managing input devices (like the mouse and keyboard) and background disk operations
- High: Suitable for processes that require significant CPU resources but still allows some system functionality
- Above Normal: Used for processes that need a bit more CPU time than usual without being overly aggressive.
- Normal: Perfectly balanced as all things should be (Thanos noises intensify). This is the default priority level for most processes, providing a fair share of CPU resources while maintaining system stability and responsiveness.
- Below Normal: Suitable for processes that can afford to wait for CPU time without impacting system responsiveness
- Low: Only appropriate for background tasks when no other processes are actively using CPU resources, ensuring minimal impact on system performance.
There is also Process Scheduling, which determines which process gets executed (based on priority), when it gets executed (considering waiting time), and for how long it gets executed (referring to burst time)."
The OS assigns each process a Process Identifier (PID), along with additional information such as the image file name (the executable associated with the process), the command line arguments (if any), and other metadata that helps manage and track the process.
Threads #
If a process is a nuclear bomb, then a thread is a firecracker—smaller and lighter.
Each process is initiated with a single thread, known as the primary thread, but it can create additional threads from any of its existing threads.
A thread is the basic unit of execution within a process, capable of being scheduled for execution. All threads within a process share the same virtual address space and system resources, enabling efficient communication and data sharing.
When a process contains multiple threads, it is referred to as multithreading. While processes operate independently of one another, threads are interdependent and share memory within their parent process, allowing for collaborative execution and resource management.
Process | Thread |
---|---|
A process means any program that is in execution. | A thread is a segment of a process. |
It takes more time to terminate. | It takes less time to terminate. |
It takes more time for creation. | It takes less time for creation. |
It takes more time for context switching. | It takes less time for context switching. |
The process is less efficient in terms of communication. | A thread is more efficient in terms of communication. |
Multiprogramming holds the concepts of multi-process. | Multiple threads can exist within a single process. |
The process is isolated. | Threads share memory. |
A process is called a heavyweight process. | A thread is lightweight as each shares code, data, and resources. |
Process switching uses an interface in an operating system. | Thread switching does not require OS calls and interrupts the kernel. |
If one process is blocked, it will not affect the execution of other processes. | If a user-level thread is blocked, all other user-level threads are blocked. |
The process has its own Process Control Block, Stack, and Address Space. | A thread has the parent’s PCB, its own Thread Control Block, Stack, and a common Address Space. |
Changes to the parent process do not affect child processes. | Changes to the main thread may affect the behavior of other threads in the same process. |
A system call is involved in process management. | No system call is involved; threads are created using APIs. |
Processes do not share data with each other. | Threads share data with each other. |
Handle #
Handle in Windows is a generic unit of identification that serves as a pointer or reference to an object or resource. It enables programs to access and manipulate that resource without requiring knowledge of its underlying representation or memory address. It’s similar to pointers in C/C++.
Common example in WinAPI:
- Handle to threads or processes: These handles are used to manage threads and processes.
Handle hThread, hProcess;
- Handle to Modules is called HMODULE: This handle represents a module (like a DLL) loaded into the address space of a process.
HMODULE hModule
- Handle to a window: This handle is used to identify a window in the GUI environment.
HWND myWindow
Handles can be system-wide. When process A creates a handle, it can pass that handle to process B, but only under certain conditions. For example, the handle must be created with specific security attributes that allow access across processes. This ensures that process B can interact with the resource associated with the handle, provided it has the necessary permissions.
Windows API #
An API (Application Programming Interface) is a set of rules and protocols that allows different software applications to communicate with each other. In Windows, the Windows API (Win32 API) provides a set of functions for interacting with the operating system.
It is designed for both 32-bit and 64-bit applications, ensuring backward compatibility while allowing developers to take advantage of the features of the 64-bit architecture.
Win API (or Win32 API) is well documented.
Let’s try out a simple Hello World program using WinAPI in Visual Studio.
We will use MessageBox, as you can see it is an integer function and it returns these values For example Clicking the OK button would return the value 1.
The naming convention like hProcess, hThread or hModule is called the Hungarian notation.
For example if we define a variable Bool Var1
in Hungarian notation it would be Bool bVar1
to denote it’s data type.
So hThread
for example is a handle to a thread.
Create a new Project in Visual Studio and select an empty project. Right-click on project name in Solution Explorer and Add hello.c
#include <windows.h>
int main(void) {
MessageBoxW(
NULL,
L"My first message box!",
L"Hello World!",
MB_YESNOCANCEL
);
return EXIT_SUCCESS;
}
Now click on this button: We can see our message box:
We can also add icons to this, refer the documentation: ```
#include <windows.h>
int main(void) {
MessageBoxW(
NULL,
L"My first message box!",
L"Hello World!",
MB_YESNOCANCEL | MB_ICONEXCLAMATION
);
return EXIT_SUCCESS;
}
I added the Exclamation icon:
Why did we use MessageBoxW
and not MessageBox
?
There are different variations of the same function as we can see in the documentation:
MessageBoxA - ANSI. We can use the ANSI version without using L. However it is best to use Unicode as ANSI is outdated.
MessageBoxW - Unicode or Wide Char. This is why we used L before the strings to convert it into Unicode.
MessageBoxEx - More parameters to play around with.
MessageBoxIndirect* - The term “indirect” refers to how the function is invoked using a pointer to a structure (MSGBOXPARAMS
)
Let’s create a process #
We’ll use CreateProcessW , it is not the same as OpenProcess. The former creates a whole new process and the latter opens an existing process.
#include <stdio.h>
#include <windows.h>
int main(void) {
STARTUPINFOW si = { 0 };
PROCESS_INFORMATION pi = { 0 };
if (!CreateProcessW(
L"C:\\Windows\\System32\\notepad.exe",
NULL, // Command-line Arguments
NULL, // Return handle can be inherited by child processes or not
NULL, // Return handle can be inherited by child threads or not
FALSE, // Inherit Handles
BELOW_NORMAL_PRIORITY_CLASS, // Priority
NULL, // Pointer to environment block
NULL, // Path to current directory of process
&si,
&pi)) {
printf("[-] Failed to create process, error: %ld", GetLastError());
return EXIT_FAILURE;
}
printf("[+] Process started! PID: %ld", pi.dwProcessId);
return EXIT_SUCCESS;
}
Feel free to take apart this code piece by piece to understand it. Run it and we can see notepad open: We also get the PID:
Let’s use System Informer (I know it doesn’t match up here, as the screenshots were taken at different times): It is running in below normal priority
We can also print more information about the process. Refer the PROCESS_INFORMATION page
#include <stdio.h>
#include <windows.h>
int main(void) {
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
if (!CreateProcessW(L"C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
printf("(-) failed to create process, error: %ld", GetLastError());
return EXIT_FAILURE;
}
DWORD TID = pi.dwThreadId;
DWORD PID = pi.dwProcessId;
HANDLE hThread = pi.hThread;
HANDLE hProcess = pi.hProcess;
printf("(+) got handle to process\n");
printf("(+) process started! pid: %ld\n", PID);
printf("\t(+) pid: %ld, handle: 0x%x\n", PID, hProcess);
printf("\t(+) tid: %ld, handle: 0x%x\n\r", TID, hThread);
WaitForSingleObject(hProcess, INFINITE);
printf("(+) finished! exiting...");
CloseHandle(hThread);
CloseHandle(hProcess);
return EXIT_SUCCESS;
}