Have you found any TheDailyWTF-worthy code during the development of Windows 95?


Mott555 is interested in some sloppy/ugly code or strange workarounds or code comments during the development of Windows 95, like "anything TheDailyWTF-worthy."

I discovered that opening a particular program churned the hard drive a lot when you opened it. I decided to hook up the debugger to see what the problem was. What I discovered was code that went roughly like this, in pseudo-code:

int TryToCallFunctionX(a, b, c)
{
  for each file in (SystemDirectory,
                    WindowsDirectory,
                    ProgramFilesDirectory(RecursiveSearch),
                    KitchenSink,
                    Uncle.GetKitchenSink)
  {
    hInstance = LoadLibrary(file);
    fn = GetProcAddress(hInstance, "FunctionX");
    if (fn != nullptr) {
        int result = fn(a,b,c);
        FreeLibrary(hInstance);
        return result;
    }
    fn = GetProcAddress(hInstance, "__imp__FunctionX");
    if (fn != nullptr) {
        int result = fn(a,b,c);
        FreeLibrary(hInstance);
        return result;
    }
    fn = GetProcAddress(hInstance, "FunctionX@12");
    if (fn != nullptr) {
        int result = fn(a,b,c);
        FreeLibrary(hInstance);
        return result;
    }
    fn = GetProcAddress(hInstance, "__imp__FunctionX@12");
    if (fn != nullptr) {
        int result = fn(a,b,c);
        FreeLibrary(hInstance);
        return result;
    }
    FreeLibrary(hInstance);
  }
  return 0;
}

The code enumerated every file in the system directory, Windows directory, Program Files directory, and possibly also the kitchen sink and their uncle's kitchen sink. It tries to load each one as a library, and sees if it has an export called FunctionX. For good measure, it also tries __imp__­FunctionX, FunctionX@12, and __imp__­FunctionX@12. If it finds any match, it calls the function.

As it happens, every single call to Get­Proc­Address failed. The function they were trying to call was an internal function in the window manager that wasn't exported. I guess they figured, "Hm, I can't find it in user32. Maybe it moved to some other DLL," and went through every DLL they could think of.

I called out this rather dubious programming technique, and word got back to the development team for that program. They came back and admitted, "Yeah, we were hoping to call that function, but couldn't find it, and the code you found is stuff we added during debugging. We have no intention of actually shipping that code."

Well, yeah, but still, what possesses you to try such a crazy technique, even if only for debugging?

