Execute a file as if it were a program, even though its extension is not EXE


Today's Little Program executes a file as if it were a program, even though its extension is not EXE. The idea here is to prevent somebody from running your program by accident, so you give it an extension like .MOD. This is great for preventing somebody from running the program by mistake, but how do you do it on purpose?

#define STRICT
#include <windows.h>
#include <shellapi.h>

int WINAPI WinMain(
    HINSTANCE hinst, HINSTANCE hinstPrev,
    LPSTR lpCmdLine, int nCmdShow)
{
  SHELLEXECUTEINFO sei = { 0 };
  sei.cbSize = sizeof(sei);
  sei.nShow = SW_SHOWNORMAL;
  sei.lpFile = TEXT("C:\\full\\path\\to\\program.mod");
  sei.fMask = SEE_MASK_CLASSNAME;
  sei.lpVerb = TEXT("open");
  sei.lpClass = TEXT("exefile");
  ShellExecuteEx(&sei);
  return 0;
}

We're merely using the lpClass member of the SHELL­EXECUTE­INFO structure to force the file to be interpreted as the type we specify, overriding the default type inference code.

Comments (32)
  1. Joshua says:

    system("cmd /c "c:\full\path\to\program.mod"")

  2. Fleet Command says:

    @Joshua: "Error: Command Prompt is disabled by domain Group Policy".

  3. Bas says:

    Of course, the NT-style CreateProcess (as opposed to the shell APIs) can be used for this just as well.

  4. nathan_works says:

    why does this seem like a rather bad idea in the first place ?

  5. Joshua says:

    @Fleet Command: cmd.exe doesn't check when invoked with /c. Older versions didn't check when invoked with /k either.

  6. Dan Bugglin says:

    @Joshua This is just to prevent accidental execution, eg from a curious user poking through Program Files.  As you've indicated you can still run it if you're determined enough; renaming it back to a .EXE, for example, is an easy way.

    @Raphael In your examples you should probably be checking your inputs anyway and aborting if there's a problem.  Most programs I've seen use that approach, or at least a "secret" parameter that they require to run, otherwise they tell you to run the main program and quit.

    Perhaps a more fool-proof process is to make it a .DLL instead and call an exported function in it from your main program.  Then it can't be run as an .EXE as there's no main().  Of course if you want the process to fork off and run out of process that won't work.  Best you can do is a new thread.

    Of course then you can use rundll32 to invoke the DLL's function.  And the cat and mouse game continues.  But I refer you to what I told Joshua.

  7. Joshua says:

    I actually did one such thing once where the user is never expected to invoke the program.

    The program is compiled as a Windows subsystem program and expects to receive it's directive from standard input (as a binary read). There's simply no obvious way the user can provide what it wants and it bails immediately on incorrect invocations (read EOF -> exit main loop). I suppose the user could try redirecting in some binary file, but all malformed invocations -> exit main loop, usually with a binary coded exception written to standard output.

    Incidentally the binary stdin/stdout is in fact a natural way to perform the invocation given what the program is doing (hoisting a third-party component call out of process as this component misbehaves).

  8. Tim says:

    "For a more concrete example, consider a program that should not be run without parameters or that should only be run once certain files have been created (saving the user an error message)."

    I still think it's kind of a weird example. Who's digging through their Program Files folder, running executables at random, and getting confused and surprised when they see an error message?

    [You guys are so cute thinking such people don't exist. -Raymond]
  9. Mike Caron says:

    @Tim – someone like my dad, who decided that the best way to save disk space was to go in to Program Files and start deleting files. That is, people who think that any possible action they take should be interpreted sensibly.

  10. Gabe says:

    There's nothing wrong with naming an exectuable program with something other than .EXE, and Windows even ships with such programs. It is standard practice to name screensavers with the .SCR extension instead of .EXE, presumably so that the dialog that lets you choose one has an easy way of determining which ones to show.

  11. mikeb says:

    It's irritating when someone gives an executable file an extension that's commonly used for other file types.  I have one software package that has some executables with a ".inf" extension.

    That seems wrong to me.

  12. Harry Johnston says:

    @Joshua: yet another approach is to require a specific argument in the first part of the command line, where the path to the executable normally goes.  Using `CreateProcess` you can pass whatever command line you like, ignoring the convention that you should put the path to the executable at the beginning, but as far as I know there's no way to do this from the shell or from the command prompt.

  13. Rick C says:

    @ The MAZZTer "Of course then you can use rundll32 to invoke the DLL's function."

    I know the system tries to patch up problems with rundll32 invocations, but to prevent it from being used to call your DLL, you could probably write your DLL's exported function in such a way as to cause it to crash.

  14. Miff says:

    So now I know how OpenOffice/LibreOffice can open up soffice.bin as an executable on Windows. I think some major installer platform (don't remember which) does this too.

  15. Azarien says:

    .mod is a known extension for a music module, so it may be associated with a media player. I would avoid this particular extension for an executable file.

  16. Raphael says:

    That's not actually a bad idea at all. Raymond gave an (somewhat abstract) example of when you might need it. For a more concrete example, consider a program that should not be run without parameters or that should only be run once certain files have been created (saving the user an error message).

    How would *you* do it?

  17. Evan says:

    I think one potential problem is that for a lot of such programs you *wouldn't* see an error message. A lot of subsidiary programs like this are written to not have any kind of UI or anything like that and just work from standard input & output — for that kind of program, putting something in there to even display an error to the user means extra dependencies and stuff like that. (I guess you could just display the message to standard output and wait for anything on standard input and have it display the console, but that's still somewhat ugly when you can just make it so the user isn't even tempted to run it in the first place.)

    Is for digging through Program Files and running executables at random — "at random" is way too strong, but I've definitely started programs directly for one reason or another, and it's not terribly uncommon that there will be a couple executables present and you have to guess which one is the thing you're actually "supposed" to run.

    In short, I consider this question well-intentioned.

  18. BOFH says:

    I wonder, what extra functionality does this little program accomplish that you don't already get with cmd.exe?

    If I take the ls.exe program from Gnu On Windows as an example and rename it to ls.mod, then I only need to execute ls.mod from the commandline:

    C:test>ren ls.exe ls.mod

    C:test>ls.mod -Alh

    total 180K

    -rwxrwxrwx  1 user 0 177K 2005-04-21 02:41 ls.mod

    The only difference being that I must use the full filename "ls.mod" on the commandline, instead of simply "ls".

    [The program is not interesting by itself, but it can be useful when incorporated into a larger program. Consider, for example, a three-pass compiler. -Raymond]
  19. Azarien says:

    @BOFH: you can do

       set PATHEXT=%PATHEXT%;.MOD

    and it will work without extension.

  20. Neil says:

    I am reminded of WINOA386.MOD which has something to do with virtualising MS-DOS programs in the days before NTVDM.EXE, and MMTASK.TSK which has something to do with playing sounds in the background. (Apparently in Windows 3.x MMTASK.TSK was passed a pointer on the command line, because it was possible to pass arbitrary bytes back then.)

    Then there's the Windows 3.x debugger that renamed its copy of DBGHELP.DLL to make it look like a font. (Under Windows 3.x fonts were actually a type of resource-only DLL anyway.)

  21. metafonzie says:

    The author has provided a perfectly valid suggestion as to why such a technique might be needed. Some developers are so limited by their ignorance…

  22. ender says:

    > I think some major installer platform (don't remember which) does this too.

    A better question would be, which installer doesn't do that.

    It's also possible to directly run something from an alternate data stream this way, if you want to be extra sneaky.

  23. MarkKB says:

    >[You guys are so cute thinking such people don't exist. -Raymond]

    Indeed, at the age of ten when I was just a proto-geek, I went through both the Program Files and Windows directory and basically launched everything I saw that had ".exe" beside it. Because when you're ten, you just want to explore everywhere and poke everything to see what it does. :D

  24. Muzer says:

    "Indeed, at the age of ten when I was just a proto-geek, I went through both the Program Files and Windows directory and basically launched everything I saw that had ".exe" beside it. Because when you're ten, you just want to explore everywhere and poke everything to see what it does. :D"

    This ^

    I also remember being delighted when I discovered clock.avi among various other things.

  25. BOFH says:

    Yes, adding .MOD to the %PATHEXT% is even better.

    I'm just curious, is there something that is gained by this little program, or was this scenario just a setup to show how to programmatically execute progam files with arbitrary file extensions?

    [Somebody who adds .MOD to the %PATHEXT% is clearly doing it on purpose, not by accident. They are outside the target audience for this trick. -Raymond]
  26. Joshua says:

    [Somebody who adds .MOD to the %PATHEXT% is clearly doing it on purpose, not by accident. They are outside the target audience for this trick. -Raymond]

    Just don't add .DLL. There's some difference about the entry point.

    On the flip side, if you're willing to tinker with assembly, you could write such an entry point as detects the sense of the invocation and does the right thing. This is not a particularly good idea.

  27. Koro says:

    [On the flip side, if you're willing to tinker with assembly, you could write such an entry point as detects the sense of the invocation and does the right thing. This is not a particularly good idea.]

    I did that once. I had an EXE that copied itself as DLL and set the "is a DLL" bit in the PE header. The entry point was an assembly stub that checked this bit and jumped to WinMain or DllMain, depending.

    (Obviously such a binary did not depend on the CRT)

  28. yuhong2 says:

    @Neil: Don't forget XCOPY32.MOD too.

  29. 640k says:

    msdn still recommends launching ctl applets with rundll32:

    msdn.microsoft.com/…/cc144191%28v=vs.85%29.aspx

  30. Cesar says:

    @MarkKB: "Program Files" didn't exist back when I tried to do that ;-)

    Though I don't remember what happened when you double-clicked on krnl386.exe or user.exe. It was either nothing or some error message.

    And MS-DOS was even better. RECOVER.EXE anyone?

    [cue someone else channeling the Four Yorkshiremen sketch and mentioning an even older system]

  31. Joshua says:

    [cue someone else channeling the Four Yorkshiremen sketch and mentioning an even older system]

    I give you <A HREF="ask.slashdot.org/…/A> (about 3/4 of the way down the long page — use find on page).

  32. Ignacio Rodriguez says:

    The last time I did that (renaming exes) I hit people with so much time in their hands that they looked for files starting with "MZ" for re-renaming.

Comments are closed.