Logo
blank Skip to main content

Securing Your Windows Solutions from DLL Injection Attacks [With Examples]

C#

Any Windows application that handles sensitive data is a potential target for cyber attacks like dynamic link library (DLL) injection. By injecting malicious code into software, attackers can change the way processes behave to turn off security measures, steal data, or harm software.

Thus, taking relevant protection mechanisms into consideration during software development is a must. Protecting your software from DLL injection attacks can help your team reduce the risk of product hacking, preventing unjustified spending of money and time on handling incidents and mitigating reputational losses.

This article covers the DLL injection workflow, consequences of DLL injection, as well as nuances of detection and prevention. In the practical part, we show two simple but unique DLL injection attack examples and how to use popular API hooking libraries and a protection mechanism to prevent such attacks. Youโ€™ll also see a real-life example of how DLL injection can be detected and countered within a running process monitoring application.

This article will be helpful for project and development leaders working on Windows applications who want to enhance protection against cybersecurity attacks.

What is a DLL injection attack?

Malicious actors can use many methods to hack or change the behavior of your software, aiming to steal sensitive data, request a ransom, or inject malware for some other reason.

A common approach to hacking Windows software is DLL injection โ€” inserting a DLL into the address space of a running process to execute code. When a DLL is injected, it becomes part of the target process. To inject a malicious DLL, attackers often use API hooking to add code that intercepts and modifies the behavior of existing functions within an application.

DLL injection attacks donโ€™t require access to the target applicationโ€™s source code. This approach is common in Trojan horse malware that changes the behavior of running applications. For instance, DLL injection and API hooking can be used to hide some processes that the user should not see or cannot close. This way, an attacker, for example, can use the victimโ€™s system to mine cryptocurrency. When applying both techniques simultaneously, hackers can integrate suspicious code into almost any existing process and gain control over it.

By inserting malicious code into a legitimate process, attackers can execute arbitrary code with the same privilege as the target process. The security consequences of DLL injection include:

Security consequences of DLL injection attacks
  • Access sensitive information from the host and steal credentials, credit card information, and other personal data.
  • Crash a target process or potentially cause system-wide crashes or instability.
  • Gain unauthorized access and modify or turn off security measures.

Ready to secure your solution?

Enhance the protection of your Windows software by leveraging Aprioritโ€™s expertise and experience in cybersecurity, C#, and Windows development.

The good and the bad of DLL injection

Detecting malicious DLL injections is a challenging task, especially since you need to understand the context and intent behind each injection.

On the one hand, this technique can be used for legitimate and illegitimate purposes. Initially, Remote library injection was introduced as an exploitation technique in 2004 by Skape and JT [PDF]. Later, more libraries appeared allowing programmers to quickly and easily inject their DLL and set hooks on an API function.

Here are a few examples of using DLL injection for good:

  • Software applications often use DLL injection as a legitimate means to extend a programโ€™s functionality via plugins or extensions.
  • Security specialists might alter system behavior to investigate internal processes and detect suspicious and dangerous code.
  • Developers use DLL injection during debugging and profiling to analyze and monitor the behavior of running processes.
  • Security software, like antivirus or intrusion detection systems, may inject DLLs into processes to monitor and protect against malicious activity.

On the other hand, unwanted DLL injection is dangerous:

  • Malware often uses DLL injection to add malicious code to legitimate processes, changing the solutionโ€™s behavior.
  • Hackers can use DLL injection as part of an attack to gain unauthorized access to a system.
  • Game cheaters might use DLL injection to modify the behavior of games to gain unfair advantages.

To monitor unauthorized DLL injection in programs, organizations have developed various antivirus solutions and game anti-cheat software. For example, Microsoft Defender Antivirus in Windows scans DLLs and executables and flags malicious ones. But you shouldnโ€™t entirely rely on such solutions, as they might not identify certain malicious injection techniques. One reason is that every time cybersecurity experts come up with a solution to prevent illegal DLL injection, attackers find ways to bypass it.

Before we move to showing methods for protecting against DLL injection, letโ€™s briefly explore how DLL injection works.

How DLL injection attacks work

To effectively detect and mitigate attacks, itโ€™s crucial to understand the logic behind the injection process. Letโ€™s overview a common DLL injection workflow and explore which Windows APIs and functions are usually involved in DLL injection attacks.

Hackers can run a DLL injection attack in four steps:

  1. Attach their injection application to the target process
  2. Allocate memory within the process
  3. Upload the DLL data or path to the processโ€™s memory and locate memory addresses appropriate for injection
  4. Instruct the process to execute the injected DLL
How DLL injection attacks work

How does DLL injection work in detail?

First, hackers determine a target for DLL injection. The most popular Windows functions that injection applications can use for this purpose are:

  • Kernel32.dll!CreateToolhelp32Snapshot()
  • Kernel32.dll!Process32First()
  • Kernel32.dll!Process32Next()

These functions read all system processes to find a target process. As a result, the injection application can get all necessary information about the target process.

Next, the injection application can call the following functions to obtain a handle value to access the target process:

  • Kernel32.dll!GetModuleHandle()
  • Kernel32.dll!OpenProcess()

Then, hackers allocate memory for writing the name of the malicious DLL within the memory area of the target process. To do so, malicious actors can use the Kernel32.dll!VirtualAllocEx() function. The process of writing the DLL into allocated memory mainly occurs through the Kernel32.dll!WriteProcessMemory() function call.

Finally, to make the target process execute their code, hackers might use the Kernel32.dll!LoadLibrary function and pass the result to one of these Windows API functions:

  • Kernel32.dll!CreateRemoteThread
  • Ntdll.dll!NtCreateThreadEx
  • ntdll.dll!RtlCreateUserThread

After all of this, a remote process must execute the injected DLL in the target application.

With basic information about the DLL injection workflow in mind, letโ€™s overview a few simple examples of DLL injection apps and a way to protect software from them.

Related project

Improving a Windows Audio Driver to Obtain a WHQL Release Signature

Explore a success story of how we helped IRIS release a secure Windows version of the noise reduction app, obtain WHQL certification, and add localization to reach even more customers across the globe.

Project details

Protection against DLL injection: practical examples

To showcase an example of protecting software from malicious DLL injection, weโ€™ll do the following:

1. Create two unique applications that can inject a DLL into a target application and insert a hook for the Kernel32.dll!OpenProcess function. These apps will simulate Trojan malware that hides the running process named DangerMiner from users. Weโ€™ll develop these injection apps using two different libraries to show different algorithms for DLL injection.

2. Create a mechanism to prevent DLL injection. Youโ€™ll see how this protection program blocks our unique apps that try to inject DLLs.

To demonstrate how these apps work, weโ€™ve created a simple process monitoring program into which weโ€™ll inject the DLL file and set the hook. This program monitors all existing and newly created processes in the system and prints information about them in the console.

We decided to create such a program because injecting a DLL into an existing solutionโ€™s process without permission from its developers or owners can be considered a violation of software terms of use, service agreements, or end-user license agreements. It also might be a breach of intellectual property rights and copyright laws. We believe itโ€™s necessary to respect the boundaries and guidelines set by software developers and the law to ensure ethical and legal behavior.

Note: The injection examples below are pretty simple, focused on only affecting one process in a system. They are created for security research purposes only. Tools like Windows Security Defender (which is built into Windows) can easily detect and prevent such injection attempts.

Hereโ€™s what our custom process monitoring application looks like with the DangerMiner process running:

The DangerMiner process displayed in the process monitoring window
Screenshot 1. The DangerMiner process displayed in the process monitoring window

Now, letโ€™s move to developing two DLL injection apps and a program for protecting against them.

1. Creating a DLL injection app using the Nektra Deviare library

Our first DLL injection program aims to:

  • Inject a DLL called ProcessHidePlugin_Deviare.dll into the target application
  • Hook the Kernel32.dll!OpenProcess function
  • Deny the target application the ability to open the DangerMiner process so a process with this name wonโ€™t appear in our custom process monitoring app

Weโ€™ll use Nektra Deviare, a library written in C++ and built on COM objects that you can use in different programming languages. This library opens a target process, allocates memory, then tries to load a DLL that must be injected.

The C# source code below demonstrates how to inject a DLL into a running process. After a DLL is injected, it will set hooks and start to intercept the target function.

