Use Image Hijacking to Globally Replace Notepad.exe

I like to use a progammer’s text editor called EmEditor. Some people like Notepad++, vim, etc. You can change file associations but some things are just hard-coded to call notepad.exe. Notepad.exe is a protected system file which makes it hard/unsafe to replace.

Instead of replacing it, you can hijack calls for notepad.exe and redirect them to another text editor by registering a fake debugger.

You just need a tiny script to capture the arguments being sent to notepad and send them to your text editor of choice, instead. Replace the paths in the example with the paths and text editor for your system.

Script

 var shell, args="",
    editor = "C:\\Program Files\\EmEditor\\EmEditor.exe";
if( WScript.Arguments.length > 0 ){
	for( i=1; i<WScript.Arguments.Length; i++ ){
		args += WScript.Arguments(i) + " ";
	}
	shell = new ActiveXObject("WScript.Shell");
	shell.Exec("\"" + editor + "\" " + args);
}

Registry

new-item 'HKLM:\software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe'
Set-ItemProperty 'HKLM:\software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe' -name  Debugger -value 'wscript "C:\Program Files (x86)\Utilities\replace-notepad.js"'

Update

It’s been pointed out by commenter “MM” that files with multiple spaces together in the name break the WScript hijack. In this scenario, the Dubugger redirect splits the file name on whitespace and discards the extra whitespace. I put together a little hijack in C# that fixes this problem as long as there aren’t two or more files with names that differ only by the amount of whitespace between words in the file name. If that’s the case, you’re out of luck and should probably think about why you are naming files this way. Otherwise, this slightly fancier hijack will handle files with multiple spaces separating words in the name.

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;

class Program
{
	public static void Main(string[] args)
	{
		string handler = args[0];
		
		if(args.Length == 3) Process.Start(handler, string.Format("\"{0}\"", args[2]));
		else if(args.Length > 3)
		{
			string dirplus = args[2];
			int index = dirplus.LastIndexOf("\\");
			var dir = new DirectoryInfo(dirplus.Substring(0,index));
			var filepart = dirplus.Substring(index + 1);
			var expression = filepart + "\\s+" + string.Join("\\s+", args.Skip(3));

			var names = dir.GetFiles(filepart + "*").Select(x => x.Name);
			var files = names.Where(x => Regex.IsMatch(x, expression));
			if(files.Count() > 1)
			{
				MessageBox.Show(
					string.Format("{0} possible matches found with file names that differ only by spaces between words.\r\nChoosing the first match.", files.Count()), 
					"Warning", 
					MessageBoxButtons.OK, 
					MessageBoxIcon.Warning);
			}	
				 
			Process.Start(handler, string.Format("\"{0}\"", files.First()));
		}
	}
}

> csc /target:winexe .\hijack.cs

In the regsitry, set the Debugger like this:

Set-ItemProperty 'HKLM:\software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe' -name  Debugger -value '"C:\Program Files (x86)\Utilities\hijack.exe" "C:\Program Files\EmEditor\EmEditor.exe"'
Advertisement

6 Responses to Use Image Hijacking to Globally Replace Notepad.exe

  1. MM says:

    How do you get around files names with two adjacent spaces in them?

    • Brian Reiter says:

      Interesting problem. I haven’t ever named a file like this, so it never came up. If there are multiple spaces in the file name, the file name gets split on whitespace and passed as multiple arguments. The information about the number of spaces is lost.

  2. wengang says:

    For the hijack.exe that I compiled under VS 2010, it crashed upon me.. Any clues?

  3. darkdog says:

    i modified the code to support multiple spaces and avoid stackoverflow errors

    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Text.RegularExpressions;
    using System.Windows.Forms;
    using System.Collections.Generic;

    class Program
    {
    public static void Main(string[] args)
    {
    if(args.Length >= 1){
    string handler = args[0];
    if(args.Length <= 2){
    Process.Start(handler);
    }
    else
    {
    Process.Start(handler, "\""+String.Join(" ",args.Skip(2).ToArray())+"\"");
    }
    }
    }
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: