Extending Total Commander with some minor functionality

As a loyal standard Windows shell (explorer.exe) user I often encounter some problems with the number of opened Windows on one desktop. Since my current notebook hardly ever goes down, so does the user’s shell. After a few working evenings, I often have difficulty localizing the desired windows. Having something like 40-50 of them, it is usually a hard task to switch between internet browser, IDA, programming IDE, virtual machines, file manager and so on. The worst thing for me turned out to be looking for the TotalCommander window (being used the most frequently). A situation like this was obviously causing much of a time waste and consequently frustration.

I came up with a few available solutions, listed below:

  1. Having the taskbar items sorted at any time, thus making the current work state much clearer.
  2. Creating a set of system-wide hotkeys, each responsible for setting focus on the associated window or a group of windows.
  3. Start using some kind of Virtual Desktop software and reorganize the whole work environment.

All of them sound pretty good, in fact, and each is worth being described in detail. What is more, there is a great amount of free software designed just to help users with such problems. However, what everyone should already know is that the best solution is the made-by-myself one ;-)

Although all of the ideas have their advantages and disadvantages as well as difficulty level, it’s not the subject of this post. This time, I would like to focus on one particular approach, listed as the 2nd, but in a little bit less complicated form. To be exact, I will show how to make ONE specific application perform some actions in response to a hotkey signal. As you could have really guessed, this application is Total Commander in my case. The hotkey we will use as the totalcmd-caller will be ALT+1.

What I eventually wanted to achieve using this slight hack was to:

  • Have an ALT+1 hotkey registered in the system.
  • Handle the incoming hotkey events in a loop and perform specific actions (set the focus on totalcmd’s window) in some cases.

Since I wanted to be able to use the key combination at any time, the message-loop would also have to be active all the time. To be honest, it’s not a good option for me to have one additional process running on my system, only to have one simple window event handled the right way. As we need only one thread to keep the event handling loop active, we can easily put it inside the affected process itself (totalcmd.exe). Thus, before beginning the real work, we have to ensure we’ve got an active thread running inside our target. This can be accomplished in a few ways (what a surprise!):

  • Spoof one of the program’s imported DLL files, redirect all of its exports to original functions and start a new thread in the DllMain routine.
  • Do the same thing at runtime: use CreateRemoteThread function to inject our own DLL module into the target’s address space and perform some actions inside DllMain.
  • Patch the application executable directly so as it creates the new event-loop thread and deals with the hotkeys. In this case no additional, external files are needed.

As having an external dll module code executed in the process context gives the biggest control over the execution track, I chose the first option. What makes it different from the second one is that we would need an additional ‘loader’ program to inject the dll into totalcmd, while dll-spoofing technique takes advantage of the fact that the attacker’s DLL gets loaded automatically by the Windows loader.

The next step is to choose the library to spoof. What should be noted is that it is possible to spoof the non-system DLLs, only. The list of modules being considered “system” can be found in registry:

HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

After comparing totalcmd imports with the list above, we can extract the library names we are able to spoof:

  • comctl32.dll
  • mpr.dll
  • winmm.dll
  • winspool.dll
  • version.dll (Vista only).

The “Vista-only” note means that the version.dll library has been one of system dlls until Windows XP, but has been removed from the KnownDLLs list in Windows Vista. As my intention was to make the hack work on Vista, and I didn’t know the difference from Windows XP, I chose the VERSION module as the spoofing target.

If we want to spoof a system dll with the one provided by us, the new library must have identical exports (their names as well as the oridinal numbers). As I aimed to use the MINGW package to compile this project, every dll-creation specifics are characteristic to dllwrap only. First of all, I created the version.def file containing the exports together with their forwarding targets. We are not interested in injecting our code in any of the exports, but just having the DllMain function called, thus all the exported functions are simple wrappers to the original ones. The following listing presents the final version.def file:

EXPORTS
GetFileVersionInfoA= myVersion.GetFileVersionInfoA @1
GetFileVersionInfoExW= myVersion.GetFileVersionInfoExW @2
GetFileVersionInfoSizeA= myVersion.GetFileVersionInfoSizeA @3
GetFileVersionInfoSizeExW= myVersion.GetFileVersionInfoSizeExW @4
GetFileVersionInfoSizeW= myVersion.GetFileVersionInfoSizeW @5
GetFileVersionInfoW= myVersion.GetFileVersionInfoW @6
VerFindFileA= myVersion.VerFindFileA @7
VerFindFileW= myVersion.VerFindFileW @8
VerInstallFileA= myVersion.VerInstallFileA @9
VerInstallFileW= myVersion.VerInstallFileW @10
VerLanguageNameA= myVersion.VerLanguageNameA @11
VerLanguageNameW= myVersion.VerLanguageNameW @12
VerQueryValueA= myVersion.VerQueryValueA @13
VerQueryValueW= myVersion.VerQueryValueW @14

