DLL – Random IT Utensils https://blog.adamfurmanek.pl IT, operating systems, maths, and more. Sat, 02 Jan 2021 19:12:42 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 DLL Injection Part 6 — Injecting managed code into specific App Domain https://blog.adamfurmanek.pl/2017/04/22/dll-injection-part-6/ https://blog.adamfurmanek.pl/2017/04/22/dll-injection-part-6/#comments Sat, 22 Apr 2017 08:00:01 +0000 https://blog.adamfurmanek.pl/?p=2119 Continue reading DLL Injection Part 6 — Injecting managed code into specific App Domain]]>

This is the sixth part of the DLL Injection series. For your convenience you can find other parts in the table of contents in Part 1 – Registry

In Part 4 we saw how to inject managed DLL and execute some code in default App Domain. Today we will inject some code into different app domains. Actually, this is not tied to DLL injection (because we might want to do this in our application as well).

Listing App Domains

If you check reflection mechanism you will see that it is possible to list all type members (fields, methods, properties), all assembly types, all assemblies of app domain, but there is no simple way to list of app domains of a process. So how do we do it?

We know that .NET exe file can be executed as an ordinary Windows application. This is because it is in fact ordinary application. What it does at start is initializing .NET platform host and loading actual .NET code. We can use this host to list domains. This code does it (.NET 4, it requires COM reference to mscoree.tlb):

public static class CLRUtil
{
	public static IEnumerable<_AppDomain> EnumAppDomains()
	{
		// Obtain ICLRMetaHost interface
		object objHost;
		int hr = CLRCreateInstance(ref CLSID_CLRMetaHost, ref IID_CLRMetaHost, out objHost);
		if (hr < 0) throw new COMException("Cannot create meta host", hr);
		var host = (ICLRMetaHost)objHost;

		// Obtain ICLRRuntimeInfo interface
		var vers = Environment.Version;
		var versString = string.Format("v{0}.{1}.{2}", vers.Major, vers.Minor, vers.Build);
		var objRuntime = host.GetRuntime(versString, ref IID_CLRRuntimeInfo);
		var runtime = (ICLRRuntimeInfo)objRuntime;
		bool started;
		uint flags;
		runtime.IsStarted(out started, out flags);
		if (!started) throw new COMException("CLR not started??");

		// Obtain legacy ICorRuntimeHost interface and iterate appdomains
		var V2Host = (ICorRuntimeHost)runtime.GetInterface(ref CLSID_CorRuntimeHost, ref IID_CorRuntimeHost);
		IntPtr hDomainEnum;
		V2Host.EnumDomains(out hDomainEnum);
		for (;;)
		{
			_AppDomain domain = null;
			V2Host.NextDomain(hDomainEnum, out domain);
			if (domain == null) break;
			yield return domain;
		}
		V2Host.CloseEnum(hDomainEnum);
	}
}

If we don’t want to reference mscoree.tlb, we can use the following code:

