Creating a Suspended Process in C#

What is a Suspended Process?

A suspended process is a process that starts without immediately executing its main thread. Now, what is the main thread? That’s a great question! You can think of the main thread like the main() function in C/C++. When a process is created in a suspended state, it means the process has started but is halted, waiting for further instructions such as calling ResumeThread() to continue execution.

In simple terms, we are starting a process and telling it to pause before executing its main thread.

Why Do We Need a Suspended Process?

1. Process Injection & Code Execution

Malware developers use suspended processes to inject malicious code into virtual memory before the main process executes. This allows attackers to execute payloads stealthily.

2. Debugging & Hooking

Suspended processes are also used by Antivirus (AV) and Endpoint Detection & Response (EDR) solutions. Instead of executing a suspicious EXE or DLL immediately, AV/EDR creates the process in a suspended state to analyze and debug binaries before execution.

Now, let’s create a C# program to start a process in a suspended state.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace SuspendedProcessDemo
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential)]
        struct STARTUPINFO
        {
            public int cb;
            public IntPtr lpReserved;
            public IntPtr lpDesktop;
            public IntPtr lpTitle;
            public int dwX;
            public int dwY;
            public int dwXSize;
            public int dwYSize;
            public int dwXCountChars;
            public int dwYCountChars;
            public int dwFillAttribute;
            public int dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CreateProcess(
            string lpApplicationName,
            string lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);       

        static void Main()
        {
            STARTUPINFO si = new STARTUPINFO();
            si.cb = Marshal.SizeOf(si);
            PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

            string targetProcess = "C:\\Windows\\System32\\notepad.exe";

                CreateProcess(
                targetProcess,
                null,
                IntPtr.Zero,
                IntPtr.Zero,
                false,
                0x00000004, // Suspended mode
                IntPtr.Zero,
                null,
                ref si,
                out pi);          
        }
    }
}

Understanding the Code Bit by Bit

The code begins with Namespace Declarations:

using System;
using System.Diagnostics; // Provides tools for working with processes
using System.Runtime.InteropServices; // Allows interaction with Windows API

Next, we define two structs: STARTUPINFO and PROCESS_INFORMATION. These structures are initially empty and will later store information when we create a process.

Using P/Invoke to Call CreateProcess

[DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CreateProcess(
            string lpApplicationName,
            string lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation);

We use P/Invoke (Platform Invocation Services) to import the CreateProcess function from from WinAPI (Microsoft Docs) kernel32.dll, enabling us to create a new process in a suspended state.

The function has 10 parameters, but we are primarily interested in dwCreationFlags, which determines how the process starts. Setting this to 0x00000004 tells Windows to create the process in a suspended state. Other parameters can be set to null or IntPtr.Zero when not needed.

Initializing STARTUPINFO

STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);

This creates an instance of the STARTUPINFO struct. cb stands for count of bytes, and Marshal.SizeOf(si) calculates the size of the structure in bytes. This is necessary because CreateProcess expects cb to be set before the function is called.

Creating the Suspended Process

PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

            string targetProcess = "C:\\Windows\\System32\\notepad.exe";

                CreateProcess(
                targetProcess,
                null,
                IntPtr.Zero,
                IntPtr.Zero,
                false,
                0x00000004, // Suspended mode
                IntPtr.Zero,
                null,
                ref si,
                out pi);

We create an instance of the PROCESS_INFORMATION struct, which will store information about the process, such as:

  • Process Handle
  • Thread Handle
  • Process ID
  • Thread ID

We define targetProcess as Notepad.exe. The dwCreationFlags is set to 0x00000004, which halts the main thread, keeping the process suspended.

Verifying the Suspended Process

After executing the program, open Task Manager and check if notepad.exe appears in the “Suspended” state (as shown in the screenshot). If it does, the code worked as expected.

Next Steps: Process Hollowing

Now that we have successfully created a suspended process, we will explore Process Hollowing in the next post.

Leave a Comment