When programming with Windows there is a lot of hidden functionality that very few really know. For example, did you know you can have one process simply request [via the Windows API] that another process execute an arbitrary amount of code on your behalf? This gives you the ability to see inside the memory of that process or even perform functions that your original process may not be able to (like connecting to the internet).

This concept has a name and it is called DLL Injection. It is surprisingly simple and can be done with 4 lines of code inside of a Visual C++ console application.

What is DLL Injection?

DLL injection is the process of causing another process to execute code within it’s process context by taking advantage of the alignment between two Windows API function headers and the Windows API allowing you to communicate with other processes. This provides mostly unlimited access to your processes within a computer.

The Limitations

There are some limitations to this:

  1. This will only work on processes running on your session. This would give you privilege escalation which is a bit trickier to implement.
  2. You can only access processes of the same “bitness”. If you write a 64bit application you can only (unless you want to do a lot of work) access other 64bit processes. The same applies for 32bit process only being able to access other 32bit processes.
  3. Some ant​i-malware/anti-virus software will capture this (sometimes and not 100% of the time).

What Do I Need?

All that is needed is Visual Studio to write code and a basic Windows XP or higher machine.

How Is This Possible?

There is a long, complicated explanation for why this functionality exists within Windows and the legitimate uses for it. Basically, the way Windows and many Windows applications work, there is a need for these APIs. The trick of the whole process is to understand the coincidence of the APIs and use that to your benefit.

How Does It All Work?

Without going into a long explanation of Windows internals, here are the basics:

Processes Within Windows

Inside of all modern operating systems and processes is a wall in which memory is divided and allocated completely separately without any possibility of seeing another process’ memory. This is done through Virtual Memory addressing and Windows implements this fully. Virtual Memory is the idea that the address space for memory and for each process is given a unique address space of 8Tb on 64bit (2G on 32bit).

From the context of a program all you ever can/should manage is your own addresses for you memory. Windows will handle the translation to Physical Memory for you. This is how processes are walled off from each other and can’t accidently (or intentionally) access the memory of another process. The idea of virtual memory is so ingrained in computing that this is also implemented in the processor, which physically will prevent access to physical memory addresses for a running process.

Kernel Mode and User Mode

This is also a feature of all modern operating systems where there are 2 modes of execution (this is also physically implemented on the processor). These 2 modes are Kernel mode and User mode. All applications you run operate in User mode. Think of it as a protected sandbox with virtual memory and many separation of processes. Also, when programs crash in User mode that process terminates, but nothing else does (the system remains stable).

In Kernel mode the opposite is true. This is the “wild-west” of code where process sandboxing and virtual memory don’t exist. This is usually where the core of an operating system exists as well as drivers and code that has to interact with physical hardware directly. This is a mode that any error will cause a blue-screen on Windows and force an immediate reboot of the system.

Windows APIs

Microsoft provides a massive amount of APIs to utilize Windows functionality to make programming simpler as well as access resources like files, USB devices, cameras, etc. This vast repository of APIs has several layers in which some API calls serve as a broker to Kernel mode in order to access resources and processes that User mode can’t access (like physical hardware, other processes, or shared memory). This is also designed to allow for communications between processes to happen without requiring networking (like Named Pipes).

ASLR

ASLR is memory address space randomization. What this really means is that when you load DLLs into memory they are loaded into an address space designated by the operating system. As a security feature, the first time a DLL is loaded the location at which it is loaded to in Virtual Memory is different (per system boot). The reason this happens only with a reboot is related to memory management and the ability to share the physical memory a DLL occupies to multiple instances of that DLL. This allows for a much lower physical memory footprint on a system. Again, this is a function of all modern operating systems and it is all implemented similarly.

One side-benefit of ASLR is that if you know the memory address of a function for a DLL in one process you know that address for all processes of the same “bitness”.

DLL Loading Initialization

In Windows, when a DLL is loaded it will have an initialization function that is called (think of it as the constructor for the DLL). This allows you as a programmer to execute code upon the loading of a DLL into memory of any process.

Putting It All Together

With that very brief Windows internals overview, now we can get back to DLL injection.

Targeting a Process

The first step is to target a process you want to execute code under. In the example I will use notepad since it is simple and has no major impacts on the system if it crashes (keep in mind you can inject to explorer and all other process you own so you can cause some serious session issues if you crash those processes).

By default all C++ projects are 32bit so we will target notepad as 32bit (on a 64bit machine this means you need to run notepad.exe from c:windowssyswow64notepad.exe and not c:windowsnotepad.exe.

Writing a DLL

Inside of Visual Studio create a new C++ Win32 Project. You can name it anything you want, but make sure you choose DLL on the wizard when it asks the type of project.

Once the project is created you will see a simple file structure and a file called dllmain.cpp. Open that file and paste the following code inside the DLLMain function:

 switch (ul_reason_for_call)
  {
  case DLL_PROCESS_ATTACH:
  MessageBox(NULL, L"Injected DLL Example", L"Injected!", MB_OK);
 }

  case DLL_THREAD_ATTACH:
  case DLL_THREAD_DETACH:
  case DLL_PROCESS_DETACH:
         break;  
 }  
 return TRUE;

Compile the DLL and copy it to c:Temp.

Writing the Injector Process

The next step is to write the helper application to inject the DLL into notepad. This is done by creating a new C++ Project (or adding it to the existing solution) and choosing console application as the type (instead of DLL like we did before).

Inside of this file we are going to paste the following code inside of Main:

 char* buffer = "C:\Temp\Inject.DLL";
 DWORD procID = 0;
  procID = GetLowestProcessIDByName(AppName);
  printf("Found Process ID: %d n", procID);

  /*
   * Get process handle passing in the process ID.
   */

  HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);

  if (process == NULL) {
    printf("Error: the specified process couldn't be found.n");
  }

  /*
   * Allocate new memory region inside the process's address space.
   */

  LPVOID arg = (LPVOID)VirtualAllocEx(process, NULL, strlen(buffer), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

  if (arg == NULL) {
    printf("Error: the memory could not be allocated inside the chosen process.n");
  }

  /*
   * Write the argument to LoadLibraryA to the process's newly allocated memory region.
   */

  int n = WriteProcessMemory(process, arg, buffer, strlen(buffer), NULL);

  if (n == 0) {
    printf("Error: there was no bytes written to the process's address space.n");
  }

  /*
   * Inject our DLL into the process's address space.
   */

  HANDLE threadID = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, arg, NULL, NULL);

  if (threadID == NULL) {
    printf("Error: the remote thread could not be created.n");
  }
  else {
    printf("Success: the remote thread was successfully created.n");
  }

  /*
   * Close the handle to the process, because we've already injected the DLL.
   */

  CloseHandle(process);

  getchar();

  return 0;

/* Make note to update the DWORD PID = 0; line to be the process id of the notepad instance you are running. */
/* Finally, add the following line to stdafx.h: */

#include

Compile this application and then run it.

Results

What you will see is a message box appear in notepad and notepad’s context will be frozen until you respond to the message box. Once you see this you will have successfully injected your first code into another process. Please note that to see the messagebox again you will need to restart notepad and update the PID in the application.

Summary

With this very basic code you can now begin to explore the ability to execute your own code within other processes. However, keep in mind that injecting some processes will make life difficult as when they crash you loose access items such as windows explorer. Also keep in mind that this technique is used for nefarious purposes along with beneficial ones. It is up to you to use this knowledge responsibly and not for nefarious purposes.

Sign up to receive the latest insights.

We promise not to spam you and only send the good stuff!

  • Should be Empty: