How to prevent DLL hijacking when publishing into a single file

McBride, Casey 5 Reputation points
2026-07-06T17:20:13.8666667+00:00

Hi, I've been making a C# WPF app that I publish into a single file. However, a coworker pointed out that it is vulnerable to Dll Hijacking. For example, if I make a fake "kernel32.dll" and put it into the same folder, it will find and run that before it loads the real kernel32.dll. I have looked for ways to fix this, that will help under debug, but once I publish it, the vulnerability comes back. I haven't seen a really good way to mitigate this problem. I was hoping there was an option for publish that lets you remove the local directory from being the first spot in the search order when starting up.

Thank you.

Developer technologies | .NET | .NET Runtime

4 answers

Sort by: Most helpful
  1. Nancy Vo (WICLOUD CORPORATION) 8,155 Reputation points Microsoft External Staff Moderator
    2026-07-07T06:28:30.4933333+00:00

    Hello @McBride, Casey ,

    Thanks for your question.

    Since publish as single file still relies on the Windows OS loader for native P/Invoke calls, you should explicitly instruct .NET on where to find these files rather than relying on the default Windows search order.

    I recommend intercepting the DLL request and forcing the application to load it directly from the secure Windows system folder by using NativeLibrary.SetDllImportResolver at the very startup of your application.

    using System;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.IO;
    
    public static class SecurityConfig
    {
        public static void SecureDllLoading()
        {
            NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), CustomDllResolver);
        }
    
        private static IntPtr CustomDllResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
        {
            if (libraryName.Equals("kernel32.dll", StringComparison.OrdinalIgnoreCase))
            {
                string securePath = Path.Combine(Environment.SystemDirectory, "kernel32.dll");
                return NativeLibrary.Load(securePath);
            }
    
            return IntPtr.Zero;
        }
    }
    

    For more details, you can reference the official documentation for NativeLibrary.SetDllImportResolver.

    I hope this addresses your question. If this response was helpful, please consider following the guidance to provide feedback.

    Was this answer helpful?

    2 people found this answer helpful.

  2. Bruce (SqlWork.com) 85,281 Reputation points
    2026-07-06T19:32:35.61+00:00

    if the dll's are going to be in the deployment bin, you can compute a hash at build time, and then at runtime compare the hash. if the dll is under your control, you should sign it.

    using System;
    using System.IO;
    using System.Security.Cryptography;
    
    public bool ValidateDllHash(string dllPath, string expectedHash)
    {
        using (var sha256 = SHA256.Create())
        {
            using (var stream = File.OpenRead(dllPath))
            {
                byte[] hashBytes = sha256.ComputeHash(stream);
                string actualHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
                
                return actualHash == expectedHash.ToLower();
            }
        }
    }
    

    Was this answer helpful?

    2 people found this answer helpful.
    0 comments No comments

  3. RLWA32 52,831 Reputation points
    2026-07-19T08:54:13.4966667+00:00

    For the benefit of any interested community member I put together a small demo using a minimal bootstrapping setup program (Win32 C++ desktop application) to create a uniquely named subdirectory under the user's Temp directory, extract WpfSingleTest.exe from its resources, copy it to the newly created subdirectory and run it. The demo creates a new guid at runtime and uses it as the subdirectory name. In this small demo the bootstrapping setup waits for WpfSingleTest.exe to finish its work and exit. At that time it cleans up by deleting the WpfSingleTest.exe program and the subdirectory that contained it. Using a guid for naming the subdirectory ensures that each use of the bootstrapping setup copies WpfSingleTest.exe to a unique (and empty) file system location.

    Folder containing the bootstrapping setup. Even though I copied its system dll dependencies to the folder they are ignored in favor of loading from System32.

    Bootstrapper

    Demo running -

    BootstrapperUI

    WpfSingleRunning

    File system information for running WpfSingleTest.exe -

    WpfSinglePaths

    WpfSingleOnly

    Was this answer helpful?

    0 comments No comments

  4. RLWA32 52,831 Reputation points
    2026-07-09T21:30:39.46+00:00

    Was this answer helpful?

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.