Comments (25)
  1. McKay says:

    So, how should one find a function (for debugging purposes) that has been moved to a different library by another team? (programatically?)

  2. Maurits says:

    Please tell me the fix wasn't to create an aaa.dll in system32 with aaa!FunctionX(a, b, c) { return 0; }

  3. @McKay: in short, you don't have/need to. If it's a public function, it will not move. If the other team really needs to move it, they will leave a stub in the original library so compatibility isn't broken, or at least post information about the move (if it were done before release). On the other hand, if it was moved without warning, chances are that it was an internal function, and you shouldn't be calling it in the first place.

  4. Mott555 says:

    For fun, write a DLL with the method they were looking for and dump it in the System directory. Make it open the user's web browser to this blog post, and push the DLL out via Windows Updates. Then wait and see if there are still any users of this application out in the wild.

  5. Mott!!!

    I thought you wanted TDWTF levels of oddness, not XKCD levels of cruelty!

  6. Seriously, how could xpclient rate this 1-star? I'm in stitches, especially after mott's suggestion of making the function open a web browser to this page!

  7. Dan Bugglin says:

    @McKay For starters, if you write something like this to help you, there's no need to compile it into your main program.  The function isn't going to change places every time you run your program so there is no need.  Put code like this into a temporary project, run it, find your function, and then use the appropriate library name in your main program.

    Assuming you did this because it's undocumented though, it's probably not the best idea.

    Of course since this specific example is because you're coding it against a system in development, ideally you'd have the phone number of a guy developing the system for questions since everything is changing all the time.  In which case, you call him up and say "Hey, where'd FunctionX go?  It's not being exported by user32 anymore".  And then he'd tell you or ask someone who would know.

    The fun part is apparently it didn't even matter that this function call wasn't working since the program still functioned just fine.  Did anyone even notice the call never succeeded?

  8. Evan says:

    I read TheDailyWTF regularly, and this is a better entry than most!

  9. Koro says:

    This sounds like a huge security hole too, if some of the scanned directories are user-writable.

  10. kinokijuf says:

    @Lockwood:

    XKCD level of cruelty would be opening a shock site.

  11. How many blog entries have we read on here about bad, misbehaving programs doing things they shouldn't in DllMain?  Now here is a program that specifically seeks them out!

    Probably the program crashes horribly on any modern computer with a smattering of random 3rd-party apps, some of them surely being ill-behaved.  Seems like it's on a suicide mission…

    The "better" method would have been to analyze the image without running any code from it (similar to Dependency Walker; I'm not sure what functions it uses but those would be the ones to use).  Once the correct function is located, then and only then LoadLibrary/GetProcAddress.

  12. alexcohn says:

    Since the function wasn't exported anymore, the next step would be to scan all relocation tables for the signature of this function. If it is not there, then somebody changed it's declaration to static. OK, this means that the scanner should look for it in all code segments.

  13. chentiangemalc says:

    another example of the great (not!) communication that is so common in the software dev/IT world over the world. let's not ask rest of the windows teams where this function might have gone, let's make a hacked up function to find it ourselves.

  14. @Malcolm: I don't see any problems with writing a function like this to scan through libs your own company wrote and return which libs contained the appropriate function, as an alternative to pestering someone. That would potentially make the communication unnecessary, or would at least obviate the first question of "where is it" so that the conversation can proceed to "why was it moved", if needed.

    The laughably obvious problem is that the code was checked in as part of some other application, wrecking the performance of the application on startup and introducing a gaping security hole in case said application was shipped to clients somewhere.

  15. Clearly, they weren't trying hard enough. They should have found an old copy of user32.dll, made a note of the first 32 or so bytes of that function, then instead of relying on GetProcAddress, searched for that byte sequence then branched into whatever code matched.

    (Health warning: idea proposed for entertainment purposes only. If you actually do this in real code, please stop programming and report to your nearest fast-food vendor for more suitable employment.)

    Apart from anything else, unless they were expecting the function to move between DLLs again in future, why not run it once, say "aha, it's moved from user32.dll to kernel32.dll – let's get it from there now"? Raymond's interpretation of "maybe it moved to another DLL so let's try them all" is close, but doesn't quite capture the depths of insanity here: they either never tested the code at all and never noticed it never worked in the first place, or thought something like "hm, it's gone from user32.dll, it hasn't shown up in any other DLL yet … maybe it just got lost, so it'll wash up ashore in some random DLL in a few weeks, so let's code on that basis". Pretty scary stuff.

    I'm reminded of some malware here: trying to obfuscate the file the payload actually attacks by enumerating all files and checking the hash against a specific value. (Researchers knowing "this virus is trying to find SpecialAccountingPackage.exe, it's probably trying to steal account data, let's get a copy of that and see what it does" will find it much easier than "it's trying to attack a file with a SHA1 hash of eed73195584166e03b3077f08cc2a5e833f468e3 … we're stumped" – though of course that particular one is easy to find.)

  16. 640k says:

    It's usually easier to write this kind of code that to communicate with people that has the social skills of a thermonuclear device.

  17. Silly says:

    So the real wtf is they weren't placing the search algo in a 'RunOnce' utility and caching the result by placing the dll name (or a null byte if not found) in some file loaded by the main program when launched by the user. Or perhaps running a thread that did the search/cache every hour or so in case the available dlls changed. Right?

  18. The Enumerator says:

    "Well, yeah, but still, what possesses you to try such a crazy technique, even if only for debugging?"

    A deadline. The answer is always a deadline.

  19. JM says:

    @V-Shorn: doing this to "helpfully" unburden your colleagues by not asking them questions is bad for everyone. It's bad for you, because you're spending time on throwaway analysis that is probably (not necessarily, but probably) better spent on other stuff. It's bad for your colleagues when, seven months down the line, they have to play consultants because you slipped in a call to an internal function that now no longer exists, or worse, does something different — when they would have preferred to discuss better solutions from the outset. And finally it's bad for the company as a whole when integration slip-ups like these bog down the entire process.

    Nobody likes the guy who's sending everyone mail every 10 minutes because he can't be arsed to open the documentation to anything, but if you're heading into an area where there is no documentation and you're making assumptions, it's generally not a bad idea to ask someone else to take a look. Even better if that other person is involved in the original development. If you don't want to interrupt someone's flow (good policy) save it up for a water cooler discussion (or as I like to call it, a coffee run ambush).

    I know you were just offering it up as a possibility of seeing where it is as preparation for an actual discussion so as to not waste time, but even that, I'd argue, is a sign of a problem. It encourages people to actually do this (write code to find stuff out) instead of investing either in better documentation or easier communication. It's only a small step from here to writing compatibility shims for the rest of your career. I'm also very skeptical this actually saves time, unless you are an idiot savant who can hack code like this in 5 minutes but cannot ask a sensible question in 5 minutes (no obvious jokes about how you know people like this, please…)

  20. RSmith says:

    Security hole? Guys, we're talking about Windows 95. I don't need to get a DLL cleverly inserted to execute arbitrary code when there is zero filesystem security…

  21. Blog hater says:

    The blogsoftware keep raring messages. Please fix this bug, ASAP.

  22. David Walker says:

    @Blog Hater:  Yep, there's nothing quite as bad as raring messages.  I hate it when that happens.  I don't want my messages to be rared.

    And of course, it always helps to say "Please fix this bug ASAP".  That's what I've been leaving out of my bug reports all along!

  23. Christ van Willegen says:

    @Henning Makholm: I mentioned this to a friend of mine (about the virus needing a specific data file on the client to be able to decode itself). He said: "Well, if that file is spread via a command center, you'll never get at the decoded data at all". Insightful at the very least, and a good tip to virus-developers out there. Ah well, if he can think of it, someone else can think of it as well…

Comments are closed.