public static class CLRUtil
	public static IList<AppDomain> EnumAppDomains()
	{
	    IList<AppDomain> _IList = new List<AppDomain>();
	    IntPtr enumHandle = IntPtr.Zero;
	    ICorRuntimeHost host = new CorRuntimeHost();
	    try
	    {
	        host.EnumDomains(out enumHandle);
	        object domain = null;
	        while (true)
	        {
	            host.NextDomain(enumHandle, out domain);
	            if (domain == null) break;
	            AppDomain appDomain = (AppDomain)domain;
	            _IList.Add(appDomain);
	        }
	        return _IList;
	    }
	    catch (Exception e)
	    {
	        Console.WriteLine(e.ToString());
	        return null;
	    }
	    finally
	    {
	        host.CloseEnum(enumHandle);
	        Marshal.ReleaseComObject(host);
	    }
	}

	private static Guid CLSID_CLRMetaHost = new Guid(0x9280188d, 0xe8e, 0x4867, 0xb3, 0xc, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde);
	private static Guid IID_CLRMetaHost = new Guid(0xD332DB9E, 0xB9B3, 0x4125, 0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16);
	private static Guid IID_CLRRuntimeInfo = new Guid(0xBD39D1D2, 0xBA2F, 0x486a, 0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91);
	private static Guid CLSID_CorRuntimeHost = new Guid(0xcb2f6723, 0xab3a, 0x11d2, 0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e);
	private static Guid IID_CorRuntimeHost = new Guid(0xcb2f6722, 0xab3a, 0x11d2, 0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e);

	[DllImport("mscoree.dll")]
	private static extern int CLRCreateInstance(ref Guid clsid, ref Guid iid,
		[MarshalAs(UnmanagedType.Interface)] out object ptr);

	[ComImport, Guid("D332DB9E-B9B3-4125-8207-A14884F53216"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	private interface ICLRMetaHost
	{
		[return: MarshalAs(UnmanagedType.Interface)]
		object GetRuntime(string version, ref Guid iid);
		// Rest omitted
	}

	[ComImport, Guid("BD39D1D2-BA2F-486a-89B0-B4B0CB466891"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	private interface ICLRRuntimeInfo
	{
		void GetVersionString(char[] buffer, int bufferLength);
		void GetRuntimeDirectory(char[] buffer, int bufferLength);
		bool IsLoaded(IntPtr hProcess);
		void LoadErrorString(uint id, char[] buffer, int bufferLength, int lcid);
		void LoadLibrary(string path, out IntPtr hMdodule);
		void GetProcAddress(string name, out IntPtr addr);
		[return: MarshalAs(UnmanagedType.Interface)]
		object GetInterface(ref Guid clsid, ref Guid iid);
		bool IsLoadable();
		void SetDefaultStartupFlags(uint flags, string configFile);
		void GetDefaultStartupFlags(out uint flags, char[] configFile, int configFileLength);
		void BindAsLegacyV2Runtime();
		void IsStarted(out bool started, out uint flags);
	}

	[ComImport, Guid("CB2F6722-AB3A-11d2-9C40-00C04FA30A3E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	private interface ICorRuntimeHost
	{
		void CreateLogicalThreadState();
		void DeleteLogicalThreadState();
		void SwitchinLogicalThreadState(IntPtr cookie);
		void SwitchoutLogicalThreadState(out IntPtr cookie);
		void LocksHeldByLogicalThread(out int count);
		void MapFile(IntPtr hFile, out IntPtr address);
		void GetConfiguration(out IntPtr config);
		void Start();
		void Stop();
		void CreateDomain(string name, object identity, out _AppDomain domain);
		void GetDefaultDomain(out _AppDomain domain);
		void EnumDomains(out IntPtr hEnum);
		void NextDomain(IntPtr hEnum, out _AppDomain domain);
		void CloseEnum(IntPtr hEnum);
		// rest omitted
	}
}

And this allows us to list all domains.

Injecting code

In order to execute code in different app domain, we need to create object there. The simplest way is to do it like this:

foreach (var d in CLRUtil.EnumAppDomains())
{
	Log("AppDomain " + d.FriendlyName);
	try
	{
		var o = d.CreateInstance(@"MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "MyAssembly.Class1");
		Log("Object in remote app domain created");
	}
	catch (Exception exception)
	{
		Log(exception.Message);
	}
}

and now in constructor of Class1 we can do anything we want because we are already in target app domain.

]]>
https://blog.adamfurmanek.pl/2017/04/22/dll-injection-part-6/feed/ 1
DLL Injection Part 5 — Hacking Minesweeper https://blog.adamfurmanek.pl/2016/10/15/dll-injection-part-5/ https://blog.adamfurmanek.pl/2016/10/15/dll-injection-part-5/#comments Sat, 15 Oct 2016 08:00:52 +0000 https://blog.adamfurmanek.pl/?p=1842 Continue reading DLL Injection Part 5 — Hacking Minesweeper]]>

This is the fifth part of the DLL Injection series. For your convenience you can find other parts in the table of contents in Part 1 – Registry

In previous posts of this series we saw how to inject DLLs in different ways. Today we are going to write simple DLL to hack Windows XP Minesweeper!

This post is based on Playing with Minesweeper post which described how to hack the game using WinDBG. We are going to do exactly the same, but with DLL injection.

Code

You can find detailed description somewhere else, so today I will just move on to the code:

#include "stdafx.h"
#include < cstdio >
#include < windows.h >
#include < string >

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:{
							char *PEB = NULL;
							__asm
							{
								mov eax, fs:[0x30]
								mov PEB, eax
							}

							

							char *target = (char*)((*(int*)((int)PEB + 8)) + 0x36FA);
							char buffer[] = { (char)0xC6, (char)0x00, (char)0x8A };
							// eb poi(@$peb + 0x8) + 0x36fa c6 00 8a - shows mines
							// eb poi(@$peb + 0x8) + 0x36fa c6 00 8e - flags mines

							SIZE_T written;
							WriteProcessMemory(GetCurrentProcess(), target, &buffer, 3, &written);
							break;
	}
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

We are basically doing exactly the same thing as we would do in WinDBG. Interesting part is how to access Process Environment Block using asm snippet, but it is not something you would do in production.

In order to get it working, you need to inject the DLL at start of application, so you probably would like to use registry injection — remember that injection via registry injects library to all processes! As this is just for demonstration purposes, there is little to no error handling.

However, there is one little surprise in the application, but you need to run it in order to find out what’s going on.

]]>
https://blog.adamfurmanek.pl/2016/10/15/dll-injection-part-5/feed/ 1
DLL Injection Part 4 — Injecting managed DLL https://blog.adamfurmanek.pl/2016/04/16/dll-injection-part-4/ https://blog.adamfurmanek.pl/2016/04/16/dll-injection-part-4/#comments Sat, 16 Apr 2016 08:00:09 +0000 https://blog.adamfurmanek.pl/?p=1624 Continue reading DLL Injection Part 4 — Injecting managed DLL]]>

This is the fourth part of the DLL Injection series. For your convenience you can find other parts in the table of contents in Part 1 – Registry

Las time we saw how to inject DLL into target process using remote threads. However, throughout this whole series we were injecting only native DLL, e.g., libraries compiled to native code. Today we will inject managed library.

Test program

We will use the following program to demonstrate the library injection:

using System;
using System.Threading;

namespace ExceptionThrower
{
   class Program
   {
      static void Main(string[] args)
      {
         while (true)
         {
            try
            {
               Console.WriteLine("Throwing exception: " + DateTime.Now);
               throw new Exception();
            }
            catch (Exception e)
            {
               Console.WriteLine("Handling exception in catch");
            }
            Thread.Sleep(2000);
         }
      }
   }
}

We throw exception every two seconds and immediately handle it with catch clause. We will use this program to demonstrate the DLL injection. We will inject a library which will handle first chance exception and print to console so we will indeed see that our code is executing in the same application domain as the code above. This is only a sample program and in general we do not require our target process to be .NET application, we will use this only for demonstration purposes.

Executing .NET code

First, we need to obtain a handle for .NET framework loaded into the process. You can load .NET into any native application using CLRCreateInstance function. You can have only one .NET framework loaded into the process. We will use this function to obtain a handle for already loaded framework (since our test application is written in C#). Once it’s loaded we can execute any managed code using ExecuteInDefaultAppDomain method.

The plan looks easy at first sight. We simply inject native DLL, next in DLLMain we get a handle for .NET framework, and finally we execute a piece of code. It looks good, however, we need to very careful when executing code in DLLMain method because of loader lock. Loading any library in this method results in a deadlock. And we probably need to load libraries in order to load .NET framework.

However, we can use very similar approach. Basically, we will create two remote threads: first one will load our native dll, the second one will load .NET and execute managed code. Let’s see some code.

Native DLL

We start with the native library. First, the code for loading .NET runtime:

__declspec(dllexport) resultESULT ImplantDotNetAssembly(_In_ LPCTSTR lpCommand)
{
	resultESULT result;
	ICLRMetaHost *metaHost = NULL;
	ICLRRuntimeInfo *runtimeInfo = NULL;
	ICLRRuntimeHost *runtimeHost = NULL;

	// Load .NET
	result = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&metaHost));
	wprintf_s((L"CLR Instance: " + std::to_wstring(result == S_OK ? 1 : 0) + L"\n").c_str());
	
	// Replace .NET version with the one you want to load (or which is already loaded)
	result = metaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&runtimeInfo));
	wprintf_s((L"Runtime: " + std::to_wstring(result == S_OK ? 1 : 0) + L"\n").c_str());
	
	result = runtimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&runtimeHost));
	wprintf_s((L"Runtime info: " + std::to_wstring(result == S_OK ? 1 : 0) + L"\n").c_str());

	// Start runtime
	result = runtimeHost->Start();	
	wprintf_s((L"Runtime started: " + std::to_wstring(result == S_OK ? 1 : 0) + L"\n").c_str());

	// Execute managed assembly
	DWORD returnValue;
	result = runtimeHost->ExecuteInDefaultAppDomain(
		"Path do managed DLL", 
		"Type containing method to run", 
		"Method to run", 
		"Argument to pass", 
		&returnValue);
	wprintf_s((L"Function executed: " + std::to_wstring(returnValue)).c_str());

	// Unload .NET
	result = runtimeHost->Stop();

	// free resources
	metaHost->Release();
	runtimeInfo->Release();
	runtimeHost->Release();

	return result;
}

