• The Old New Thing

    Why .shared sections are a security hole

    • 30 Comments

    Many people will recommend using shared data sections as a way to share data between multiple instances of an application. This sounds like a great idea, but in fact it's a security hole.

    Proper shared memory objects created by the CreateFileMapping function can be secured. They have security descriptors that let you specify which users are allowed to have what level of access. By contrast, anybody who loads your EXE or DLL gets access to your shared memory section.

    Allow me to demonstrate with an intentionally insecure program.

    Take the scratch program and make the following changes:

    #pragma comment(linker, "/SECTION:.shared,RWS")
    #pragma data_seg(".shared")
    int g_iShared = 0;
    #pragma data_seg()
    
    void CALLBACK TimerProc(HWND hwnd, UINT, UINT_PTR, DWORD)
    {
      int iNew = g_iShared + 1;
      if (iNew == 10) iNew = 0;
      g_iShared = iNew;
      InvalidateRect(hwnd, NULL, TRUE);
    }
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
        SetTimer(hwnd, 1, 1000, TimerProc);
        return TRUE;
    }
    
    void
    PaintContent(HWND hwnd, PAINTSTRUCT *pps)
    {
      TCHAR sz[2];
      wsprintf(sz, TEXT("%d"), g_iShared);
      TextOut(pps->hdc, 0, 0, sz, 1);
    }
    

    Go ahead and run this program. It counts from 0 to 9 over and over again. Since the TimerProc function never lets g_iShared go above 9, the wsprintf is safe from buffer overflow.

    Or is it?

    Run this program. Then use the runas utility to run a second copy of this program under a different user. For extra fun, make one of the users an administrator and another a non-administrator.

    Notice that the counter counts up at double speed. That's to be expected since the counter is shared.

    Okay, now close one of the copies and relaunch it under a debugger. (It's more fun if you let the administrator's copy run free and run the non-administrator's copy run under a debugger.) Let both programs run, then break into the debugger and change the value of the variable g_iShared to something really big, say, 1000000.

    Now, depending on how intrusive your debugger is, you might or might not see the crash. Some debuggers are "helpful" and "unshare" shared memory sections when you change their values from the debugger. Helpful for debugging (maybe), bad for my demonstration (definitely).

    Here's how I did it with the built-in ntsd debugger. I opened a command prompt, which runs as myself (and I am not an administrator). I then used the runas utility to run the scratch program as administrator. It is the administrator's copy of the scratch program that I'm going to cause to crash even though I am just a boring normal non-administrative user.

    From the normal command prompt, I typed "ntsd scratch" to run the scratch program under the debugger. From the debugger prompt, I typed "u TimerProc" to disassemble the TimerProc function, looking for

    01001143 a300300001       mov     [scratch!g_iShared (01003000)],eax
    
    (note: your numbers may differ). I then typed "g 1001143" to instruct the debugger to execute normally until that instruction is reached. When the debugger broke, I typed "r eax=12341234;t" to change the value of the eax register to 0x12341324 and then trace one instruction. That one-instruction trace wrote the out-of-range value into shared memory, and one second later, the administrator version of the program crashed with a buffer overflow.

    What happened?

    Since the memory is shared, all running copies of the scratch program have access to it. ALl I did was use the debugger to run a copy of the scratch program and change the value of the shared memory variable. Since the variable is shared, the value also changes in the administrator's copy of the program, which then causes the wsprintf buffer to overflow, thereby crashing the administrator's copy of the program.

    A denial of service is bad enough, but you can really do fun things if a program keeps anything of value in shared memory. If there is a pointer, you can corrupt the pointer. If there is a string, you can remove the null terminator and cause it to become "impossibly" long, resulting in a potential buffer overflow if somebody copies it without checking the length.

    And if there is a C++ object with a vtable, then you have just hit the mother lode! What you do is redirect the vtable to a bogus vtable (which you construct in the shared memory section), and put a function pointer entry in that vtable that points into some code that you generated (also into the shared memory section) that takes over the machine. (If NX is enabled, then the attack is much harder but still possible in principle.)

    Even if you can't trigger a buffer overflow by messing with variables in shared memory, you can still cause the program to behave erratically. Just scribbling random numbers all over the shared memory section will certainly induce "interesting" behavior in the program under attack.

    Moral of the story: Avoid shared memory sections. Since you can't attach an ACL to the section, anybody who can load your EXE or DLL can modify your variables and cause havoc in another instance of the program that is running at a higher security level.

  • The Old New Thing

    Spammers look stupid when they don't read the blog they spam on

    • 8 Comments

    Yesterday, I got a 419 scam via the contact form. This is a new low for spam stupidity. You'd think people who blog are more likely to be aware of Internet scams because they're reading the news, are clearly more comfortable with technology, and often make fun of news stories about 419 scammers.

    But if you're for real, I have this to say to you: Be careful! I'm sure you are being completely truthful when you say you are the only child of Deposed Corrupt Dictator XYZ, but be aware that there are many unscrupulous people claiming the same thing! So watch out for Vivian, Pierre, Lama, Austine, Martin, Kabilla, Jean Paul, Farek, David, Curimbada, Cynthia, and of course, Dictator Junior. It's very disappointing that there are so many impostors trying to get their hands on the fortune that you stole and that by rights belongs to you.

    Also watch out for XYZ's former Finance Minister, accountant, widow, brother, and hair stylist. They're after the same thing.

    Good luck.

    (I also find it impressive that you were able to embezzle $100 billion U.S. dollars, since your country raises less than $2 billion per year in tax revenues. How you managed to amass that much money so quickly is truly an amazing accomplishment of economics. If you returned even just half of that money back to the country in exchange for amnesty, that would fund your government for a generation. Think about it. Do it for the children.)

    P.S. I was joking about the hair stylist. Please don't hurt him.

  • The Old New Thing

    Never leave focus on a disabled control

    • 16 Comments

    One of the big no-no's in dialog box management is disabling the control that has focus without first moving focus somewhere else. When you do this, the keyboard becomes dead to the dialog box, since disabled windows do not receive input. For users who don't have a mouse (say, because they have physical limitations that confine them to the keyboard), this kills your dialog box.

    (I've seen this happen even in Microsoft software. It's very frustrating.)

    Before you disable a control, check whether it has focus. If so, then move focus somewhere else before you disable it, so that the user isn't left stranded.

    If you don't know which control focus should go to, you can always let the dialog manager decide. The WM_NEXTDLGCTL message once again comes to the rescue.

    void DialogDisableWindow(HWND hdlg, HWND hwndControl)
    {
      if (hwndControl == GetFocus()) {
        SendMessage(hdlg, WM_NEXTDLGCTL, 0, FALSE);
      }
      EnableWindow(hwndControl, FALSE);
    }
    

    (And of course you should never disable the last control on a dialog. That would leave the user completely stranded with no hope of escape!)

    [This was supposed to go out yesterday but the autoblog tool had a bad day and forgot to post this. Sorry.]

  • The Old New Thing

    How to set focus in a dialog box

    • 22 Comments

    Setting focus in a dialog box is more than just calling SetFocus.

    A dialog box maintains the concept of a "default button" (which is always a pushbutton). The default button is typically drawn with a distinctive look (a heavy outline or a different color) and indicates what action the dialog box will take when you hit Enter. Note that this is not the same as the control that has the focus.

    For example, open the Run dialog from the Start menu. Observe that the OK button is the default button; it has a different look from the other buttons. But focus is on the edit control. Your typing goes to the edit control, until you hit Enter; the Enter activates the default button, which is OK.

    As you tab through the dialog, observe what happens to the default button. When the dialog box moves focus to a pushbutton, that pushbutton becomes the new default button. But when the dialog box moves focus to something that isn't a pushbutton at all, the OK button resumes its position as the default button.

    The dialog manager remebers which control was the default button when the dialog was initially created, and when it moves focus to something that isn't a button, it restores that original button as the default button.

    You can ask a dialog box what the default button is by sending it the DM_GETDEFID message; similarly, you can change it with the DM_SETDEFID message.

    (Notice that the return value of the DM_GETDEFID message packs the control ID in the low word and flags in the high word. Another place where expanding dialog control IDs to 32-bit values doesn't buy you anything.)

    As the remarks to the DM_SETDEFID function note, messing directly with the default ID carelessly can lead to odd cases like a dialog box with two default buttons. Fortunately, you rarely need to change the default ID for a dialog.

    A bigger problem is using SetFocus to shove focus around a dialog. If you do this, you are going directly to the window manager, bypassing the dialog manager. This means that you can create "impossible" situations like having focus on a pushbutton without that button being the default!

    To avoid this problem, don't use SetFocus to change focus on a dialog. Instead, use the WM_NEXTDLGCTL message.

    void SetDialogFocus(HWND hdlg, HWND hwndControl)
    {
     SendMessage(hdlg, WM_NEXTDLGCTL, (WPARAM)hwndControl, TRUE);
    }
    

    As the remarks for the WM_NEXTDLGCTL message observe, the DefDlgProc function handles the WM_NEXTDLGCTL message by updating all the internal dialog manager bookkeeping, deciding which button should be default, all that good stuff.

    Now you can update dialog boxes like the professionals, avoiding oddities like having no default button, or worse, multiple default buttons!

  • The Old New Thing

    The company picnic, sponsored by Microsoft

    • 11 Comments

    Robert Scoble blogs from the Microsoft company picnic. Although he writes, "Microsoft holds a picnic for its employees every year," this suggests that the picnic is organized by Microsoft. It isn't.

    The company picnic is run by a professional company picnic company. They also do the Alaska Airlines company picnic, and probably those of many other organizations who don't put their picnic information on the public Internet. Wander down to Mountain Meadows Farm any weekend during the summer and you're likely to see a picnic or other group event under way.

  • The Old New Thing

    What is the DS_CONTROL style for?

    • 16 Comments

    The DS_CONTROL dialog box style indicates that the dialog being created is being used as the child of another dialog rather than being a top-level dialog in and of itself.

    Nested dialogs are hardly a scary sight. You see them in property sheets, for example. Each page on a property sheet is a separate dialog; they all live inside the outer dialog, the property sheet itself. Nested dialogs are sometimes used in the common file dialogs: You can see one in action if you do a Save As from Notepad. The extra options at the bottom come from a nested dialog.

    When you set the DS_CONTROL style on a dialog template (or set the WS_EX_CONTROLPARENT extended style on a regular window), a bunch of new rules kick in.

    First of all, the WS_CAPTION and WS_SYSMENU styles in your dialog template are ignored. Because you're a child window now, not a top-level window, so you don't get a caption bar or a system menu. (The caption bar and system menu come from the outer window.)

    Next, the dialog navigation functions like GetNextDlgTabItem will recurse into windows marked WS_EX_CONTROLPARENT when they inspect the controls on a dialog box (in GetNextDlgTabItem's case, because it is looking for a control to tab to). Without the extended style, the control search treats the embedded dialog box as one giant control rather than as a container for other controls.

    When you create a dialog with the DS_CONTROL style, you invariably use one of the creation functions like CreateDialogParam rather than one of the dialog box functions like DialogBoxParam, because the modal loop is controlled by the outer dialog, not the inner one.

    The recursive behavior is important to know in order to avoid sending the dialog manager into an infinite loop. When you ask GetNextDlgTabItem to look for the previous item, what it does is take the starting control, then walk through the controls on the dialog until it comes back to the starting point, at which point it returns the one it saw before that one. If you forget to mark your dialog as DS_CONTROL, and focus started out in the sub-dialog, then the control enumeration will not recurse into the sub-dialog and consequently the starting point will never be found. The dialog manager will just keep looping, hunting for that starting-point control and never finding it.

    (This problem exists even without DS_CONTROL. If you start out on a disabled or invisible control, then the walk through the controls will again never find the starting point, since disabled and invisible controls are skipped over when tabbing through a dialog.)

  • The Old New Thing

    When should you use a sunken client area?

    • 23 Comments

    The WS_EX_CLIENTEDGE extended window style allows you to create a window whose client area is "sunken". When should you use this style?

    The Guidelines for User Interface Developers and Designers says in the section on the Design of Visual Elements that the sunken border should be used "to define the work area within a window".

    Specifically what this means is that a sunken client area indicates that the window is a "container". So, for example, the Explorer contents pane gets a sunken client area since a folder "contains" its elements. Users expect to be able to manipulate the items inside a container. By contrast, a dialog box is not a container, so it doesn't get a sunken client area.

    At least those were the rules back in 1995. Perhaps the rules have changed since then. (Indeed I wouldn't be surprised if they have.)

  • The Old New Thing

    The evolution of mascara in Windows UI

    • 86 Comments

    The "look" of the Windows user interface has gone through fashion cycles.

    In the beginning, there was Windows 1.0, which looked very flat because screen resolutions were rather low in those days and color depth was practically nonexistent. If you had 16 colors, you were doing pretty good. You couldn't afford to spend very many pixels on fluff like borders, and shadows were out of the question due to lack of color depth.

    The "flat look" continued in Windows 2.0, but Windows 3.0 added a hint of 3D (notice the beveling in the minimize/maximize buttons and in the toolbar buttons on the help window).

    Other people decided that the 3D look was the hot new thing, and libraries sprung up to add 3D shadow and outlining effects to nearly everything. The library CTL3D.DLL started out as just an Excel thing, but it grew in popularity until it became the "standard" way to make your dialog boxes "even more 3D".

    Come Windows 95, even more of the system had a 3D look. Notice the beveling along the inside edge of the panes in the Explorer window. Furthermore, 3D-ness was turned on by default for all programs that marked themselves as "4.0"; i.e., programs that were designed for Windows 95. For programs that wanted to run on older versions of Windows as well, a new dialog style DS_3DLOOK was added, so that they could indicate that they wanted 3D-ization if available.

    And if the 3D provided by Windows 95 by default wasn't enough, you could use CTL3D32.DLL to make your controls even more 3D. By this point, things started getting really ugly. Buttons on dialog boxes had so many heavy black outlines that it started to look like a really bad mascara job.

    Fortunately, like many fashions that get out of hand, people realized that too much 3D is not a good thing. User interfaces got flatter. Instead of using 3D effects and bold outlines to separate items, subtler dividers were used. Divider lines became more subdued and sometimes disappeared entirely.

    Microsoft Office and Microsoft Money were two programs that embraced the "less is more" approach. In this screenshot from Microsoft Money, observe that the beveling is gone. There are no 3D effects. Buttons are flat and unobtrusive. The task pane separates itself from the content pane by a simple gray line and a change in background shade. Even the toolbar has gone flat. Office 2000 also went largely flat, though some 3D effects linger, in the grooves and in the scrollbars (not visible in picture).

    Windows XP jumped on the "flat is good" bandwagon and even got rid of the separator line between the tasks pane and the contents pane. The division is merely implied by the change in color. "Separation through juxtaposition."

    Office XP and Outlook 2003 continue the trend and flatten nearly everything aside from the scrollbar elements. Blocks of color are used to separate elements onscreen, sometimes with the help of simple outlines.

    So now the pendulum of fashion has swung away from 3D back towards flatness. Who knows how long this school of visual expression will hold the upper hand. Will 3D return with a vengeance when people tire of the starkness of the flat look?

  • The Old New Thing

    Disabling the program crash dialog

    • 48 Comments

    If you don't want your program to display the standard crash dialog, you can disable it by setting the SEM_NOGPFAULTERRORBOX flag in the process error mode.

    The simple-minded way is just to do

    SetErrorMode(SEM_NOGPFAULTERRORBOX);
    

    but this overwrites the previous error mode rather than augmenting it. In other words, you inadvertently turned off the other error modes!

    Unfortunately, there is no GetErrorMode function, so you have to do a double-shuffle.

    DWORD dwMode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
    SetErrorMode(dwMode | SEM_NOGPFAULTERRORBOX);
    

    This sets the new error mode (possibly disabling some other error modes that had been set) and saves the previous mode. Then it sets the error mode the right way, adding the SEM_NOGPFAULTERRORBOX flag to the existing error modes.

    Merging with existing error modes is important. For example, the previous error mode may have included SEM_NOALIGNMENTFAULTEXCEPT. If you casually turned that off, then the program would not longer receive automatic alignment fault fixups and will start crashing.

    (But if you read the documentation, you'll see that SEM_NOALIGNMENTFAULTEXCEPT is special: The system won't let you turn it off once it's been turned on. Why? Because too many people were already making this mistake. I remember before this special rule was introduced. Programs were crashing left and right because they didn't do the double-set shuffle above; as a result, they started crashing on alignment faults. So the special rule had to be added. Welcome to the world of compatibility, where it is the operating system's duty to cover for other people's mistakes. Mind you, the design of the SetErrorMode function makes this mistake a very easy one to commit.)

    Note that the error mode is a process-wide setting, not a per-thread setting. This means that manipulating the process error mode is not something you should do lightly, since it may have unintended consequences for other threads (which you might not have control over). For safety's sake, a program should set its error mode when it starts up and not mess with it thereafter.

    Of course, if you disable the crash dialog, then you also miss out on the opportunity to retrieve crash reports collected by Windows Error Reporting so you can see where your program is crashing in the real world.

  • The Old New Thing

    Norway, drunk on success, becomes a country of layabouts

    • 11 Comments

    Norwegians have it so good that they've started getting lazy:

    Before the oil boom, when Norway was mostly poor and largely isolated, the country survived on its hard work and self-reliance, two stalwart Scandinavian virtues.

    Now, with the country still bulging from three decades of oil money, Norway is discovering that sudden wealth does not come without complications: The country's bedrock work ethic is caving in.

    On a similar note, here's an essay on Norwegian 'fellesferie'—vacation time—and its impact on getting anything done at all during the summer.

Page 384 of 431 (4,310 items) «382383384385386»