• The Old New Thing

    It rather involved being on the other side of this airtight hatchway: Elevation from Administrator to SYSTEM


    A security vulnerability report arrived that took the following form:

    I have discovered a critical security vulnerability in Windows which I intend to present at the XYZ conference. It allows any user with administrator privileges to perform operation Q, something that should be available only to SYSTEM.

    I think you know how this story ends. If you have administrator privileges, then you are already on the other side of the airtight hatchway. That you can use administrator privileges to pwn the machine is not interesting, because by virtue of being an administrator you already pwn the machine.

    There is formally a distinction between Administrator and SYSTEM, seeing as they are some things which are ACL'd so that SYSTEM can do them and not arbitrary adminitrators, but that distinction is formal and not practical. An administrator who wanted to get some code running as SYSTEM could install a service that runs as SYSTEM. Or use Debug Privilege to take over a process (say, a service) running as SYSTEM. Or simply open a command prompt as SYSTEM and go to town. No need to go through the complex operation Q to get SYSTEM access.

    So yes, a user with administrator privileges can use operation Q to do things that are normally limited to SYSTEM. But so what? Users with administrator privileges already have plenty of easier ways of doing things that are normally limited to SYSTEM. The distinction between the SYSTEM and Administrator accounts is a roadblock to make it harder to mess up your system by mistake. You can still mess up your system, but you have to try harder.

    Before dismissing these reports, you have to verify that the attack is effective only against the current machine. In other words, that obtaining administrator privileges on the computer gets you nothing more than administrator privileges on the computer. And in this case, that was true. The attack described gives the user access to the local machine, but had no effect on other machines.

  • The Old New Thing

    Thanks for informing us that you know about the compatibility issue in your application, so we don't have to fix it


    During the public prerelease period of a version of Windows some time ago, I remember a bug came in from an application developer. It went something like this:

    Hi, we just tried our program on the most recent public prerelease version of Windows, and we noticed that you changed the undocumented XYZ function. Our program uses that undocumented function, and your change to the function breaks us. can you change it back? Yes, we understand that there are no guarantees surrounding the use of undocumented APIs, but still we'd like you to continue supporting the old version. It should just be a few dozen lines of code on your side.

    How very nice of the application vendor to identify their compatibility issues for us. Now we don't need to create a compatibility shim, because not only do we know that the application vendor is still actively supporting the program in question, but that they have already been alerted to the problem with plenty of advance warning for them to issue a patch to their customers before the next version of Windows is released.

    The product team rejected this change request with the simple comment, "Unsupported APIs are unsupported." Presumably the customer liaison translated this into something a bit more polite before communicating it back to the vendor.

  • The Old New Thing

    How can I tell if Windows Update is waiting for the system to reboot?


    Today's Little Program shows how to ask whether Windows Update is waiting for the system to reboot. You might want to check this in your installer, so that you don't try to install your program while the system is in a mixed state where an update is partially-installed.

    Testing this from script is easy. MSDN contains sample code to do that. Let's do it from C++. Today's smart pointer class is (rolls dice) _com_ptr_t! Remember that Little Programs do little to no error checking.

    #include <windows.h>
    #include <comdef.h> // for _COM_SMARTPTR_TYPEDEF
    #include <wuapi.h>  // for ISystemInformation
    #include <stdio.h>  // for printf (horrors! mixing stdio and C++!)
    _COM_SMARTPTR_TYPEDEF(ISystemInformation, __uuidof(ISystemInformation));
    int __cdecl main(int argc, char** argv)
     CCoInitialize init;
     ISystemInformationPtr info;
     VARIANT_BOOL rebootRequired;
     printf("Reboot required? %d\n", rebootRequired);
     return 0;

    Remember that VARIANT_BOOL uses -1 to represent VARIANT_TRUE, so if a reboot is required, you will see -1. Personally, I would treat any nonzero value as logically true.

  • The Old New Thing

    Why are there all these processes lingering near death, and what is keeping them alive?


    A customer (via their customer liaison) asked for assistance debugging a failure that occurs when their system remains up for several weeks.

    The customer seems to have a complicated system where they create and kill processes, and I am seeing hundreds of processes in the following state.

    PROCESS fffffa80082a7960
        SessionId: 0  Cid: 1490    Peb: 7efdf000  ParentCid: 2614
        DirBase: 1b3fd0000  ObjectTable: 00000000  HandleCount:   0.
        Image: contoso.exe
        VadRoot 0000000000000000
        DeviceMap fffff8a000008ca0
        Token                             fffff8a00dbf9060
        ElapsedTime                       1 Day 01:25:38.983
        UserTime                          00:00:01.903
        KernelTime                        00:00:00.265
        QuotaPoolUsage[PagedPool]         0
        QuotaPoolUsage[NonPagedPool]      0
        Working Set Sizes (now,min,max)  (5, 50, 345) (20KB, 200KB, 1380KB)
        PeakWorkingSetSize                17981
        VirtualSize                       222 Mb
        PeakVirtualSize                   246 Mb
        PageFaultCount                    29532
        MemoryPriority                    BACKGROUND
        BasePriority                      8
        CommitCharge                      0
    No active threads
            THREAD fffffa800a358b50  Cid 1490.2704  TERMINATED

    This got me curious: Why are there so many of these near-death processes, and what could be keeping them alive?

    I'll let you puzzle on this for a little bit. But you already know the answer.




    First thing you should observe is that this process is not actually alive. It has already exited. "No active threads." The one thread that is still associated with the process has terminated.

    Why would you have a terminated thread hanging around inside a terminated process?

    Because there is still an outstanding handle to the thread.

    Even though the thread has exited, the thread object can't go away until all handles to it have been closed.

    Now, when you create a process with the Create­Process function, you get a PROCESS_INFORMATION structure back which contains four pieces of information:

    1. A handle to the created process.
    2. A handle to the initial thread in the process.
    3. The ID of the created process.
    4. The ID of the initial thread in the process.

    Of those things, you are probably interested in the process handle, because that's the thing you can wait on to learn when the process has exited. And you probably ignore the thread handle.


    You need to close the thread handle, or the thread cannot go away. It may have stopped executing, but the fact that you have a handle to it means that you can still do things like check if the thread has exited (yes, already!), ask for the exit code, ask for the thread ID, ask how much CPU the thread consumed during its lifetime, and so on, and all those statistics are kept in the thread object. And since thread and process IDs need to remain unique as long as there is still a handle to the object, the object needs to hang around so that it "occupies space" so that no other thread can grab its ID.

    The customer called this process near death, but the more conventional term for it is zombie. In fact, zombie isn't a good term either, because this process and this thread are well and truly dead, never to walk again.

    A better name would be corpse. The process and thread are dead. They're just lying there, rotting away in memory, waiting for all references to be released so they can disappear entirely.

    Since the customer liaison said that the customer has "a complicated system where they create and kill processes", it's entirely possible that somewhere in the complicated system, somebody loses track of the thread handle, causing it to leak. It also calls into question whether they need this complicated system at all. Maybe their complicated system exists to work around some other problem, and we should be trying to solve that other problem.

    Just for completeness, another possibility for the thread lying dead in the process is that some kernel driver has taken a reference to the thread and has gotten stuck.

    We left the customer liaison with that information. We didn't hear back, so either our guess about thread handles was correct, or the customer decided we weren't being helpful enough and decided to stop talking to us.

  • The Old New Thing

    How can I start my service as soon as possible, before any other service?


    A customer wanted to know how they could make their service start before any other service.


    Of course, a simple application of the What if two programs did this? principle shows that this is not possible.

    Upon closer questioning, the real problem was that they had a service which configured some piece of hardware, and they wanted to make sure this configuration was completed before starting the service that uses the hardware. "But we don't want to create a dependency between the two services."

    Okay, so first of all, they didn't actually want to be FRIST. They merely wanted to come ahead of the other service. And the way to do that is to create a service dependency so that the other service depends on their custom service. But then they added a remark where they pre-emptively rejected the solution to their problem.

    The way to control the order in which services start up is to create dependencies among them. Rejecting the correct solution only leads you down the path to creating incorrect solutions. And incorrect solutions tend to create all sorts of problems.

    So do the right thing. Create the service dependency.

  • The Old New Thing

    Why doesn't GetAddrInfo work from behind a proxy?


    A customer was having a problem with the Get­Addr­Info function when running inside a corporate proxy environment.

    We are trying to get the IP address of, say, www.microsoft.com by using the Get­Addr­Info function. This works fine if the computer is not behind a proxy, but if it is run in a corporate proxy environment, the call fails with WSAHOST_NOT_FOUND.

    The Get­Addr­Info function operates at a level below proxies. When you have a Web proxy, the computer never actually talks to www.microsoft.com directly. Instead, you connect to the proxy and tell the proxy, "Please contact www.microsoft.com for me, thanks." That's why it's called a "proxy".

    You never see the IP address of www.microsoft.com; the only IP address you see is that of the proxy. Besides, since you are inside a corporate proxy environment, even if you had the IP address for www.microsoft.com, it is if no use to you since you cannot connect to it.

    There are products that try to smooth over this boundary, so that programs think that they are connected directly to the Internet when in fact they are talking through the proxy.

  • The Old New Thing

    A process inherits its environment from its parent, and the consequences of this simple statement


    A customer reported that they changed some system-level enviornment variables (include PATH). They also have a batch file that, among other things, runs a program that requires that the PATH be set a certain way. The batch file is set to run automatically every five minutes, and the customer observed that the batch file is not picking up the changes to the system-wide environment variables.

    The customer developed two theories as to why this was happening.

    Theory number one was that batch files inherit the environment from its parent process (unless the parent process passes an explicit environment to Create­Process). "Since we changed a global environment variable and the parent process is continually running, it does not pick up the new environment variables. Then it passes this outdated set of environment variables to the batch file."

    Theory number two is that changes to global environment variables will eventually become applied to running processes, but the effect is not immediate; you have to wait a while for the change to propagate. "We started a new command prompt, and the new command prompt did not have the new environment variables, even though the new values did appear in the System control panel. After setting the environment variable a few more times, eventually the new values started showing up in the command prompt."

    The first theory is correct. Processes inherit their initial environment from their parents. Of course, once the process is running, it is free to change its environment variables by calling Set­Environment­Variable, and those modified environment variables are passed to any child processes launched after the new variable is set. (A parent process can also pass a custom environment to the child process.)

    Now, a child process inherits its initial environment from its parent, but it only gets a snapshot of that environment. If the parent subsequently modifies its environment, the child environment is not updated.

    When the customer said that they "changed some global environment variables", that was a rather vague statement, because as we saw above, there is technically no such thing as a "global environment variable". Every environment variable is local to a process. What the customer actually did was make a chance to the template environment that is used to get the ball rolling.

    When you log on, the system constructs an initial environment from the template specified in the Environment Variables control panel. That initial environment is then given to Explorer, where it becomes Explorer's initial environment, and from there, it becomes inherited by anything that gets launched by Explorer, and then anything that gets launched by anything that gets launched by Explorer, and so on. If you can trace your lineage back to Explorer, then your initial environment was based on the copy in Explorer.

    Now, each process along the way may have edited its environment before spawning a child process, in which case those edited values are inherited by the children.

    In order for the changes to the environment template to take effect in a process, that process needs to support the "Throw away all your environment variables and create a new set of environment variables based on the current template" message. That message is the WM_SETTING­CHANGED message, where the lParam points to the string "Environment".

    The only program in common use that actually responds to that message is Explorer.

    This means that when you change the template for the initial environment from the Environment Variables control panel, the only program that reacts immediately to those changes is Explorer. Any other programs that are already running will continue to operate with whatever environment variables they had when they started (or subsequently edited).

    If you run a program from Explorer, it will get the updated environment because Explorer updated its environment in response to the message.

    If you run a program from Task Manager or a command prompt or anything else, then it will not get the updated environment because Task Manager, the command prompt, and pretty much anything else does not update its environment in response to the message.

    If you want to regenerate your environment in response to the WM_SETTING­CHANGED message with an lParam of Environment, you can use the Create­Environment­Block function. You can enumerate the contents of this environment block and copy it to your current environment.

    But personally, I wouldn't bother, because pretty much nobody else bothers either.

  • The Old New Thing

    How do I run a Web search in the user's default Web browser using their default search provider?


    More than one customer has asked, "How do I run a Web search in the user's default Web browser using their default search provider?"

    Nobody knows for sure.

    Windows does define a mechanism for determining the user's default Web browser, or more specifically, default handler for the http protocol. But that's as far as it goes. There is no standard mechanism for invoking the user's default search provider. The concept of the default search provider is internal to the Web browser; Windows itself has no insight into that, any more than it knows which of your bank accounts is in danger of being overdrawn, which is a concept internal to your personal finance program.

    But that doesn't stop people from trying. In particular, Process Explorer uses the following technique: It acts as if it's launching a URL, but instead of passing something like http://www.microsoft.com, it passes "? search string". That is, a quotation mark, a question mark, a space, and then the search parameters, and then a closing quotation mark.

    Experimentation revealed that most Web browsers interpret this as a request to perform a search in the user's default search provider. (Not all Web browsers do this, however. For example, it doesn't work in Internet Explorer 6 because Internet Explorer 6 didn't even have the concept of a default search provider.)

    Here's some code that tries to follow the above algorithm:

    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    class Program
     [DllImport("shlwapi.dll", CharSet=CharSet.Unicode, PreserveSig=false)]
     static extern void AssocQueryString(
        int flags, int str, string assoc, string extra,
        [Out] StringBuilder buffer, ref int bufferSize);
     const int ASSOCF_ISPROTOCOL = 0x00001000;
     const int ASSOCSTR_COMMAND = 1;
     [DllImport("shell32.dll", CharSet=CharSet.Unicode, PreserveSig=false)]
     static extern void SHEvaluateSystemCommandTemplate(
        string template,
        [Out] out string application,
        [Out] out string commandLine,
        [Out] out string parameters);
     public static void Main(string[] args)
      int bufferSize = 260;
      var buffer = new StringBuilder(bufferSize);
                       "http", "open", buffer, ref bufferSize);
      var template = buffer.ToString();
      Console.WriteLine("Template = {0}", template);
      string application, commandLine, parameters;
      SHEvaluateSystemCommandTemplate(template, out application,
                                      out commandLine, out parameters);
      Console.WriteLine("Application = {0}", application);
      Console.WriteLine("Parameters = {0}", parameters);
      parameters = parameters.Replace("%1", "\"? " + args[0] + "\"");
      Console.WriteLine("Parameters after replacement = {0}", parameters);
      Process.Start(application, parameters);

    Most of this program is style points.

    First, we call Assoc­Query­String to get the answer to the question "If I wanted to open a URL that begins with http:, what command template should I use?" This will return something like

    "C:\Program Files\Internet Explorer\iexplore.exe" %1

    All we have to do is replace the %1 with "? search phrase", then run the resulting command line.

    Now come the style points.

    The SHEvaluate­System­Command­Template function conveniently splits split the command template into the pieces we need in order to pass them to Process.Start. The path to the excutable comes out as the application, the command line arguments come out as the parameters, and we don't use the commandLine part.

    After splitting the command line into the executable and the parameters, we perform the %1 substitution on the parameters, and then we're ready to pass it all to Process.Start.

    And there you have it. Note that there is no guarantee that this code will actually work, but in practice it works with most of the major Web browsers out there.

  • The Old New Thing

    The C runtime library cannot be mixed and matched


    In 2011, a customer had an application written in C++ with Visual Studio 2003 that consumes a static library provided by a third party, let's call it contoso.lib. Now, contoso.lib is a static library compiled with Visual C++ 6. The customer is migrating from Visual Studio 2003 to Visual Studio 2008, but they are still using that old contoso.lib library that was compiled with Visual C++ 6. They were afraid that they would encounter some unresolved externals due to name mangling issues, but they were pleasantly surprised when there were no such issues.

    Now the questions.

    1. Is it correct to link a VC6 static library into a VS2008 project?
    2. Even if the linking is successful, do you see any issues or disadvantages with this approach?

    The customer liaison's opinion was "Due to missing security features like SAFESEH, GS, DYNAMICBASE, and NXCOMPAT, there may be a lot of drawbacks to using VC6 libraries in a VS2008 project. What do you think?"

    It's nice that you're thinking about the security features added in recent versions of Visual Studio, using a generous definition of recent to mean less than nine years old. But the issue is more fundamental than just security. The issue is correctness.

    You cannot mix libraries across compiler versions. That you're trying to mix libraries with compiler versions that are nineteen years apart in age is mind-boggling. The Win32 ABI does not extend into compiler-specific behavior, like its internal lookup tables for exception dispatching, private helper functions for RTTI, class member layout, the order of base classes, the implementations of STL classes, the layout of various internal structures, or what happens if you return FALSE from Dll­Main.

    Name mangling will not catch any of these issues. If you modify a class, say by adding a new member variable or base class, the mangled name does not change, even though the new class is probably incompatible with the old one.

    Assuming you manage to dodge all the link errors, what will happen is that these discrepancies will manifest themselves as random failures or memory corruption at run time.

    You will have to go back to Contoso and ask them for a version of the library that is compatible with Visual Studio 2008.

    (Then again, since this question was asked in 2011, they may want to go straight to Visual Studio 2010, which was the most recent version of Visual Studio available at the time.)

    Bonus chatter: Another solution is to create a project in Visual Studio 2003 whose sole job is to wrap the static library in a DLL. The rest of your program can be developed in Visual Studio 2008, using the DLL interface to access the static library.

  • The Old New Thing

    How do you get network connectivity from the worst PC in the world?


    Some time ago, I wrote about the two worst PCs ever. The worst PC of all time, according to PC World magazine was the Packard Bell PC. As installed at the factory, the computer came with every single expansion slot filled. Need to add a peripheral to your computer? Ha-ha, you can't!

    Now, this was in the days before motherboards had integrated network adapters, and a network adapter was not one of the expansion cards that came preinstalled. But I needed to get network access in order to install the latest builds, and I teased at the end of the article that I had to resort to other devious means of obtaining network connectivity.

    Nobody ever asked me to follow up on that teaser, but I'm going to answer it anyway. "So, Raymond, how did you get network connectivity on a computer that had no network adapter and nowhere to plug in a network adapter card?"

    Of course, the cheat would be to unplug one of the existing expansion cards (the dial-up modem was a good candidate), but that would remove a piece of hardware that the senior executive's identical home PC was using.

    My solution was to use a little-known feature of Windows 95 known as Direct Cable Connection, or DCC. DCC allowed you to use your parallel port as a network adapter. (I am not making this up.) I obtained a special cable from the manager of the DCC project and hooked up one end to the Packed Bell PC and the other end to my main development machine, which acted as a bridge between the Packard Bell PC and the rest of the corporate network.

    I installed new builds of Windows 95 this way, which was a great source of amusement (and by amusement, I mean frustration) to the Windows 95 setup team, who found themselves dealing with failures that occurred on a network configuration most of them had never heard of. (But which, realistically, was one of the flakiest network configurations in the world.)

    I also ran nightly stress tests this way, which offered a similar degree of amusement/frustration to whatever developer had to investigate the failures turned up by the accursed machine. For example, one of the things that made debugging difficult was that if you broke into the kernel debugger for too long, the DCC network connection dropped, and you lost network connectivity.

    I think it's kind of fitting that the worst PC in the world also offered the worst debugging experience in the world.

Page 6 of 464 (4,636 items) «45678»