다음을 통해 공유


Modifying application behavior with Detours (for Application Compatibility reasons)

My previous post was about Application Compatibility Toolkit and shims. But what if you have a bit different situation...

Imagine that I have my good old My Windows App application that has been working correctly in the past. Now it’s not maintained anymore and it seems that no one has created similar application that fulfills my needs... And now something has come up that makes this application useless in your Windows 7. Let’s take a look.

Here’s my My Windows App application that has very interesting check before you can do anything with it (notice the longest menu item text ever!):

My Windows App

And when you click the menu item you’ll get this kind of error and application exits:

My Windows App issue

Clearly this is not something want. You want to continue using your application despite it weird flaws etc. But since it’s not maintained anymore there’s nothing you can do with it... No support, no hot fixes... no help whatsoever... And for this particular case there isn’t readymade shim that could fix the application. So you’re left without your nice application... Unless you’re ready to do some engineering :-)

Let’s do some digging for the application itself before rushing to the solution. First I’ll do dumpbin /imports “My Windows App.exe” to see what the application actually imports. I can also use dumpbin /disasm to take closer look of it but PEBrowse does both jobs better than dumpbin so I’ll use that instead.
And since I suspect my applications “insane sanity check” to have something to do with date time functions I’ll identify GetSystemTime as potential candidate from Import Address Table (IAT). After some browsing at the PEBrowse I’m able to find this kind of disassembly:

PEBrowse and disassembled output
Here is same output in text format:

 123456789101112131415161718192021222324252627
 CALL    DWORD PTR [KERNEL32.DLL!GetSystemTime]; (0x41A2AC) CMP     ESI,ESP      CALL    0x40159F      MOVZX    EAX,WORD PTR [EBP-0x84]CMP     EAX,0x7D0    ; NOTE: 0x7D0 = 2000 JE     0x404545      ; (*+0x34) MOV     ESI,ESP      PUSH    0x10        PUSH    'Error'      ; (0x416C2C) PUSH    'This application works in year 2000 only!'; (0x4177B8) MOV     EAX,DWORD PTR [EBP+0x8]PUSH    EAX        CALL    DWORD PTR [USER32.DLL!MessageBoxW]; (0x41A3E8) CMP     ESI,ESP      CALL    0x40159F      MOV     ESI,ESP      MOV     EAX,DWORD PTR [EBP+0x8]PUSH    EAX        CALL    DWORD PTR [USER32.DLL!DestroyWindow]; (0x41A3A4) CMP     ESI,ESP      CALL    0x40159F      PUSH    0x0        PUSH    'Do something'   ; (0x416F10) PUSH    'Whoo-hoo! This works correctly.'; (0x417778) MOV     EAX,DWORD PTR [EBP+0x8]PUSH    EAX        CALL    DWORD PTR [USER32.DLL!MessageBoxW]; (0x41A3E8)

Line 1 shows that GetSystemTime gets called and it fills SYSTEMTIME structure. Value from SYSTEMTIME structure (wYear) is then compared to the value 2000 on line 5. And if that value isn’t 2000 then user is displayed error message (see line 8 to 13). After error message application is closed (line 19).

I could do nasty fix (that would be easy in this example)... which would be editing the executable to bypass that check but I’m not going to do that. I don’t want to touch the executable itself.

But now we know that in order to fix this exact issue we can just _handle_ returned structure from GetSystemTime function. We need to change it to return 2000 instead of current year.

Now it’s time to bring in Detours created by Microsoft Research. Detours allows you to do exactly what we want in this case. It allows you to _detour_ function calls so that your component gets called when application tries to call that function. In order to use Detours you just need to download it and run nmake on the installation directory to build the detours itself and all the samples too. Then we’re ready to proceed to next stage.

So I’ll quickly create new Win32 project with application type DLL and name the project “My Getsystemtime” (using simple.cpp as an example from the samples\simple –folder). Then I’ll add following code to it:

 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
 #define WIN32_LEAN_AND_MEAN       // Exclude rarely-used stuff from Windows headers#include <windows.h>#include "detours.h"static VOID (WINAPI * TrueGetSystemTime)(LPSYSTEMTIME lpSystemTime) = GetSystemTime;VOID WINAPI MyGetSystemTime(LPSYSTEMTIME lpSystemTime){  OutputDebugString(TEXT("'My GetSystemTime' called (faking year 2000 :-)!"));  TrueGetSystemTime(lpSystemTime);  lpSystemTime->wYear = 2000;}BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,LPVOID lpReserved){  LONG error;  switch (ul_reason_for_call)  {  case DLL_PROCESS_ATTACH:    OutputDebugString(TEXT("Attaching 'Fake GetSystemtime'..."));    DetourRestoreAfterWith();    DetourTransactionBegin();    DetourUpdateThread(GetCurrentThread());    DetourAttach(&(PVOID&)TrueGetSystemTime, MyGetSystemTime);    error = DetourTransactionCommit();    if (error == NO_ERROR)     {      OutputDebugString(TEXT("Attaching 'Fake GetSystemtime'... Done!"));    }    else     {      OutputDebugString(TEXT("Attaching 'Fake GetSystemtime'... Failed!"));    }    break;  case DLL_PROCESS_DETACH:    DetourTransactionBegin();    DetourUpdateThread(GetCurrentThread());    DetourDetach(&(PVOID&)TrueGetSystemTime, MyGetSystemTime);    error = DetourTransactionCommit();    OutputDebugString(TEXT("Detaching 'Fake GetSystemtime'."));    break;  }  return TRUE;}

And in order this code to be build correctly I’ll also need to add include folder that points to the detours installation folder and include folder underneath it. I also need to add detours.lib and detoured.lib so that linker is happy too.

Now I have my newly built DLL that I use to modify behavior of My Windows App. Detours installation package also contains command line tool withdll.exe so that you can easily test you detour DLL. I’ll just launch my application using following command line and then all should be fine (assuming that everything is at the same folder):

withdll.exe /d:"My GetSystemtime.dll" "My Windows App.exe"

And if I now click the interesting menu item I’ll see this (since my application changes the year to be 2000 on line 11 in above code sample):

My Windows App Detoured!

And since I used OutputDebugString for demonstration purposes I’m able to verify the behavior using DebugView:

 DebugView displays our messages

So we’re able to modify functionality of the application without touching the executable itself. This example was a bit different than my previous application compatibility one. This is much more engineering kind of solution than to use readymade tools for fixing applications. But both have it’s time and it’s place. I’ll probably continue posting more information to this category so stay tuned!

Anyways... Happy hacking!

J