We will inject this library and execute the ImplantDotNetAssembly method. You can use the code from previous part to inject the library. Now we need to get the address of ImplantDotNetAssembly method in the target address space. See the code below:

// Load library into this process
HMODULE libraryAddress = LoadLibrary("Native library.dll");

// Get address of function to invoke
void* functionAddress = GetProcAddress(libraryAddress , "ImplantDotNetAssembly");

// Compute the distance between the base address and the function to invoke
DWORD_PTR offset = (DWORD_PTR)functionAddress - (DWORD_PTR)libraryAddress ;

// Unload library
FreeLibrary(libraryAddress );

// return the offset to the function
return offset;

We load native library into our process and calculate the offset of the function. We assume that the offset will be the same in the target process. Now we need to get the handle for library in the target process:

MODULEENTRY32 moduleEntry; 
HANDLE snapshot = INVALID_HANDLE_VALUE;

// Get snapshot of all modules in the remote process 
moduleEntry.dwSize = sizeof(MODULEENTRY32); 
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);

if (!Module32First(snapshot, &moduleEntry)) 
{
	CloseHandle(snapshot);
	return 0;
}

// Find the module which we look for
while (wcscmp(moduleEntry.szModule, "Native library.dll") != 0 && Module32Next(snapshot, &moduleEntry));

