October, 2007

  • The Old New Thing

    It rather involved being on the other side of this airtight hatchway: Local execution


    The security response team gets all sorts of reports, and a good number of them are from people who just get excited that they were able to do something unusual, even if it isn't a security vulnerability.

    Attached please find a security exploit in the ABC ActiveX control. If you save this Web page to a file and double-click it, it <does something that Web pages shouldn't be allowed to do>.

    The security folks study the Web page and discover that it indeed uses the ABC ActiveX control and invokes a method that is not safe from untrusted script, say, delete a file. But the control is marked not safe for scripting. How can script execute it?

    More careful study shows that the not safe for scripting attribute is indeed being respected. Copying the page to a Web server and visiting it from Internet Explorer blocks the creation of the ActiveX object, as expected. The only reason the local Web page version works is that you copied the file to your computer and ran it from there. If you do that, it runs in the context of the local computer rather than an untrusted Web server.

    When this was pointed out to the person reporting the alleged vulnerability, the explanation was, "That's right. To use this exploit, you have to convince users to save the file to their computer and double-click it. I understand that there is a lot that would have to happen for this exploit to succeed, but it's still possible."

    Well, heck, if that's your M.O, then why bother with the Web page? You can do the same thing with a boring executable. "To use this exploit, you simply have to convince users to save the file to their computer and double-click it. I understand that there is a lot that would have to happen for this exploit to succeed, but it's still possible."

    Saving the file to the local computer is the step that crossed the security boundary. And that's the step these people just waved their hands at. They're assuming they're on the other side of the airtight hatchway and then proclaiming, "Woo-hoo! I managed to sneak to the other side of the airtight hatchway!"

  • The Old New Thing

    Why aren't shortcuts as easy as unix links?


    Commenter dingo asks, "Why are shortcuts so hard to manipulate? Why can't they be as easy as unix links?"

    Well, if you want something like unix links, then you can just create a hard link. Creating them is about the same difficulty (CreateHardLink vs link) and manipulating them is the same since you don't actually manipulate a hard link. You just use it like a regular file (since a regular file is a hard link).

    If you want something like unix symbolic links, then you can create an NTFS junction, such as this one that mounts a drive into a directory. (I'm told that Windows Vista expands the repertoire of symbolic links as well.)

    But neither of these features is available on FAT (or CD-ROMs or Novell Netware or email), which meant that Windows 95 couldn't use them. Last year I discussed in some detail why shortcuts are files. Maybe that's what your question is really about.

  • The Old New Thing

    How do I delay the automatic logon process?


    To solve some problems you need to place one foot outside the box.

    We have a number of kiosk machines that are networked wirelessly. Each machine is configured with automatic logon so that things return to normal after power is restored after an outage. The problem is that the wireless switch takes a long time to recover from a power failure, so when the kiosk machines try to log on, they can't. We have to go around to all the machines and manually log them on after waiting a few minutes for the switch to get itself back up. Is there a way we can delay the automatic logon or convince automatic logon to pause and retry?

    Your first reaction may be to write a custom logon provider or otherwise control the GINA. But there's a much lower-tech solution.

    Go to your boot.ini file (or if you're using Windows Vista, use bcdedit) to increase the boot menu timeout. The timeout value in boot.ini can go as high as 11 million seconds (about four months). If your wireless switch takes more than four months to get itself into a ready state, then you've got worse problems.

  • The Old New Thing

    Win32 user interface work is inherently single-threaded


    At the end of the day, there's a window handle in there.

    As we saw a few years ago, windows have thread affinity. You can push work to a background thread all you want, but once you send a message to the window (or do something that results in a message being sent to the window), you've given control to the UI thread.

    Since the shell is all about user interfaces, the shell naturally expects that all threads that use it are single-threaded apartments. Historically, however, you sort of got away with violating this rule because older versions of the shell used their own custom fake version of COM rather than using the official one in OLE32.DLL. As a result, programs that broke the rule on apartment threading often managed to get away with it because the shell assumed everybody was doing the right thing. (The Internet Explorer team faced a similar problem with browser plug-ins.) If you tried to use an object on the wrong thread, nobody would step in and try to stop you. Mind you, you might corrupt memory or crash, but that was your own fault. Nobody said this was going to be easy.

    As the shell gradually switched to using real COM instead of fake COM, and as new features were added to the shell which relied more and more heavily on callers following the rules for apartment threading, programs that had skirted the rules started running into problems. If you got away with it on earlier versions of Windows and the problem was severe, there was a good chance the shell would have to do some re-architecting to allow your dodgy code to keep working.

    A member of the COM team explained that COM assumes fundamentally that multi-threaded apartments are UI-free. No UI means no need to pump messages. If you initialize COM in MTA mode on a thread, you'd better not do any UI or your thread will stop responding to messages whenever COM needs to talk to another thread.

  • The Old New Thing

    Don't assume the first person to run your program is an administrator


    One "optimization" I've seen from some programs is to defer a bunch of system configuration work to the first time the program is run or the first time a feature is requested instead of doing it at install time. The problem with this is that the first person to run your program is not guaranteed to be an administrator.

    For example, there is one popular media program that doesn't install its CD AutoPlay handler until you run it for the first time. If the first person to run the program is not an administrator, then their AutoPlay handler doesn't get installed and consequently never works.

    This problem became more acute in Windows Vista, where users do not flex their administrative rights by default, even if they are administrators. Consequently, when run on Windows Vista, this program never installs its AutoPlay handler.

    It fell to the application compatibility folks to see what they could do to rescue this program from the "not compatible with Windows Vista" pile. It's frustrating having to "fix" something that was broken when you got there.

  • The Old New Thing

    The perils of translating words blindly without verifying them in context


    My fancy new office phone has an option to change the language of its user interface, so naturally I chose Swedish. Once I did that, I saw some obvious translation errors.

    • The Edit command was called Bearb... instead of Redig.... Apparently, the Swedish translation was created by starting with the German version, and they missed a spot and left a word in German (Bearbeiten). This also illustrates the perils of not leaving enough room for expansion. The English word Edit is just four letters long, but the German word is ten letters long (and the Swedish one eight letters), and the longer German and Swedish words get truncated.
    • The label First name was translated as Första namn. Första namn does mean first name, but in a literal sense, as in the sentence "There are ten names. The first name is Wilson." The correct translation is förnamn, which means given name (in contrast to efternamn, or family name). Similarly, the button Forward was translated as Vidare (opposite of backward) instead of Vidarekoppla (to forward a phone call).

    The first example above is just sloppiness, but the second one illustrates how a simple LocalizeString("some text") algorithm doesn't work. As Lance Fisher's teacher put it, "Russian is not a translation of English." You can't just take words and phrases in one language and put them through a simple mapping table and expect the result to be accurate.

    Back in the Windows 95 days, the German translation team needed some beta testers, and I volunteered. The most interesting translation bug I reported was one in which an English menu item Sort was translated as Art (which means class, kind) instead of Anordnen (which means to arrange). The one English word has two different meanings, and a blind dictionary translation won't know which one is intended.

  • The Old New Thing

    Other problems traced to violating COM single-threaded apartment rules in the shell


    Probably the biggest category of problems that can be traced to violating COM single-threaded apartment rules in the shell is using an object from the wrong thread. Of course, nobody admits to doing this up front, They just report that the shell is broken.

    We can't enumerate the items on the desktop any more. We take the pointer returned by SHGetDesktopFolder and call IShellFolder::EnumObjects, but no objects come out. This code used to work on Windows XP.

    There isn't enough information to diagnose the problem, and if you just do what they claim doesn't work, you find that it works:

    #include <windows.h>
    #include <ole2.h>
    #include <shlobj.h>
    #include <shlwapi.h>
    #include <stdio.h>
    #include <tchar.h>
    INT __cdecl
        INT iArgc,
        __in_ecount(iArgc) PTSTR ppszArgv[]
     if (SUCCEEDED(CoInitialize(NULL))) {
      IShellFolder *psf;
      if (SUCCEEDED(SHGetDesktopFolder(&psf))) {
       IEnumIDList *peidl;
       if (SUCCEEDED(psf->EnumObjects(NULL, SHCONTF_FOLDERS |
                             SHCONTF_NONFOLDERS, &peidl)) && peidl) {
        LPITEMIDLIST pidl;
        while (peidl->Next(1, &pidl, NULL) == S_OK) {
         STRRET str;
         if (SUCCEEDED(psf->GetDisplayNameOf(pidl,
                                             SHGDN_NORMAL, &str))) {
          TCHAR sz[MAX_PATH];
          if (SUCCEEDED(StrRetToBuf(&str, pidl, sz, MAX_PATH))) {
           _tprintf(TEXT("%s\n"), sz);
     return 0;

    When given this simple program that does what they claim doesn't work, the customer explained that they cache the desktop folder. It works for a while, and then stops working. The code is complicated, so they haven't been able to isolate the problem yet. They did find that if they didn't cache the pointer and just called SHGetDesktopFolder each time they needed it, then they didn't have the problem.

    I never got a confirmation, but I'm pretty sure that they are violating COM apartment threading model rules and obtaining the desktop folder obtained on one thread, then using it on another. Apartment model rules specify that you must use an object on the same thread that created it. If you want to use it on another thread, you have to use a helper function like CoMarshalInterThreadInterfaceInStream. If you just dive in and use it on another thread (known informally as "smuggling"), then all sorts of strange things happen. In this case, the folder can't enumerate objects any more.

    Moral of the story: Stick to the rules for COM objects. If you don't, you may get away with it for a little while, but someday your sins may catch up to you.

  • The Old New Thing

    Superstition: Why is GetFileAttributes the way old-timers test file existence?


    If you ask an old-timer how to test for file existence, they'll say, "Use GetFileAttributes." This is still probably the quickest way to test for file existence, since it requires only a single call. Other methods such as FindFirstFile or CreateFile require a separate FindClose or CloseHandle call, which triggers another network round-trip, which adds to the cost.

    But back in the old days, the preference for GetFileAttributes wasn't just a performance tweak. If you tried to open the file to see if it existed, you could get the wrong answer!

    Some network providers had a feature called a "data path". You can add directories to the data path, and if any attempt to open a file failed, the network provider would try again in all the directories in the data path. For example, suppose your data path was \\server1\backups\dir1;\\server1\backups\dir2 and you tried to open the file File.txt. If the file File.txt could not be found in the current directory, the network provider would try again, looking for \\server1\backups\dir1\File.txt, and if it couldn't find that file, it would try again with \\server1\backups\dir2\File.txt.

    All this extra path searching happened behind Windows's back. Windows had no idea it was happening. All it knew is that it issued an open call, and the open succeeded.

    As a result, if you used the "open a file to see if it exists" algorithm, you would get the wrong result for any file that existed on the data path but not in the current directory. These network providers didn't search the data path in response to GetFileAttributes, however, so a call to GetFileAttributes would fail if the file didn't exist in the current directory, regardless of whether it existed on the data path.

    I don't know if any network providers still implement this data path feature, but Windows code in general sticks to GetFileAttributes, because it's better to be safe than sorry.

  • The Old New Thing

    Why doesn't Explorer show recursive directory size as an optional column?


    "Why start up another program to see folder sizes, when they should just be right there, in Explorer, all the time?"

    The same reason \\ does not autocomplete to all the computers on the network: Because it would destroy corporate networks.

    Showing folder sizes "all the time" means that when you open, say, the root of a large server, Explorer would start running around recursively enumerating every single directory on the server in order to compute the folder sizes. One person doing this to a server is bad enough. Imagine if hundreds of people did it simultaneously: The server would be hammered continously.

    Even worse: imagine doing this across a limited-bandwidth link like a VPN or an overseas link. The link would be saturated with file enumerations and wouldn't have any bandwidth remaining for "real work". Even the change-notifications that Explorer registers are cause for much hair-pulling on corporate networks. (And these are change-notifications, which are passive.)

    Even on a home computer, computing folder sizes automatically is is still not a good idea. How would you like it if opening a folder caused Explorer to start churning your disk computing all the folder sizes recursively? (Then again, maybe you don't mind, in which case, go nuts.)

    (Of course, the question sidesteps the question the linked article tries to address, namely, "What do you mean by the size of a directory anyway?")

  • The Old New Thing

    You can't change it, but you can hide it and add something that looks like it


    Today we have another case of "Now you have two problems." The corporate customer already solved their problem halfway and needed help on the other half. The impossible half.

    We want to change the Add or Remove Programs icon in the Windows XP control panel so it runs our custom install management program instead of running the one built into Windows. In other words, we want to change what happens when the user clicks the Add or Remove Programs icon. Is this possible?

    What they specifically request isn't possible because the icon is provided by Add or Remove Programs itself, but you can easily get the same effect. This is why it's important to understand why somebody wants to do something even though they are asking how to do it. Once you understand why they're doing something, you can unwind the steps they've taken and come up with something that doesn't answer the specific question but still solves the bigger problem.

    You can't redirect an icon but you can remove the existing icon and replace it with something else. Set a policy to remove the existing Add or Remove Programs icon from the control panel and then write your own control panel called Add or Remove Programs.

    I'm not sure what happened next, but they acted as if I wrote, "Could you please repeat the question?"

    Raymond, we need to replace the link in the Control Panel with a custom program that elevates the user's privileges so that they can manage the programs on the computer. Is there any registry key or setting that specifies how the control panel applet is launched that we could inject an EXE into? This EXE would just be a wrapper for launching the original CPL.

    I'm assuming that the extra wrinkle of "And then we want to re-launch the original CPL" made them think their situation was somehow special, so that regular-grade advice wasn't good advice. They needed premium.

    You can still follow the original suggestion. Remove the original CPL via policy and add your custom EXE (which then launches the original CPL after doing whatever it wants).

    But no, that wasn't the problem. They simply missed the point entirely. They were so focused on looking for the sentence "Here is how you change what the icon launches" that they completely missed the point of the suggestion. They're looking for a solution to the specific problem instead of the bigger problem.

    How do you configure the policy to launch our EXE instead of the CPL?

    At this point I pulled out my bad analogy gun.

    In your car there is an ignition switch. It is wired to the starter. You want to make the driver take a breathalyzer test before they can start the car.

    Plan A (what you are proposing): Patch into the wire connecting the ignition switch to the starter so it goes through a breathalyzer. Unfortunately the wire is sheathed in kevlar and cannot be cut open. The designers of the wire did not include any hooks for breathalyzers.

    Plan B (my suggestion): Remove the old ignition switch and install a new ignition switch that is connected to a breathalyzer. If the driver passes the breathalyzer test, then your ignition switch turns the original ignition switch. Notice that this method doesn't require you to bust open the kevlar sheathing that protects the wire between the old ignition switch and the starter.

    All the driver sees is a keyhole. They stick the key in and turn the key. If you're really clever you make your new ignition switch's faceplate look just like the original so the driver can't tell the difference between the two methods (all they see is a keyhole, a breathalyzer, and if they pass, the car starts).

    • The existing CPL is the old ignition switch.
    • Your replacement program is the new ignition switch.
    • Deploying the Hide the Add or Remove Programs control panel policy is removing the old ignition switch.
    • The Add or Remove Programs icon is the keyhole.
    • The way Windows XP works, the keyhole leads to the old ignition switch.
    • Plan B: Use policy to remove the old ignition switch, and install new ignition switch (your replacement program). Use some paint to make the new keyhole look just like old keyhole. The person in driver's seat is none the wiser.

    You know you're in trouble when I have to pull out my bad analogy gun.

    Note: I don't know whether this trick will actually work, but it seemed a useful example of the principle of "Just because it has to look like something doesn't mean it has to be that something."

Page 1 of 4 (33 items) 1234