C#
// Constructing a file path to inject a DLL in the running target process. 
string injectionLibrary = Path.Combine( 
	Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), 
	"ProcessHidePlugin_Deviare.dll" 
); 
 
// Opening the target process. 
IntPtr result = WinAPI.OpenProcess(WinAPI.ProcessAccessFlags.AllAccess, false, TargetPID); 
if (result == IntPtr.Zero) 
{ 
	Console.WriteLine("Error: Unable to open process with PID: {0}", TargetPID); 
	Console.WriteLine("<Press any key to exit>"); 
	Console.ReadKey(); 
	return; 
} 
 
// Injecting the DLL into the opened process. 
cHook.InjectDll(TargetPID, injectionLibrary, "", 5000); 
Console.WriteLine("Injected into the target process {0}", TargetPID); 
 
WinAPI.WaitForSingleObject(result, 0xFFFFFFFF);

As a result, the DLL is injected into CustomProcessMonitor.exe, and the DangerMiner process isnโ€™t shown in our process monitor app. Everything works as expected:

Results of DLL injection using the Nektra Deviare library
Screenshot 2. Results of DLL injection using the Nektra Deviare library

You can find the full source code for this project on Aprioritโ€™s GitHub page for ProcessHide_Deviare and on the page for ProcessHidePlugin_Deviare.

Read also

3 Effective DLL Injection Techniques for Setting API Hooks

Find out how the ability to control and manipulate system behavior and API calls can help your team investigate internal processes and detect suspicious and malicious code.

Learn more

2. Building a DLL injection app using the EasyHook library

Our EasyHook-based program aims to:

  • Inject a DLL called ProcessHidePlugin_EasyHook.dll into the target application. It works the same way as our previous application.
  • Hook the Kernel32.dll!OpenProcess function.
  • Prevent the target application from opening a DangerMiner process so that a process with this name wonโ€™t appear in our custom process monitoring app

EasyHook is written in C# and divided into different DLLs like EasyHook.dll and EasyLoad32.dll. Because of that division, the library first loads these DLLs into the target process and only then injects the DLL.

The C# source code below demonstrates how to inject DLL into a running process using the EasyHook library:

C#
RemoteHooking.IpcCreateServer<ProcessHideInterface>(ref ChannelName, WellKnownObjectMode.SingleCall); 
 
// Constructing a file path to inject the DLL in the running target process. 
string injectionLibrary = Path.Combine( 
                	Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "ProcessHidePlugin_EasyHook.dll"); 
 
if (String.IsNullOrEmpty(targetExe)) 
{ 
                	// Injecting the DLL into the target process. 
                	RemoteHooking.Inject( 
                    	TargetPID, 
                    	injectionLibrary, 
                    	injectionLibrary, 
                    	ChannelName); 
 
Console.WriteLine("Injected to process {0}", TargetPID); 
} 
else 
{ 
// Creating the target process and injecting the DLL if the target process isn't running 
RemoteHooking.CreateAndInject(targetExe, "", 0, InjectionOptions.DoNotRequireStrongName, injectionLibrary, injectionLibrary, out TargetPID, ChannelName); 
Console.WriteLine("Created and injected process {0}", TargetPID); 
            	} 
Console.WriteLine("<Press any key to exit>"); 
Console.ReadKey(); 
}

As a result, the DLL is injected into CustomProcessMonitor.exe, and the DangerMiner process isnโ€™t shown in our process monitoring app. The second program also works as intended.

Results of DLL injection using the EasyHook library
Screenshot 3. Results of DLL injection using the EasyHook library

To see the full source code of this app, check out Aprioritโ€™s GitHub pages for ProcessHide_EasyHook and ProcessHidePlugin_EasyHook.

3. Developing a solution for preventing and detecting DLL injection

In previous articles, we proposed methods for protecting against hook and return-oriented programming attacks as well as security techniques to mitigate splicer-type hooking attacks. This time, weโ€™d like to offer another way to safeguard your applications from DLL injection attacks.

As described earlier, to perform DLL injection, hackers can use function calls like Kernel32.dll!LoadLibrary and Kernel32.dll!CreateRemoteThread to upload to and execute DLLs in the target process. Therefore, we came up with the idea of hooking the LoadLibrary function to help us:

  • Detect DLL injection
  • Check whether a DLL injection is unauthorized
  • Deny an unauthorized DLL injection