// close the handle
CloseHandle(snapshot);

// check if module handle was found and return it
if (wcscmp(moduleEntry.szModule, "Native library.dll") == 0)
	return (DWORD_PTR)moduleEntry.modBaseAddr;

return 0;

Finally, we add offset to the module base address and use it as a function address for remote thread. We create remote thread as usually.

Injected managed code

All we need to do is write a managed library which will handle first chance exception. We can use the following code:

using System;

namespace ExceptionHandler
{
    public class Handler
    {
       public static int AddHandler(string arg)
       {
            AppDomain.CurrentDomain.FirstChanceException += (sender, args) => Console.WriteLine("First Chance exception!");
            return 1;
       }
    }
}

As we can see, our function accepts one string argument and returns int so it can be executed. We simply call this function from our native library and we should see that the lambda is called every time an exception is thrown.

Summary

As we can see, we are able to inject both native and managed libraries into other processes. We are also able to choose application domain for our injected code thanks to API for loading .NET into a process. In fact, you can configure many more options when loading framework: you can implement custom memory manager or override routines for creating .NET threads. However, today we only inject managed code.

]]>
https://blog.adamfurmanek.pl/2016/04/16/dll-injection-part-4/feed/ 1
DLL Injection Part 3 — Creating remote thread https://blog.adamfurmanek.pl/2016/04/09/dll-injection-part-3/ https://blog.adamfurmanek.pl/2016/04/09/dll-injection-part-3/#comments Sat, 09 Apr 2016 08:00:33 +0000 https://blog.adamfurmanek.pl/?p=1618 Continue reading DLL Injection Part 3 — Creating remote thread]]>