As you can see, there are only 14 exported addresses, all of them pointing to their equivalements inside the original library – myVersion.dll. You can read more about DLL export forwarding in [1]. The last thing we need to build our fake version.dll file is the hotkey-handling code itself. Let’s begin with the DllMain part:

BOOL WINAPI DllMain(
  HANDLE hinstDLL, 
  DWORD dwReason, 
  LPVOID lpvReserved
)
{
  if(dwReason==DLL_PROCESS_ATTACH)
  {
    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)MessageOnlyWindow,NULL,0,NULL);
  }
  
  return TRUE;
}

Nothing really interesting, just creating a thread beginning in the MessageOnlyWindow function. Note that the CreateThread function is called only once, right after the application is launched (since the DLL_PROCESS_ATTACH parameter is passed to every module right before the
program’s EntryPoint is called). Let’s go a step further:

DWORD MessageOnlyWindow(LPVOID arg)
{
  MSG msg;
  WNDCLASS wndclass;

  wndclass.style = 0; 
  wndclass.lpfnWndProc = MainWndProc; 
  wndclass.cbClsExtra = 0; 
  wndclass.cbWndExtra = 0; 
  wndclass.hInstance = GetModuleHandle(0); 
  wndclass.hIcon = NULL; 
  wndclass.hCursor = 0; 
  wndclass.hbrBackground = 0; 
  wndclass.lpszMenuName = NULL; 
  wndclass.lpszClassName = TEXT("TotalcmdBringToTop"); 

  if(RegisterClass(&wndclass) == 0) 
    return FALSE; 

  if(CreateWindow(TEXT("TotalcmdBringToTop"), TEXT("TotalcmdBringToTop"), 0,CW_USEDEFAULT, CW_USEDEFAULT,
                  CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandle(0), NULL) == NULL)
    return FALSE;

  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return msg.wParam;
}

It actually looks like a standard function creating a window (do we want to create any window? ^_*) and executing an event-handling loop. The only interesting thing here is the HWND_MESSAGE contant passed as the CreateWindow argument. It indicates that we want to create a Message-Only window. Such windows are usually created to handle some events that are not related to a particular window itself (read more in [2]). In this case, I used it to deal with the WM_HOTKEY events generated everytime a keyboard hotkey is used. Everything should become clear after seeing the last part of the library code:

LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  switch (uMsg)
  {
    case WM_CREATE:
      RegisterHotKey(hWnd,0x1337,MOD_ALT,VkKeyScan('1'));
      break;

    case WM_HOTKEY:
      {
        HWND hMainWnd = FindWindow("TTOTAL_CMD",NULL);
        SetForegroundWindow(hMainWnd);
        ShowWindow(hMainWnd,SW_SHOWMAXIMIZED);
      }
      break;

    case WM_CLOSE:
      UnregisterHotKey(hWnd,0x1337);

    default:
      return (DefWindowProc(hWnd, uMsg, wParam, lParam));
  }
  
  return 0;
}

What can be seen here is a hothey being registered and given the 0x1337 identifier, during the window initialization (RegisterHotKey @ MSDN). The new hotkey is associated with the current window through the hWnd handle passed as the first argument. Since now, we’re guaranteed to receive a WM_HOTKEY signal when the defined (MOD_ALT+1) combination is pressed. When the callback function receives such an event, the TotalCmd window handle is obtained (TTOTAL_CMD is the application window’s class), and then used to maximize and set focus on the main window. What should be noted is that the

FindWindow method is quite unreliable, since it will find only one window handle, which is a problem when there are many Total Commander instances running on one machine. We’ve got a design problem now: which of the windows should be chosen to focus on? The code can be easily extended to perform more advanced actions, this one is just a concept of how an application behaviour can be customized to fit our needs. When something goes wrong and a WM_CLOSE event occurs (for example, when the TotalCmd itself decides to exit), we use the 0x1337 ID to unregister our hotkey from the system.

After putting everything into one .cpp file, we can eventually compile the hack:

19:46:11 Vexillium> g++ dllmain.cpp -o dllmain.o -c
19:46:35 Vexillium> dllwrap --def version.def -o version.dll dllmain.o --driver-name g++
19:46:40 Vexillium> strip version.dll

The last thing to do is to copy the fake version.dll file to \totalcmd directory and do the same with the original VERSION module from \Windows\System32 (renaming it to myVersion.dll in the meanwhile). When copied, we can launch the totalcmd.exe executable and use the ALT+1 hotkey everytime we want to get back to totalcmd window.

The original code package can be downloaded from here.
Have a nice evening!

PS. The previous post has been updated – as I promised, a Proof of Code package can be downloaded now (link).

References & Links

  1. Exported functions that are really forwarders
  2. http://msdn.microsoft.com/en-us/library/ms632599(VS.85).aspx#message_only
  3. Dll Spoofing in Windows
  4. DLL forwarding is not the same as delay-loading
  5. An In-Depth Look into the Win32 Portable Executable File Format, Part 2
  6. RegisterHotKey Function

5 thoughts on “Extending Total Commander with some minor functionality”

Comments are closed.