The LoadLibrary function takes as a parameter the full path to the DLL to be loaded. In this case, we need to figure out how to determine that injection of a particular DLL should be blocked.

To determine that a DLL can be injected, you can use many options like digital signature verification, hash verification, or vendor and source validation.

For the purposes of this article, we decided to use two methods:

  • Digital signature verification
  • Allow-list of trusted folders with DLLs

Every time our application loads a DLL library, we check whether this DLL passes signature validation and is contained in the allow-list folder. After all checks, we can determine with a high degree of probability whether the library is legitimate.

Note that such a technique isnโ€™t comprehensive enough to detect and prevent illegal DLL injection. You can add other checks to more accurately determine the legitimacy of the downloaded DLL as well as hook other calls to prevent malicious injection.

To set protection from illegal DLL injection, letโ€™s use the Microsoft Detours library. Hereโ€™s how it can help us set a hook to the Kernel32.dll!LoadLibrary function call:

C++
DetourRestoreAfterWith(); 
fflush(stdout); 
 
DetourTransactionBegin(); 
DetourUpdateThread(GetCurrentThread()); 
DetourAttach(&(reinterpret_cast<PVOID&>(TrueLoadLibraryExW)), HookLoadLibraryExW); 
const LONG error = DetourTransactionCommit(); 
 
if (error == NO_ERROR) 
{ 
	std::wcout << L"InjectDLLProtectionPlugin.dll - Detoured LoadLibraryExW()." << std::endl; 
} 
else 
{ 
	std::wcout << L"InjectDLLProtectionPlugin.dll - Error detouring LoadLibraryExW(): " << error << std::endl; 
}

After setting the hook, the protection plugin intercepts all LoadLibrary calls. Thus, when LoadLibrary is called, we need to determine whether the loading DLL is legal. In case itโ€™s not, we must deny the function call to prevent DLL injection. Hereโ€™s how to do so:

C++
HMODULE __stdcall HookLoadLibraryExW(IN LPCWSTR lpLibFileName, HANDLE hFile, IN DWORD dwFlags) 
{ 
	const std::wstring libName = std::wstring(lpLibFileName); 
 
	// Checking whether a DLL is in the allowed list or has passed digital signature verification.
	// If any of these checks are passed, the DLL is legitimate and can be injected into the application.
	if (!FileExists(libName) || 
    	IsDllInAllowList(libName) || 
    	VerifyDigitalSignature(libName.c_str())) 
	{ 
    	return TrueLoadLibraryExW(lpLibFileName, hFile, dwFlags); 
	} 
 
	// If a DLL hasnโ€™t passed any of the checks, loading must be blocked.
	fflush(stdout); 
	std::wcout << L"InjectDLLProtectionPlugin.dll - Something is trying to inject DLL: " << libName.c_str() << std::endl; 
	std::wcout << L"InjectDLLProtectionPlugin.dll - Rejected LoadLibrary function call! Application is protected from DLL injection!" << std::endl; 
 
	SetLastError(ERROR_ACCESS_DENIED); 
	return NULL; 
}

To illustrate how such detection and prevention of illegal DLL injection works, letโ€™s try injecting the earlier created DLLs aimed at changing the behavior of our custom process monitoring app.

Here are the results of an attempt to inject a DLL using the Nektra Deviare library:

Failed attempt at injecting a DLL using the Nektra Deviare library
Screenshot 4. Failed attempt at injecting a DLL using the Nektra Deviare library

Letโ€™s also try injecting a DLL using the EasyHook library:

Failed attempt to inject a DLL using the EasyHook library
Screenshot 5. Failed attempt to inject a DLL using the EasyHook library

As you can see, our solution managed to properly detect a DLL injection and block it, informing the user about this malicious attempt. The injection process failed, and the target application continued working as before.

Check out the source code of all these projects on Aprioritโ€™s GitHub page.

You can use the described DLL injection prevention approach for almost every Windows process. Thus, itโ€™s applicable for a wide range of projects, especially those that work with sensitive data and critical processes where protection against unauthorized or malicious code injection is a priority. 