This is the third part of the DLL Injection series. For your convenience you can find other parts in the table of contents in Part 1 – Registry

We already know how to inject a DLL into process using registry or hooks. With the former approach we cannot control the moment of injection (because it usually happens when the process is starting) whilst with the latter approach we cannot choose the target process (since our library is called to every process). Today we will see yet another method. This time we will be able to precisely choose the target process and the time of injection.

Loading library

In order to load library during runtime we can call LoadLibrary or LoadLibraryEx method and pass it a path to the library. What if we could do the same but in another process? Fortunately, Windows gives us an option to execute code in other process using CreateRemoteThread function. Basically, we are able to start a thread in other process.

Every thread requires a thread function: a piece of code which newly spawned thread will execute. This method can accept at most one parameter. How can we use this to inject DLL? Well, we can create remote thread and point its thread function to LoadLibrary in target process. However, we need to be able to pass a path to the library as a parameter. We cannot simply allocate memory using malloc because this method allocates memory in our process.

There is a method which is able to allocate memory in other process: VirtualAllocEx. We can use it to allocate some bytes in target address space and write there a DLL path using WriteProcessMemory function. This should work good.

So the plan looks as follows: we allocate memory in the target process and fill it with the path to the DLL. Next, we get the address of LoadLibrary function and use it as a thread function for thread created in remote process. Let’s see some code.

Code

The code should be pretty obvious:

BOOL ok = FALSE; // Assume that the function fails
HANDLE process = NULL, thread = NULL;
PWSTR memory = NULL;

__try {
  // Get a handle for the target process
  process = OpenProcess(
	 PROCESS_QUERY_INFORMATION |   // Required by Alpha
	 PROCESS_CREATE_THREAD     |   // For CreateRemoteThread
	 PROCESS_VM_OPERATION      |   // For VirtualAllocEx
	 PROCESS_VM_WRITE,             // For WriteProcessMemory
	 FALSE, targetProcessId);
  if (!process) __leave;

  // Calculate the number of bytes required to store DLL path
  int numberOfCharacters = 1 + lstrlenW("Path to DLL");
  int numberOfBytes  = numberOfCharacters * sizeof(wchar_t);

  // Allocate memory
  memory = (PWSTR) VirtualAllocEx(process, NULL, numberOfBytes, MEM_COMMIT, PAGE_READWRITE);
  if (!memory) __leave;

  // Write path to DLL
  if (!WriteProcessMemory(process, memory, (PVOID) pszLibFile, numberOfBytes, NULL)) __leave;

  // Get the address of LoadLibraryW in Kernell32.dll
  PTHREAD_START_ROUTINE loadLibraryFunction = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
  if (!loadLibraryFunction) __leave;

  // Create a remote thread 
  thread = CreateRemoteThread(process, NULL, 0, loadLibraryFunction, memory, 0, NULL);
  if (!thread) __leave;

  // Wait for the remote thread to terminate
  WaitForSingleObject(thread, INFINITE);

  // We are done
  ok = true; 
}
__finally { 
  // Cleanup
  if (memory) VirtualFreeEx(process, memory, 0, MEM_RELEASE);
  if (thread) CloseHandle(thread);
  if (process) CloseHandle(process);
}

return ok;

Summary

We are now able to inject DLL into the process we choose and when we decide. Next time we will use this method to inject managed (.NET) DLL.

]]>
https://blog.adamfurmanek.pl/2016/04/09/dll-injection-part-3/feed/ 1
DLL Injection Part 2 — Hooks https://blog.adamfurmanek.pl/2016/04/02/dll-injection-part-2/ https://blog.adamfurmanek.pl/2016/04/02/dll-injection-part-2/#comments Sat, 02 Apr 2016 08:00:18 +0000 https://blog.adamfurmanek.pl/?p=1616 Continue reading DLL Injection Part 2 — Hooks]]>

This is the second part of the DLL Injection series. For your convenience you can find other parts in the table of contents in Part 1 – Registry

Last time we saw how to inject DLL using registry. Today we are going to implement another approach using Hooks.

Hooks

Hooks allow us to execute a piece of code when specified type of message is sent to window. For instance, we might want to implement keylogger which would register all pressed keys without modifying them. Since there is an option to register global hook — e.g., hook which will execute in every process in the system — we can use this approach to inject our custom logic. Of course “every process in the system” means only processes which we can access easily, we will not overcome the UAC or hack system this way.

Code

Let’s assume that we have the following method:

extern "C" __declspec(dllexport) int kbhook(int code, WPARAM wParam, LPARAM lParam) {
	// Do something with keyboard message
	return CallNextHookEx(NULL, code, wParam, lParam);
}

We receive three arguments describing the message, do something with them, and finally call method CallNextHookEx which passes the hook information to the next method in the hook chain. We need to compile this code and output DLL library. Let’s call it KeyboardHook.dll. Now we can inject the routine using the following code:

#include "stdafx.h"
#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
	// Load library in which we'll be hooking our functions.
	HMODULE dll = LoadLibrary(L"KeyboardHook.dll");
	if(!dll) {
		printf("The DLL could not be found.\n");
		return -1;
	}

	// Get the address of the function inside the DLL./
	HOOKPROC address = (HOOKPROC)GetProcAddress(dll, "kbhook");
	if(!address) {
		printf("The function was not found.\n");
		return -2;
	}

	// Hook the function.
	HHOOK handle = SetWindowsHookEx(WH_KEYBOARD, address, dll, 0);
	if(!handle) {
		printf("The KEYBOARD could not be hooked.\n");
		return -3;
	}

	printf("Program successfully hooked. Press enter to unhook the function and stop the program.");
	getchar();

	// Unhook the function.
	UnhookWindowsHookEx(handle);

	return 0;
}

When we compile the code, put DLL into correct place (see “search order” in previous part), and execute it, we should successfully hook our code. To verify this, try to press any key on the keyboard and our code from DLL should execute. It should work across the processes, so it should execute when you type in notepad as well.

Summary

As we can see, this method is pretty easy. All we need to do is to prepare custom routine for handling Windows messages and execute a piece of code to register the hook. We are also able to inject the code into already started process. However, there are drawbacks: we do not control into which processes we inject our code (because it is injected into every program), we need to rely on windows messages what sometimes might be difficult (e.g., when passing a keyboard message to windows service), also anti-viruses sometimes blocks this method.

]]>
https://blog.adamfurmanek.pl/2016/04/02/dll-injection-part-2/feed/ 1
DLL Injection Part 1 — Registry https://blog.adamfurmanek.pl/2016/03/26/dll-injection-part-1/ https://blog.adamfurmanek.pl/2016/03/26/dll-injection-part-1/#comments Fri, 25 Mar 2016 23:00:26 +0000 https://blog.adamfurmanek.pl/?p=1602 Continue reading DLL Injection Part 1 — Registry]]>

This is the first part of the DLL Injection series. For your convenience you can find other parts using the links below :
Part 1 — Registry
Part 2 — Hooks
Part 3 — Creating remote thread
Part 4 — Injecting managed DLL
Part 5 — Hacking Minesweeper
Part 6 — Injecting managed code into specific App Domain
Part 7 — Injecting Java code

If you are interested in the topic see the talk page

Hello. In this post I start a new series in which we will see some methods of injecting DLLs. Today we start with an introduction and a few simple methods.

Introduction

Why would you want to inject DLL? Usually DLL injection is used for running custom code within the address space of another process. There are multiple applications which do that:

  • ConEmu uses this technique to hijack console windows
  • EMET loads its custom DLL to enhance application security
  • Sophos injects its libraries to protect system
  • ForceBindIP loadss its DLLs to modify sockets behavior

Usually we want to inject our library to modify behavior of some system APIs, simplify reading and writing memory, add plugins, or to control another application.

Requirements

In order to execute our code within different process we need to consider some basics.

First, we are not going to hack a machine. We assume that we control the system (however, we might not have an administrator privileges) and we are able to freely save files, run applications, and execute code we like.

We do not want to modify existing application’s source code, especially we do not want to recompile it. We want to start application normally and then inject our code.