The offered approach wonโ€™t work for some processes: not because of our technique, but because of other reasons such as process design, security, or access rights. For example:

  • Processes designed to be more secure and resistant to tampering, such as antivirus or anti-malware processes, as they employ various security mechanisms to prevent external interference, including DLL injection
  • Processes that require administrator privileges to be attached to or injected into
  • Critical system processes and services that are protected against modification to ensure system stability
  • Universal Windows Platform apps that run in a more isolated environment for security reasons and have restrictions on attaching and manipulating them
  • Processes that enable debugging protection to prevent unauthorized debugging or code injection, which can restrict or disable attaching to, setting hooks, or injecting into those processes

You can use our approach both as a standalone security check and as part of a larger product like a cybersecurity system, antivirus software, game anti-cheat solution, or system monitoring application.

Note that this method doesnโ€™t protect against other common attacks. Also, this is not the primary technique to detect and prevent illegal DLL injection. To get the most out of this method, you might need to add more checks to accurately determine the legitimacy of the downloaded DLL as well as hook some other calls to prevent injections. We believe that such DLL injection protection works well as part of (and can complement) larger protection systems.

Related project

Enhancing the Security and Performance of a Virtual Application Delivery Platform

Discover a real-life example of how we helped Cameo add new features for their VAD platform, improve product performance, and implement complex GFX mode functionality, addressing the needs of end users.

Project details

Tips for enhancing protection against malicious injection

The solution we presented above is a simple example aimed to demonstrate one of many ways to secure your applications and systems from DLL injection attacks. Thus, our proposed solution has its flaws.

Only relying on a folder-based whitelist and signature verification might not provide foolproof protection against all forms of DLL injection or unauthorized DLL loading. A signature verification may return an invalid value for an illegal DLL, or the returned value may be modified by an attacker. Also, sophisticated attackers can find ways to place malicious DLLs in trusted folders, modify the whitelist, or bypass whitelist checks altogether.

To achieve robust protection from DLL injection, your team requires enough experience and knowledge to understand the possible context behind a particular DLL injection. Remember that security measures should be comprehensive and multi-layered to effectively address various attack vectors. We also recommend your team take the following steps to keep your systems secure:

How to keep your systems secure
  • Install security updates and patches as soon as they become available to ensure that your system is protected against known vulnerabilities.
  • Download software only from reputable sources and verify its authenticity before installing it.
  • Turn off any unneeded services or applications.
  • Use the principle of least privilege, giving users and processes only the access they need to carry out their tasks.
  • Enable Windows Defender Application Control to prevent the loading of unsigned DLLs.
  • Sign your DLLs using code signing certificates to ensure they have not been modified.
  • Monitor your systems for suspicious activity and investigate any anomalies you detect.

In all Apriorit projects, we assess a variety of security risks and choose relevant security measures that ensure complex software protection. Our experts will gladly help you protect your solutions against malicious attacks.

Conclusion

Knowing how to detect DLL injections and mitigate them in your software can prevent costly incident handling, improve your reputation, and help your product better handle security threats. You can use the method for protecting against DLL injections proposed in this article as one way to increase product and data security. But keep in mind that your product will need more robust defenses to achieve comprehensive protection against various attacks.

In this article, we discussed the basics of how to prevent DLL injection. A comprehensive protection strategy requires a combination of security measures and a proactive security mindset. Itโ€™s essential to follow secure coding practices, implement proper access controls, and stay informed about the latest security threats and best practices.

At Apriorit, we have dedicated teams of Windows developers and cybersecurity experts with vast experience delivering efficient and protected software to our clients. Our engineers are ready to help you enhance your projectโ€™s security.

Want an efficient and secure Windows application?

Empower your software development process with experienced and skilled engineers from Apriorit.

Have a question?

Ask our expert!

Tell us about
your project

...And our team will:

  • Process your request within 1-2 business days.
  • Get back to you with an offer based on your project's scope and requirements.
  • Set a call to discuss your future project in detail and finalize the offer.
  • Sign a contract with you to start working on your project.

Do not have any specific task for us in mind but our skills seem interesting? Get a quick Apriorit intro to better understand our team capabilities.