We need to remember about virtual memory. Every application runs in its own memory address space and, from its point of view, the memory is linear. The process doesn’t care about physical mapping of memory frames, however, we need to be aware of this process.

We want the process to be clean and reliable.

Virtual Address Space

Every process run in its own address space and memory is divided into partitions. On x86 architecture there are four of them:

  • NULL-Pointer assignment
  • User-Mode
  • 64KB Off-Limits
  • Kernel-Mode

Below are ranges of these partitions:

    \begin{gather*} \label{tab:sets} \begin{tabular}{|c|c|c|} \hline Partition & 32-Bit Windows & 32-Bit Windows with 3 GB User-Mode\\ \hline NULL-Pointer & 0x00000000 - 0x0000FFFF & 0x00000000 - 0x0000FFFF\\ \hline User-Mode & 0x00010000 - 0x7FFEFFFF & 0x00010000 - 0xBFFEFFFF \\ \hline 64-KB Off-Limits & 0x7FFF0000 - 0x7FFFFFFF & 0xBFFFF0000 - 0xBFFFFFFF \\ \hline Kernel-Mode & 0x80000000 - 0xFFFFFFFF & 0xC0000000 - 0xFFFFFFFF  \\ \hline \end{tabular} \end{gather*}

Ranges for x64:

    \begin{gather*} \label{tab:sets} \begin{tabular}{|c|c|} \hline Partition &  64-Bit Windows \\ \hline NULL-Pointer &  0x00000000'00000000 - 0x00000000'0000FFFF\\ \hline User-Mode &  0x00000000'00010000 - 0x000007FF'FFFEFFFF\\ \hline 64-KB Off-Limits &  0x000007FF'FFFF0000 - 0x000007FF'FFFFFFFF\\ \hline Kernel-Mode &  0x00000800'00000000 - 0xFFFFFFFF'FFFFFFFF \\ \hline \end{tabular} \end{gather*}

As we can see, process thinks that its memory is linear. It is up to the operating system how the actual frames of memory are stored — they can be in RAM, paged to disk, mapped to files etc.

In order to load and execute code from library, process needs to load the library into its address space. Every binary (executable or library) has its preferred address meaning where the binary would like to be loaded. It is important because if we load binary to different place we need to rebase the binary. We also need to remember that the same library can be loaded into different address in different processes. There is also a mechanism for randomizing address space layout.

So when we load our code we need to be very careful. We cannot just reference memory by addresses blindly because they can differ between processes.

Global Loader Lock

During execution of LoadLibrary your application holds a lock. Because of this there are things which should not be done when loading library. This means that even if we are able to load our library, we cannot execute every code during initialization and we need to take another actions. We will see some details in next parts of this series.

Search order

When process wants to load a library, the following places are searched for matching file:

  1. The directory containing the executable image file
  2. The Windows system directory returned by GetWindowsDirectory function
  3. The 16-bit system directory (System subfolder under the Windows directory)
  4. The Windows directory returned by GetSystemDirectory
  5. The process’ current directory
  6. The directories listed in the PATH environment variable

We can change order of last two steps in registry.

This gives us first method of injecting our code into another process: we simply replace the DLL file stored on the disk with our library, which should export methods with the same signatures as the original library.

Registry

There is an easy way to inject DLL into a process which loads User32.dll. During initialization User32.dll looks into the registry and checks if HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\LoadAppInit_DLLs is set to 1. If it is so, then library loads all DLLs listed in HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs. So we can just add our DLL in the registry and have it loaded into every process which loads User32.dll. Good news is: most of Windows applications loads this library so it is fairly common.

Of course there are shortcomings of this method. First, we need to have an administrator privileges in order to modify the registry. What’s more, our library is loaded into every process loading system library, so we need to make sure that we are executing code in a correct process. Finally, we cannot control the moment of library injection and we are unable to load our library into already running process.

Injecting into spawned process

There is another way using registry. If we add library to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDLLs key, our library will be loaded into every process that calls the Win32 API functions like CreateProcess. Shortcomings are almost the same as in the previous method.

Summary

In the next part we will see another method based on system hooks.

]]>
https://blog.adamfurmanek.pl/2016/03/26/dll-injection-part-1/feed/ 6